public void Test_S2RegionTermIndexer_MaxLevelSetLoosely() { // Test that correct terms are generated even when (max_level - min_level) // is not a multiple of level_mod. var options = new S2RegionTermIndexer.Options(); options.MinLevel = (1); options.LevelMod = (2); options.MaxLevel = (19); var indexer1 = new S2RegionTermIndexer(options); options.MaxLevel = (20); var indexer2 = new S2RegionTermIndexer(options); S2Point point = S2Testing.RandomPoint(); Assert.Equal(indexer1.GetIndexTerms(point, ""), indexer2.GetIndexTerms(point, "")); Assert.Equal(indexer1.GetQueryTerms(point, ""), indexer2.GetQueryTerms(point, "")); S2Cap cap = S2Testing.GetRandomCap(0.0, 1.0); // Area range. Assert.Equal(indexer1.GetIndexTerms(cap, ""), indexer2.GetIndexTerms(cap, "")); Assert.Equal(indexer1.GetQueryTerms(cap, ""), indexer2.GetQueryTerms(cap, "")); }
public void Test_S2PointVectorShape_ConstructionAndAccess() { const int kNumPoints = 100; S2Point[] points = new S2Point[kNumPoints]; S2Testing.Random.Reset(S2Testing.Random.RandomSeed); for (int i = 0; i < kNumPoints; ++i) { points[i] = S2Testing.RandomPoint(); } S2PointVectorShape shape = new(points); Assert.Equal(kNumPoints, shape.NumEdges()); Assert.Equal(kNumPoints, shape.NumChains()); Assert.Equal(0, shape.Dimension()); Assert.False(shape.IsEmpty()); Assert.False(shape.IsFull()); for (int i = 0; i < kNumPoints; ++i) { Assert.Equal(i, shape.GetChain(i).Start); Assert.Equal(1, shape.GetChain(i).Length); var edge = shape.GetEdge(i); S2Point pt = points[i]; Assert.Equal(pt, edge.V0); Assert.Equal(pt, edge.V1); } }
public void Test_S2EdgeVectorShape_EdgeAccess() { S2EdgeVectorShape shape = new(); S2Testing.Random.Reset(S2Testing.Random.RandomSeed); int kNumEdges = 100; List <(S2Point, S2Point)> edges = new(); for (int i = 0; i < kNumEdges; ++i) { S2Point a = S2Testing.RandomPoint(); // Control the evaluation order edges.Add((a, S2Testing.RandomPoint())); shape.Add(edges.Last().Item1, edges.Last().Item2); } Assert.Equal(kNumEdges, shape.NumEdges()); Assert.Equal(kNumEdges, shape.NumChains()); Assert.Equal(1, shape.Dimension()); Assert.False(shape.IsEmpty()); Assert.False(shape.IsFull()); for (int i = 0; i < kNumEdges; ++i) { Assert.Equal(i, shape.GetChain(i).Start); Assert.Equal(1, shape.GetChain(i).Length); var edge = shape.GetEdge(i); Assert.Equal(edges[i].Item1, edge.V0); Assert.Equal(edges[i].Item2, edge.V1); } }
public void Test_S2_Rotate() { for (int iter = 0; iter < 1000; ++iter) { S2Point axis = S2Testing.RandomPoint(); S2Point target = S2Testing.RandomPoint(); // Choose a distance whose logarithm is uniformly distributed. double distance = Math.PI * Math.Pow(1e-15, S2Testing.Random.RandDouble()); // Sometimes choose points near the far side of the axis. if (S2Testing.Random.OneIn(5)) { distance = Math.PI - distance; } S2Point p = S2.InterpolateAtDistance(S1Angle.FromRadians(distance), axis, target); // Choose the rotation angle. double angle = S2.M_2_PI * Math.Pow(1e-15, S2Testing.Random.RandDouble()); if (S2Testing.Random.OneIn(3)) { angle = -angle; } if (S2Testing.Random.OneIn(10)) { angle = 0; } TestRotate(p, axis, S1Angle.FromRadians(angle)); } }
public void Test_S2ClosestEdgeQuery_IsConservativeDistanceLessOrEqual() { // Test int num_tested = 0; int num_conservative_needed = 0; for (int iter = 0; iter < 1000; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. S2Point x = S2Testing.RandomPoint(); S2Point dir = S2Testing.RandomPoint(); S1Angle r = S1Angle.FromRadians(Math.PI * Math.Pow(1e-30, S2Testing.Random.RandDouble())); S2Point y = S2.InterpolateAtDistance(r, x, dir); Distance limit = new(r); if (S2Pred.CompareDistance(x, y, limit) <= 0) { MutableS2ShapeIndex index = new(); index.Add(new S2PointVectorShape(new S2Point[] { x })); S2ClosestEdgeQuery query = new(index); S2ClosestEdgeQuery.PointTarget target = new(y); Assert.True(query.IsConservativeDistanceLessOrEqual(target, limit)); ++num_tested; if (!query.IsDistanceLess(target, limit)) { ++num_conservative_needed; } } } // Verify that in most test cases, the distance between the target points // was close to the desired value. Also verify that at least in some test // cases, the conservative distance test was actually necessary. Assert.True(num_tested >= 300); Assert.True(num_tested <= 700); Assert.True(num_conservative_needed >= 25); }
private static void ChooseEdgeNearCell(S2Cell cell, out S2Point a, out S2Point b) { S2Cap cap = cell.GetCapBound(); if (S2Testing.Random.OneIn(5)) { // Choose a point anywhere on the sphere. a = S2Testing.RandomPoint(); } else { // Choose a point inside or somewhere near the cell. a = S2Testing.SamplePoint(new S2Cap(cap.Center, 1.5 * cap.RadiusAngle())); } // Now choose a maximum edge length ranging from very short to very long // relative to the cell size, and choose the other endpoint. double max_length = Math.Min(100 * Math.Pow(1e-4, S2Testing.Random.RandDouble()) * cap.Radius.Radians(), S2.M_PI_2); b = S2Testing.SamplePoint(new S2Cap(a, S1Angle.FromRadians(max_length))); if (S2Testing.Random.OneIn(20)) { // Occasionally replace edge with antipodal edge. a = -a; b = -b; } }
public void Test_S2LatLngRect_GetDirectedHausdorffDistanceRandomPairs() { // Test random pairs. int kIters = 1000; for (int i = 0; i < kIters; ++i) { S2LatLngRect a = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); S2LatLngRect b = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); // a and b are *minimum* bounding rectangles of two random points, in // particular, their Voronoi diagrams are always of the same topology. We // take the "complements" of a and b for more thorough testing. S2LatLngRect a2 = new(a.Lat, a.Lng.Complement()); S2LatLngRect b2 = new(b.Lat, b.Lng.Complement()); // Note that "a" and "b" come from the same distribution, so there is no // need to test pairs such as (b, a), (b, a2), etc. VerifyGetDirectedHausdorffDistance(a, b); VerifyGetDirectedHausdorffDistance(a, b2); VerifyGetDirectedHausdorffDistance(a2, b); VerifyGetDirectedHausdorffDistance(a2, b2); } }
// This function is designed to choose line segment endpoints that are difficult // to handle correctly. Given two adjacent cube vertices P and Q, it returns // either an edge midpoint, face midpoint, or corner vertex along the edge PQ // and then perturbs it slightly. It also sometimes returns a random point from // anywhere on the sphere. private S2Point PerturbedCornerOrMidpoint(S2Point p, S2Point q) { S2Point a = (S2Testing.Random.Uniform(3) - 1) * p + (S2Testing.Random.Uniform(3) - 1) * q; if (S2Testing.Random.OneIn(10)) { // This perturbation often has no effect except on coordinates that are // zero, in which case the perturbed value is so small that operations on // it often result in underflow. a += Math.Pow(1e-300, S2Testing.Random.RandDouble()) * S2Testing.RandomPoint(); } else if (S2Testing.Random.OneIn(2)) { // For coordinates near 1 (say > 0.5), this perturbation yields values // that are only a few representable values away from the initial value. a += 4 * S2.DoubleEpsilon * S2Testing.RandomPoint(); } else { // A perturbation whose magnitude is in the range [1e-25, 1e-10]. a += 1e-10 * Math.Pow(S2.DoubleError, S2Testing.Random.RandDouble()) * S2Testing.RandomPoint(); } if (a.Norm2() < S2.DoubleMinNorm) { // If a.Norm2 is denormalized, Normalize() loses too much precision. return(PerturbedCornerOrMidpoint(p, q)); } return(a); }
public void Test_S1ChordAngle_GetS2PointConstructorMaxError() { // Check that the error bound returned by GetS2PointConstructorMaxError() is // large enough. for (var iter = 0; iter < 100000; ++iter) { S2Testing.Random.Reset(iter); // Easier to reproduce a specific case. var x = S2Testing.RandomPoint(); var y = S2Testing.RandomPoint(); if (S2Testing.Random.OneIn(10)) { // Occasionally test a point pair that is nearly identical or antipodal. var r = S1Angle.FromRadians(S2.DoubleError * S2Testing.Random.RandDouble()); y = S2.GetPointOnLine(x, y, r); if (S2Testing.Random.OneIn(2)) { y = -y; } } S1ChordAngle dist = new(x, y); var error = dist.GetS2PointConstructorMaxError(); var er1 = S2Pred.CompareDistance(x, y, dist.PlusError(error)); if (er1 > 0) { } Assert.True(er1 <= 0); var er2 = S2Pred.CompareDistance(x, y, dist.PlusError(-error)); if (er2 < 0) { } Assert.True(er2 >= 0); } }
public void Test_E6() { for (var i = 0; i < kIters; i++) { var ll = S2LatLng.FromPoint(S2Testing.RandomPoint()); var ll_e6 = S2LatLng.FromE6(ll.Lat().E6(), ll.Lng().E6()); ExpectMaxDigits(ll_e6, 6); } }
public void Test_E7() { ExpectMaxDigits(S2LatLng.FromDegrees(0, 0), 7); for (var i = 0; i < kIters; i++) { var ll = S2LatLng.FromPoint(S2Testing.RandomPoint()); var ll_e7 = S2LatLng.FromE7(ll.Lat().E7(), ll.Lng().E7()); ExpectMaxDigits(ll_e7, 7); } }
// Chooses a random S2Point that is often near the intersection of one of the // coodinates planes or coordinate axes with the unit sphere. (It is possible // to represent very small perturbations near such points.) private static S2Point ChoosePoint() { var x = S2Testing.RandomPoint().ToArray(); for (int i = 0; i < 3; ++i) { if (S2Testing.Random.OneIn(3)) { x[i] *= Math.Pow(1e-50, S2Testing.Random.RandDouble()); } } return(new S2Point(x).Normalize()); }
public void Test_VisitIntersectingShapes_Points() { List <S2Point> vertices = new(); for (int i = 0; i < 100; ++i) { vertices.Add(S2Testing.RandomPoint()); } MutableS2ShapeIndex index = new(); index.Add(new S2PointVectorShape(vertices.ToArray())); new VisitIntersectingShapesTest(index).Run(); }
public void Test_S2_RepeatedInterpolation() { // Check that points do not drift away from unit length when repeated // interpolations are done. for (int i = 0; i < 100; ++i) { S2Point a = S2Testing.RandomPoint(); S2Point b = S2Testing.RandomPoint(); for (int j = 0; j < 1000; ++j) { a = S2.Interpolate(a, b, 0.01); } Assert.True(a.IsUnitLength()); } }
public void Test_S2LatLng_TestConversion() { // Test special cases: poles, "date line" Assert2.DoubleEqual(90.0, new S2LatLng(S2LatLng.FromDegrees(90.0, 65.0).ToPoint()).Lat().GetDegrees()); Assert.Equal(-S2.M_PI_2, new S2LatLng(S2LatLng.FromRadians(-S2.M_PI_2, 1).ToPoint()).LatRadians); Assert2.DoubleEqual(180.0, Math.Abs(new S2LatLng(S2LatLng.FromDegrees(12.2, 180.0).ToPoint()).Lng().GetDegrees())); Assert.Equal(Math.PI, Math.Abs(new S2LatLng(S2LatLng.FromRadians(0.1, -Math.PI).ToPoint()).LngRadians)); // Test a bunch of random points. for (int i = 0; i < 100000; ++i) { S2Point p = S2Testing.RandomPoint(); Assert.True(S2.ApproxEquals(p, new S2LatLng(p).ToPoint())); } }
public void Test_S2_CollinearEdgesThatDontTouch() { int kIters = 500; for (int iter = 0; iter < kIters; ++iter) { S2Point a = S2Testing.RandomPoint(); S2Point d = S2Testing.RandomPoint(); S2Point b = S2.Interpolate(0.05, a, d); S2Point c = S2.Interpolate(0.95, a, d); Assert.True(0 > S2.CrossingSign(a, b, c, d)); Assert.True(0 > S2.CrossingSign(a, b, c, d)); S2EdgeCrosser crosser = new(a, b, c); Assert.True(0 > crosser.CrossingSign(d)); Assert.True(0 > crosser.CrossingSign(c)); } }
public void Test_S2Cap_GetCentroid() { // Empty and full caps. Assert.Equal(new S2Point(), S2Cap.Empty.Centroid()); Assert.True(S2Cap.Full.Centroid().Norm() <= S2.DoubleError); // Random caps. for (int i = 0; i < 100; ++i) { S2Point center = S2Testing.RandomPoint(); double height = S2Testing.Random.UniformDouble(0.0, 2.0); S2Cap cap = S2Cap.FromCenterHeight(center, height); S2Point centroid = cap.Centroid(); S2Point expected = center * (1.0 - height / 2.0) * cap.Area(); Assert.True((expected - centroid).Norm() <= S2.DoubleError); } }
public void Test_S2ClosestPointQuery_EmptyTargetOptimized() { // Ensure that the optimized algorithm handles empty targets when a distance // limit is specified. TestIndex index = new(); for (int i = 0; i < 1000; ++i) { index.Add(S2Testing.RandomPoint(), i); } TestQuery query = new(index); query.Options_.MaxDistance = new S1ChordAngle(S1Angle.FromRadians(1e-5)); MutableS2ShapeIndex target_index = new(); TestQuery.ShapeIndexTarget target = new(target_index); Assert.Empty(query.FindClosestPoints(target)); }
public void Test_S2CellId_Coverage() { // Make sure that random points on the sphere can be represented to the // expected level of accuracy, which in the worst case is Math.Sqrt(2/3) times // the maximum arc length between the points on the sphere associated with // adjacent values of "i" or "j". (It is Math.Sqrt(2/3) rather than 1/2 because // the cells at the corners of each face are stretched -- they have 60 and // 120 degree angles.) double max_dist = 0.5 * S2.kMaxDiag.GetValue(S2.kMaxCellLevel); for (int i = 0; i < 1000000; ++i) { S2Point p = S2Testing.RandomPoint(); S2Point q = new S2CellId(p).ToPointRaw(); Assert.True(p.Angle(q) <= max_dist); } }
public void Test_S2LatLngRect_GetDistanceRandomPairs() { // Test random pairs. for (int i = 0; i < 10000; ++i) { S2LatLngRect a = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); S2LatLngRect b = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); VerifyGetDistance(a, b); S2LatLng c = new(S2Testing.RandomPoint()); VerifyGetRectPointDistance(a, c); VerifyGetRectPointDistance(b, c); } }
public void Test_S2_GetUpdateMinInteriorDistanceMaxError() { // Check that the error bound returned by // GetUpdateMinInteriorDistanceMaxError() is large enough. for (int iter = 0; iter < 10000; ++iter) { S2Point a0 = S2Testing.RandomPoint(); var lenRadians = Math.PI * Math.Pow(1e-20, S2Testing.Random.RandDouble()); S1Angle len = S1Angle.FromRadians(lenRadians); if (S2Testing.Random.OneIn(4)) { len = S1Angle.FromRadians(S2.M_PI) - len; } S2Point a1 = S2.GetPointOnLine(a0, S2Testing.RandomPoint(), len); // TODO(ericv): The error bound holds for antipodal points, but the S2 // predicates used to test the error do not support antipodal points yet. if (a1 == -a0) { continue; } S2Point n = S2.RobustCrossProd(a0, a1).Normalize(); double f = Math.Pow(1e-20, S2Testing.Random.RandDouble()); S2Point a = ((1 - f) * a0 + f * a1).Normalize(); var rRadians = S2.M_PI_2 * Math.Pow(1e-20, S2Testing.Random.RandDouble()); S1Angle r = S1Angle.FromRadians(rRadians); if (S2Testing.Random.OneIn(2)) { r = S1Angle.FromRadians(S2.M_PI_2) - r; } S2Point x = S2.GetPointOnLine(a, n, r); S1ChordAngle min_dist = S1ChordAngle.Infinity; if (!S2.UpdateMinInteriorDistance(x, a0, a1, ref min_dist)) { --iter; continue; } double error = S2.GetUpdateMinDistanceMaxError(min_dist); Assert.True(S2Pred.CompareEdgeDistance(x, a0, a1, min_dist.PlusError(error)) <= 0); Assert.True(S2Pred.CompareEdgeDistance(x, a0, a1, min_dist.PlusError(-error)) >= 0); } }
public void Test_S2LaxPolygonShape_CompareToS2Loop() { for (int iter = 0; iter < 100; ++iter) { var fractal = new S2Testing.Fractal(); fractal.MaxLevel = (S2Testing.Random.Uniform(5)); fractal.FractalDimension = (1 + S2Testing.Random.RandDouble()); S2Point center = S2Testing.RandomPoint(); var loop = fractal.MakeLoop( S2Testing.GetRandomFrameAt(center), S1Angle.FromDegrees(5)); // Compare S2Loop to S2LaxLoopShape. CompareS2LoopToShape(loop, new S2LaxLoopShape(loop)); // Compare S2Loop to S2LaxPolygonShape. var loops = new List <List <S2Point> > { loop.CloneVertices().ToList() }; CompareS2LoopToShape(loop, new S2LaxPolygonShape(loops)); } }
public void Test_S2_GoodFastHashSpreads() { int kTestPoints = 1 << 16; var set = new List <int>(); var points = new SortedSet <S2Point>(); var base_ = new S2Point(1, 1, 1); for (var i = 0; i < kTestPoints; ++i) { // All points in a tiny cap to test avalanche property of hash // function (the cap would be of radius 1mm on Earth (4*10^9/2^35). S2Point perturbed = base_ + S2Testing.RandomPoint() / (1UL << 35); perturbed = perturbed.Normalize(); set.Add(perturbed.GetHashCode()); points.Add(perturbed); } // A real collision is extremely unlikely. Assert.Equal(0, kTestPoints - points.Count); // Allow a few for the hash. Assert.True(10 >= kTestPoints - set.Count); }
public void Test_IntLatLngSnapFunction_SnapPoint() { for (int iter = 0; iter < 1000; ++iter) { // Test that IntLatLngSnapFunction does not modify points that were // generated using the S2LatLng.From{E5,E6,E7} methods. This ensures // that both functions are using bitwise-compatible conversion methods. S2Point p = S2Testing.RandomPoint(); S2LatLng ll = new(p); S2Point p5 = S2LatLng.FromE5(ll.Lat().E5(), ll.Lng().E5()).ToPoint(); Assert.Equal(p5, new IntLatLngSnapFunction(5).SnapPoint(p5)); S2Point p6 = S2LatLng.FromE6(ll.Lat().E6(), ll.Lng().E6()).ToPoint(); Assert.Equal(p6, new IntLatLngSnapFunction(6).SnapPoint(p6)); S2Point p7 = S2LatLng.FromE7(ll.Lat().E7(), ll.Lng().E7()).ToPoint(); Assert.Equal(p7, new IntLatLngSnapFunction(7).SnapPoint(p7)); // Make sure that we're not snapping using some lower exponent. S2Point p7not6 = S2LatLng.FromE7(10 * ll.Lat().E6() + 1, 10 * ll.Lng().E6() + 1).ToPoint(); Assert.NotEqual(p7not6, new IntLatLngSnapFunction(6).SnapPoint(p7not6)); } }
public void Test_S2ContainsPointQuery_GetContainingShapes() { // Also tests ShapeContains(). int kNumVerticesPerLoop = 10; S1Angle kMaxLoopRadius = S2Testing.KmToAngle(10); S2Cap center_cap = new(S2Testing.RandomPoint(), kMaxLoopRadius); MutableS2ShapeIndex index = new(); for (int i = 0; i < 100; ++i) { var loop = S2Loop.MakeRegularLoop( S2Testing.SamplePoint(center_cap), S2Testing.Random.RandDouble() * kMaxLoopRadius, kNumVerticesPerLoop); index.Add(new S2Loop.Shape(loop)); } var query = index.MakeS2ContainsPointQuery(); for (int i = 0; i < 100; ++i) { S2Point p = S2Testing.SamplePoint(center_cap); List <S2Shape> expected = new(); foreach (var shape in index) { var loop = ((S2Loop.Shape)shape).Loop; if (loop.Contains(p)) { Assert.True(query.ShapeContains(shape, p)); expected.Add(shape); } else { Assert.False(query.ShapeContains(shape, p)); } } var actual = query.GetContainingShapes(p); Assert.Equal(expected, actual); } }
public void Test_S2Cell_GetDistanceToPoint() { S2Testing.Random.Reset(S2Testing.Random.RandomSeed); for (int iter = 0; iter < 1000; ++iter) { _logger.WriteLine($"Iteration {iter}"); S2Cell cell = new(S2Testing.GetRandomCellId()); S2Point target = S2Testing.RandomPoint(); S1Angle expected_to_boundary = GetDistanceToPointBruteForce(cell, target).ToAngle(); S1Angle expected_to_interior = cell.Contains(target) ? S1Angle.Zero : expected_to_boundary; S1Angle expected_max = GetMaxDistanceToPointBruteForce(cell, target).ToAngle(); S1Angle actual_to_boundary = cell.BoundaryDistance(target).ToAngle(); S1Angle actual_to_interior = cell.Distance(target).ToAngle(); S1Angle actual_max = cell.MaxDistance(target).ToAngle(); // The error has a peak near Pi/2 for edge distance, and another peak near // Pi for vertex distance. Assert2.Near(expected_to_boundary.Radians, actual_to_boundary.Radians, 1e-12); Assert2.Near(expected_to_interior.Radians, actual_to_interior.Radians, 1e-12); Assert2.Near(expected_max.Radians, actual_max.Radians, 1e-12); if (expected_to_boundary.Radians <= Math.PI / 3) { Assert2.Near(expected_to_boundary.Radians, actual_to_boundary.Radians, S2.DoubleError); Assert2.Near(expected_to_interior.Radians, actual_to_interior.Radians, S2.DoubleError); } if (expected_max.Radians <= Math.PI / 3) { Assert2.Near(expected_max.Radians, actual_max.Radians, S2.DoubleError); } } }
public void Test_RectBounder_MaxLatitudeRandom() { // Check that the maximum latitude of edges is computed accurately to within // 3 * S2Constants.DoubleEpsilon (the expected maximum error). We concentrate on maximum // latitudes near the equator and north pole since these are the extremes. const int kIters = 100; for (int iter = 0; iter < kIters; ++iter) { // Construct a right-handed coordinate frame (U,V,W) such that U points // slightly above the equator, V points at the equator, and W is slightly // offset from the north pole. S2Point u = S2Testing.RandomPoint(); u = new S2Point(u.X, u.Y, S2.DoubleEpsilon * 1e-6 * Math.Pow(1e12, S2Testing.Random.RandDouble())); // log is uniform u = u.Normalize(); S2Point v = S2.RobustCrossProd(new S2Point(0, 0, 1), u).Normalize(); S2Point w = S2.RobustCrossProd(u, v).Normalize(); // Construct a line segment AB that passes through U, and check that the // maximum latitude of this segment matches the latitude of U. S2Point a = (u - S2Testing.Random.RandDouble() * v).Normalize(); S2Point b = (u + S2Testing.Random.RandDouble() * v).Normalize(); S2LatLngRect ab_bound = GetEdgeBound(a, b); Assert2.Near(S2LatLng.Latitude(u).Radians, ab_bound.Lat.Hi, kRectError.LatRadians); // Construct a line segment CD that passes through W, and check that the // maximum latitude of this segment matches the latitude of W. S2Point c = (w - S2Testing.Random.RandDouble() * v).Normalize(); S2Point d = (w + S2Testing.Random.RandDouble() * v).Normalize(); S2LatLngRect cd_bound = GetEdgeBound(c, d); Assert2.Near(S2LatLng.Latitude(w).Radians, cd_bound.Lat.Hi, kRectError.LatRadians); } }
public void Test_S2PolylineSimplifier_Precision() { // This is a rough upper bound on both the error in constructing the disc // locations (i.e., S2.InterpolateAtDistance, etc), and also on the // padding that S2PolylineSimplifier uses to ensure that its results are // conservative (i.e., the error calculated by GetSemiwidth). S1Angle kMaxError = S1Angle.FromRadians(25 * S2.DoubleEpsilon); // We repeatedly generate a random edge. We then target several discs that // barely overlap the edge, and avoid several discs that barely miss the // edge. About half the time, we choose one disc and make it slightly too // large or too small so that targeting fails. int kIters = 1000; // Passes with 1 million iterations. for (int iter = 0; iter < kIters; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. S2Point src = S2Testing.RandomPoint(); var simplifier = new S2PolylineSimplifier(src); S2Point dst = S2.InterpolateAtDistance( S1Angle.FromRadians(S2Testing.Random.RandDouble()), src, S2Testing.RandomPoint()); S2Point n = S2.RobustCrossProd(src, dst).Normalize(); // If bad_disc >= 0, then we make targeting fail for that disc. int kNumDiscs = 5; int bad_disc = S2Testing.Random.Uniform(2 * kNumDiscs) - kNumDiscs; for (int i = 0; i < kNumDiscs; ++i) { // The center of the disc projects to a point that is the given fraction // "f" along the edge (src, dst). If f < 0, the center is located // behind "src" (in order to test this case). double f = S2Testing.Random.UniformDouble(-0.5, 1.0); S2Point a = ((1 - f) * src + f * dst).Normalize(); S1Angle r = S1Angle.FromRadians(S2Testing.Random.RandDouble()); bool on_left = S2Testing.Random.OneIn(2); S2Point x = S2.InterpolateAtDistance(r, a, on_left ? n : -n); // If the disc is behind "src", adjust its radius so that it just // touches "src" rather than just touching the line through (src, dst). if (f < 0) { r = new S1Angle(src, x); } // We grow the radius slightly if we want to target the disc and shrink // it otherwise, *unless* we want targeting to fail for this disc, in // which case these actions are reversed. bool avoid = S2Testing.Random.OneIn(2); bool grow_radius = (avoid == (i == bad_disc)); var radius = new S1ChordAngle(grow_radius ? r + kMaxError : r - kMaxError); if (avoid) { simplifier.AvoidDisc(x, radius, on_left); } else { simplifier.TargetDisc(x, radius); } } // The result is true iff all the discraints were satisfiable. Assert.Equal(bad_disc < 0, simplifier.Extend(dst)); } }
public void Test_S2_AreaMethods() { S2Point pz = new(0, 0, 1); S2Point p000 = new(1, 0, 0); S2Point p045 = new S2Point(1, 1, 0).Normalize(); S2Point p090 = new(0, 1, 0); S2Point p180 = new(-1, 0, 0); Assert2.Near(S2.Area(p000, p090, pz), S2.M_PI_2); Assert2.Near(S2.Area(p045, pz, p180), 3 * S2.M_PI_4); // Make sure that Area() has good *relative* accuracy even for // very small areas. const double eps = 1e-10; S2Point pepsx = new S2Point(eps, 0, 1).Normalize(); S2Point pepsy = new S2Point(0, eps, 1).Normalize(); double expected1 = 0.5 * eps * eps; Assert2.Near(S2.Area(pepsx, pepsy, pz), expected1, 1e-14 * expected1); // Make sure that it can handle degenerate triangles. S2Point pr = new S2Point(0.257, -0.5723, 0.112).Normalize(); S2Point pq = new S2Point(-0.747, 0.401, 0.2235).Normalize(); Assert.Equal(0, S2.Area(pr, pr, pr)); // The following test is not exact due to rounding error. Assert2.Near(S2.Area(pr, pq, pr), 0, S2.DoubleError); Assert.Equal(0, S2.Area(p000, p045, p090)); double max_girard = 0; for (int i = 0; i < 10000; ++i) { S2Point p0 = S2Testing.RandomPoint(); S2Point d1 = S2Testing.RandomPoint(); S2Point d2 = S2Testing.RandomPoint(); S2Point p1 = (p0 + S2.DoubleError * d1).Normalize(); S2Point p2 = (p0 + S2.DoubleError * d2).Normalize(); // The actual displacement can be as much as 1.2e-15 due to roundoff. // This yields a maximum triangle area of about 0.7e-30. Assert.True(S2.Area(p0, p1, p2) <= 0.7e-30); max_girard = Math.Max(max_girard, S2.GirardArea(p0, p1, p2)); } // This check only passes if GirardArea() uses RobustCrossProd(). Assert.True(max_girard <= 1e-14); // Try a very long and skinny triangle. S2Point p045eps = new S2Point(1, 1, eps).Normalize(); double expected2 = 5.8578643762690495119753e-11; // Mathematica. Assert2.Near(S2.Area(p000, p045eps, p090), expected2, 1e-9 * expected2); // Triangles with near-180 degree edges that sum to a quarter-sphere. const double eps2 = 1e-14; S2Point p000eps2 = new S2Point(1, 0.1 * eps2, eps2).Normalize(); double quarter_area1 = S2.Area(p000eps2, p000, p045) + S2.Area(p000eps2, p045, p180) + S2.Area(p000eps2, p180, pz) + S2.Area(p000eps2, pz, p000); Assert2.Near(quarter_area1, Math.PI); // Four other triangles that sum to a quarter-sphere. S2Point p045eps2 = new S2Point(1, 1, eps2).Normalize(); double quarter_area2 = S2.Area(p045eps2, p000, p045) + S2.Area(p045eps2, p045, p180) + S2.Area(p045eps2, p180, pz) + S2.Area(p045eps2, pz, p000); Assert2.Near(quarter_area2, Math.PI); // Compute the area of a hemisphere using four triangles with one near-180 // degree edge and one near-degenerate edge. for (int i = 0; i < 100; ++i) { double lng = S2.M_2_PI * S2Testing.Random.RandDouble(); S2Point p0 = S2LatLng.FromRadians(1e-20, lng).Normalized().ToPoint(); S2Point p1 = S2LatLng.FromRadians(0, lng).Normalized().ToPoint(); double p2_lng = lng + S2Testing.Random.RandDouble(); S2Point p2 = S2LatLng.FromRadians(0, p2_lng).Normalized().ToPoint(); S2Point p3 = S2LatLng.FromRadians(0, lng + Math.PI).Normalized().ToPoint(); S2Point p4 = S2LatLng.FromRadians(0, lng + 5.0).Normalized().ToPoint(); double area = (S2.Area(p0, p1, p2) + S2.Area(p0, p2, p3) + S2.Area(p0, p3, p4) + S2.Area(p0, p4, p1)); Assert2.Near(area, S2.M_2_PI, 2e-15); } // This tests a case where the triangle has zero area, but S2.Area() // computes (dmin > 0) due to rounding errors. Assert.Equal(0.0, S2.Area(S2LatLng.FromDegrees(-45, -170).ToPoint(), S2LatLng.FromDegrees(45, -170).ToPoint(), S2LatLng.FromDegrees(0, -170).ToPoint())); }
private void TestRandomCaps(S2RegionTermIndexer.Options options, QueryType query_type) { // This function creates an index consisting either of points (if // options.index_contains_points_only() is true) or S2Caps of random size. // It then executes queries consisting of points (if query_type == POINT) // or S2Caps of random size (if query_type == CAP). var indexer = new S2RegionTermIndexer(options); var coverer = new S2RegionCoverer(options); var caps = new List <S2Cap>(); var coverings = new List <S2CellUnion>(); var index = new Dictionary <string, List <int> >(); int index_terms = 0, query_terms = 0; for (int i = 0; i < iters; ++i) { // Choose the region to be indexed: either a single point or a cap // of random size (up to a full sphere). S2Cap cap; List <string> terms; if (options.IndexContainsPointsOnly) { cap = S2Cap.FromPoint(S2Testing.RandomPoint()); terms = indexer.GetIndexTerms(cap.Center, ""); } else { cap = S2Testing.GetRandomCap( 0.3 * S2Cell.AverageArea(options.MaxLevel), 4.0 * S2Cell.AverageArea(options.MinLevel)); terms = indexer.GetIndexTerms(cap, ""); } caps.Add(cap); coverings.Add(coverer.GetCovering(cap)); foreach (var term in terms) { if (!index.ContainsKey(term)) { index.Add(term, new List <int>()); } index[term].Add(i); } index_terms += terms.Count; } for (int i = 0; i < iters; ++i) { // Choose the region to be queried: either a random point or a cap of // random size. S2Cap cap; List <string> terms; if (query_type == QueryType.CAP) { cap = S2Cap.FromPoint(S2Testing.RandomPoint()); terms = indexer.GetQueryTerms(cap.Center, ""); } else { cap = S2Testing.GetRandomCap( 0.3 * S2Cell.AverageArea(options.MaxLevel), 4.0 * S2Cell.AverageArea(options.MinLevel)); terms = indexer.GetQueryTerms(cap, ""); } // Compute the expected results of the S2Cell query by brute force. S2CellUnion covering = coverer.GetCovering(cap); var expected = new List <int>(); var actual = new List <int>(); for (int j = 0; j < caps.Count; ++j) { if (covering.Intersects(coverings[j])) { expected.Add(j); } } foreach (var term in terms) { actual.AddRange(index[term]); } Assert.Equal(expected, actual); query_terms += terms.Count; } _logger.WriteLine($"Index terms/doc: {((double)index_terms) / iters:2f}, Query terms/doc: {((double)query_terms) / iters:2f}"); }