private static void gatherStats(S2Cell cell)
        {
            var    s = levelStats[cell.Level];
            var    exactArea = cell.ExactArea();
            var    approxArea = cell.ApproxArea();
            double minEdge = 100, maxEdge = 0, avgEdge = 0;
            double minDiag = 100, maxDiag = 0;
            double minWidth = 100, maxWidth = 0;
            double minAngleSpan = 100, maxAngleSpan = 0;

            for (var i = 0; i < 4; ++i)
            {
                var edge = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw((i + 1) & 3));
                minEdge  = Math.Min(edge, minEdge);
                maxEdge  = Math.Max(edge, maxEdge);
                avgEdge += 0.25 * edge;
                var mid   = cell.GetVertexRaw(i) + cell.GetVertexRaw((i + 1) & 3);
                var width = S2.PiOver2 - mid.Angle(cell.GetEdgeRaw(i ^ 2));
                minWidth = Math.Min(width, minWidth);
                maxWidth = Math.Max(width, maxWidth);
                if (i < 2)
                {
                    var diag = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw(i ^ 2));
                    minDiag = Math.Min(diag, minDiag);
                    maxDiag = Math.Max(diag, maxDiag);
                    var angleSpan = cell.GetEdgeRaw(i).Angle(
                        -cell.GetEdgeRaw(i ^ 2));
                    minAngleSpan = Math.Min(angleSpan, minAngleSpan);
                    maxAngleSpan = Math.Max(angleSpan, maxAngleSpan);
                }
            }
            s.count        += 1;
            s.minArea       = Math.Min(exactArea, s.minArea);
            s.maxArea       = Math.Max(exactArea, s.maxArea);
            s.avgArea      += exactArea;
            s.minWidth      = Math.Min(minWidth, s.minWidth);
            s.maxWidth      = Math.Max(maxWidth, s.maxWidth);
            s.avgWidth     += 0.5 * (minWidth + maxWidth);
            s.minEdge       = Math.Min(minEdge, s.minEdge);
            s.maxEdge       = Math.Max(maxEdge, s.maxEdge);
            s.avgEdge      += avgEdge;
            s.maxEdgeAspect = Math.Max(maxEdge / minEdge, s.maxEdgeAspect);
            s.minDiag       = Math.Min(minDiag, s.minDiag);
            s.maxDiag       = Math.Max(maxDiag, s.maxDiag);
            s.avgDiag      += 0.5 * (minDiag + maxDiag);
            s.maxDiagAspect = Math.Max(maxDiag / minDiag, s.maxDiagAspect);
            s.minAngleSpan  = Math.Min(minAngleSpan, s.minAngleSpan);
            s.maxAngleSpan  = Math.Max(maxAngleSpan, s.maxAngleSpan);
            s.avgAngleSpan += 0.5 * (minAngleSpan + maxAngleSpan);
            var approxRatio = approxArea / exactArea;

            s.minApproxRatio = Math.Min(approxRatio, s.minApproxRatio);
            s.maxApproxRatio = Math.Max(approxRatio, s.maxApproxRatio);
        }
Beispiel #2
0
        private static void GatherStats(S2Cell cell)
        {
            var    s = level_stats[cell.Level];
            double exact_area = cell.ExactArea();
            double approx_area = cell.ApproxArea();
            double min_edge = 100, max_edge = 0, avg_edge = 0;
            double min_diag = 100, max_diag = 0;
            double min_width = 100, max_width = 0;
            double min_angle_span = 100, max_angle_span = 0;

            for (int i = 0; i < 4; ++i)
            {
                double edge = cell.VertexRaw(i).Angle(cell.VertexRaw(i + 1));
                min_edge  = Math.Min(edge, min_edge);
                max_edge  = Math.Max(edge, max_edge);
                avg_edge += 0.25 * edge;
                S2Point mid   = cell.VertexRaw(i) + cell.VertexRaw(i + 1);
                double  width = S2.M_PI_2 - mid.Angle(cell.EdgeRaw(i + 2));
                min_width = Math.Min(width, min_width);
                max_width = Math.Max(width, max_width);
                if (i < 2)
                {
                    double diag = cell.VertexRaw(i).Angle(cell.VertexRaw(i + 2));
                    min_diag = Math.Min(diag, min_diag);
                    max_diag = Math.Max(diag, max_diag);
                    double angle_span = cell.EdgeRaw(i).Angle(-cell.EdgeRaw(i + 2));
                    min_angle_span = Math.Min(angle_span, min_angle_span);
                    max_angle_span = Math.Max(angle_span, max_angle_span);
                }
            }
            s.Count          += 1;
            s.Min_area        = Math.Min(exact_area, s.Min_area);
            s.Max_area        = Math.Max(exact_area, s.Max_area);
            s.Avg_area       += exact_area;
            s.Min_width       = Math.Min(min_width, s.Min_width);
            s.Max_width       = Math.Max(max_width, s.Max_width);
            s.Avg_width      += 0.5 * (min_width + max_width);
            s.Min_edge        = Math.Min(min_edge, s.Min_edge);
            s.Max_edge        = Math.Max(max_edge, s.Max_edge);
            s.Avg_edge       += avg_edge;
            s.Max_edge_aspect = Math.Max(max_edge / min_edge, s.Max_edge_aspect);
            s.Min_diag        = Math.Min(min_diag, s.Min_diag);
            s.Max_diag        = Math.Max(max_diag, s.Max_diag);
            s.Avg_diag       += 0.5 * (min_diag + max_diag);
            s.Max_diag_aspect = Math.Max(max_diag / min_diag, s.Max_diag_aspect);
            s.Min_angle_span  = Math.Min(min_angle_span, s.Min_angle_span);
            s.Max_angle_span  = Math.Max(max_angle_span, s.Max_angle_span);
            s.Avg_angle_span += 0.5 * (min_angle_span + max_angle_span);
            double approx_ratio = approx_area / exact_area;

            s.Min_approx_ratio = Math.Min(approx_ratio, s.Min_approx_ratio);
            s.Max_approx_ratio = Math.Max(approx_ratio, s.Max_approx_ratio);
        }
 private static void gatherStats(S2Cell cell)
 {
     var s = levelStats[cell.Level];
     var exactArea = cell.ExactArea();
     var approxArea = cell.ApproxArea();
     double minEdge = 100, maxEdge = 0, avgEdge = 0;
     double minDiag = 100, maxDiag = 0;
     double minWidth = 100, maxWidth = 0;
     double minAngleSpan = 100, maxAngleSpan = 0;
     for (var i = 0; i < 4; ++i)
     {
         var edge = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw((i + 1) & 3));
         minEdge = Math.Min(edge, minEdge);
         maxEdge = Math.Max(edge, maxEdge);
         avgEdge += 0.25*edge;
         var mid = cell.GetVertexRaw(i) + cell.GetVertexRaw((i + 1) & 3);
         var width = S2.PiOver2 - mid.Angle(cell.GetEdgeRaw(i ^ 2));
         minWidth = Math.Min(width, minWidth);
         maxWidth = Math.Max(width, maxWidth);
         if (i < 2)
         {
             var diag = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw(i ^ 2));
             minDiag = Math.Min(diag, minDiag);
             maxDiag = Math.Max(diag, maxDiag);
             var angleSpan = cell.GetEdgeRaw(i).Angle(
                 -cell.GetEdgeRaw(i ^ 2));
             minAngleSpan = Math.Min(angleSpan, minAngleSpan);
             maxAngleSpan = Math.Max(angleSpan, maxAngleSpan);
         }
     }
     s.count += 1;
     s.minArea = Math.Min(exactArea, s.minArea);
     s.maxArea = Math.Max(exactArea, s.maxArea);
     s.avgArea += exactArea;
     s.minWidth = Math.Min(minWidth, s.minWidth);
     s.maxWidth = Math.Max(maxWidth, s.maxWidth);
     s.avgWidth += 0.5*(minWidth + maxWidth);
     s.minEdge = Math.Min(minEdge, s.minEdge);
     s.maxEdge = Math.Max(maxEdge, s.maxEdge);
     s.avgEdge += avgEdge;
     s.maxEdgeAspect = Math.Max(maxEdge/minEdge, s.maxEdgeAspect);
     s.minDiag = Math.Min(minDiag, s.minDiag);
     s.maxDiag = Math.Max(maxDiag, s.maxDiag);
     s.avgDiag += 0.5*(minDiag + maxDiag);
     s.maxDiagAspect = Math.Max(maxDiag/minDiag, s.maxDiagAspect);
     s.minAngleSpan = Math.Min(minAngleSpan, s.minAngleSpan);
     s.maxAngleSpan = Math.Max(maxAngleSpan, s.maxAngleSpan);
     s.avgAngleSpan += 0.5*(minAngleSpan + maxAngleSpan);
     var approxRatio = approxArea/exactArea;
     s.minApproxRatio = Math.Min(approxRatio, s.minApproxRatio);
     s.maxApproxRatio = Math.Max(approxRatio, s.maxApproxRatio);
 }
        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)));
        }
Beispiel #5
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)));
        }