func (l *Loop) initBound() { // The bounding rectangle of a loop is not necessarily the same as the // bounding rectangle of its vertices. First, the loop may wrap entirely // around the sphere (e.g. a loop that defines two revolutions of a // candy-cane stripe). Second, the loop may include one or both poles. // Note that a small clockwise loop near the equator contains both poles. bounder := NewRectBounder() for i := 0; i <= l.NumVertices(); i++ { bounder.AddPoint(l.Vertex(i)) } b := bounder.GetBound() // Note that we need to initialize bound with a temporary value since // contains() does a bounding rectangle check before doing anything else. l.bound = FullRect() if l.ContainsPoint(PointFromCoordsRaw(0, 0, 1)) { b = Rect{r1.IntervalFromPointPair(b.Lat.Lo, math.Pi/2), s1.FullInterval()} } // If a loop contains the south pole, then either it wraps entirely // around the sphere (full longitude range), or it also contains the // north pole in which case b.lng().isFull() due to the test above. if b.Lng.IsFull() && l.ContainsPoint(PointFromCoordsRaw(0, 0, -1)) { b = Rect{r1.IntervalFromPointPair(-math.Pi/2, b.Lat.Hi), b.Lng} } l.bound = b }
// RectBound returns a bounding latitude-longitude rectangle that contains // the region. The bounds are not guaranteed to be tight. 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.IntervalFromPointPair(c.latitude(i, j), c.latitude(1-i, 1-j)) lat = lat.Expanded(MAX_ERROR).Intersection(validRectLatRange) if lat.Lo == validRectLatRange.Lo || lat.Hi == validRectLatRange.Hi { return Rect{lat, s1.FullInterval()} } lng := s1.IntervalFromPointPair(c.longitude(i, 1-j), c.longitude(1-i, j)) return Rect{lat, lng.Expanded(MAX_ERROR)} } switch c.face { case 0: return Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}} case 1: return Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}} case 2: return Rect{r1.Interval{POLE_MIN_LAT, math.Pi / 2}, s1.Interval{-math.Pi, math.Pi}} case 3: return Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}} case 4: return Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}} default: return Rect{r1.Interval{-math.Pi / 2, -POLE_MIN_LAT}, s1.Interval{-math.Pi, math.Pi}} } }
// RectBound returns a bounding latitude-longitude rectangle. // The bounds are not guaranteed to be tight. func (c Cap) RectBound() Rect { if c.IsEmpty() { return EmptyRect() } capAngle := c.Radius().Radians() allLongitudes := false lat := r1.Interval{ Lo: latitude(c.center).Radians() - capAngle, Hi: latitude(c.center).Radians() + capAngle, } lng := s1.FullInterval() // Check whether cap includes the south pole. if lat.Lo <= -math.Pi/2 { lat.Lo = -math.Pi / 2 allLongitudes = true } // Check whether cap includes the north pole. if lat.Hi >= math.Pi/2 { lat.Hi = math.Pi / 2 allLongitudes = true } if !allLongitudes { // Compute the range of longitudes covered by the cap. We use the law // of sines for spherical triangles. Consider the triangle ABC where // A is the north pole, B is the center of the cap, and C is the point // of tangency between the cap boundary and a line of longitude. Then // C is a right angle, and letting a,b,c denote the sides opposite A,B,C, // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c). // Here "a" is the cap angle, and "c" is the colatitude (90 degrees // minus the latitude). This formula also works for negative latitudes. // // The formula for sin(a) follows from the relationship h = 1 - cos(a). sinA := math.Sqrt(c.height * (2 - c.height)) sinC := math.Cos(latitude(c.center).Radians()) if sinA <= sinC { angleA := math.Asin(sinA / sinC) lng.Lo = math.Remainder(longitude(c.center).Radians()-angleA, math.Pi*2) lng.Hi = math.Remainder(longitude(c.center).Radians()+angleA, math.Pi*2) } } return Rect{lat, lng} }