예제 #1
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);
    }
예제 #2
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]);
        }
예제 #3
0
        public void Test_S2RegionEncodeDecodeTest_S2Cell()
        {
            S2Cell cell_from_point        = new(new S2Point(1, 2, 3));
            S2Cell cell_from_latlng       = new(S2LatLng.FromDegrees(39.0, -120.0));
            S2Cell cell_from_face_pos_lvl = S2Cell.FromFacePosLevel(3, 0x12345678, S2.kMaxCellLevel - 4);
            S2Cell cell_from_from_face    = S2Cell.FromFace(0);

            var cell = TestEncodeDecode(kEncodedCellFromPoint, cell_from_point);

            Assert.Equal(cell_from_point, cell);
            cell = TestEncodeDecode(kEncodedCellFromLatLng, cell_from_latlng);
            Assert.Equal(cell_from_latlng, cell);
            cell = TestEncodeDecode(kEncodedCellFromFacePosLevel, cell_from_face_pos_lvl);
            Assert.Equal(cell_from_face_pos_lvl, cell);
            cell = TestEncodeDecode(kEncodedCellFace0, cell_from_from_face);
            Assert.Equal(cell_from_from_face, cell);
        }
예제 #4
0
        public void Test_S2Cell_TestSubdivide()
        {
            // Only test a sample of faces to reduce the runtime.
            TestSubdivide(S2Cell.FromFace(0));
            TestSubdivide(S2Cell.FromFace(3));
            TestSubdivide(S2Cell.FromFace(5));

            #region Print Header

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

            #endregion

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

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

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

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

                // The aspect ratio calculations are ratios of lengths and are therefore
                // less accurate at higher subdivision levels.
                Assert.True(s.Max_edge_aspect <= S2.kMaxEdgeAspect + 1e-15 * (1 << i));
                Assert.True(s.Max_diag_aspect <= S2.kMaxDiagAspect + 1e-15 * (1 << i));
            }
        }
예제 #5
0
        public void Test_S2Cap_S2CellMethods()
        {
            // 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.

            for (var face = 0; face < 6; ++face)
            {
                // The cell consisting of the entire face.
                S2Cell root_cell = S2Cell.FromFace(face);

                // A leaf cell at the midpoint of the v=1 edge.
                S2Cell edge_cell = new(S2.FaceUVtoXYZ(face, 0, 1 - kEps));

                // A leaf cell at the u=1, v=1 corner.
                S2Cell corner_cell = new(S2.FaceUVtoXYZ(face, 1 - kEps, 1 - kEps));

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

                // 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.
                S2CellId first = corner_cell.Id.Advance(-3);
                S2CellId last  = corner_cell.Id.Advance(4);
                for (S2CellId id = first; id < last; id = id.Next())
                {
                    S2Cell cell = new(id);
                    Assert.Equal(id == corner_cell.Id,
                                 cell.GetCapBound().Contains(corner_cell));
                    Assert.Equal(id.Parent().Contains(corner_cell.Id),
                                 cell.GetCapBound().MayIntersect(corner_cell));
                }

                var anti_face = (face + 3) % 6;  // Opposite face.
                for (var cap_face = 0; cap_face < 6; ++cap_face)
                {
                    // A cap that barely contains all of 'cap_face'.
                    S2Point center   = S2.GetNorm(cap_face);
                    S2Cap   covering = new(center, S1Angle.FromRadians(kFaceRadius + kEps));
                    Assert.Equal(cap_face == face, covering.Contains(root_cell));
                    Assert.Equal(cap_face != anti_face, covering.MayIntersect(root_cell));
                    Assert.Equal(center.DotProd(edge_cell.Center()) > 0.1,
                                 covering.Contains(edge_cell));
                    Assert.Equal(covering.MayIntersect(edge_cell), covering.Contains(edge_cell));
                    Assert.Equal(cap_face == face, covering.Contains(corner_cell));
                    Assert.Equal(center.DotProd(corner_cell.Center()) > 0,
                                 covering.MayIntersect(corner_cell));

                    // A cap that barely intersects the edges of 'cap_face'.
                    S2Cap bulging = new(center, S1Angle.FromRadians(S2.M_PI_4 + kEps));
                    Assert.False(bulging.Contains(root_cell));
                    Assert.Equal(cap_face != anti_face, bulging.MayIntersect(root_cell));
                    Assert.Equal(cap_face == face, bulging.Contains(edge_cell));
                    Assert.Equal(center.DotProd(edge_cell.Center()) > 0.1,
                                 bulging.MayIntersect(edge_cell));
                    Assert.False(bulging.Contains(corner_cell));
                    Assert.False(bulging.MayIntersect(corner_cell));

                    // A singleton cap.
                    S2Cap singleton = new(center, S1Angle.Zero);
                    Assert.Equal(cap_face == face, singleton.MayIntersect(root_cell));
                    Assert.False(singleton.MayIntersect(edge_cell));
                    Assert.False(singleton.MayIntersect(corner_cell));
                }
            }
        }