Пример #1
0
        private static S1ChordAngle GetMaxDistanceToEdgeBruteForce(S2Cell cell, S2Point a, S2Point b)
        {
            // If any antipodal endpoint is within the cell, the max distance is Pi.
            if (cell.Contains(-a) || cell.Contains(-b))
            {
                return(S1ChordAngle.Straight);
            }

            S1ChordAngle max_dist = S1ChordAngle.Negative;

            for (int i = 0; i < 4; ++i)
            {
                S2Point v0 = cell.Vertex(i);
                S2Point v1 = cell.Vertex(i + 1);
                // If the antipodal edge crosses through the cell, max distance is Pi.
                if (S2.CrossingSign(-a, -b, v0, v1) >= 0)
                {
                    return(S1ChordAngle.Straight);
                }
                S2.UpdateMaxDistance(a, v0, v1, ref max_dist);
                S2.UpdateMaxDistance(b, v0, v1, ref max_dist);
                S2.UpdateMaxDistance(v0, a, b, ref max_dist);
            }
            return(max_dist);
        }
Пример #2
0
    // Returns true if any buffered shape intersects "cell" (to within a very
    // small error margin).
    public bool MayIntersect(S2Cell cell)
    {
        // Return true if the distance is less than or equal to "radius_".
        var target = new S2ClosestEdgeQuery.CellTarget(cell);

        return(query_.IsDistanceLess(target, radius_successor_));
    }
Пример #3
0
        /// <summary>
        /// Find the cell of the given level that covers the given Geoposition.
        /// This method uses the library given S2RegionCoverer to find the leaf cell containing the given Geoposition
        /// and uses binary operation on the leaf cell's Id to find the higher level cell containing the leaf.
        /// This is somehow more accurate than using the S2RegionCoverer for the higher level cell, where some errors occured.
        /// </summary>
        /// <param name="pos">Position in the cell</param>
        /// <param name="level">Level of the cell</param>
        /// <returns>The cell covering the given position that matches the specifications</returns>
        private S2Cell FindExactCell(BasicGeoposition pos, int level)
        {
            var point = S2LatLngRect.FromPoint(S2LatLng.FromDegrees(pos.Latitude, pos.Longitude));

            var cells = new List <S2CellId>();

            // find leaf cell
            var coverer = new S2RegionCoverer()
            {
                MinLevel = 30,
                MaxLevel = 30,
                MaxCells = 1
            };

            coverer.GetCovering(point, cells);

            var leaf = new S2Cell(cells[0]);

            int   shift = 64 - (3 + level * 2 + 1);
            ulong id    = leaf.Id.Id & ulong.MaxValue << shift;

            id |= 0 | ((ulong)1 << shift);

            return(new S2Cell(new S2CellId(id)));
        }
Пример #4
0
    // Populates the children of "candidate" by expanding the given number of
    // levels from the given cell.  Returns the number of children that were
    // marked "terminal".
    private int ExpandChildren(Candidate candidate, S2Cell cell, int num_levels)
    {
        num_levels--;
        var child_cells = new S2Cell[4];

        cell.Subdivide(child_cells);
        int num_terminals = 0;

        for (int i = 0; i < 4; ++i)
        {
            if (num_levels > 0)
            {
                if (region_.MayIntersect(child_cells[i]))
                {
                    num_terminals += ExpandChildren(candidate, child_cells[i], num_levels);
                }
                continue;
            }
            var child = NewCandidate(child_cells[i]);
            if (child != null)
            {
                candidate.Children[candidate.NumChildren++] = child;
                if (child.IsTerminal)
                {
                    ++num_terminals;
                }
            }
        }
        return(num_terminals);
    }
Пример #5
0
        }                                           // Actual size may be 0, 4, 16, or 64 elements.

        public Candidate(S2Cell cell, int max_children)
        {
            Cell        = cell;
            IsTerminal  = max_children == 0;
            Children    = new Candidate[max_children].Fill(() => null);
            NumChildren = 0;
        }
        private async Task <S2Cell> GetS2Cell(ulong s2CellId)
        {
            if (this.s2CellsCache.TryGetValue(s2CellId, out S2Cell s2Cell) == false)
            {
                var getS2CellResult = await PlayablesManager.Instance.GetS2Cell(new GetS2CellRequest
                {
                    CurrentLatLong = GPSManager.Instance.CurrentRawLatLong,
                    S2CellId       = s2CellId,
                });

                s2Cell = new S2Cell
                {
                    S2CellId      = s2CellId,
                    Locations     = getS2CellResult.Locations,
                    ExpirationUtc = DateTime.UtcNow.AddSeconds(getS2CellResult.SecondsToLive),
                };

                if (this.disableCaching == false)
                {
                    this.s2CellsCache.Add(s2Cell.S2CellId, s2Cell);
                }
            }

            return(s2Cell);
        }
Пример #7
0
        public List <S2CellId> GetS2CellIds(ushort level, int maxCells)
        {
            //var geofence = Geofence.FromMultiPolygon(this);
            var bbox          = GetBoundingBox();
            var regionCoverer = new S2RegionCoverer
            {
                MinLevel = level,
                MaxLevel = level,
                MaxCells = maxCells,
            };
            var region = new S2LatLngRect(
                S2LatLng.FromDegrees(bbox.MinimumLatitude, bbox.MinimumLongitude),
                S2LatLng.FromDegrees(bbox.MaximumLatitude, bbox.MaximumLongitude)
                );
            var coverage = new List <S2CellId>();

            regionCoverer.GetCovering(region, coverage);
            var result = new List <S2CellId>();

            foreach (var cellId in coverage)
            {
                var cell = new S2Cell(cellId);
                for (var i = 0; i <= 3; i++)
                {
                    var vertex = cell.GetVertex(i);
                    var coord  = new S2LatLng(new S2Point(vertex.X, vertex.Y, vertex.Z));
                    //if (geofence.Intersects(coord.LatDegrees, coord.LngDegrees))
                    if (GeofenceService.InPolygon(this, coord.LatDegrees, coord.LngDegrees))
                    {
                        result.Add(cellId);
                    }
                }
            }
            return(result);
        }
Пример #8
0
        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;
            }
        }
Пример #9
0
        public void Test_S2ClosestCellQuery_DistanceEqualToLimit()
        {
            // Tests the behavior of IsDistanceLess, IsDistanceLessOrEqual, and
            // IsConservativeDistanceLessOrEqual (and the corresponding Options) when
            // the distance to the target exactly equals the chosen limit.
            S2CellId    id0 = new(MakePointOrDie("23:12")), id1 = new(MakePointOrDie("47:11"));
            S2CellIndex index = new();

            index.Add(id0, 0);
            index.Build();
            S2ClosestCellQuery query = new(index);

            // Start with two identical cells and a zero distance.
            S2ClosestCellQuery.CellTarget target0 = new(new S2Cell(id0));
            S1ChordAngle dist0 = S1ChordAngle.Zero;

            Assert.False(query.IsDistanceLess(target0, dist0));
            Assert.True(query.IsDistanceLessOrEqual(target0, dist0));
            Assert.True(query.IsConservativeDistanceLessOrEqual(target0, dist0));

            // Now try two cells separated by a non-zero distance.
            S2ClosestCellQuery.CellTarget target1 = new(new S2Cell(id1));
            S1ChordAngle dist1 = new S2Cell(id0).Distance(new S2Cell(id1));

            Assert.False(query.IsDistanceLess(target1, dist1));
            Assert.True(query.IsDistanceLessOrEqual(target1, dist1));
            Assert.True(query.IsConservativeDistanceLessOrEqual(target1, dist1));
        }
Пример #10
0
        public static ulong GenerateGeohash(GeoPoint geoPoint)
        {
            var latLng = S2LatLng.FromDegrees(geoPoint.lat, geoPoint.lng);
            var cell   = new S2Cell(latLng);
            var cellId = cell.Id;

            return(cellId.Id);
        }
        public static ulong GenerateGeohash(double latitude, double longitude)
        {
            var latLng = S2LatLng.FromDegrees(latitude, longitude);
            var cell   = new S2Cell(latLng);
            var cellId = cell.Id;

            return(cellId.Id);
        }
Пример #12
0
    public void Test_S2LatLngRect_CellOps()
    {
        // Contains(S2Cell), MayIntersect(S2Cell), Intersects(S2Cell)

        // Special cases.
        TestCellOps(S2LatLngRect.Empty, S2Cell.FromFacePosLevel(3, 0, 0), 0);
        TestCellOps(S2LatLngRect.Full, S2Cell.FromFacePosLevel(2, 0, 0), 4);
        TestCellOps(S2LatLngRect.Full, S2Cell.FromFacePosLevel(5, 0, 25), 4);

        // This rectangle includes the first quadrant of face 0.  It's expanded
        // slightly because cell bounding rectangles are slightly conservative.
        S2LatLngRect r4 = RectFromDegrees(-45.1, -45.1, 0.1, 0.1);

        TestCellOps(r4, S2Cell.FromFacePosLevel(0, 0, 0), 3);
        TestCellOps(r4, S2Cell.FromFacePosLevel(0, 0, 1), 4);
        TestCellOps(r4, S2Cell.FromFacePosLevel(1, 0, 1), 0);

        // This rectangle intersects the first quadrant of face 0.
        S2LatLngRect r5 = RectFromDegrees(-10, -45, 10, 0);

        TestCellOps(r5, S2Cell.FromFacePosLevel(0, 0, 0), 3);
        TestCellOps(r5, S2Cell.FromFacePosLevel(0, 0, 1), 3);
        TestCellOps(r5, S2Cell.FromFacePosLevel(1, 0, 1), 0);

        // Rectangle consisting of a single point.
        TestCellOps(RectFromDegrees(4, 4, 4, 4), S2Cell.FromFace(0), 3);

        // Rectangles that intersect the bounding rectangle of a face
        // but not the face itself.
        TestCellOps(RectFromDegrees(41, -87, 42, -79), S2Cell.FromFace(2), 1);
        TestCellOps(RectFromDegrees(-41, 160, -40, -160), S2Cell.FromFace(5), 1);

        // This is the leaf cell at the top right hand corner of face 0.
        // It has two angles of 60 degrees and two of 120 degrees.
        S2Cell cell0tr = new(new S2Point(1 + 1e-12, 1, 1));

        _ = cell0tr.GetRectBound();
        S2LatLng v0 = new(cell0tr.VertexRaw(0));

        TestCellOps(RectFromDegrees(v0.Lat().GetDegrees() - 1e-8,
                                    v0.Lng().GetDegrees() - 1e-8,
                                    v0.Lat().GetDegrees() - 2e-10,
                                    v0.Lng().GetDegrees() + 1e-10), cell0tr, 1);

        // Rectangles that intersect a face but where no vertex of one region
        // is contained by the other region.  The first one passes through
        // a corner of one of the face cells.
        TestCellOps(RectFromDegrees(-37, -70, -36, -20), S2Cell.FromFace(5), 2);

        // These two intersect like a diamond and a square.
        S2Cell       cell202  = S2Cell.FromFacePosLevel(2, 0, 2);
        S2LatLngRect bound202 = cell202.GetRectBound();

        TestCellOps(RectFromDegrees(bound202.Lo().Lat().GetDegrees() + 3,
                                    bound202.Lo().Lng().GetDegrees() + 3,
                                    bound202.Hi().Lat().GetDegrees() - 3,
                                    bound202.Hi().Lng().GetDegrees() - 3), cell202, 2);
    }
Пример #13
0
        public void Test_ShapeIndexTarget_UpdateMinDistanceToCellWhenEqual()
        {
            var target_index = MakeIndexOrDie("1:0 # #");
            var target       = new S2MinDistanceShapeIndexTarget(target_index);
            var dist         = S1ChordAngle.Infinity;
            var cell         = new S2Cell(new S2CellId(MakePointOrDie("0:0")));

            Assert.True(target.UpdateMinDistance(cell, ref dist));
            Assert.False(target.UpdateMinDistance(cell, ref dist));
        }
Пример #14
0
        public void Test_CellTarget_UpdateMinDistanceToCellWhenEqual()
        {
            var target = new S2MinDistanceCellTarget(new S2Cell(
                                                         new S2CellId(MakePointOrDie("0:1"))));
            var dist = S1ChordAngle.Infinity;
            var cell = new S2Cell(new S2CellId(MakePointOrDie("0:0")));

            Assert.True(target.UpdateMinDistance(cell, ref dist));
            Assert.False(target.UpdateMinDistance(cell, ref dist));
        }
Пример #15
0
        private void TestCell(S2Cell target)
        {
            // Indicates whether each shape that intersects "target" also contains it.
            Dictionary <int, bool> shape_contains = new();

            Assert.True(region_.VisitIntersectingShapes(
                            target, (S2Shape shape, bool contains_target) => {
                // Verify that each shape is visited at most once.
                Assert.False(shape_contains.ContainsKey(shape.Id));
                shape_contains[shape.Id] = contains_target;
                return(true);
            }));
            for (int s = 0; s < index_.NumShapeIds(); ++s)
            {
                var shape_region = shape_indexes_[s].MakeS2ShapeIndexRegion();
                if (!shape_region.MayIntersect(target))
                {
                    Assert.False(shape_contains.ContainsKey(s));
                }
                else
                {
                    Assert.Equal(shape_contains[s], shape_region.Contains(target));
                }
            }
            var(cellRelation, pos) = index_.LocateCell(target.Id);
            iter_.SetPosition(pos);
            switch (cellRelation)
            {
            case S2ShapeIndex.CellRelation.DISJOINT:
                return;

            case S2ShapeIndex.CellRelation.SUBDIVIDED:
            {
                S2Cell[] children = new S2Cell[4];
                Assert.True(target.Subdivide(children));
                foreach (var child in children)
                {
                    TestCell(child);
                }
                return;
            }

            case S2ShapeIndex.CellRelation.INDEXED:
            {
                // We check a few random descendant cells by continuing randomly down
                // one branch of the tree for a few levels.
                if (target.IsLeaf() || S2Testing.Random.OneIn(3))
                {
                    return;
                }
                TestCell(new S2Cell(target.Id.Child(S2Testing.Random.Uniform(4))));
                return;
            }
            }
        }
        private S2Loop makeCellLoop(S2CellId begin, S2CellId end)
        {
            // Construct a CCW polygon whose boundary is the union of the cell ids
            // in the range [begin, end). We Add the edges one by one, removing
            // any edges that are already present in the opposite direction.

            IDictionary <S2Point, ISet <S2Point> > edges = new Dictionary <S2Point, ISet <S2Point> >();

            for (var id = begin; !id.Equals(end); id = id.Next)
            {
                var cell = new S2Cell(id);
                for (var k = 0; k < 4; ++k)
                {
                    var a = cell.GetVertex(k);
                    var b = cell.GetVertex((k + 1) & 3);
                    if (!edges.ContainsKey(b))
                    {
                        edges.Add(b, new HashSet <S2Point>());
                    }
                    // if a is in b's set, remove it and remove b's set if it's empty
                    // otherwise, Add b to a's set
                    if (!edges[b].Remove(a))
                    {
                        if (!edges.ContainsKey(a))
                        {
                            edges.Add(a, new HashSet <S2Point>());
                        }
                        edges[a].Add(b);
                    }
                    else if (edges[b].Count == 0)
                    {
                        edges.Remove(b);
                    }
                }
            }

            // The remaining edges form a single loop. We simply follow it starting
            // at an arbitrary vertex and build up a list of vertices.

            var vertices = new List <S2Point>();
            //S2Point p = edges.keySet().iterator().next();
            var p = edges.Keys.First();

            while (edges.Any())
            {
                assertEquals(1, edges[p].Count);
                var next = edges[p].First();
                //S2Point next = edges[p].iterator().next();
                vertices.Add(p);
                edges.Remove(p);
                p = next;
            }

            return(new S2Loop(vertices));
        }
Пример #17
0
        private static S1ChordAngle GetDistanceToPointBruteForce(S2Cell cell, S2Point target)
        {
            S1ChordAngle min_distance = S1ChordAngle.Infinity;

            for (int i = 0; i < 4; ++i)
            {
                S2.UpdateMinDistance(target, cell.Vertex(i),
                                     cell.Vertex(i + 1), ref min_distance);
            }
            return(min_distance);
        }
        public void testFaces()
        {
            IDictionary <S2Point, int> edgeCounts   = new Dictionary <S2Point, int>();
            IDictionary <S2Point, int> vertexCounts = new Dictionary <S2Point, int>();

            for (var face = 0; face < 6; ++face)
            {
                var id   = S2CellId.FromFacePosLevel(face, 0, 0);
                var cell = new S2Cell(id);
                JavaAssert.Equal(cell.Id, id);
                JavaAssert.Equal(cell.Face, face);
                JavaAssert.Equal(cell.Level, 0);
                // Top-level faces have alternating orientations to get RHS coordinates.
                JavaAssert.Equal(cell.Orientation, face & S2.SwapMask);
                Assert.True(!cell.IsLeaf);
                for (var k = 0; k < 4; ++k)
                {
                    if (edgeCounts.ContainsKey(cell.GetEdgeRaw(k)))
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = edgeCounts[cell
                                                                    .GetEdgeRaw(k)] + 1;
                    }
                    else
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = 1;
                    }

                    if (vertexCounts.ContainsKey(cell.GetVertexRaw(k)))
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = vertexCounts[cell
                                                                          .GetVertexRaw(k)] + 1;
                    }
                    else
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = 1;
                    }
                    assertDoubleNear(cell.GetVertexRaw(k).DotProd(cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(cell.GetVertexRaw((k + 1) & 3).DotProd(
                                         cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(S2Point.Normalize(
                                         S2Point.CrossProd(cell.GetVertexRaw(k), cell
                                                           .GetVertexRaw((k + 1) & 3))).DotProd(cell.GetEdge(k)), 1.0);
                }
            }
            // Check that edges have multiplicity 2 and vertices have multiplicity 3.
            foreach (var i in edgeCounts.Values)
            {
                JavaAssert.Equal(i, 2);
            }
            foreach (var i in vertexCounts.Values)
            {
                JavaAssert.Equal(i, 3);
            }
        }
        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);
        }
Пример #20
0
 public bool Contains(S2Cell cell)
 {
     for (int i = 0; i < Regions.Length; ++i)
     {
         if (!Region(i).Contains(cell))
         {
             return(false);
         }
     }
     return(true);
 }
Пример #21
0
        private static void CompareS2CellToPadded(S2Cell cell, S2PaddedCell pcell, double padding)
        {
            Assert.Equal(cell.Id, pcell.Id);
            Assert.Equal(cell.Level, pcell.Level);
            Assert.Equal(padding, pcell.Padding);
            Assert.Equal(cell.BoundUV.Expanded(padding), pcell.Bound);
            var center_uv = cell.Id.CenterUV();

            Assert.Equal(R2Rect.FromPoint(center_uv).Expanded(padding), pcell.Middle);
            Assert.Equal(cell.Center(), pcell.GetCenter());
        }
Пример #22
0
        public Cell(ulong cellId)
        {
            var s2cell = new S2Cell(new S2CellId(cellId));
            var center = s2cell.RectBound.Center;

            Id        = cellId;
            Latitude  = center.LatDegrees;
            Longitude = center.LngDegrees;
            Level     = 15;
            Updated   = DateTime.UtcNow.ToTotalSeconds();
        }
Пример #23
0
 public bool MayIntersect(S2Cell cell)
 {
     for (int i = 0; i < Regions.Length; ++i)
     {
         if (!Region(i).MayIntersect(cell))
         {
             return(false);
         }
     }
     return(true);
 }
Пример #24
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);
        }
Пример #25
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 testContains()
        {
            assertTrue(candyCane.Contains(S2LatLng.FromDegrees(5, 71).ToPoint()));
            for (var i = 0; i < 4; ++i)
            {
                assertTrue(northHemi.Contains(new S2Point(0, 0, 1)));
                assertTrue(!northHemi.Contains(new S2Point(0, 0, -1)));
                assertTrue(!southHemi.Contains(new S2Point(0, 0, 1)));
                assertTrue(southHemi.Contains(new S2Point(0, 0, -1)));
                assertTrue(!westHemi.Contains(new S2Point(0, 1, 0)));
                assertTrue(westHemi.Contains(new S2Point(0, -1, 0)));
                assertTrue(eastHemi.Contains(new S2Point(0, 1, 0)));
                assertTrue(!eastHemi.Contains(new S2Point(0, -1, 0)));
                northHemi = rotate(northHemi);
                southHemi = rotate(southHemi);
                eastHemi  = rotate(eastHemi);
                westHemi  = rotate(westHemi);
            }

            // This code checks each cell vertex is contained by exactly one of
            // the adjacent cells.
            for (var level = 0; level < 3; ++level)
            {
                var            loops        = new List <S2Loop>();
                var            loopVertices = new List <S2Point>();
                ISet <S2Point> points       = new HashSet <S2Point>();
                for (var id = S2CellId.Begin(level); !id.Equals(S2CellId.End(level)); id = id.Next)
                {
                    var cell = new S2Cell(id);
                    points.Add(cell.Center);
                    for (var k = 0; k < 4; ++k)
                    {
                        loopVertices.Add(cell.GetVertex(k));
                        points.Add(cell.GetVertex(k));
                    }
                    loops.Add(new S2Loop(loopVertices));
                    loopVertices.Clear();
                }
                foreach (var point in points)
                {
                    var count = 0;
                    for (var j = 0; j < loops.Count; ++j)
                    {
                        if (loops[j].Contains(point))
                        {
                            ++count;
                        }
                    }
                    assertEquals(count, 1);
                }
            }
        }
 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);
 }
Пример #28
0
    public void Test_S2ShapeIndexRegion_GetRectBound()
    {
        var id = S2CellId.FromDebugString("3/0123012301230123012301230123");

        // Add a polygon that is slightly smaller than the cell being tested.
        MutableS2ShapeIndex index = new();

        index.Add(NewPaddedCell(id, -kPadding));
        S2LatLngRect cell_bound  = new S2Cell(id).GetRectBound();
        S2LatLngRect index_bound = index.MakeS2ShapeIndexRegion().GetRectBound();

        Assert.Equal(index_bound, cell_bound);
    }
        public void expandChildren1(S2Cell cell)
        {
            var children = new S2Cell[4];

            Assert.True(cell.Subdivide(children));
            if (children[0].Level < MAX_LEVEL)
            {
                for (var pos = 0; pos < 4; ++pos)
                {
                    expandChildren1(children[pos]);
                }
            }
        }
        public void expandChildren2(S2Cell cell)
        {
            var id = cell.Id.ChildBegin;

            for (var pos = 0; pos < 4; ++pos, id = id.Next)
            {
                var child = new S2Cell(id);
                if (child.Level < MAX_LEVEL)
                {
                    expandChildren2(child);
                }
            }
        }
Пример #31
0
        public void Test_S2RegionUnionTest_Basic()
        {
            S2RegionUnion ru_empty = new(new List <IS2Region>());

            Assert.Equal(0, ru_empty.Count());
            Assert.Equal(S2Cap.Empty, ru_empty.GetCapBound());
            Assert.Equal(S2LatLngRect.Empty, ru_empty.GetRectBound());
            var empty_clone = (S2RegionUnion)ru_empty.CustomClone();

            var two_point_region = new List <IS2Region>
            {
                new S2PointRegion(S2LatLng.FromDegrees(35, 40).ToPoint()),
                new S2PointRegion(S2LatLng.FromDegrees(-35, -40).ToPoint())
            };

            var two_points_orig = new S2RegionUnion(two_point_region);
            // two_point_region is in a valid, but unspecified, state.

            // Check that Clone() returns a deep copy.
            var two_points = (S2RegionUnion)two_points_orig.CustomClone();

            // The bounds below may not be exactly equal because the S2PointRegion
            // version converts each S2LatLng value to an S2Point and back.
            Assert.True(MakeLatLngRectOrDie("-35:-40,35:40") !.Value
                        .ApproxEquals(two_points.GetRectBound()));

            S2Cell face0 = S2Cell.FromFace(0);

            Assert.True(two_points.MayIntersect(face0));
            Assert.False(two_points.Contains(face0));

            Assert.True(two_points.Contains(S2LatLng.FromDegrees(35, 40).ToPoint()));
            Assert.True(two_points.Contains(S2LatLng.FromDegrees(-35, -40).ToPoint()));
            Assert.False(two_points.Contains(S2LatLng.FromDegrees(0, 0).ToPoint()));

            // Check that we can Add() another region.
            var three_points = (S2RegionUnion)two_points.CustomClone();

            Assert.False(three_points.Contains(S2LatLng.FromDegrees(10, 10).ToPoint()));
            three_points.Add(new S2RegionUnion(new List <IS2Region> {
                new S2PointRegion(S2LatLng.FromDegrees(10, 10).ToPoint())
            }));
            Assert.True(three_points.Contains(S2LatLng.FromDegrees(10, 10).ToPoint()));

            var coverer = new S2RegionCoverer();

            coverer.Options_.MaxCells = 1;
            coverer.GetCovering(two_points, out var covering);
            Assert.Single(covering);
            Assert.Equal(face0.Id, covering[0]);
        }
        public static void DrawS2Cells(List<ulong> cellsIds, GMapOverlay mapLayer)
        {
            for (int i=0; i<cellsIds.Count; i++)
            {
                S2CellId cellId = new S2CellId(cellsIds[i]);
                S2Cell cell = new S2Cell(cellId);

                List<PointLatLng> points = new List<PointLatLng>();
                for (int j=0; j<4; j++)
                {
                    S2LatLng point = new S2LatLng(cell.GetVertex(j));
                    points.Add(new PointLatLng(point.LatDegrees, point.LngDegrees));
                }
                GMapPolygon polygon = new GMapPolygon(points, "mypolygon");
                polygon.Fill = new SolidBrush(Color.FromArgb(50, Color.Red));
                polygon.Stroke = new Pen(Color.Red, 1);
                mapLayer.Polygons.Add(polygon);
            }
        }
        public void testCellOps(S2LatLngRect r, S2Cell cell, int level)
        {
            // Test the relationship between the given rectangle and cell:
            // 0 == no intersection, 1 == MayIntersect, 2 == Intersects,
            // 3 == Vertex Containment, 4 == Contains

            var vertexContained = false;
            for (var i = 0; i < 4; ++i)
            {
                if (r.Contains(cell.GetVertexRaw(i))
                    || (!r.IsEmpty && cell.Contains(r.GetVertex(i).ToPoint())))
                {
                    vertexContained = true;
                }
            }
            assertEquals(r.MayIntersect(cell), level >= 1);
            assertEquals(r.Intersects(cell), level >= 2);
            assertEquals(vertexContained, level >= 3);
            assertEquals(r.Contains(cell), level >= 4);
        }
 public void expandChildren1(S2Cell cell)
 {
     var children = new S2Cell[4];
     Assert.True(cell.Subdivide(children));
     if (children[0].Level < MAX_LEVEL)
     {
         for (var pos = 0; pos < 4; ++pos)
         {
             expandChildren1(children[pos]);
         }
     }
 }
        private S2Loop makeCellLoop(S2CellId begin, S2CellId end)
        {
            // Construct a CCW polygon whose boundary is the union of the cell ids
            // in the range [begin, end). We Add the edges one by one, removing
            // any edges that are already present in the opposite direction.

            IDictionary<S2Point, ISet<S2Point>> edges = new Dictionary<S2Point, ISet<S2Point>>();
            for (var id = begin; !id.Equals(end); id = id.Next)
            {
                var cell = new S2Cell(id);
                for (var k = 0; k < 4; ++k)
                {
                    var a = cell.GetVertex(k);
                    var b = cell.GetVertex((k + 1) & 3);
                    if (!edges.ContainsKey(b))
                    {
                        edges.Add(b, new HashSet<S2Point>());
                    }
                    // if a is in b's set, remove it and remove b's set if it's empty
                    // otherwise, Add b to a's set
                    if (!edges[b].Remove(a))
                    {
                        if (!edges.ContainsKey(a))
                        {
                            edges.Add(a, new HashSet<S2Point>());
                        }
                        edges[a].Add(b);
                    }
                    else if (edges[b].Count == 0)
                    {
                        edges.Remove(b);
                    }
                }
            }

            // The remaining edges form a single loop. We simply follow it starting
            // at an arbitrary vertex and build up a list of vertices.

            var vertices = new List<S2Point>();
            //S2Point p = edges.keySet().iterator().next();
            var p = edges.Keys.First();
            while (edges.Any())
            {
                assertEquals(1, edges[p].Count);
                var next = edges[p].First();
                //S2Point next = edges[p].iterator().next();
                vertices.Add(p);
                edges.Remove(p);
                p = next;
            }

            return new S2Loop(vertices);
        }
        public void testContains()
        {
            assertTrue(candyCane.Contains(S2LatLng.FromDegrees(5, 71).ToPoint()));
            for (var i = 0; i < 4; ++i)
            {
                assertTrue(northHemi.Contains(new S2Point(0, 0, 1)));
                assertTrue(!northHemi.Contains(new S2Point(0, 0, -1)));
                assertTrue(!southHemi.Contains(new S2Point(0, 0, 1)));
                assertTrue(southHemi.Contains(new S2Point(0, 0, -1)));
                assertTrue(!westHemi.Contains(new S2Point(0, 1, 0)));
                assertTrue(westHemi.Contains(new S2Point(0, -1, 0)));
                assertTrue(eastHemi.Contains(new S2Point(0, 1, 0)));
                assertTrue(!eastHemi.Contains(new S2Point(0, -1, 0)));
                northHemi = rotate(northHemi);
                southHemi = rotate(southHemi);
                eastHemi = rotate(eastHemi);
                westHemi = rotate(westHemi);
            }

            // This code checks each cell vertex is contained by exactly one of
            // the adjacent cells.
            for (var level = 0; level < 3; ++level)
            {
                var loops = new List<S2Loop>();
                var loopVertices = new List<S2Point>();
                ISet<S2Point> points = new HashSet<S2Point>();
                for (var id = S2CellId.Begin(level); !id.Equals(S2CellId.End(level)); id = id.Next)
                {
                    var cell = new S2Cell(id);
                    points.Add(cell.Center);
                    for (var k = 0; k < 4; ++k)
                    {
                        loopVertices.Add(cell.GetVertex(k));
                        points.Add(cell.GetVertex(k));
                    }
                    loops.Add(new S2Loop(loopVertices));
                    loopVertices.Clear();
                }
                foreach (var point in points)
                {
                    var count = 0;
                    for (var j = 0; j < loops.Count; ++j)
                    {
                        if (loops[j].Contains(point))
                        {
                            ++count;
                        }
                    }
                    assertEquals(count, 1);
                }
            }
        }
        public void testFaces()
        {
            IDictionary<S2Point, int> edgeCounts = new Dictionary<S2Point, int>();
            IDictionary<S2Point, int> vertexCounts = new Dictionary<S2Point, int>();
            for (var face = 0; face < 6; ++face)
            {
                var id = S2CellId.FromFacePosLevel(face, 0, 0);
                var cell = new S2Cell(id);
                JavaAssert.Equal(cell.Id, id);
                JavaAssert.Equal(cell.Face, face);
                JavaAssert.Equal(cell.Level, 0);
                // Top-level faces have alternating orientations to get RHS coordinates.
                JavaAssert.Equal(cell.Orientation, face & S2.SwapMask);
                Assert.True(!cell.IsLeaf);
                for (var k = 0; k < 4; ++k)
                {
                    if (edgeCounts.ContainsKey(cell.GetEdgeRaw(k)))
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = edgeCounts[cell
                                                                        .GetEdgeRaw(k)] + 1;
                    }
                    else
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = 1;
                    }

                    if (vertexCounts.ContainsKey(cell.GetVertexRaw(k)))
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = vertexCounts[cell
                                                                              .GetVertexRaw(k)] + 1;
                    }
                    else
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = 1;
                    }
                    assertDoubleNear(cell.GetVertexRaw(k).DotProd(cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(cell.GetVertexRaw((k + 1) & 3).DotProd(
                        cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(S2Point.Normalize(
                        S2Point.CrossProd(cell.GetVertexRaw(k), cell
                                                                    .GetVertexRaw((k + 1) & 3))).DotProd(cell.GetEdge(k)), 1.0);
                }
            }
            // Check that edges have multiplicity 2 and vertices have multiplicity 3.
            foreach (var i in edgeCounts.Values)
            {
                JavaAssert.Equal(i, 2);
            }
            foreach (var i in vertexCounts.Values)
            {
                JavaAssert.Equal(i, 3);
            }
        }
        public void testBasic()
        {
            // Most of the S2LatLngRect methods have trivial implementations that
            // use the R1Interval and S1Interval classes, so most of the testing
            // is done in those unit tests.

            // Test basic properties of empty and full caps.
            var empty = S2LatLngRect.Empty;
            var full = S2LatLngRect.Full;
            assertTrue(empty.IsValid);
            assertTrue(empty.IsEmpty);
            assertTrue(full.IsValid);
            assertTrue(full.IsFull);

            // assertTrue various constructors and accessor methods.
            var d1 = rectFromDegrees(-90, 0, -45, 180);
            assertDoubleNear(d1.LatLo.Degrees, -90);
            assertDoubleNear(d1.LatHi.Degrees, -45);
            assertDoubleNear(d1.LngLo.Degrees, 0);
            assertDoubleNear(d1.LngHi.Degrees, 180);
            assertTrue(d1.Lat.Equals(new R1Interval(-S2.PiOver2, -S2.PiOver4)));
            assertTrue(d1.Lng.Equals(new S1Interval(0, S2.Pi)));

            // FromCenterSize()
            assertTrue(
                S2LatLngRect.FromCenterSize(S2LatLng.FromDegrees(80, 170), S2LatLng.FromDegrees(40, 60))
                            .ApproxEquals(rectFromDegrees(60, 140, 90, -160)));
            assertTrue(S2LatLngRect
                           .FromCenterSize(S2LatLng.FromDegrees(10, 40), S2LatLng.FromDegrees(210, 400)).IsFull);
            assertTrue(
                S2LatLngRect.FromCenterSize(S2LatLng.FromDegrees(-90, 180), S2LatLng.FromDegrees(20, 50))
                            .ApproxEquals(rectFromDegrees(-90, 155, -80, -155)));

            // FromPoint(), FromPointPair()
            assertEquals(S2LatLngRect.FromPoint(d1.Lo), new S2LatLngRect(d1.Lo, d1.Lo));
            assertEquals(
                S2LatLngRect.FromPointPair(S2LatLng.FromDegrees(-35, -140), S2LatLng.FromDegrees(15, 155)),
                rectFromDegrees(-35, 155, 15, -140));
            assertEquals(
                S2LatLngRect.FromPointPair(S2LatLng.FromDegrees(25, -70), S2LatLng.FromDegrees(-90, 80)),
                rectFromDegrees(-90, -70, 25, 80));

            // GetCenter(), GetVertex(), Contains(S2LatLng), InteriorContains(S2LatLng).
            var eqM180 = S2LatLng.FromRadians(0, -S2.Pi);
            var northPole = S2LatLng.FromRadians(S2.PiOver2, 0);
            var r1 = new S2LatLngRect(eqM180, northPole);

            assertEquals(r1.Center, S2LatLng.FromRadians(S2.PiOver4, -S2.PiOver2));
            assertEquals(r1.GetVertex(0), S2LatLng.FromRadians(0, S2.Pi));
            assertEquals(r1.GetVertex(1), S2LatLng.FromRadians(0, 0));
            assertEquals(r1.GetVertex(2), S2LatLng.FromRadians(S2.PiOver2, 0));
            assertEquals(r1.GetVertex(3), S2LatLng.FromRadians(S2.PiOver2, S2.Pi));
            assertTrue(r1.Contains(S2LatLng.FromDegrees(30, -45)));
            assertTrue(!r1.Contains(S2LatLng.FromDegrees(30, 45)));
            assertTrue(!r1.InteriorContains(eqM180) && !r1.InteriorContains(northPole));
            assertTrue(r1.Contains(new S2Point(0.5, -0.3, 0.1)));
            assertTrue(!r1.Contains(new S2Point(0.5, 0.2, 0.1)));

            // Make sure that GetVertex() returns vertices in CCW order.
            for (var i = 0; i < 4; ++i)
            {
                var lat = S2.PiOver4*(i - 2);
                var lng = S2.PiOver2*(i - 2) + 0.2;
                var r = new S2LatLngRect(new R1Interval(lat, lat + S2.PiOver4), new S1Interval(
                                                                                   Math.IEEERemainder(lng, 2*S2.Pi), Math.IEEERemainder(lng + S2.PiOver2, 2*S2.Pi)));
                for (var k = 0; k < 4; ++k)
                {
                    assertTrue(
                        S2.SimpleCcw(r.GetVertex((k - 1) & 3).ToPoint(), r.GetVertex(k).ToPoint(),
                                     r.GetVertex((k + 1) & 3).ToPoint()));
                }
            }

            // Contains(S2LatLngRect), InteriorContains(S2LatLngRect),
            // Intersects(), InteriorIntersects(), Union(), Intersection().
            //
            // Much more testing of these methods is done in s1interval_unittest
            // and r1interval_unittest.

            var r1Mid = rectFromDegrees(45, -90, 45, -90);
            var reqM180 = new S2LatLngRect(eqM180, eqM180);
            var rNorthPole = new S2LatLngRect(northPole, northPole);

            testIntervalOps(r1, r1Mid, "TTTT", r1, r1Mid);
            testIntervalOps(r1, reqM180, "TFTF", r1, reqM180);
            testIntervalOps(r1, rNorthPole, "TFTF", r1, rNorthPole);

            assertTrue(r1.Equals(rectFromDegrees(0, -180, 90, 0)));
            testIntervalOps(r1, rectFromDegrees(-10, -1, 1, 20), "FFTT", rectFromDegrees(-10, -180, 90, 20),
                            rectFromDegrees(0, -1, 1, 0));
            testIntervalOps(r1, rectFromDegrees(-10, -1, 0, 20), "FFTF", rectFromDegrees(-10, -180, 90, 20),
                            rectFromDegrees(0, -1, 0, 0));
            testIntervalOps(r1, rectFromDegrees(-10, 0, 1, 20), "FFTF", rectFromDegrees(-10, -180, 90, 20),
                            rectFromDegrees(0, 0, 1, 0));

            testIntervalOps(rectFromDegrees(-15, -160, -15, -150), rectFromDegrees(20, 145, 25, 155),
                            "FFFF", rectFromDegrees(-15, 145, 25, -150), empty);
            testIntervalOps(rectFromDegrees(70, -10, 90, -140), rectFromDegrees(60, 175, 80, 5), "FFTT",
                            rectFromDegrees(60, -180, 90, 180), rectFromDegrees(70, 175, 80, 5));

            // assertTrue that the intersection of two rectangles that overlap in
            // latitude
            // but not longitude is valid, and vice versa.
            testIntervalOps(rectFromDegrees(12, 30, 60, 60), rectFromDegrees(0, 0, 30, 18), "FFFF",
                            rectFromDegrees(0, 0, 60, 60), empty);
            testIntervalOps(rectFromDegrees(0, 0, 18, 42), rectFromDegrees(30, 12, 42, 60), "FFFF",
                            rectFromDegrees(0, 0, 42, 60), empty);

            // AddPoint()
            var p = S2LatLngRect.Empty;
            p = p.AddPoint(S2LatLng.FromDegrees(0, 0));
            p = p.AddPoint(S2LatLng.FromRadians(0, -S2.PiOver2));
            p = p.AddPoint(S2LatLng.FromRadians(S2.PiOver4, -S2.Pi));
            p = p.AddPoint(new S2Point(0, 0, 1));
            assertTrue(p.Equals(r1));

            // Expanded()
            assertTrue(
                rectFromDegrees(70, 150, 80, 170).Expanded(S2LatLng.FromDegrees(20, 30)).ApproxEquals(
                    rectFromDegrees(50, 120, 90, -160)));
            assertTrue(S2LatLngRect.Empty.Expanded(S2LatLng.FromDegrees(20, 30)).IsEmpty);
            assertTrue(S2LatLngRect.Full.Expanded(S2LatLng.FromDegrees(20, 30)).IsFull);
            assertTrue(
                rectFromDegrees(-90, 170, 10, 20).Expanded(S2LatLng.FromDegrees(30, 80)).ApproxEquals(
                    rectFromDegrees(-90, -180, 40, 180)));

            // ConvolveWithCap()
            var llr1 =
                new S2LatLngRect(S2LatLng.FromDegrees(0, 170), S2LatLng.FromDegrees(0, -170))
                    .ConvolveWithCap(S1Angle.FromDegrees(15));
            var llr2 =
                new S2LatLngRect(S2LatLng.FromDegrees(-15, 155), S2LatLng.FromDegrees(15, -155));
            assertTrue(llr1.ApproxEquals(llr2));

            llr1 = new S2LatLngRect(S2LatLng.FromDegrees(60, 150), S2LatLng.FromDegrees(80, 10))
                .ConvolveWithCap(S1Angle.FromDegrees(15));
            llr2 = new S2LatLngRect(S2LatLng.FromDegrees(45, -180), S2LatLng.FromDegrees(90, 180));
            assertTrue(llr1.ApproxEquals(llr2));

            // GetCapBound(), bounding cap at center is smaller:
            assertTrue(new S2LatLngRect(S2LatLng.FromDegrees(-45, -45), S2LatLng.FromDegrees(45, 45)).CapBound.ApproxEquals(S2Cap.FromAxisHeight(new S2Point(1, 0, 0), 0.5)));
            // GetCapBound(), bounding cap at north pole is smaller:
            assertTrue(new S2LatLngRect(S2LatLng.FromDegrees(88, -80), S2LatLng.FromDegrees(89, 80)).CapBound.ApproxEquals(S2Cap.FromAxisAngle(new S2Point(0, 0, 1), S1Angle.FromDegrees(2))));
            // GetCapBound(), longitude span > 180 degrees:
            assertTrue(
                new S2LatLngRect(S2LatLng.FromDegrees(-30, -150), S2LatLng.FromDegrees(-10, 50)).CapBound
                    .ApproxEquals(S2Cap.FromAxisAngle(new S2Point(0, 0, -1), S1Angle.FromDegrees(80))));

            // Contains(S2Cell), MayIntersect(S2Cell), Intersects(S2Cell)

            // Special cases.
            testCellOps(empty, S2Cell.FromFacePosLevel(3, (byte)0, 0), 0);
            testCellOps(full, S2Cell.FromFacePosLevel(2, (byte)0, 0), 4);
            testCellOps(full, S2Cell.FromFacePosLevel(5, (byte)0, 25), 4);

            // This rectangle includes the first quadrant of face 0. It's expanded
            // slightly because cell bounding rectangles are slightly conservative.
            var r4 = rectFromDegrees(-45.1, -45.1, 0.1, 0.1);
            testCellOps(r4, S2Cell.FromFacePosLevel(0, (byte)0, 0), 3);
            testCellOps(r4, S2Cell.FromFacePosLevel(0, (byte)0, 1), 4);
            testCellOps(r4, S2Cell.FromFacePosLevel(1, (byte)0, 1), 0);

            // This rectangle intersects the first quadrant of face 0.
            var r5 = rectFromDegrees(-10, -45, 10, 0);
            testCellOps(r5, S2Cell.FromFacePosLevel(0, (byte)0, 0), 3);
            testCellOps(r5, S2Cell.FromFacePosLevel(0, (byte)0, 1), 3);
            testCellOps(r5, S2Cell.FromFacePosLevel(1, (byte)0, 1), 0);

            // Rectangle consisting of a single point.
            testCellOps(rectFromDegrees(4, 4, 4, 4), S2Cell.FromFacePosLevel(0, (byte)0, 0), 3);

            // Rectangles that intersect the bounding rectangle of a face
            // but not the face itself.
            testCellOps(rectFromDegrees(41, -87, 42, -79), S2Cell.FromFacePosLevel(2, (byte)0, 0), 1);
            testCellOps(rectFromDegrees(-41, 160, -40, -160), S2Cell.FromFacePosLevel(5, (byte)0, 0), 1);
            {
                // This is the leaf cell at the top right hand corner of face 0.
                // It has two angles of 60 degrees and two of 120 degrees.
                var cell0tr = new S2Cell(new S2Point(1 + 1e-12, 1, 1));
                var bound0tr = cell0tr.RectBound;
                var v0 = new S2LatLng(cell0tr.GetVertexRaw(0));
                testCellOps(
                    rectFromDegrees(v0.Lat.Degrees - 1e-8, v0.Lng.Degrees - 1e-8,
                                    v0.Lat.Degrees - 2e-10, v0.Lng.Degrees + 1e-10), cell0tr, 1);
            }

            // Rectangles that intersect a face but where no vertex of one region
            // is contained by the other region. The first one passes through
            // a corner of one of the face cells.
            testCellOps(rectFromDegrees(-37, -70, -36, -20), S2Cell.FromFacePosLevel(5, (byte)0, 0), 2);
            {
                // These two intersect like a diamond and a square.
                var cell202 = S2Cell.FromFacePosLevel(2, (byte)0, 2);
                var bound202 = cell202.RectBound;
                testCellOps(
                    rectFromDegrees(bound202.Lo.Lat.Degrees + 3, bound202.Lo.Lng.Degrees + 3,
                                    bound202.Hi.Lat.Degrees - 3, bound202.Hi.Lng.Degrees - 3), cell202, 2);
            }
        }
        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)));
        }
        public void testCells()
        {
            // For each cube face, we construct some cells on
            // that face and some caps whose positions are relative to that face,
            // and then check for the expected intersection/containment results.

            // The distance from the center of a face to one of its vertices.
            var kFaceRadius = Math.Atan(S2.Sqrt2);

            for (var face = 0; face < 6; ++face)
            {
                // The cell consisting of the entire face.
                var rootCell = S2Cell.FromFacePosLevel(face, (byte)0, 0);

                // A leaf cell at the midpoint of the v=1 edge.
                var edgeCell = new S2Cell(S2Projections.FaceUvToXyz(face, 0, 1 - EPS));

                // A leaf cell at the u=1, v=1 corner.
                var cornerCell = new S2Cell(S2Projections.FaceUvToXyz(face, 1 - EPS, 1 - EPS));

                // Quick check for full and empty caps.
                Assert.True(S2Cap.Full.Contains(rootCell));
                Assert.True(!S2Cap.Empty.MayIntersect(rootCell));

                // Check intersections with the bounding caps of the leaf cells that are
                // adjacent to 'corner_cell' along the Hilbert curve. Because this corner
                // is at (u=1,v=1), the curve stays locally within the same cube face.
                var first = cornerCell.Id.Previous.Previous.Previous;
                var last = cornerCell.Id.Next.Next.Next.Next;
                for (var id = first; id <last; id = id.Next)
                {
                    var cell = new S2Cell(id);
                    JavaAssert.Equal(cell.CapBound.Contains(cornerCell), id.Equals(cornerCell.Id));
                    JavaAssert.Equal(
                        cell.CapBound.MayIntersect(cornerCell), id.Parent.Contains(cornerCell.Id));
                }

                var antiFace = (face + 3)%6; // Opposite face.
                for (var capFace = 0; capFace < 6; ++capFace)
                {
                    // A cap that barely contains all of 'cap_face'.
                    var center = S2Projections.GetNorm(capFace);
                    var covering = S2Cap.FromAxisAngle(center, S1Angle.FromRadians(kFaceRadius + EPS));
                    JavaAssert.Equal(covering.Contains(rootCell), capFace == face);
                    JavaAssert.Equal(covering.MayIntersect(rootCell), capFace != antiFace);
                    JavaAssert.Equal(covering.Contains(edgeCell), center.DotProd(edgeCell.Center) > 0.1);
                    JavaAssert.Equal(covering.Contains(edgeCell), covering.MayIntersect(edgeCell));
                    JavaAssert.Equal(covering.Contains(cornerCell), capFace == face);
                    JavaAssert.Equal(
                        covering.MayIntersect(cornerCell), center.DotProd(cornerCell.Center) > 0);

                    // A cap that barely intersects the edges of 'cap_face'.
                    var bulging = S2Cap.FromAxisAngle(center, S1Angle.FromRadians(S2.PiOver4 + EPS));
                    Assert.True(!bulging.Contains(rootCell));
                    JavaAssert.Equal(bulging.MayIntersect(rootCell), capFace != antiFace);
                    JavaAssert.Equal(bulging.Contains(edgeCell), capFace == face);
                    JavaAssert.Equal(bulging.MayIntersect(edgeCell), center.DotProd(edgeCell.Center) > 0.1);
                    Assert.True(!bulging.Contains(cornerCell));
                    Assert.True(!bulging.MayIntersect(cornerCell));

                    // A singleton cap.
                    var singleton = S2Cap.FromAxisAngle(center, S1Angle.FromRadians(0));
                    JavaAssert.Equal(singleton.MayIntersect(rootCell), capFace == face);
                    Assert.True(!singleton.MayIntersect(edgeCell));
                    Assert.True(!singleton.MayIntersect(cornerCell));
                }
            }
        }
 public void expandChildren2(S2Cell cell)
 {
     var id = cell.Id.ChildBegin;
     for (var pos = 0; pos < 4; ++pos, id = id.Next)
     {
         var child = new S2Cell(id);
         if (child.Level < MAX_LEVEL)
         {
             expandChildren2(child);
         }
     }
 }