Пример #1
0
    private static void TestIntervalOps(S2LatLngRect x, S2LatLngRect y, string expected_relation, S2LatLngRect expected_union, S2LatLngRect expected_intersection)
    {
        // Test all of the interval operations on the given pair of intervals.
        // "expected_relation" is a sequence of "T" and "F" characters corresponding
        // to the expected results of Contains(), InteriorContains(), Intersects(),
        // and InteriorIntersects() respectively.

        Assert.Equal(x.Contains(y), expected_relation[0] == 'T');
        Assert.Equal(x.InteriorContains(y), expected_relation[1] == 'T');
        Assert.Equal(x.Intersects(y), expected_relation[2] == 'T');
        Assert.Equal(x.InteriorIntersects(y), expected_relation[3] == 'T');

        Assert.Equal(x.Contains(y), x.Union(y) == x);
        Assert.Equal(x.Intersects(y), !x.Intersection(y).IsEmpty());

        Assert.Equal(x.Union(y), expected_union);
        Assert.Equal(x.Intersection(y), expected_intersection);

        if (y.Size() == S2LatLng.FromRadians(0, 0))
        {
            S2LatLngRect r = x;
            r.AddPoint(y.Lo());
            Assert.Equal(r, expected_union);
        }
    }
        public void testIntervalOps(S2LatLngRect x, S2LatLngRect y, String expectedRelation,
                                    S2LatLngRect expectedUnion, S2LatLngRect expectedIntersection)
        {
            // Test all of the interval operations on the given pair of intervals.
            // "expected_relation" is a sequence of "T" and "F" characters corresponding
            // to the expected results of Contains(), InteriorContains(), Intersects(),
            // and InteriorIntersects() respectively.

            assertEquals(x.Contains(y), expectedRelation[0] == 'T');
            assertEquals(x.InteriorContains(y), expectedRelation[1] == 'T');
            assertEquals(x.Intersects(y), expectedRelation[2] == 'T');
            assertEquals(x.InteriorIntersects(y), expectedRelation[3] == 'T');

            assertEquals(x.Contains(y), x.Union(y).Equals(x));
            assertEquals(x.Intersects(y), !x.Intersection(y).IsEmpty);

            assertTrue(x.Union(y).Equals(expectedUnion));
            assertTrue(x.Intersection(y).Equals(expectedIntersection));

            if (y.Size == S2LatLng.FromRadians(0, 0))
            {
                var r = x.AddPoint(y.Lo);
                assertTrue(r == expectedUnion);
            }
        }
        public void testIntervalOps(S2LatLngRect x, S2LatLngRect y, String expectedRelation,
                                    S2LatLngRect expectedUnion, S2LatLngRect expectedIntersection)
        {
            // Test all of the interval operations on the given pair of intervals.
            // "expected_relation" is a sequence of "T" and "F" characters corresponding
            // to the expected results of Contains(), InteriorContains(), Intersects(),
            // and InteriorIntersects() respectively.

            assertEquals(x.Contains(y), expectedRelation[0] == 'T');
            assertEquals(x.InteriorContains(y), expectedRelation[1] == 'T');
            assertEquals(x.Intersects(y), expectedRelation[2] == 'T');
            assertEquals(x.InteriorIntersects(y), expectedRelation[3] == 'T');

            assertEquals(x.Contains(y), x.Union(y).Equals(x));
            assertEquals(x.Intersects(y), !x.Intersection(y).IsEmpty);

            assertTrue(x.Union(y).Equals(expectedUnion));
            assertTrue(x.Intersection(y).Equals(expectedIntersection));

            if (y.Size == S2LatLng.FromRadians(0, 0))
            {
                var r = x.AddPoint(y.Lo);
                assertTrue(r == expectedUnion);
            }
        }
Пример #4
0
    public void Test_BoundaryIntersects_RectCrossingAntiMeridian()
    {
        S2LatLngRect rect = RectFromDegrees(20, 170, 40, -170);

        Assert.True(rect.Contains(MakePointOrDie("30:180")));

        // Check that crossings of all four sides are detected.
        Assert.True(rect.BoundaryIntersects(
                        MakePointOrDie("25:160"), MakePointOrDie("25:180")));
        Assert.True(rect.BoundaryIntersects(
                        MakePointOrDie("25:-160"), MakePointOrDie("25:-180")));
        Assert.True(rect.BoundaryIntersects(
                        MakePointOrDie("15:175"), MakePointOrDie("30:175")));
        Assert.True(rect.BoundaryIntersects(
                        MakePointOrDie("45:175"), MakePointOrDie("30:175")));

        // Check that the edges on the opposite side of the sphere but at the same
        // latitude do not intersect the rectangle boundary.
        Assert.False(rect.BoundaryIntersects(
                         MakePointOrDie("25:-20"), MakePointOrDie("25:0")));
        Assert.False(rect.BoundaryIntersects(
                         MakePointOrDie("25:20"), MakePointOrDie("25:0")));
        Assert.False(rect.BoundaryIntersects(
                         MakePointOrDie("15:-5"), MakePointOrDie("30:-5")));
        Assert.False(rect.BoundaryIntersects(
                         MakePointOrDie("45:-5"), MakePointOrDie("30:-5")));
    }
        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);
        }
Пример #6
0
    private static 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

        bool vertex_contained = false;

        for (int i = 0; i < 4; ++i)
        {
            if (r.Contains(cell.VertexRaw(i)) ||
                (!r.IsEmpty() && cell.Contains(r.Vertex(i).ToPoint())))
            {
                vertex_contained = true;
            }
        }
        Assert.Equal(r.MayIntersect(cell), level >= 1);
        Assert.Equal(r.Intersects(cell), level >= 2);
        Assert.Equal(vertex_contained, level >= 3);
        Assert.Equal(r.Contains(cell), level >= 4);
    }
        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);
        }
Пример #8
0
    private static S1Angle BruteForceRectPointDistance(S2LatLngRect a, S2LatLng b)
    {
        if (a.Contains(b))
        {
            return(S1Angle.FromRadians(0));
        }

        S1Angle b_to_lo_lat = GetDistance(b, a.LatLo(), a.Lng);
        S1Angle b_to_hi_lat = GetDistance(b, a.LatHi(), a.Lng);
        S1Angle b_to_lo_lng = S2.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo(), a.LngLo()).ToPoint(), new S2LatLng(a.LatHi(), a.LngLo()).ToPoint());
        S1Angle b_to_hi_lng = S2.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo(), a.LngHi()).ToPoint(), new S2LatLng(a.LatHi(), a.LngHi()).ToPoint());

        return(new[] { b_to_lo_lat, b_to_hi_lat, b_to_lo_lng, b_to_hi_lng }.Min());
    }
        private static S1Angle bruteForceRectPointDistance(S2LatLngRect a, S2LatLng b)
        {
            if (a.Contains(b))
            {
                return(S1Angle.FromRadians(0));
            }

            var bToLoLat = getDistance(b, a.LatLo, a.Lng);
            var bToHiLat = getDistance(b, a.LatHi, a.Lng);
            var bToLoLng =
                S2EdgeUtil.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo, a.LngLo).ToPoint(),
                                       new S2LatLng(a.LatHi, a.LngLo).ToPoint());
            var bToHiLng =
                S2EdgeUtil.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo, a.LngHi).ToPoint(),
                                       new S2LatLng(a.LatHi, a.LngHi).ToPoint());

            return(S1Angle.Min(bToLoLat, S1Angle.Min(bToHiLat, S1Angle.Min(bToLoLng, bToHiLng))));
        }
        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);
            }
        }
        private static S1Angle bruteForceRectPointDistance(S2LatLngRect a, S2LatLng b)
        {
            if (a.Contains(b))
            {
                return S1Angle.FromRadians(0);
            }

            var bToLoLat = getDistance(b, a.LatLo, a.Lng);
            var bToHiLat = getDistance(b, a.LatHi, a.Lng);
            var bToLoLng =
                S2EdgeUtil.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo, a.LngLo).ToPoint(),
                                       new S2LatLng(a.LatHi, a.LngLo).ToPoint());
            var bToHiLng =
                S2EdgeUtil.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo, a.LngHi).ToPoint(),
                                       new S2LatLng(a.LatHi, a.LngHi).ToPoint());
            return S1Angle.Min(bToLoLat, S1Angle.Min(bToHiLat, S1Angle.Min(bToLoLng, bToHiLng)));
        }
        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);
            }
        }
Пример #13
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))));
        }