diff --git a/s2/contains_point_query_test.go b/s2/contains_point_query_test.go index 0a3d82d..c0143e2 100644 --- a/s2/contains_point_query_test.go +++ b/s2/contains_point_query_test.go @@ -138,7 +138,7 @@ func TestContainsPointQueryVertexModelClosed(t *testing.T) { func TestContainsPointQueryContainingShapes(t *testing.T) { const numVerticesPerLoop = 10 - maxLoopRadius := kmToAngle(10) + maxLoopRadius := testRadiusMedium centerCap := CapFromCenterAngle(randomPoint(), maxLoopRadius) index := NewShapeIndex() diff --git a/s2/edge_distances_test.go b/s2/edge_distances_test.go index a943c40..41b1980 100644 --- a/s2/edge_distances_test.go +++ b/s2/edge_distances_test.go @@ -867,7 +867,7 @@ func TestEdgeDistancesEdgePairMaxDistance(t *testing.T) { func TestEdgeDistancesPointToLeft(t *testing.T) { a := PointFromLatLng(LatLngFromDegrees(0, 0)) b := PointFromLatLng(LatLngFromDegrees(0, 5)) // east - dist := kmToAngle(10 / 1000.0) + dist := testRadiusSmall c := PointToLeft(a, b, dist) if got := a.Distance(c).Radians(); !float64Near(got, dist.Radians(), epsilon) { @@ -882,7 +882,7 @@ func TestEdgeDistancesPointToLeft(t *testing.T) { func TestEdgeDistancesPointToRight(t *testing.T) { a := PointFromLatLng(LatLngFromDegrees(0, 0)) b := PointFromLatLng(LatLngFromDegrees(0, 5)) // east - dist := kmToAngle(10 / 1000.0) + dist := testRadiusSmall c := PointToRight(a, b, dist) if got := a.Distance(c).Radians(); !float64Near(got, dist.Radians(), epsilon) { diff --git a/s2/edge_query_closest_test.go b/s2/edge_query_closest_test.go index a376290..e0752bd 100644 --- a/s2/edge_query_closest_test.go +++ b/s2/edge_query_closest_test.go @@ -238,7 +238,7 @@ func BenchmarkEdgeQueryFindEdges(b *testing.B) { targetType: queryTypePoint, numTargetEdges: 0, chooseTargetFromIndex: false, - radiusKm: 1000, + radius: testRadiusLarge, maxDistanceFraction: -1, maxErrorFraction: -1, targetRadiusFraction: 0.0, @@ -254,7 +254,7 @@ func BenchmarkEdgeQueryFindEdges(b *testing.B) { targetType: queryTypePoint, numTargetEdges: 0, chooseTargetFromIndex: false, - radiusKm: 1000, + radius: testRadiusLarge, maxDistanceFraction: -1, maxErrorFraction: -1, targetRadiusFraction: 0.0, @@ -271,7 +271,7 @@ func BenchmarkEdgeQueryFindEdges(b *testing.B) { targetType: queryTypePoint, numTargetEdges: 0, chooseTargetFromIndex: false, - radiusKm: 1000, + radius: testRadiusLarge, maxDistanceFraction: -1, maxErrorFraction: 0.1, targetRadiusFraction: 0.0, @@ -288,7 +288,7 @@ func BenchmarkEdgeQueryFindEdges(b *testing.B) { targetType: queryTypePoint, numTargetEdges: 0, chooseTargetFromIndex: false, - radiusKm: 1000, + radius: testRadiusLarge, maxDistanceFraction: -1, maxErrorFraction: 0.01, targetRadiusFraction: 0.0, @@ -302,7 +302,7 @@ func BenchmarkEdgeQueryFindEdges(b *testing.B) { targetType: queryTypeEdge, numTargetEdges: 0, chooseTargetFromIndex: false, - radiusKm: 1000, + radius: testRadiusLarge, maxDistanceFraction: -1, maxErrorFraction: -1, targetRadiusFraction: -1.0, @@ -316,7 +316,7 @@ func BenchmarkEdgeQueryFindEdges(b *testing.B) { targetType: queryTypeCell, numTargetEdges: 0, chooseTargetFromIndex: false, - radiusKm: 1000, + radius: testRadiusLarge, maxDistanceFraction: -1, maxErrorFraction: -1, targetRadiusFraction: -1.0, diff --git a/s2/edge_query_test.go b/s2/edge_query_test.go index 66f82a5..1abad20 100644 --- a/s2/edge_query_test.go +++ b/s2/edge_query_test.go @@ -222,8 +222,8 @@ const edgeQueryTestNumIndexes = 50 const edgeQueryTestNumEdges = 100 const edgeQueryTestNumQueries = 200 -// The approximate radius of Cap from which query edges are chosen. -var testCapRadius = kmToAngle(10) +// testCapRadius is the angular radius of the Cap from which query edges are chosen. +var testCapRadius = testRadiusMedium /* // testEdgeQueryWithGenerator is used to perform high volume random testing on EdqeQuery @@ -340,7 +340,7 @@ func benchmarkEdgeQueryFindClosest(b *testing.B, bmOpts *edgeQueryBenchmarkOptio index := NewShapeIndex() opts := NewClosestEdgeQueryOptions().MaxResults(1).IncludeInteriors(bmOpts.includeInteriors) - radius := kmToAngle(bmOpts.radiusKm.Radians()) + radius := bmOpts.radius if bmOpts.maxDistanceFraction > 0 { opts.DistanceLimit(s1.ChordAngleFromAngle(s1.Angle(bmOpts.maxDistanceFraction) * radius)) } @@ -389,7 +389,7 @@ type edgeQueryBenchmarkOptions struct { targetType queryTargetType numTargetEdges int chooseTargetFromIndex bool - radiusKm s1.Angle + radius s1.Angle maxDistanceFraction float64 maxErrorFraction float64 targetRadiusFraction float64 @@ -402,7 +402,7 @@ type edgeQueryBenchmarkOptions struct { // // Approximately numIndexEdges will be generated by the requested generator and // inserted. The geometry is generated within a Cap of the radius specified -// by radiusKm (the index radius). Parameters with fraction in their +// by radius (the index radius). Parameters with fraction in their // names are expressed as a fraction of this radius. // // Also generates a set of target geometries for the query, based on the @@ -436,7 +436,7 @@ func generateEdgeQueryWithTargets(opts *edgeQueryBenchmarkOptions, query *EdgeQu // Replace with r := rand.New(rand.NewSource(opts.randomSeed)) and pass through. r := rand.New(rand.NewSource(opts.randomSeed)) opts.randomSeed++ - indexCap := CapFromCenterAngle(randomPoint(r), opts.radiusKm) + indexCap := CapFromCenterAngle(randomPoint(r), opts.radius) query.Reset() queryIndex.Reset() @@ -452,10 +452,10 @@ func generateEdgeQueryWithTargets(opts *edgeQueryBenchmarkOptions, query *EdgeQu } for i := 0; i < numTargets; i++ { - targetDist := fractionToRadius(opts.centerSeparationFraction, opts.radiusKm.Radians()) + targetDist := fractionToRadius(opts.centerSeparationFraction, opts.radius) targetCap := CapFromCenterAngle( sampleBoundaryFromCap(CapFromCenterAngle(indexCap.Center(), targetDist)), - fractionToRadius(opts.targetRadiusFraction, opts.radiusKm.Radians())) + fractionToRadius(opts.targetRadiusFraction, opts.radius)) switch opts.targetType { case queryTypePoint: @@ -535,9 +535,9 @@ func sampleCellFromIndex(index *ShapeIndex) CellID { return iter.CellID() } -func fractionToRadius(fraction, radiusKm float64) s1.Angle { +func fractionToRadius(fraction float64, radius s1.Angle) s1.Angle { if fraction < 0 { fraction = -randomFloat64() * fraction } - return s1.Angle(fraction) * kmToAngle(radiusKm) + return s1.Angle(fraction) * radius } diff --git a/s2/loop_test.go b/s2/loop_test.go index 7abfa21..8d30d65 100644 --- a/s2/loop_test.go +++ b/s2/loop_test.go @@ -1864,7 +1864,7 @@ func BenchmarkLoopContainsPoint(b *testing.B) { b.StopTimer() loops := make([]*Loop, numLoopSamples) for i := range numLoopSamples { - loops[i] = RegularLoop(randomPoint(), kmToAngle(10.0), vertices) + loops[i] = RegularLoop(randomPoint(), testRadiusMedium, vertices) } queries := make([][]Point, numLoopSamples) diff --git a/s2/point_test.go b/s2/point_test.go index 5da788a..484df20 100644 --- a/s2/point_test.go +++ b/s2/point_test.go @@ -45,8 +45,11 @@ func TestOriginPoint(t *testing.T) { } // Check that the origin is not too close to either pole. - if dist := math.Acos(OriginPoint().Z) * earthRadiusKm; dist <= 50 { - t.Errorf("Origin point is to close to the North Pole. Got %v, want >= 50km", dist) + // The threshold of 0.45 degrees (~50km on Earth-scale sphere) ensures the origin + // is far enough from poles for numerical stability in tests. + minAngleFromPole := 0.45 * s1.Degree + if angle := s1.Angle(math.Acos(OriginPoint().Z)); angle <= minAngleFromPole { + t.Errorf("Origin point is too close to the North Pole. Got %v, want > %v", angle, minAngleFromPole) } } diff --git a/s2/pointcompression_test.go b/s2/pointcompression_test.go index 3a4707b..ca7a2b4 100644 --- a/s2/pointcompression_test.go +++ b/s2/pointcompression_test.go @@ -33,9 +33,9 @@ func TestFacesIterator(t *testing.T) { } func makeSnappedPoints(nvertices int, level int) []Point { - const radiusKM = 0.1 + radius := testRadiusSmall center := PointFromCoords(1, 1, 1) - pts := regularPoints(center, kmToAngle(radiusKM), nvertices) + pts := regularPoints(center, radius, nvertices) for i, pt := range pts { id := CellFromPoint(pt).ID() if level < id.Level() { diff --git a/s2/polygon_test.go b/s2/polygon_test.go index 52511a8..a18a84b 100644 --- a/s2/polygon_test.go +++ b/s2/polygon_test.go @@ -1144,7 +1144,7 @@ func TestPolygonInvert(t *testing.T) { origin := PointFromLatLng(LatLngFromDegrees(0, 0)) pt := PointFromLatLng(LatLngFromDegrees(30, 30)) p := PolygonFromLoops([]*Loop{ - RegularLoop(origin, 1000/earthRadiusKm, 100), + RegularLoop(origin, testRadiusLarge, 100), }) if p.ContainsPoint(pt) { diff --git a/s2/predicates_test.go b/s2/predicates_test.go index 3121242..0f27f4f 100644 --- a/s2/predicates_test.go +++ b/s2/predicates_test.go @@ -305,7 +305,10 @@ func TestPredicatesStableSignFailureRate(t *testing.T) { // that are as collinear as possible and spaced the given distance apart // by counting up the times it returns Indeterminate. failureCount := 0 - m := math.Tan(spacing / earthRadiusKm) + // The spacing factor determines how far apart the near-collinear test points are. + // Original used 1km spacing on Earth-scale sphere (~0.009 degrees). + spacingAngle := 0.009 * s1.Degree + m := math.Tan(spacingAngle.Radians() * spacing) for range iters { f := randomFrame() a := f.col(0) diff --git a/s2/regioncoverer_test.go b/s2/regioncoverer_test.go index a7c86e5..80d544f 100644 --- a/s2/regioncoverer_test.go +++ b/s2/regioncoverer_test.go @@ -337,7 +337,7 @@ func BenchmarkRegionCovererCoveringLoop(b *testing.B) { size := int(math.Pow(2.0, float64(n))) regions := make([]Region, numCoveringBMRegions) for i := range numCoveringBMRegions { - regions[i] = RegularLoop(randomPoint(), kmToAngle(10.0), size) + regions[i] = RegularLoop(randomPoint(), testRadiusMedium, size) } return regions }) diff --git a/s2/s2_test.go b/s2/s2_test.go index 6445f54..f75f114 100644 --- a/s2/s2_test.go +++ b/s2/s2_test.go @@ -74,13 +74,19 @@ func float64Near(x, y, ε float64) bool { return math.Abs(x-y) <= ε } -// The Earth's mean radius in kilometers (according to NASA). -const earthRadiusKm = 6371.01 +const ( + // testRadiusSmall is approximately 1km on a unit sphere scaled to Earth size. + // Used for tests needing very small distances (e.g., point offset tests). + testRadiusSmall = 9e-5 * s1.Degree // ~10 meters -// kmToAngle converts a distance on the Earth's surface to an angle. -func kmToAngle(km float64) s1.Angle { - return s1.Angle(km / earthRadiusKm) -} + // testRadiusMedium is approximately 10km on a unit sphere scaled to Earth size. + // Used for most geometry tests (containment, edge queries, etc.). + testRadiusMedium = 0.09 * s1.Degree // ~10 km + + // testRadiusLarge is approximately 1000km on a unit sphere scaled to Earth size. + // Used for tests needing larger regions (polygon inversion, etc.). + testRadiusLarge = 9 * s1.Degree // ~1000 km +) // randomBits returns a 64-bit random unsigned integer whose lowest "num" are random, and // whose other bits are zero. diff --git a/s2/s2_test_test.go b/s2/s2_test_test.go index 4598fd9..492bb62 100644 --- a/s2/s2_test_test.go +++ b/s2/s2_test_test.go @@ -22,25 +22,6 @@ import ( "github.com/golang/geo/s1" ) -func TestKmToAngle(t *testing.T) { - tests := []struct { - have float64 - want s1.Angle - }{ - {0.0, 0.0}, - {1.0, 0.00015696098420815537 * s1.Radian}, - {earthRadiusKm, 1.0 * s1.Radian}, - {-1.0, -0.00015696098420815537 * s1.Radian}, - {-10000.0, -1.5696098420815536300 * s1.Radian}, - {1e9, 156960.984208155363007 * s1.Radian}, - } - for _, test := range tests { - if got := kmToAngle(test.have); !float64Eq(float64(got), float64(test.want)) { - t.Errorf("kmToAngle(%f) = %0.20f, want %0.20f", test.have, got, test.want) - } - } -} - func numVerticesAtLevel(level int) int { // Sanity / overflow check if level < 0 || level > 14 {