Exemple #1
0
        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 testRandomCaps()
        {
            Console.WriteLine("TestRandomCaps");

            var kMaxLevel = S2CellId.MaxLevel;
            var coverer   = new S2RegionCoverer();

            for (var i = 0; i < 1000; ++i)
            {
                do
                {
                    coverer.MinLevel = random(kMaxLevel + 1);
                    coverer.MaxLevel = random(kMaxLevel + 1);
                } while (coverer.MinLevel > coverer.MaxLevel);
                coverer.MaxCells = skewed(10);
                coverer.LevelMod = 1 + random(3);
                var maxArea = Math.Min(
                    4 * S2.Pi, (3 * coverer.MaxCells + 1) * S2Cell.AverageArea(coverer.MinLevel));
                var cap      = getRandomCap(0.1 * S2Cell.AverageArea(kMaxLevel), maxArea);
                var covering = new List <S2CellId>();
                var interior = new List <S2CellId>();

                coverer.GetCovering(cap, covering);
                checkCovering(coverer, cap, covering, false);

                coverer.GetInteriorCovering(cap, interior);
                checkCovering(coverer, cap, interior, true);


                // Check that GetCovering is deterministic.
                var covering2 = new List <S2CellId>();
                coverer.GetCovering(cap, covering2);
                assertTrue(covering.SequenceEqual(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.
                var cells = new S2CellUnion();
                cells.InitFromCellIds(covering);
                var denormalized = new List <S2CellId>();
                cells.Denormalize(coverer.MinLevel, coverer.LevelMod, denormalized);
                checkCovering(coverer, cap, denormalized, false);
            }
        }
        public void testSimpleCoverings()
        {
            Console.WriteLine("TestSimpleCoverings");

            var kMaxLevel = S2CellId.MaxLevel;
            var coverer   = new S2RegionCoverer();

            coverer.MaxCells = int.MaxValue;
            for (var i = 0; i < 1000; ++i)
            {
                var level = random(kMaxLevel + 1);
                coverer.MinLevel = level;
                coverer.MaxLevel = level;
                var maxArea  = Math.Min(4 * S2.Pi, 1000 * S2Cell.AverageArea(level));
                var cap      = getRandomCap(0.1 * S2Cell.AverageArea(kMaxLevel), maxArea);
                var covering = new List <S2CellId>();
                S2RegionCoverer.GetSimpleCovering(cap, cap.Axis, level, covering);
                checkCovering(coverer, cap, covering, false);
            }
        }
        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);
            }
        }
Exemple #6
0
    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}");
    }
        public void testSubdivide()
        {
            for (var face = 0; face < 6; ++face)
            {
                testSubdivide(S2Cell.FromFacePosLevel(face, (byte)0, 0));
            }

            // The maximum edge *ratio* is the ratio of the longest edge of any cell to
            // the shortest edge of any cell at the same level (and similarly for the
            // maximum diagonal ratio).
            //
            // The maximum edge *aspect* is the maximum ratio of the longest edge of a
            // cell to the shortest edge of that same cell (and similarly for the
            // maximum diagonal aspect).

            Console
            .WriteLine("Level    Area      Edge          Diag          Approx       Average\n");
            Console
            .WriteLine("        Ratio  Ratio Aspect  Ratio Aspect    Min    Max    Min    Max\n");
            for (var i = 0; i <= S2CellId.MaxLevel; ++i)
            {
                var s = levelStats[i];
                if (s.count > 0)
                {
                    s.avgArea      /= s.count;
                    s.avgWidth     /= s.count;
                    s.avgEdge      /= s.count;
                    s.avgDiag      /= s.count;
                    s.avgAngleSpan /= s.count;
                }
                Console.WriteLine(
                    "%5d  %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n", i,
                    s.maxArea / s.minArea, s.maxEdge / s.minEdge, s.maxEdgeAspect,
                    s.maxDiag / s.minDiag, s.maxDiagAspect, s.minApproxRatio,
                    s.maxApproxRatio, S2Cell.AverageArea(i) / s.maxArea, S2Cell
                    .AverageArea(i)
                    / s.minArea);
            }

            // Now check the validity of the S2 length and area metrics.
            for (var i = 0; i <= S2CellId.MaxLevel; ++i)
            {
                var s = levelStats[i];
                if (s.count == 0)
                {
                    continue;
                }

                Console.WriteLine(
                    "Level {0} - metric (error/actual : error/tolerance)\n", i);

                // The various length calculations are only accurate to 1e-15 or so,
                // so we need to allow for this amount of discrepancy with the theoretical
                // minimums and maximums. The area calculation is accurate to about 1e-15
                // times the cell width.
                testMinMaxAvg("area", i, s.count, 1e-15 * s.minWidth, s.minArea,
                              s.maxArea, s.avgArea, S2Projections.MinArea, S2Projections.MaxArea,
                              S2Projections.AvgArea);
                testMinMaxAvg("width", i, s.count, 1e-15, s.minWidth, s.maxWidth,
                              s.avgWidth, S2Projections.MinWidth, S2Projections.MaxWidth,
                              S2Projections.AvgWidth);
                testMinMaxAvg("edge", i, s.count, 1e-15, s.minEdge, s.maxEdge,
                              s.avgEdge, S2Projections.MinEdge, S2Projections.MaxEdge,
                              S2Projections.AvgEdge);
                testMinMaxAvg("diagonal", i, s.count, 1e-15, s.minDiag, s.maxDiag,
                              s.avgDiag, S2Projections.MinDiag, S2Projections.MaxDiag,
                              S2Projections.AvgDiag);
                testMinMaxAvg("angle span", i, s.count, 1e-15, s.minAngleSpan,
                              s.maxAngleSpan, s.avgAngleSpan, S2Projections.MinAngleSpan,
                              S2Projections.MaxAngleSpan, S2Projections.AvgAngleSpan);

                // The aspect ratio calculations are ratios of lengths and are therefore
                // less accurate at higher subdivision levels.
                Assert.True(s.maxEdgeAspect <= S2Projections.MaxEdgeAspect + 1e-15
                            * (1 << i));
                Assert.True(s.maxDiagAspect <= S2Projections.MaxDiagAspect + 1e-15
                            * (1 << i));
            }
        }
        public void testSubdivide(S2Cell cell)
        {
            gatherStats(cell);
            if (cell.IsLeaf)
            {
                return;
            }

            var children = new S2Cell[4];

            for (var i = 0; i < children.Length; ++i)
            {
                children[i] = new S2Cell();
            }
            Assert.True(cell.Subdivide(children));
            var    childId     = cell.Id.ChildBegin;
            double exactArea   = 0;
            double approxArea  = 0;
            double averageArea = 0;

            for (var i = 0; i < 4; ++i, childId = childId.Next)
            {
                exactArea   += children[i].ExactArea();
                approxArea  += children[i].ApproxArea();
                averageArea += children[i].AverageArea();

                // Check that the child geometry is consistent with its cell id.
                JavaAssert.Equal(children[i].Id, childId);
                Assert.True(children[i].Center.ApproxEquals(childId.ToPoint(), 1e-15));
                var direct = new S2Cell(childId);
                JavaAssert.Equal(children[i].Face, direct.Face);
                JavaAssert.Equal(children[i].Level, direct.Level);
                JavaAssert.Equal(children[i].Orientation, direct.Orientation);
                JavaAssert.Equal(children[i].CenterRaw, direct.CenterRaw);
                for (var k = 0; k < 4; ++k)
                {
                    JavaAssert.Equal(children[i].GetVertexRaw(k), direct.GetVertexRaw(k));
                    JavaAssert.Equal(children[i].GetEdgeRaw(k), direct.GetEdgeRaw(k));
                }

                // Test Contains() and MayIntersect().
                Assert.True(cell.Contains(children[i]));
                Assert.True(cell.MayIntersect(children[i]));
                Assert.True(!children[i].Contains(cell));
                Assert.True(cell.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(cell.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        Assert.True(!children[i].Contains(children[j].CenterRaw));
                        Assert.True(!children[i].MayIntersect(children[j]));
                    }
                }

                // Test GetCapBound and GetRectBound.
                var parentCap  = cell.CapBound;
                var parentRect = cell.RectBound;
                if (cell.Contains(new S2Point(0, 0, 1)) ||
                    cell.Contains(new S2Point(0, 0, -1)))
                {
                    Assert.True(parentRect.Lng.IsFull);
                }
                var childCap  = children[i].CapBound;
                var childRect = children[i].RectBound;
                Assert.True(childCap.Contains(children[i].Center));
                Assert.True(childRect.Contains(children[i].CenterRaw));
                Assert.True(parentCap.Contains(children[i].Center));
                Assert.True(parentRect.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(childCap.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertexRaw(j)));
                    Assert.True(parentCap.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertex(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertex(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertex(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertexRaw(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertexRaw(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertexRaw(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        // The bounding caps and rectangles should be tight enough so that
                        // they exclude at least two vertices of each adjacent cell.
                        var capCount  = 0;
                        var rectCount = 0;
                        for (var k = 0; k < 4; ++k)
                        {
                            if (childCap.Contains(children[j].GetVertex(k)))
                            {
                                ++capCount;
                            }
                            if (childRect.Contains(children[j].GetVertexRaw(k)))
                            {
                                ++rectCount;
                            }
                        }
                        Assert.True(capCount <= 2);
                        if (childRect.LatLo.Radians > -S2.PiOver2 &&
                            childRect.LatHi.Radians < S2.PiOver2)
                        {
                            // Bounding rectangles may be too large at the poles because the
                            // pole itself has an arbitrary fixed longitude.
                            Assert.True(rectCount <= 2);
                        }
                    }
                }

                // Check all children for the first few levels, and then sample randomly.
                // Also subdivide one corner cell, one edge cell, and one center cell
                // so that we have a better chance of sample the minimum metric values.
                var forceSubdivide = false;
                var center         = S2Projections.GetNorm(children[i].Face);
                var edge           = center + S2Projections.GetUAxis(children[i].Face);
                var corner         = edge + S2Projections.GetVAxis(children[i].Face);
                for (var j = 0; j < 4; ++j)
                {
                    var p = children[i].GetVertexRaw(j);
                    if (p.Equals(center) || p.Equals(edge) || p.Equals(corner))
                    {
                        forceSubdivide = true;
                    }
                }
                if (forceSubdivide || cell.Level < (DEBUG_MODE ? 5 : 6) ||
                    random(DEBUG_MODE ? 10 : 4) == 0)
                {
                    testSubdivide(children[i]);
                }
            }

            // Check sum of child areas equals parent area.
            //
            // For ExactArea(), the best relative error we can expect is about 1e-6
            // because the precision of the unit vector coordinates is only about 1e-15
            // and the edge length of a leaf cell is about 1e-9.
            //
            // For ApproxArea(), the areas are accurate to within a few percent.
            //
            // For AverageArea(), the areas themselves are not very accurate, but
            // the average area of a parent is exactly 4 times the area of a child.

            Assert.True(Math.Abs(Math.Log(exactArea / cell.ExactArea())) <= Math
                        .Abs(Math.Log(1 + 1e-6)));
            Assert.True(Math.Abs(Math.Log(approxArea / cell.ApproxArea())) <= Math
                        .Abs(Math.Log(1.03)));
            Assert.True(Math.Abs(Math.Log(averageArea / cell.AverageArea())) <= Math
                        .Abs(Math.Log(1 + 1e-15)));
        }
Exemple #9
0
        public void Test_S2Cell_TestSubdivide()
        {
            // Only test a sample of faces to reduce the runtime.
            TestSubdivide(S2Cell.FromFace(0));
            TestSubdivide(S2Cell.FromFace(3));
            TestSubdivide(S2Cell.FromFace(5));

            #region Print Header

            // This table is useful in evaluating the quality of the various S2
            // projections.
            //
            // The maximum edge *ratio* is the ratio of the longest edge of any cell to
            // the shortest edge of any cell at the same level (and similarly for the
            // maximum diagonal ratio).
            //
            // The maximum edge *aspect* is the maximum ratio of the longest edge of a
            // cell to the shortest edge of that same cell (and similarly for the
            // maximum diagonal aspect).
            _logger.WriteLine(
                @"Ratio:  (Max value for any cell) / (Min value for any cell)
Aspect: (Max value / min value) for any cell
                   Edge          Diag       Approx Area/    Avg Area/
         Area     Length        Length       Exact Area    Exact Area
Level   Ratio  Ratio Aspect  Ratio Aspect    Min    Max    Min    Max
--------------------------------------------------------------------");

            #endregion

            for (int i = 0; i <= S2.kMaxCellLevel; ++i)
            {
                var s = level_stats[i];
                if (s.Count > 0)
                {
                    s.Avg_area       /= s.Count;
                    s.Avg_width      /= s.Count;
                    s.Avg_edge       /= s.Count;
                    s.Avg_diag       /= s.Count;
                    s.Avg_angle_span /= s.Count;
                }
                _logger.WriteLine($"{i:d5}  {s.Max_area / s.Min_area:f6.3} {s.Max_edge / s.Min_edge:f6.3} {s.Max_edge_aspect:f6.3} {s.Max_diag / s.Min_diag:f6.3} {s.Max_diag_aspect:f6.3} {s.Min_approx_ratio:f6.3} {s.Max_approx_ratio:f6.3} {S2Cell.AverageArea(i) / s.Max_area:f6.3} {S2Cell.AverageArea(i) / s.Min_area:f6.3}");
            }

            // Now check the validity of the S2 length and area metrics.
            for (int i = 0; i <= S2.kMaxCellLevel; ++i)
            {
                var s = level_stats[i];
                if (s.Count == 0)
                {
                    continue;
                }

                _logger.WriteLine($"Level {i:2d} - metric value (error/actual : error/tolerance)");

                // The various length calculations are only accurate to 1e-15 or so,
                // so we need to allow for this amount of discrepancy with the theoretical
                // minimums and maximums.  The area calculation is accurate to about 1e-15
                // times the cell width.
                CheckMinMaxAvg("area", i, s.Count, 1e-15 * s.Min_width,
                               s.Min_area, s.Max_area, s.Avg_area,
                               S2.kMinArea, S2.kMaxArea, S2.kAvgArea);
                CheckMinMaxAvg("width", i, s.Count, 1e-15,
                               s.Min_width, s.Max_width, s.Avg_width,
                               S2.kMinWidth, S2.kMaxWidth, S2.kAvgWidth);
                CheckMinMaxAvg("edge", i, s.Count, 1e-15,
                               s.Min_edge, s.Max_edge, s.Avg_edge,
                               S2.kMinEdge, S2.kMaxEdge, S2.kAvgEdge);
                CheckMinMaxAvg("diagonal", i, s.Count, 1e-15,
                               s.Min_diag, s.Max_diag, s.Avg_diag,
                               S2.kMinDiag, S2.kMaxDiag, S2.kAvgDiag);
                CheckMinMaxAvg("angle span", i, s.Count, 1e-15,
                               s.Min_angle_span, s.Max_angle_span, s.Avg_angle_span,
                               S2.kMinAngleSpan, S2.kMaxAngleSpan, S2.kAvgAngleSpan);

                // The aspect ratio calculations are ratios of lengths and are therefore
                // less accurate at higher subdivision levels.
                Assert.True(s.Max_edge_aspect <= S2.kMaxEdgeAspect + 1e-15 * (1 << i));
                Assert.True(s.Max_diag_aspect <= S2.kMaxDiagAspect + 1e-15 * (1 << i));
            }
        }
Exemple #10
0
        static void TestSubdivide(S2Cell cell)
        {
            GatherStats(cell);
            if (cell.IsLeaf())
            {
                return;
            }

            var children = new S2Cell[4];

            Assert.True(cell.Subdivide(children));
            S2CellId child_id     = cell.Id.ChildBegin();
            double   exact_area   = 0;
            double   approx_area  = 0;
            double   average_area = 0;

            for (int i = 0; i < 4; ++i, child_id = child_id.Next())
            {
                exact_area   += children[i].ExactArea();
                approx_area  += children[i].ApproxArea();
                average_area += children[i].AverageArea();

                // Check that the child geometry is consistent with its cell ID.
                Assert.Equal(child_id, children[i].Id);
                Assert.True(S2.ApproxEquals(children[i].Center(), child_id.ToPoint()));
                S2Cell direct = new(child_id);
                Assert.Equal(direct.Face, children[i].Face);
                Assert.Equal(direct.Level, children[i].Level);
                Assert.Equal(direct.Orientation, children[i].Orientation);
                Assert.Equal(direct.CenterRaw(), children[i].CenterRaw());
                for (int k = 0; k < 4; ++k)
                {
                    Assert.Equal(direct.VertexRaw(k), children[i].VertexRaw(k));
                    Assert.Equal(direct.EdgeRaw(k), children[i].EdgeRaw(k));
                }

                // Test Contains() and MayIntersect().
                Assert.True(cell.Contains(children[i]));
                Assert.True(cell.MayIntersect(children[i]));
                Assert.False(children[i].Contains(cell));
                Assert.True(cell.Contains(children[i].CenterRaw()));
                for (int j = 0; j < 4; ++j)
                {
                    Assert.True(cell.Contains(children[i].VertexRaw(j)));
                    if (j != i)
                    {
                        Assert.False(children[i].Contains(children[j].CenterRaw()));
                        Assert.False(children[i].MayIntersect(children[j]));
                    }
                }

                // Test GetCapBound and GetRectBound.
                S2Cap        parent_cap  = cell.GetCapBound();
                S2LatLngRect parent_rect = cell.GetRectBound();
                if (cell.Contains(new S2Point(0, 0, 1)) || cell.Contains(new S2Point(0, 0, -1)))
                {
                    Assert.True(parent_rect.Lng.IsFull());
                }
                S2Cap        child_cap  = children[i].GetCapBound();
                S2LatLngRect child_rect = children[i].GetRectBound();
                Assert.True(child_cap.Contains(children[i].Center()));
                Assert.True(child_rect.Contains(children[i].CenterRaw()));
                Assert.True(parent_cap.Contains(children[i].Center()));
                Assert.True(parent_rect.Contains(children[i].CenterRaw()));
                for (int j = 0; j < 4; ++j)
                {
                    Assert.True(child_cap.Contains(children[i].Vertex(j)));
                    Assert.True(child_rect.Contains(children[i].Vertex(j)));
                    Assert.True(child_rect.Contains(children[i].VertexRaw(j)));
                    Assert.True(parent_cap.Contains(children[i].Vertex(j)));
                    Assert.True(parent_rect.Contains(children[i].Vertex(j)));
                    Assert.True(parent_rect.Contains(children[i].VertexRaw(j)));
                    if (j != i)
                    {
                        // The bounding caps and rectangles should be tight enough so that
                        // they exclude at least two vertices of each adjacent cell.
                        int cap_count  = 0;
                        int rect_count = 0;
                        for (int k = 0; k < 4; ++k)
                        {
                            if (child_cap.Contains(children[j].Vertex(k)))
                            {
                                ++cap_count;
                            }
                            if (child_rect.Contains(children[j].VertexRaw(k)))
                            {
                                ++rect_count;
                            }
                        }
                        Assert.True(cap_count <= 2);
                        if (child_rect.LatLo().Radians > -S2.M_PI_2 &&
                            child_rect.LatHi().Radians < S2.M_PI_2)
                        {
                            // Bounding rectangles may be too large at the poles because the
                            // pole itself has an arbitrary fixed longitude.
                            Assert.True(rect_count <= 2);
                        }
                    }
                }

                // Check all children for the first few levels, and then sample randomly.
                // We also always subdivide the cells containing a few chosen points so
                // that we have a better chance of sampling the minimum and maximum metric
                // values.  kMaxSizeUV is the absolute value of the u- and v-coordinate
                // where the cell size at a given level is maximal.
                double    kMaxSizeUV = 0.3964182625366691;
                R2Point[] special_uv =
                {
                    new R2Point(S2.DoubleEpsilon, S2.DoubleEpsilon), // Face center
                    new R2Point(S2.DoubleEpsilon, 1),                // Edge midpoint
                    new R2Point(1, 1),                               // Face corner
                    new R2Point(kMaxSizeUV, kMaxSizeUV),             // Largest cell area
                    new R2Point(S2.DoubleEpsilon, kMaxSizeUV),       // Longest edge/diagonal
                };
                bool force_subdivide = false;
                foreach (R2Point uv in special_uv)
                {
                    if (children[i].BoundUV.Contains(uv))
                    {
                        force_subdivide = true;
                    }
                }

                var debugFlag =
#if s2debug
                    true;
#else
                    false;
#endif

                if (force_subdivide ||
                    cell.Level < (debugFlag ? 5 : 6) ||
                    S2Testing.Random.OneIn(debugFlag ? 5 : 4))
                {
                    TestSubdivide(children[i]);
                }
            }

            // Check sum of child areas equals parent area.
            //
            // For ExactArea(), the best relative error we can expect is about 1e-6
            // because the precision of the unit vector coordinates is only about 1e-15
            // and the edge length of a leaf cell is about 1e-9.
            //
            // For ApproxArea(), the areas are accurate to within a few percent.
            //
            // For AverageArea(), the areas themselves are not very accurate, but
            // the average area of a parent is exactly 4 times the area of a child.

            Assert.True(Math.Abs(Math.Log(exact_area / cell.ExactArea())) <= Math.Abs(Math.Log((1 + 1e-6))));
            Assert.True(Math.Abs(Math.Log((approx_area / cell.ApproxArea()))) <= Math.Abs(Math.Log((1.03))));
            Assert.True(Math.Abs(Math.Log((average_area / cell.AverageArea()))) <= Math.Abs(Math.Log((1 + 1e-15))));
        }
        public void testSubdivide(S2Cell cell)
        {
            gatherStats(cell);
            if (cell.IsLeaf)
            {
                return;
            }

            var children = new S2Cell[4];
            for (var i = 0; i < children.Length; ++i)
            {
                children[i] = new S2Cell();
            }
            Assert.True(cell.Subdivide(children));
            var childId = cell.Id.ChildBegin;
            double exactArea = 0;
            double approxArea = 0;
            double averageArea = 0;
            for (var i = 0; i < 4; ++i, childId = childId.Next)
            {
                exactArea += children[i].ExactArea();
                approxArea += children[i].ApproxArea();
                averageArea += children[i].AverageArea();

                // Check that the child geometry is consistent with its cell id.
                JavaAssert.Equal(children[i].Id, childId);
                Assert.True(children[i].Center.ApproxEquals(childId.ToPoint(), 1e-15));
                var direct = new S2Cell(childId);
                JavaAssert.Equal(children[i].Face, direct.Face);
                JavaAssert.Equal(children[i].Level, direct.Level);
                JavaAssert.Equal(children[i].Orientation, direct.Orientation);
                JavaAssert.Equal(children[i].CenterRaw, direct.CenterRaw);
                for (var k = 0; k < 4; ++k)
                {
                    JavaAssert.Equal(children[i].GetVertexRaw(k), direct.GetVertexRaw(k));
                    JavaAssert.Equal(children[i].GetEdgeRaw(k), direct.GetEdgeRaw(k));
                }

                // Test Contains() and MayIntersect().
                Assert.True(cell.Contains(children[i]));
                Assert.True(cell.MayIntersect(children[i]));
                Assert.True(!children[i].Contains(cell));
                Assert.True(cell.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(cell.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        Assert.True(!children[i].Contains(children[j].CenterRaw));
                        Assert.True(!children[i].MayIntersect(children[j]));
                    }
                }

                // Test GetCapBound and GetRectBound.
                var parentCap = cell.CapBound;
                var parentRect = cell.RectBound;
                if (cell.Contains(new S2Point(0, 0, 1))
                    || cell.Contains(new S2Point(0, 0, -1)))
                {
                    Assert.True(parentRect.Lng.IsFull);
                }
                var childCap = children[i].CapBound;
                var childRect = children[i].RectBound;
                Assert.True(childCap.Contains(children[i].Center));
                Assert.True(childRect.Contains(children[i].CenterRaw));
                Assert.True(parentCap.Contains(children[i].Center));
                Assert.True(parentRect.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(childCap.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertexRaw(j)));
                    Assert.True(parentCap.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertex(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertex(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertex(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertexRaw(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertexRaw(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertexRaw(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        // The bounding caps and rectangles should be tight enough so that
                        // they exclude at least two vertices of each adjacent cell.
                        var capCount = 0;
                        var rectCount = 0;
                        for (var k = 0; k < 4; ++k)
                        {
                            if (childCap.Contains(children[j].GetVertex(k)))
                            {
                                ++capCount;
                            }
                            if (childRect.Contains(children[j].GetVertexRaw(k)))
                            {
                                ++rectCount;
                            }
                        }
                        Assert.True(capCount <= 2);
                        if (childRect.LatLo.Radians > -S2.PiOver2
                            && childRect.LatHi.Radians < S2.PiOver2)
                        {
                            // Bounding rectangles may be too large at the poles because the
                            // pole itself has an arbitrary fixed longitude.
                            Assert.True(rectCount <= 2);
                        }
                    }
                }

                // Check all children for the first few levels, and then sample randomly.
                // Also subdivide one corner cell, one edge cell, and one center cell
                // so that we have a better chance of sample the minimum metric values.
                var forceSubdivide = false;
                var center = S2Projections.GetNorm(children[i].Face);
                var edge = center + S2Projections.GetUAxis(children[i].Face);
                var corner = edge + S2Projections.GetVAxis(children[i].Face);
                for (var j = 0; j < 4; ++j)
                {
                    var p = children[i].GetVertexRaw(j);
                    if (p.Equals(center) || p.Equals(edge) || p.Equals(corner))
                    {
                        forceSubdivide = true;
                    }
                }
                if (forceSubdivide || cell.Level < (DEBUG_MODE ? 5 : 6)
                    || random(DEBUG_MODE ? 10 : 4) == 0)
                {
                    testSubdivide(children[i]);
                }
            }

            // Check sum of child areas equals parent area.
            //
            // For ExactArea(), the best relative error we can expect is about 1e-6
            // because the precision of the unit vector coordinates is only about 1e-15
            // and the edge length of a leaf cell is about 1e-9.
            //
            // For ApproxArea(), the areas are accurate to within a few percent.
            //
            // For AverageArea(), the areas themselves are not very accurate, but
            // the average area of a parent is exactly 4 times the area of a child.

            Assert.True(Math.Abs(Math.Log(exactArea/cell.ExactArea())) <= Math
                                                                              .Abs(Math.Log(1 + 1e-6)));
            Assert.True(Math.Abs(Math.Log(approxArea/cell.ApproxArea())) <= Math
                                                                                .Abs(Math.Log(1.03)));
            Assert.True(Math.Abs(Math.Log(averageArea/cell.AverageArea())) <= Math
                                                                                  .Abs(Math.Log(1 + 1e-15)));
        }