Beispiel #1
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("")
	}
}
Beispiel #2
0
func TestCapContains(t *testing.T) {
	tests := []struct {
		c1, c2 Cap
		want   bool
	}{
		{empty, empty, true},
		{full, empty, true},
		{full, full, true},
		{empty, xAxis, false},
		{full, xAxis, true},
		{xAxis, full, false},
		{xAxis, xAxis, true},
		{xAxis, empty, true},
		{hemi, tiny, true},
		{hemi, CapFromCenterAngle(xAxisPt, s1.Angle(math.Pi/4-epsilon)), true},
		{hemi, CapFromCenterAngle(xAxisPt, s1.Angle(math.Pi/4+epsilon)), false},
		{concave, hemi, true},
		{concave, CapFromCenterHeight(Point{concave.center.Mul(-1.0)}, 0.1), false},
	}
	for _, test := range tests {
		if got := test.c1.Contains(test.c2); got != test.want {
			t.Errorf("%v.Contains(%v) = %t; want %t", test.c1, test.c2, got, test.want)
		}
	}
}
Beispiel #3
0
/**
 * A slightly more efficient version of getDistance() where the cross product
 * of the two endpoints has been precomputed. The cross product does not need
 * to be normalized, but should be computed using S2.robustCrossProd() for the
 * most accurate results.
 */
func getDistanceWithCross(x, a, b, aCrossB Point) s1.Angle {
	if !x.IsUnit() || !a.IsUnit() || !b.IsUnit() {
		panic("x, a and b need to be unit length")
	}

	// There are three cases. If X is located in the spherical wedge defined by
	// A, B, and the axis A x B, then the closest point is on the segment AB.
	// Otherwise the closest point is either A or B; the dividing line between
	// these two cases is the great circle passing through (A x B) and the
	// midpoint of AB.

	if simpleCCW(aCrossB, a, x) && simpleCCW(x, b, aCrossB) {
		// The closest point to X lies on the segment AB. We compute the distance
		// to the corresponding great circle. The result is accurate for small
		// distances but not necessarily for large distances (approaching Pi/2).

		sinDist := math.Abs(x.Dot(aCrossB.Vector)) / aCrossB.Norm()
		return s1.Angle(math.Asin(math.Min(1.0, sinDist)))
	}

	// Otherwise, the closest point is either A or B. The cheapest method is
	// just to compute the minimum of the two linear (as opposed to spherical)
	// distances and convert the result to an angle. Again, this method is
	// accurate for small but not large distances (approaching Pi).

	linearDist2 := math.Min(x.Sub(a.Vector).Norm2(), x.Sub(b.Vector).Norm2())
	return s1.Angle(2 * math.Asin(math.Min(1.0, 0.5*math.Sqrt(linearDist2))))
}
Beispiel #4
0
func TestCapExpanded(t *testing.T) {
	cap50 := CapFromCenterAngle(xAxisPt, 50.0*s1.Degree)
	cap51 := CapFromCenterAngle(xAxisPt, 51.0*s1.Degree)

	if !empty.Expanded(s1.Angle(fullHeight)).IsEmpty() {
		t.Error("Expanding empty cap should return an empty cap")
	}
	if !full.Expanded(s1.Angle(fullHeight)).IsFull() {
		t.Error("Expanding a full cap should return an full cap")
	}

	if !cap50.Expanded(0).ApproxEqual(cap50) {
		t.Error("Expanding a cap by 0° should be equal to the original")
	}
	if !cap50.Expanded(1 * s1.Degree).ApproxEqual(cap51) {
		t.Error("Expanding 50° by 1° should equal the 51° cap")
	}

	if cap50.Expanded(129.99 * s1.Degree).IsFull() {
		t.Error("Expanding 50° by 129.99° should not give a full cap")
	}
	if !cap50.Expanded(130.01 * s1.Degree).IsFull() {
		t.Error("Expanding 50° by 130.01° should give a full cap")
	}
}
Beispiel #5
0
// Radius returns the cap's radius.
func (c Cap) Radius() s1.Angle {
	if c.IsEmpty() {
		return s1.Angle(emptyHeight)
	}

	// This could also be computed as acos(1 - height_), but the following
	// formula is much more accurate when the cap height is small. It
	// follows from the relationship h = 1 - cos(r) = 2 sin^2(r/2).
	return s1.Angle(2 * math.Asin(math.Sqrt(0.5*c.height)))
}
Beispiel #6
0
func TestCapAddPoint(t *testing.T) {
	tests := []struct {
		have Cap
		p    Point
		want Cap
	}{
		// Cap plus its center equals itself.
		{xAxis, xAxisPt, xAxis},
		{yAxis, yAxisPt, yAxis},

		// Cap plus opposite point equals full.
		{xAxis, PointFromCoords(-1, 0, 0), full},
		{yAxis, PointFromCoords(0, -1, 0), full},

		// Cap plus orthogonal axis equals half cap.
		{xAxis, PointFromCoords(0, 0, 1), CapFromCenterAngle(xAxisPt, s1.Angle(math.Pi/2.0))},
		{xAxis, PointFromCoords(0, 0, -1), CapFromCenterAngle(xAxisPt, s1.Angle(math.Pi/2.0))},

		// The 45 degree angled hemisphere plus some points.
		{
			hemi,
			PointFromCoords(0, 1, -1),
			CapFromCenterAngle(Point{PointFromCoords(1, 0, 1).Normalize()},
				s1.Angle(120.0)*s1.Degree),
		},
		{
			hemi,
			PointFromCoords(0, -1, -1),
			CapFromCenterAngle(Point{PointFromCoords(1, 0, 1).Normalize()},
				s1.Angle(120.0)*s1.Degree),
		},
		{
			// This angle between this point and the center is acos(-sqrt(2/3))
			hemi,
			PointFromCoords(-1, -1, -1),
			CapFromCenterAngle(Point{PointFromCoords(1, 0, 1).Normalize()},
				s1.Angle(2.5261129449194)),
		},
		{hemi, PointFromCoords(0, 1, 1), hemi},
		{hemi, PointFromCoords(1, 0, 0), hemi},
	}

	for _, test := range tests {
		got := test.have.AddPoint(test.p)
		if !got.ApproxEqual(test.want) {
			t.Errorf("%v.AddPoint(%v) = %v, want %v", test.have, test.p, got, test.want)
		}

		if !got.ContainsPoint(test.p) {
			t.Errorf("%v.AddPoint(%v) did not contain added point", test.have, test.p)
		}
	}
}
Beispiel #7
0
func rectFromDegrees(latLo, lngLo, latHi, lngHi float64) Rect {
	// Convenience method to construct a rectangle. This method is
	// intentionally *not* in the S2LatLngRect interface because the
	// argument order is ambiguous, but is fine for the test.
	return Rect{
		Lat: r1.Interval{
			Lo: (s1.Angle(latLo) * s1.Degree).Radians(),
			Hi: (s1.Angle(latHi) * s1.Degree).Radians(),
		},
		Lng: s1.IntervalFromEndpoints(
			(s1.Angle(lngLo) * s1.Degree).Radians(),
			(s1.Angle(lngHi) * s1.Degree).Radians(),
		),
	}
}
Beispiel #8
0
func TestCapAddCap(t *testing.T) {
	tests := []struct {
		have  Cap
		other Cap
		want  Cap
	}{
		// Identity cases.
		{empty, empty, empty},
		{full, full, full},

		// Anything plus empty equals itself.
		{full, empty, full},
		{empty, full, full},
		{xAxis, empty, xAxis},
		{empty, xAxis, xAxis},
		{yAxis, empty, yAxis},
		{empty, yAxis, yAxis},

		// Two halves make a whole.
		{xAxis, xComp, full},

		// Two zero-height orthogonal axis caps make a half-cap.
		{xAxis, yAxis, CapFromCenterAngle(xAxisPt, s1.Angle(math.Pi/2.0))},
	}

	for _, test := range tests {
		got := test.have.AddCap(test.other)
		if !got.ApproxEqual(test.want) {
			t.Errorf("%v.AddCap(%v) = %v, want %v", test.have, test.other, got, test.want)
		}
	}
}
Beispiel #9
0
// Distance returns the angle between two LatLngs.
func (ll LatLng) Distance(ll2 LatLng) s1.Angle {
	// Haversine formula, as used in C++ S2LatLng::GetDistance.
	lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians()
	lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians()
	dlat := math.Sin(0.5 * (lat2 - lat1))
	dlng := math.Sin(0.5 * (lng2 - lng1))
	x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2)
	return s1.Angle(2 * math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x))))
}
Beispiel #10
0
/**
 * Returns the shortest distance from a point P to this loop, given as the
 * angle formed between P, the origin and the nearest point on the loop to P.
 * This angle in radians is equivalent to the arclength along the unit sphere.
 */
func (l *Loop) GetDistance(p Point) s1.Angle {
	normalized := Point{p.Normalize()}

	// The furthest point from p on the sphere is its antipode, which is an
	// angle of PI radians. This is an upper bound on the angle.
	minDistance := math.Pi
	for i := 0; i < l.NumVertices(); i++ {
		minDistance = math.Min(minDistance, getDistance(normalized, l.Vertex(i), l.Vertex(i+1)).Radians())
	}
	return s1.Angle(minDistance)
}
Beispiel #11
0
func TestRadiusToHeight(t *testing.T) {
	tests := []struct {
		got  s1.Angle
		want float64
	}{
		// Above/below boundary checks.
		{s1.Angle(-0.5), emptyHeight},
		{s1.Angle(0), 0},
		{s1.Angle(math.Pi), fullHeight},
		{s1.Angle(2 * math.Pi), fullHeight},
		// Degree tests.
		{-7.0 * s1.Degree, emptyHeight},
		{-0.0 * s1.Degree, 0},
		{0.0 * s1.Degree, 0},
		{12.0 * s1.Degree, 0.02185239926619},
		{30.0 * s1.Degree, 0.13397459621556},
		{45.0 * s1.Degree, 0.29289321881345},
		{90.0 * s1.Degree, 1.0},
		{179.99 * s1.Degree, 1.99999998476912},
		{180.0 * s1.Degree, fullHeight},
		{270.0 * s1.Degree, fullHeight},
		// Radians tests.
		{-1.0 * s1.Radian, emptyHeight},
		{-0.0 * s1.Radian, 0},
		{0.0 * s1.Radian, 0},
		{1.0 * s1.Radian, 0.45969769413186},
		{math.Pi / 2.0 * s1.Radian, 1.0},
		{2.0 * s1.Radian, 1.41614683654714},
		{3.0 * s1.Radian, 1.98999249660044},
		{math.Pi * s1.Radian, fullHeight},
		{4.0 * s1.Radian, fullHeight},
	}
	for _, test := range tests {
		// float64Eq comes from s2latlng_test.go
		if got := radiusToHeight(test.got); !float64Eq(got, test.want) {
			t.Errorf("radiusToHeight(%v) = %v; want %v", test.got, got, test.want)
		}
	}
}
Beispiel #12
0
func TestCapGetRectBounds(t *testing.T) {
	const epsilon = 1e-13
	var tests = []struct {
		desc     string
		have     Cap
		latLoDeg float64
		latHiDeg float64
		lngLoDeg float64
		lngHiDeg float64
		isFull   bool
	}{
		{
			"Cap that includes South Pole.",
			CapFromCenterAngle(PointFromLatLng(LatLngFromDegrees(-45, 57)), s1.Degree*50),
			-90, 5, -180, 180, true,
		},
		{
			"Cap that is tangent to the North Pole.",
			CapFromCenterAngle(PointFromCoords(1, 0, 1), s1.Radian*(math.Pi/4.0+1e-16)),
			0, 90, -180, 180, true,
		},
		{
			"Cap that at 45 degree center that goes from equator to the pole.",
			CapFromCenterAngle(PointFromCoords(1, 0, 1), s1.Degree*(45+5e-15)),
			0, 90, -180, 180, true,
		},
		{
			"The eastern hemisphere.",
			CapFromCenterAngle(PointFromCoords(0, 1, 0), s1.Radian*(math.Pi/2+2e-16)),
			-90, 90, -180, 180, true,
		},
		{
			"A cap centered on the equator.",
			CapFromCenterAngle(PointFromLatLng(LatLngFromDegrees(0, 50)), s1.Degree*20),
			-20, 20, 30, 70, false,
		},
		{
			"A cap centered on the North Pole.",
			CapFromCenterAngle(PointFromLatLng(LatLngFromDegrees(90, 123)), s1.Degree*10),
			80, 90, -180, 180, true,
		},
	}

	for _, test := range tests {
		r := test.have.RectBound()
		if !float64Near(s1.Angle(r.Lat.Lo).Degrees(), test.latLoDeg, epsilon) {
			t.Errorf("%s: %v.RectBound(), Lat.Lo not close enough, got %0.20f, want %0.20f",
				test.desc, test.have, s1.Angle(r.Lat.Lo).Degrees(), test.latLoDeg)
		}
		if !float64Near(s1.Angle(r.Lat.Hi).Degrees(), test.latHiDeg, epsilon) {
			t.Errorf("%s: %v.RectBound(), Lat.Hi not close enough, got %0.20f, want %0.20f",
				test.desc, test.have, s1.Angle(r.Lat.Hi).Degrees(), test.latHiDeg)
		}
		if !float64Near(s1.Angle(r.Lng.Lo).Degrees(), test.lngLoDeg, epsilon) {
			t.Errorf("%s: %v.RectBound(), Lng.Lo not close enough, got %0.20f, want %0.20f",
				test.desc, test.have, s1.Angle(r.Lng.Lo).Degrees(), test.lngLoDeg)
		}
		if !float64Near(s1.Angle(r.Lng.Hi).Degrees(), test.lngHiDeg, epsilon) {
			t.Errorf("%s: %v.RectBound(), Lng.Hi not close enough, got %0.20f, want %0.20f",
				test.desc, test.have, s1.Angle(r.Lng.Hi).Degrees(), test.lngHiDeg)
		}
		if got := r.Lng.IsFull(); got != test.isFull {
			t.Errorf("%s: RectBound(%v).isFull() = %t, want %t", test.desc, test.have, got, test.isFull)
		}
	}

	// Empty and full caps.
	if !EmptyCap().RectBound().IsEmpty() {
		t.Errorf("RectBound() on EmptyCap should be empty.")
	}

	if !FullCap().RectBound().IsFull() {
		t.Errorf("RectBound() on FullCap should be full.")
	}
}
Beispiel #13
0
var (
	empty      = EmptyCap()
	full       = FullCap()
	defaultCap = EmptyCap()

	xAxisPt = PointFromCoords(1, 0, 0)
	yAxisPt = PointFromCoords(0, 1, 0)

	xAxis = CapFromPoint(xAxisPt)
	yAxis = CapFromPoint(yAxisPt)
	xComp = xAxis.Complement()

	hemi    = CapFromCenterHeight(Point{PointFromCoords(1, 0, 1).Normalize()}, 1)
	concave = CapFromCenterAngle(PointFromLatLng(LatLngFromDegrees(80, 10)),
		s1.Angle(150.0)*s1.Degree)
	tiny = CapFromCenterAngle(Point{PointFromCoords(1, 2, 3).Normalize()},
		s1.Angle(tinyRad))
)

func TestCapBasicEmptyFullValid(t *testing.T) {
	tests := []struct {
		got                Cap
		empty, full, valid bool
	}{
		{Cap{}, false, false, false},

		{empty, true, false, true},
		{empty.Complement(), false, true, true},
		{full, false, true, true},
		{full.Complement(), true, false, true},
Beispiel #14
0
func longitude(p Point) s1.Angle {
	return s1.Angle(math.Atan2(p.Y, p.X))
}
Beispiel #15
0
func latitude(p Point) s1.Angle {
	return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y)))
}
Beispiel #16
0
// LatLngFromDegrees returns a LatLng for the coordinates given in degrees.
func LatLngFromDegrees(lat, lng float64) LatLng {
	return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree}
}
Beispiel #17
0
// kmToAngle converts a distance on the Earth's surface to an angle.
func kmToAngle(km float64) s1.Angle {
	// The Earth's mean radius in kilometers (according to NASA).
	const earthRadiusKm = 6371.01
	return s1.Angle(km / earthRadiusKm)
}
Beispiel #18
0
// Angle returns the angle between v and ov.
func (v Vector) Angle(ov Vector) s1.Angle {
	return s1.Angle(math.Atan2(v.Cross(ov).Norm(), v.Dot(ov))) * s1.Radian
}