Example #1
0
File: loop.go Project: hailocab/geo
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
}
Example #2
0
File: cell.go Project: hailocab/geo
// 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}}
	}
}
Example #3
0
File: cap.go Project: hailocab/geo
// 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}
}