예제 #1
0
        public void Test_S2Cap_Union()
        {
            // Two caps which have the same center but one has a larger radius.
            S2Cap a = new(GetLatLngPoint(50.0, 10.0), S1Angle.FromDegrees(0.2));
            S2Cap b = new(GetLatLngPoint(50.0, 10.0), S1Angle.FromDegrees(0.3));

            Assert.True(b.Contains(a));
            Assert.Equal(b, a.Union(b));

            // Two caps where one is the full cap.
            Assert.True(a.Union(S2Cap.Full).IsFull());

            // Two caps where one is the empty cap.
            Assert.Equal(a, a.Union(S2Cap.Empty));

            // Two caps which have different centers, one entirely encompasses the other.
            S2Cap c = new(GetLatLngPoint(51.0, 11.0), S1Angle.FromDegrees(1.5));

            Assert.True(c.Contains(a));
            Assert.Equal(a.Union(c).Center, c.Center);
            Assert.Equal(a.Union(c).Radius, c.Radius);

            // Two entirely disjoint caps.
            S2Cap d = new(GetLatLngPoint(51.0, 11.0), S1Angle.FromDegrees(0.1));

            Assert.False(d.Contains(a));
            Assert.False(d.Intersects(a));
            Assert.True(a.Union(d).ApproxEquals(d.Union(a)));
            Assert2.Near(50.4588, new S2LatLng(a.Union(d).Center).Lat().GetDegrees(), 0.001);
            Assert2.Near(10.4525, new S2LatLng(a.Union(d).Center).Lng().GetDegrees(), 0.001);
            Assert2.Near(0.7425, a.Union(d).Radius.Degrees(), 0.001);

            // Two partially overlapping caps.
            S2Cap e = new(GetLatLngPoint(50.3, 10.3), S1Angle.FromDegrees(0.2));

            Assert.False(e.Contains(a));
            Assert.True(e.Intersects(a));
            Assert.True(a.Union(e).ApproxEquals(e.Union(a)));
            Assert2.Near(50.1500, new S2LatLng(a.Union(e).Center).Lat().GetDegrees(), 0.001);
            Assert2.Near(10.1495, new S2LatLng(a.Union(e).Center).Lng().GetDegrees(), 0.001);
            Assert2.Near(0.3781, a.Union(e).Radius.Degrees(), 0.001);

            // Two very large caps, whose radius sums to in excess of 180 degrees, and
            // whose centers are not antipodal.
            S2Cap f = new(new S2Point(0, 0, 1).Normalize(), S1Angle.FromDegrees(150));
            S2Cap g = new(new S2Point(0, 1, 0).Normalize(), S1Angle.FromDegrees(150));

            Assert.True(f.Union(g).IsFull());

            // Two non-overlapping hemisphere caps with antipodal centers.
            S2Cap hemi = S2Cap.FromCenterHeight(new S2Point(0, 0, 1).Normalize(), 1);

            Assert.True(hemi.Union(hemi.Complement()).IsFull());
        }
예제 #2
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));
            }
        }
예제 #3
0
        public void Test_S2Cap_EncodeDecode()
        {
            S2Cap   cap     = S2Cap.FromCenterHeight(new S2Point(3, 2, 1).Normalize(), 1);
            Encoder encoder = new();

            cap.Encode(encoder);
            var decoder = encoder.Decoder();

            var(success, decoded_cap) = S2Cap.Decode(decoder);
            Assert.True(success);
            Assert.Equal(cap, decoded_cap);
        }
예제 #4
0
        public void Test_S2RegionEncodeDecodeTest_S2Cap()
        {
            S2Cap cap_from_point         = S2Cap.FromPoint(new S2Point(3, 2, 1).Normalize());
            var   cap_from_center_height = S2Cap.FromCenterHeight(new S2Point(0, 0, 1).Normalize(), 5);

            var cap = TestEncodeDecode(kEncodedCapEmpty, S2Cap.Empty);

            Assert.True(S2Cap.Empty.ApproxEquals(cap));
            cap = TestEncodeDecode(kEncodedCapFull, S2Cap.Full);
            Assert.True(S2Cap.Full.ApproxEquals(cap));
            cap = TestEncodeDecode(kEncodedCapFromPoint, cap_from_point);
            Assert.True(cap_from_point.ApproxEquals(cap));
            cap = TestEncodeDecode(kEncodedCapFromCenterHeight, cap_from_center_height);
            Assert.True(cap_from_center_height.ApproxEquals(cap));
        }
예제 #5
0
        public void Test_S2Cap_GetCentroid()
        {
            // Empty and full caps.
            Assert.Equal(new S2Point(), S2Cap.Empty.Centroid());
            Assert.True(S2Cap.Full.Centroid().Norm() <= S2.DoubleError);

            // Random caps.
            for (int i = 0; i < 100; ++i)
            {
                S2Point center   = S2Testing.RandomPoint();
                double  height   = S2Testing.Random.UniformDouble(0.0, 2.0);
                S2Cap   cap      = S2Cap.FromCenterHeight(center, height);
                S2Point centroid = cap.Centroid();
                S2Point expected = center * (1.0 - height / 2.0) * cap.Area();
                Assert.True((expected - centroid).Norm() <= S2.DoubleError);
            }
        }
예제 #6
0
    public void Test_S2LatLngRect_GetCapBound()
    {
        // Bounding cap at center is smaller:
        Assert.True(RectFromDegrees(-45, -45, 45, 45).GetCapBound().
                    ApproxEquals(S2Cap.FromCenterHeight(new S2Point(1, 0, 0), 0.5)));

        // Bounding cap at north pole is smaller:
        Assert.True(RectFromDegrees(88, -80, 89, 80).GetCapBound().
                    ApproxEquals(new S2Cap(new S2Point(0, 0, 1), S1Angle.FromDegrees(2))));

        // Longitude span > 180 degrees:
        Assert.True(RectFromDegrees(-30, -150, -10, 50).GetCapBound().
                    ApproxEquals(new S2Cap(new S2Point(0, 0, -1), S1Angle.FromDegrees(80))));

        // Ensure hemispheres are bounded conservatively.
        Assert.True(RectFromDegrees(-10, -100, 0, 100).GetCapBound().Radius >=
                    S1ChordAngle.Right);
    }
예제 #7
0
        public void Test_S2Cap_Basic()
        {
            // Test basic properties of empty and full caps.
            S2Cap empty = S2Cap.Empty;
            S2Cap full  = S2Cap.Full;

            Assert.True(empty.IsValid());
            Assert.True(empty.IsEmpty());
            Assert.True(empty.Complement().IsFull());
            Assert.True(full.IsValid());
            Assert.True(full.IsFull());
            Assert.True(full.Complement().IsEmpty());
            Assert.Equal(2, full.Height());
            Assert2.DoubleEqual(180.0, full.Radius.Degrees());

            // Test ==/!=.
            Assert.Equal(full, full);
            Assert.Equal(empty, empty);
            Assert.NotEqual(full, empty);

            // Test the S1Angle constructor using out-of-range arguments.
            Assert.True(new S2Cap(new S2Point(1, 0, 0), S1Angle.FromRadians(-20)).IsEmpty());
            Assert.True(new S2Cap(new S2Point(1, 0, 0), S1Angle.FromRadians(5)).IsFull());
            Assert.True(new S2Cap(new S2Point(1, 0, 0), S1Angle.Infinity).IsFull());

            // Check that the default S2Cap is identical to Empty().
            var default_empty = S2Cap.Empty;

            Assert.True(default_empty.IsValid());
            Assert.True(default_empty.IsEmpty());
            Assert.Equal(empty.Center, default_empty.Center);
            Assert.Equal(empty.Height(), default_empty.Height());

            // Containment and intersection of empty and full caps.
            Assert.True(empty.Contains(empty));
            Assert.True(full.Contains(empty));
            Assert.True(full.Contains(full));
            Assert.False(empty.InteriorIntersects(empty));
            Assert.True(full.InteriorIntersects(full));
            Assert.False(full.InteriorIntersects(empty));

            // Singleton cap containing the x-axis.
            S2Cap xaxis = S2Cap.FromPoint(new S2Point(1, 0, 0));

            Assert.True(xaxis.Contains(new S2Point(1, 0, 0)));
            Assert.False(xaxis.Contains(new S2Point(1, 1e-20, 0)));
            Assert.Equal(0, xaxis.Radius.Radians());

            // Singleton cap containing the y-axis.
            S2Cap yaxis = S2Cap.FromPoint(new S2Point(0, 1, 0));

            Assert.False(yaxis.Contains(xaxis.Center));
            Assert.Equal(0, xaxis.Height());

            // Check that the complement of a singleton cap is the full cap.
            S2Cap xcomp = xaxis.Complement();

            Assert.True(xcomp.IsValid());
            Assert.True(xcomp.IsFull());
            Assert.True(xcomp.Contains(xaxis.Center));

            // Check that the complement of the complement is *not* the original.
            Assert.True(xcomp.Complement().IsValid());
            Assert.True(xcomp.Complement().IsEmpty());
            Assert.False(xcomp.Complement().Contains(xaxis.Center));

            // Check that very small caps can be represented accurately.
            // Here "kTinyRad" is small enough that unit vectors perturbed by this
            // amount along a tangent do not need to be renormalized.
            S2Cap tiny    = new(new S2Point(1, 2, 3).Normalize(), S1Angle.FromRadians(kTinyRad));
            var   tangent = tiny.Center.CrossProd(new S2Point(3, 2, 1)).Normalize();

            Assert.True(tiny.Contains(tiny.Center + 0.99 * kTinyRad * tangent));
            Assert.False(tiny.Contains(tiny.Center + 1.01 * kTinyRad * tangent));

            // Basic tests on a hemispherical cap.
            S2Cap hemi = S2Cap.FromCenterHeight(new S2Point(1, 0, 1).Normalize(), 1);

            Assert.Equal(-hemi.Center, hemi.Complement().Center);
            Assert.Equal(1, hemi.Complement().Height());
            Assert.True(hemi.Contains(new S2Point(1, 0, 0)));
            Assert.False(hemi.Complement().Contains(new S2Point(1, 0, 0)));
            Assert.True(hemi.Contains(new S2Point(1, 0, -(1 - kEps)).Normalize()));
            Assert.False(hemi.InteriorContains(new S2Point(1, 0, -(1 + kEps)).Normalize()));

            // A concave cap.  Note that the error bounds for point containment tests
            // increase with the cap angle, so we need to use a larger error bound
            // here.  (It would be painful to do this everywhere, but this at least
            // gives an example of how to compute the maximum error.)
            S2Point      center    = GetLatLngPoint(80, 10);
            S1ChordAngle radius    = new(S1Angle.FromDegrees(150));
            double       max_error =
                radius.GetS2PointConstructorMaxError() +
                radius.S1AngleConstructorMaxError +
                3 * S2.DoubleEpsilon;  // GetLatLngPoint() error
            S2Cap concave     = new(center, radius);
            S2Cap concave_min = new(center, radius.PlusError(-max_error));
            S2Cap concave_max = new(center, radius.PlusError(max_error));

            Assert.True(concave_max.Contains(GetLatLngPoint(-70, 10)));
            Assert.False(concave_min.Contains(GetLatLngPoint(-70, 10)));
            Assert.True(concave_max.Contains(GetLatLngPoint(-50, -170)));
            Assert.False(concave_min.Contains(GetLatLngPoint(-50, -170)));

            // Cap containment tests.
            Assert.False(empty.Contains(xaxis));
            Assert.False(empty.InteriorIntersects(xaxis));
            Assert.True(full.Contains(xaxis));
            Assert.True(full.InteriorIntersects(xaxis));
            Assert.False(xaxis.Contains(full));
            Assert.False(xaxis.InteriorIntersects(full));
            Assert.True(xaxis.Contains(xaxis));
            Assert.False(xaxis.InteriorIntersects(xaxis));
            Assert.True(xaxis.Contains(empty));
            Assert.False(xaxis.InteriorIntersects(empty));
            Assert.True(hemi.Contains(tiny));
            Assert.True(hemi.Contains(new S2Cap(new S2Point(1, 0, 0), S1Angle.FromRadians(S2.M_PI_4 - kEps))));
            Assert.False(hemi.Contains(new S2Cap(new S2Point(1, 0, 0), S1Angle.FromRadians(S2.M_PI_4 + kEps))));
            Assert.True(concave.Contains(hemi));
            Assert.True(concave.InteriorIntersects(hemi.Complement()));
            Assert.False(concave.Contains(S2Cap.FromCenterHeight(-concave.Center, 0.1)));
        }