Пример #1
0
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
}
Пример #2
0
func TestLoopBounds(t *testing.T) {
	if !(candyCane.RectBound().Lng.IsFull()) {
		t.Fatal("ttttt")
	}
	if !(s1.Angle(candyCane.RectBound().Lat.Lo).Degrees() < -20) {
		t.Fatal("")
	}
	if !(s1.Angle(candyCane.RectBound().Lat.Hi).Degrees() > 10) {
		t.Fatal("")
	}
	if !(smallNeCw.RectBound().IsFull()) {
		t.Fatal("")
	}
	if arctic80.RectBound() != RectFromLatLngLoHi(LatLngFromDegrees(80, -180), LatLngFromDegrees(90, 180)) {
		t.Fatal("")
	}
	if antarctic80.RectBound() != RectFromLatLngLoHi(LatLngFromDegrees(-90, -180), LatLngFromDegrees(-80, 180)) {
		t.Fatal("")
	}

	arctic80.Invert()
	// The highest latitude of each edge is attained at its midpoint.
	mid := Point{arctic80.Vertex(0).Add(arctic80.Vertex(1).Vector).Mul(0.5)}
	if math.Abs(s1.Angle(arctic80.RectBound().Lat.Hi).Radians()-LatLngFromPoint(mid).Lat.Radians()) > EPSILON {
		t.Fatal("")
	}
	arctic80.Invert()

	if !(southHemi.RectBound().Lng.IsFull()) {
		t.Fatal("")
	}
	if !(southHemi.RectBound().Lat == r1.IntervalFromPointPair(-math.Pi/2, 0)) {
		t.Fatal("")
	}
}
Пример #3
0
// 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}}
	}
}
Пример #4
0
func (rb *RectBounder) AddPoint(b Point) {
	bLatLng := LatLngFromPoint(b)

	if rb.bound.IsEmpty() {
		rb.bound = rb.bound.AddPoint(bLatLng)
	} else {
		// We can't just call bound.addPoint(bLatLng) here, since we need to
		// ensure that all the longitudes between "a" and "b" are included.
		rb.bound = rb.bound.Union(RectFromLatLngPointPair(rb.aLatLng, bLatLng))

		// Check whether the min/max latitude occurs in the edge interior.
		// We find the normal to the plane containing AB, and then a vector
		// "dir" in this plane that also passes through the equator. We use
		// RobustCrossProd to ensure that the edge normal is accurate even
		// when the two points are very close together.
		aCrossB := rb.a.PointCross(b)
		dir := aCrossB.Cross(PointFromCoordsRaw(0, 0, 1).Vector)
		da := dir.Dot(rb.a.Vector)
		db := dir.Dot(b.Vector)

		if da*db < 0 {
			// Minimum/maximum latitude occurs in the edge interior. This affects
			// the latitude bounds but not the longitude bounds.
			absLat := math.Acos(math.Abs(aCrossB.Z / aCrossB.Norm()))
			lat := rb.bound.Lat
			if da < 0 {
				// It's possible that absLat < lat.lo() due to numerical errors.
				lat = r1.IntervalFromPointPair(lat.Lo, math.Max(absLat, rb.bound.Lat.Hi))
			} else {
				lat = r1.IntervalFromPointPair(math.Min(-absLat, rb.bound.Lat.Lo), lat.Hi)
			}
			rb.bound = Rect{lat, rb.bound.Lng}
		}
	}

	rb.a = b
	rb.aLatLng = bLatLng
}