in s2/cell.go [281:360]
func (c Cell) RectBound() Rect {
if c.level > 0 {
// Except for cells at level 0, the latitude and longitude extremes are
// attained at the vertices. Furthermore, the latitude range is
// determined by one pair of diagonally opposite vertices and the
// longitude range is determined by the other pair.
//
// We first determine which corner (i,j) of the cell has the largest
// absolute latitude. To maximize latitude, we want to find the point in
// the cell that has the largest absolute z-coordinate and the smallest
// absolute x- and y-coordinates. To do this we look at each coordinate
// (u and v), and determine whether we want to minimize or maximize that
// coordinate based on the axis direction and the cell's (u,v) quadrant.
u := c.uv.X.Lo + c.uv.X.Hi
v := c.uv.Y.Lo + c.uv.Y.Hi
var i, j int
if uAxis(int(c.face)).Z == 0 {
if u < 0 {
i = 1
}
} else if u > 0 {
i = 1
}
if vAxis(int(c.face)).Z == 0 {
if v < 0 {
j = 1
}
} else if v > 0 {
j = 1
}
lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))
// We grow the bounds slightly to make sure that the bounding rectangle
// contains LatLngFromPoint(P) for any point P inside the loop L defined by the
// four *normalized* vertices. Note that normalization of a vector can
// change its direction by up to 0.5 * dblEpsilon radians, and it is not
// enough just to add Normalize calls to the code above because the
// latitude/longitude ranges are not necessarily determined by diagonally
// opposite vertex pairs after normalization.
//
// We would like to bound the amount by which the latitude/longitude of a
// contained point P can exceed the bounds computed above. In the case of
// longitude, the normalization error can change the direction of rounding
// leading to a maximum difference in longitude of 2 * dblEpsilon. In
// the case of latitude, the normalization error can shift the latitude by
// up to 0.5 * dblEpsilon and the other sources of error can cause the
// two latitudes to differ by up to another 1.5 * dblEpsilon, which also
// leads to a maximum difference of 2 * dblEpsilon.
return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
}
// The 4 cells around the equator extend to +/-45 degrees latitude at the
// midpoints of their top and bottom edges. The two cells covering the
// poles extend down to +/-35.26 degrees at their vertices. The maximum
// error in this calculation is 0.5 * dblEpsilon.
var bound Rect
switch c.face {
case 0:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
case 1:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
case 2:
bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
case 3:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
case 4:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
default:
bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
}
// Finally, we expand the bound to account for the error when a point P is
// converted to an LatLng to test for containment. (The bound should be
// large enough so that it contains the computed LatLng of any contained
// point, not just the infinite-precision version.) We don't need to expand
// longitude because longitude is calculated via a single call to math.Atan2,
// which is guaranteed to be semi-monotonic.
return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
}