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_S2ConvexHullQuery_PointsInsideHull() { // Repeatedly build the convex hull of a set of points, then add more points // inside that loop and build the convex hull again. The result should // always be the same. int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. // Choose points from within a cap of random size, up to but not including // an entire hemisphere. S2Cap cap = S2Testing.GetRandomCap(S2.DoubleError, 1.999 * Math.PI); S2ConvexHullQuery query = new(); int num_points1 = S2Testing.Random.Uniform(100) + 3; for (int i = 0; i < num_points1; ++i) { query.AddPoint(S2Testing.SamplePoint(cap)); } var hull = query.GetConvexHull(); // When the convex hull is nearly a hemisphere, the algorithm sometimes // returns a full cap instead. This is because it first computes a // bounding rectangle for all the input points/edges and then converts it // to a bounding cap, which sometimes yields a non-convex cap (radius // larger than 90 degrees). This should not be a problem in practice // (since most convex hulls are not hemispheres), but in order make this // test pass reliably it means that we need to reject convex hulls whose // bounding cap (when computed from a bounding rectangle) is not convex. // // TODO(b/203702905): This test can still fail (about 1 iteration in // 500,000) because the S2LatLngRect::GetCapBound implementation does not // guarantee that A.Contains(B) implies // A.GetCapBound().Contains(B.GetCapBound()). if (hull.GetCapBound().Height() >= 1) { continue; } // Otherwise, add more points inside the convex hull. int num_points2 = 1000; for (int i = 0; i < num_points2; ++i) { S2Point p = S2Testing.SamplePoint(cap); if (hull.Contains(p)) { query.AddPoint(p); } } // Finally, build a new convex hull and check that it hasn't changed. var hull2 = (query.GetConvexHull()); _logger.WriteLine("Iteration: " + iter); Assert.True(hull2.BoundaryEquals(hull)); } }
public void Test_S2CellUnion_Expand() { // This test generates coverings for caps of random sizes, expands // the coverings by a random radius, and then make sure that the new // covering covers the expanded cap. It also makes sure that the // new covering is not too much larger than expected. var coverer = new S2RegionCoverer(); for (var i = 0; i < 1000; ++i) { _logger.WriteLine($"Iteration {i}"); var cap = S2Testing.GetRandomCap( S2Cell.AverageArea(S2.kMaxCellLevel), S2.M_4_PI); // Expand the cap area by a random factor whose log is uniformly // distributed between 0 and log(1e2). var expanded_cap = S2Cap.FromCenterHeight( cap.Center, Math.Min(2.0, Math.Pow(1e2, rnd.RandDouble()) * cap.Height())); var radius = (expanded_cap.Radius - cap.Radius).Radians(); var max_level_diff = rnd.Uniform(8); // Generate a covering for the original cap, and measure the maximum // distance from the cap center to any point in the covering. coverer.Options_.MaxCells = 1 + rnd.Skewed(10); var covering = coverer.GetCovering(cap); S2Testing.CheckCovering(cap, covering, true); var covering_radius = GetRadius(covering, cap.Center); // This code duplicates the logic in Expand(min_radius, max_level_diff) // that figures out an appropriate cell level to use for the expansion. int min_level = S2.kMaxCellLevel; foreach (var id in covering) { min_level = Math.Min(min_level, id.Level()); } var expand_level = Math.Min(min_level + max_level_diff, S2.kMinWidth.GetLevelForMinValue(radius)); // Generate a covering for the expanded cap, and measure the new maximum // distance from the cap center to any point in the covering. covering.Expand(S1Angle.FromRadians(radius), max_level_diff); S2Testing.CheckCovering(expanded_cap, covering, false); double expanded_covering_radius = GetRadius(covering, cap.Center); // If the covering includes a tiny cell along the boundary, in theory the // maximum angle of the covering from the cap center can increase by up to // twice the maximum length of a cell diagonal. Assert.True(expanded_covering_radius - covering_radius <= 2 * S2.kMaxDiag.GetValue(expand_level)); } }
public void Test_S2RegionCoverer_SimpleCoverings() { Assert.True(false); //TODO const int kMaxLevel = S2.kMaxCellLevel; var options = new S2RegionCoverer.Options { MaxCells = Int32.MaxValue }; for (int i = 0; i < 1000; ++i) { int level = S2Testing.Random.Uniform(kMaxLevel + 1); options.MinLevel = (level); options.MaxLevel = (level); double max_area = Math.Min(S2.M_4_PI, 1000 * S2Cell.AverageArea(level)); S2Cap cap = S2Testing.GetRandomCap(0.1 * S2Cell.AverageArea(kMaxLevel), max_area); var covering = new List <S2CellId>(); S2RegionCoverer.GetSimpleCovering(cap, cap.Center, level, covering); CheckCovering(options, cap, covering, false); } }
public void Test_S2RegionCoverer_RandomCaps() { const int kMaxLevel = S2.kMaxCellLevel; S2RegionCoverer.Options options = new(); for (int i = 0; i < 1000; ++i) { do { options.MinLevel = (S2Testing.Random.Uniform(kMaxLevel + 1)); options.MaxLevel = (S2Testing.Random.Uniform(kMaxLevel + 1)); } while (options.MinLevel > options.MaxLevel); options.MaxCells = S2Testing.Random.Skewed(10); options.LevelMod = (1 + S2Testing.Random.Uniform(3)); double max_area = Math.Min(S2.M_4_PI, (3 * options.MaxCells + 1) * S2Cell.AverageArea(options.MinLevel)); S2Cap cap = S2Testing.GetRandomCap(0.1 * S2Cell.AverageArea(kMaxLevel), max_area); S2RegionCoverer coverer = new(options); coverer.GetCovering(cap, out var covering); CheckCovering(options, cap, covering, false); coverer.GetInteriorCovering(cap, out var interior); CheckCovering(options, cap, interior, true); // Check that GetCovering is deterministic. coverer.GetCovering(cap, out var covering2); Assert.Equal(covering, covering2); // Also check S2CellUnion.Denormalize(). The denormalized covering // may still be different and smaller than "covering" because // S2RegionCoverer does not guarantee that it will not output all four // children of the same parent. S2CellUnion cells = new(covering); var denormalized = new List <S2CellId>(); cells.Denormalize(options.MinLevel, options.LevelMod, denormalized); CheckCovering(options, cap, denormalized, false); } }
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}"); }