コード例 #1
0
        public void Test_S2ClosestCellQuery_OptionsNotModified()
        {
            // Tests that FindClosestCell(), GetDistance(), and IsDistanceLess() do not
            // modify query.Options_, even though all of these methods have their own
            // specific options requirements.
            S2ClosestCellQuery.Options options = new();
            options.MaxResults  = (3);
            options.MaxDistance = (S1ChordAngle.FromDegrees(3));
            options.MaxError    = (S1ChordAngle.FromDegrees(0.001));
            S2CellIndex index = new();

            index.Add(new S2CellId(MakePointOrDie("1:1")), 1);
            index.Add(new S2CellId(MakePointOrDie("1:2")), 2);
            index.Add(new S2CellId(MakePointOrDie("1:3")), 3);
            index.Build();
            S2ClosestCellQuery query = new(index, options);

            S2ClosestCellQuery.PointTarget target = new(MakePointOrDie("2:2"));
            Assert.Equal(2, query.FindClosestCell(target).Label);
            Assert2.Near(1.0, query.GetDistance(target).Degrees(), 1e-7);
            Assert.True(query.IsDistanceLess(target, S1ChordAngle.FromDegrees(1.5)));

            // Verify that none of the options above were modified.
            Assert.Equal(options.MaxResults, query.Options_.MaxResults);
            Assert.Equal(options.MaxDistance, query.Options_.MaxDistance);
            Assert.Equal(options.MaxError, query.Options_.MaxError);
        }
コード例 #2
0
 public void Test_GetApproxArea_LargeShellAndHolePolygon()
 {
     // Make sure that GetApproxArea works well for large polygons.
     Assert2.Near(S2.GetApproxArea(MakeLaxPolygonOrDie(
                                       "0:0, 0:90, 90:0; 0:22.5, 90:0, 0:67.5")),
                  S2.M_PI_4, 1e-12);
 }
コード例 #3
0
    // Given a point X and an edge AB, check that the distance from X to AB is
    // "distance_radians" and the closest point on AB is "expected_closest".
    private static void CheckDistance(S2Point x, S2Point a, S2Point b, double distance_radians, S2Point expected_closest)
    {
        x = x.Normalize();
        a = a.Normalize();
        b = b.Normalize();
        expected_closest = expected_closest.Normalize();
        Assert2.Near(distance_radians, S2.GetDistance(x, a, b).Radians, S2.DoubleError);
        S2Point closest = S2.Project(x, a, b);

        Assert.True(S2Pred.CompareEdgeDistance(
                        closest, a, b, new S1ChordAngle(S2.kProjectPerpendicularErrorS1Angle)) < 0);

        // If X is perpendicular to AB then there is nothing further we can expect.
        if (distance_radians != S2.M_PI_2)
        {
            if (expected_closest == new S2Point())
            {
                // This special value says that the result should be A or B.
                Assert.True(closest == a || closest == b);
            }
            else
            {
                Assert.True(S2.ApproxEquals(closest, expected_closest));
            }
        }
        S1ChordAngle min_distance = S1ChordAngle.Zero;

        Assert.False(S2.UpdateMinDistance(x, a, b, ref min_distance));
        min_distance = S1ChordAngle.Infinity;
        Assert.True(S2.UpdateMinDistance(x, a, b, ref min_distance));
        Assert2.Near(distance_radians, min_distance.ToAngle().Radians, S2.DoubleError);
    }
コード例 #4
0
ファイル: S2LatLngRectTests.cs プロジェクト: alas/s2geometry
    // This method verifies a.GetDistance(b) by comparing its result against a
    // brute-force implementation. The correctness of the brute-force version is
    // much easier to verify by inspection.
    private static void VerifyGetDistance(S2LatLngRect a, S2LatLngRect b)
    {
        S1Angle distance1 = BruteForceDistance(a, b);
        S1Angle distance2 = a.GetDistance(b);

        Assert2.Near(distance1.Radians - distance2.Radians, 0, 1e-10);
    }
コード例 #5
0
ファイル: S2LatLngRectTests.cs プロジェクト: alas/s2geometry
    // This method verifies a.GetDistance(b), where b is a S2LatLng, by comparing
    // its result against a.GetDistance(c), c being the point rectangle created
    // from b.
    private static void VerifyGetRectPointDistance(S2LatLngRect a, S2LatLng p)
    {
        S1Angle distance1 = BruteForceRectPointDistance(a, p.Normalized());
        S1Angle distance2 = a.GetDistance(p.Normalized());

        Assert2.Near(Math.Abs(distance1.Radians - distance2.Radians), 0, 1e-10);
    }
コード例 #6
0
    public void Test_S1ChordAngle_Trigonometry()
    {
        const int kIters = 20;

        for (int iter = 0; iter <= kIters; ++iter)
        {
            double       radians = Math.PI * iter / kIters;
            S1ChordAngle angle   = new(S1Angle.FromRadians(radians));
            Assert2.Near(Math.Sin(radians), angle.Sin(), S2.DoubleError);
            Assert2.Near(Math.Cos(radians), angle.Cos(), S2.DoubleError);
            // Since the tan(x) is unbounded near Pi/4, we map the result back to an
            // angle before comparing.  (The assertion is that the result is equal to
            // the tangent of a nearby angle.)
            Assert2.Near(Math.Atan(Math.Tan(radians)), Math.Atan(angle.Tan()), S2.DoubleError);
        }

        // Unlike S1Angle, S1ChordAngle can represent 90 and 180 degrees exactly.
        S1ChordAngle angle90  = S1ChordAngle.FromLength2(2);
        S1ChordAngle angle180 = S1ChordAngle.FromLength2(4);

        Assert.Equal(1, angle90.Sin());
        Assert.Equal(0, angle90.Cos());
        Assert.Equal(double.PositiveInfinity, angle90.Tan());
        Assert.Equal(0, angle180.Sin());
        Assert.Equal(-1, angle180.Cos());
        Assert.Equal(0, angle180.Tan());
    }
コード例 #7
0
    public void Test_LoopTestBase_GetAreaConsistentWithOrientation()
    {
        // Test that GetArea() returns an area near 0 for degenerate loops that
        // contain almost no points, and an area near 4*Pi for degenerate loops that
        // contain almost all points.

        const int kMaxVertices = 6;

        for (int i = 0; i < 50; ++i)
        {
            int num_vertices = 3 + S2Testing.Random.Uniform(kMaxVertices - 3 + 1);
            // Repeatedly choose N vertices that are exactly on the equator until we
            // find some that form a valid loop.
            S2PointLoopSpan loop = new();
            do
            {
                for (int i2 = 0; i2 < num_vertices; ++i2)
                {
                    // We limit longitude to the range [0, 90] to ensure that the loop is
                    // degenerate (as opposed to following the entire equator).
                    loop.Add(
                        S2LatLng.FromRadians(0, S2Testing.Random.RandDouble() * S2.M_PI_2).ToPoint());
                }
            } while (!new S2Loop(loop, S2Debug.DISABLE).IsValid());
            bool ccw = S2.IsNormalized(loop);
            // The error bound is sufficient for current tests but not guaranteed.
            _ = i + ": " + loop.ToDebugString();
            Assert2.Near(ccw ? 0 : S2.M_4_PI, S2.GetArea(loop), 1e-14);
            Assert.Equal(!ccw, new S2Loop(loop).Contains(new S2Point(0, 0, 1)));
        }
    }
コード例 #8
0
ファイル: S2LatLngTests.cs プロジェクト: alas/s2geometry
 public void Test_S2LatLng_TestDistance()
 {
     Assert.Equal(0.0, S2LatLng.FromDegrees(90, 0).GetDistance(S2LatLng.FromDegrees(90, 0)).Radians);
     Assert2.Near(77.0, S2LatLng.FromDegrees(-37, 25).GetDistance(S2LatLng.FromDegrees(-66, -155)).GetDegrees(), 1e-13);
     Assert2.Near(115.0, S2LatLng.FromDegrees(0, 165).GetDistance(S2LatLng.FromDegrees(0, -80)).GetDegrees(), 1e-13);
     Assert2.Near(180.0, S2LatLng.FromDegrees(47, -127).GetDistance(S2LatLng.FromDegrees(-47, 53)).GetDegrees(), 2e-6);
 }
コード例 #9
0
    public void Test_LoopTestBase_GetCurvature()
    {
        Assert.Equal(-S2.M_2_PI, S2.GetCurvature(full_));

        Assert.Equal(S2.M_2_PI, S2.GetCurvature(v_loop_));
        CheckCurvatureInvariants(v_loop_);

        // This curvature should be computed exactly.
        Assert.Equal(0, S2.GetCurvature(north_hemi3_));
        CheckCurvatureInvariants(north_hemi3_);

        Assert2.Near(0, S2.GetCurvature(west_hemi_), 1e-15);
        CheckCurvatureInvariants(west_hemi_);

        // We don't have an easy way to estimate the curvature of these loops, but
        // we can still check that the expected invariants hold.
        CheckCurvatureInvariants(candy_cane_);
        CheckCurvatureInvariants(three_leaf_clover_);

        Assert2.DoubleEqual(S2.M_2_PI, S2.GetCurvature(line_triangle_));
        CheckCurvatureInvariants(line_triangle_);

        Assert2.DoubleEqual(S2.M_2_PI, S2.GetCurvature(skinny_chevron_));
        CheckCurvatureInvariants(skinny_chevron_);

        // Build a narrow spiral loop starting at the north pole.  This is designed
        // to test that the error in GetCurvature is linear in the number of
        // vertices even when the partial sum of the curvatures gets very large.
        // The spiral consists of two "arms" defining opposite sides of the loop.
        // This is a pathological loop that contains many long parallel edges.
        int             kArmPoints = 10000; // Number of vertices in each "arm"
        double          kArmRadius = 0.01;  // Radius of spiral.
        S2PointLoopSpan spiral     = new(2 * kArmPoints);

        spiral[kArmPoints] = new S2Point(0, 0, 1);
        for (int i = 0; i < kArmPoints; ++i)
        {
            double angle = (S2.M_2_PI / 3) * i;
            double x     = Math.Cos(angle);
            double y     = Math.Sin(angle);
            double r1    = i * kArmRadius / kArmPoints;
            double r2    = (i + 1.5) * kArmRadius / kArmPoints;
            spiral[kArmPoints - i - 1] = new S2Point(r1 * x, r1 * y, 1).Normalize();
            spiral[kArmPoints + i]     = new S2Point(r2 * x, r2 * y, 1).Normalize();
        }

        // Check that GetCurvature() is consistent with GetArea() to within the
        // error bound of the former.  We actually use a tiny fraction of the
        // worst-case error bound, since the worst case only happens when all the
        // roundoff errors happen in the same direction and this test is not
        // designed to achieve that.  The error in GetArea() can be ignored for the
        // purposes of this test since it is generally much smaller.
        Assert2.Near(
            S2.M_2_PI - S2.GetArea(spiral),
            S2.GetCurvature(spiral),
            0.01 * S2.GetCurvatureMaxError(spiral));
    }
コード例 #10
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());
        }
コード例 #11
0
        public void Test_S2Cap_GetRectBound()
        {
            // Empty and full caps.
            Assert.True(S2Cap.Empty.GetRectBound().IsEmpty());
            Assert.True(S2Cap.Full.GetRectBound().IsFull());

            // Maximum allowable error for latitudes and longitudes measured in
            // degrees.  (Assert2.Near isn't sufficient.)

            // Cap that includes the south pole.
            S2LatLngRect rect = new S2Cap(GetLatLngPoint(-45, 57),
                                          S1Angle.FromDegrees(50)).GetRectBound();

            Assert2.Near(rect.LatLo().GetDegrees(), -90, kDegreeEps);
            Assert2.Near(rect.LatHi().GetDegrees(), 5, kDegreeEps);
            Assert.True(rect.Lng.IsFull());

            // Cap that is tangent to the north pole.
            rect = new S2Cap(new S2Point(1, 0, 1).Normalize(),
                             S1Angle.FromRadians(S2.M_PI_4 + 1e-16)).GetRectBound();
            Assert2.Near(rect.Lat.Lo, 0, kEps);
            Assert2.Near(rect.Lat.Hi, S2.M_PI_2, kEps);
            Assert.True(rect.Lng.IsFull());

            var p   = new S2Point(1, 0, 1).Normalize();
            var rb1 = S1Angle.FromDegrees(45 + 5e-15);

            rect = new S2Cap(p, rb1).GetRectBound();
            Assert2.Near(rect.LatLo().GetDegrees(), 0, kDegreeEps);
            Assert2.Near(rect.LatHi().GetDegrees(), 90, kDegreeEps);
            Assert.True(rect.Lng.IsFull());

            // The eastern hemisphere.
            var rb2 = S1Angle.FromRadians(S2.M_PI_2 + 2e-16);

            rect = new S2Cap(new S2Point(0, 1, 0), rb2).GetRectBound();
            Assert2.Near(rect.LatLo().GetDegrees(), -90, kDegreeEps);
            Assert2.Near(rect.LatHi().GetDegrees(), 90, kDegreeEps);
            Assert.True(rect.Lng.IsFull());

            // A cap centered on the equator.
            rect = new S2Cap(GetLatLngPoint(0, 50), S1Angle.FromDegrees(20)).GetRectBound();
            Assert2.Near(rect.LatLo().GetDegrees(), -20, kDegreeEps);
            Assert2.Near(rect.LatHi().GetDegrees(), 20, kDegreeEps);
            Assert2.Near(rect.LngLo().GetDegrees(), 30, kDegreeEps);
            Assert2.Near(rect.LngHi().GetDegrees(), 70, kDegreeEps);

            // A cap centered on the north pole.
            rect = new S2Cap(GetLatLngPoint(90, 123), S1Angle.FromDegrees(10)).GetRectBound();
            Assert2.Near(rect.LatLo().GetDegrees(), 80, kDegreeEps);
            Assert2.Near(rect.LatHi().GetDegrees(), 90, kDegreeEps);
            Assert.True(rect.Lng.IsFull());
        }
コード例 #12
0
    public void Test_S2_GetPointToRightS1ChordAngle()
    {
        S2Point a         = S2LatLng.FromDegrees(0, 0).ToPoint();
        S2Point b         = S2LatLng.FromDegrees(0, 5).ToPoint(); // east
        S1Angle kDistance = S2Testing.MetersToAngle(10);

        S2Point c = S2.GetPointToRight(a, b, new S1ChordAngle(kDistance));

        Assert2.Near(new S1Angle(a, c).Radians, kDistance.Radians, 1e-15);
        // CAB must be a right angle with C to the right of AB.
        Assert2.Near(S2.TurnAngle(c, a, b), -S2.M_PI_2 /*radians*/, 1e-15);
    }
コード例 #13
0
 public void Test_S1ChordAngle_TwoPointConstructor()
 {
     for (int iter = 0; iter < 100; ++iter)
     {
         S2Testing.GetRandomFrame(out var x, out var y, out var z);
         Assert.Equal(S1Angle.Zero, new S1ChordAngle(z, z).ToAngle());
         Assert2.Near(Math.PI, new S1ChordAngle(-z, z).Radians(), 1e-7);
         Assert2.DoubleEqual(S2.M_PI_2, new S1ChordAngle(x, z).Radians());
         S2Point w = (y + z).Normalize();
         Assert2.DoubleEqual(S2.M_PI_4, new S1ChordAngle(w, z).Radians());
     }
 }
コード例 #14
0
    private static void CheckMaxDistance(S2Point x, S2Point a, S2Point b, double distance_radians)
    {
        x = x.Normalize();
        a = a.Normalize();
        b = b.Normalize();

        S1ChordAngle max_distance = S1ChordAngle.Straight;

        Assert.False(S2.UpdateMaxDistance(x, a, b, ref max_distance));
        max_distance = S1ChordAngle.Negative;
        Assert.True(S2.UpdateMaxDistance(x, a, b, ref max_distance));
        Assert2.Near(distance_radians, max_distance.Radians(), S2.DoubleError);
    }
コード例 #15
0
ファイル: S1IntervalTests.cs プロジェクト: alas/s2geometry
    public void Test_S1IntervalTestBase_GetDirectedHausdorffDistance()
    {
        Assert2.Near(0.0, empty.GetDirectedHausdorffDistance(empty));
        Assert2.Near(0.0, empty.GetDirectedHausdorffDistance(mid12));
        Assert2.Near(Math.PI, mid12.GetDirectedHausdorffDistance(empty));

        Assert.Equal(0.0, quad12.GetDirectedHausdorffDistance(quad123));
        S1Interval in_ = new(3.0, -3.0);  // an interval whose complement center is 0.

        Assert2.Near(3.0, new S1Interval(-0.1, 0.2).GetDirectedHausdorffDistance(in_));
        Assert2.Near(3.0 - 0.1, new S1Interval(0.1, 0.2).GetDirectedHausdorffDistance(in_));
        Assert2.Near(3.0 - 0.1, new S1Interval(-0.2, -0.1).GetDirectedHausdorffDistance(in_));
    }
コード例 #16
0
    // Given two edges a0a1 and b0b1, check that the maximum distance between them
    // is "distance_radians".  Parameters are passed by value so that this
    // function can normalize them.
    private static void CheckEdgePairMaxDistance(S2Point a0, S2Point a1, S2Point b0, S2Point b1, double distance_radians)
    {
        a0 = a0.Normalize();
        a1 = a1.Normalize();
        b0 = b0.Normalize();
        b1 = b1.Normalize();

        S1ChordAngle max_distance = S1ChordAngle.Straight;

        Assert.False(S2.UpdateEdgePairMaxDistance(a0, a1, b0, b1, ref max_distance));
        max_distance = S1ChordAngle.Negative;
        Assert.True(S2.UpdateEdgePairMaxDistance(a0, a1, b0, b1, ref max_distance));
        Assert2.Near(distance_radians, max_distance.Radians(), S2.DoubleError);
    }
コード例 #17
0
        public void Test_S2Cell_GetMaxDistanceToEdge()
        {
            // Test an edge for which its antipode crosses the cell. Validates both the
            // standard and brute force implementations for this case.
            S2Cell  cell = S2Cell.FromFacePosLevel(0, 0, 20);
            S2Point a    = -S2.Interpolate(2.0, cell.Center(), cell.Vertex(0));
            S2Point b    = -S2.Interpolate(2.0, cell.Center(), cell.Vertex(2));

            S1ChordAngle actual   = cell.MaxDistance(a, b);
            S1ChordAngle expected = GetMaxDistanceToEdgeBruteForce(cell, a, b);

            Assert2.Near(expected.Radians(), S1ChordAngle.Straight.Radians(), S2.DoubleError);
            Assert2.Near(actual.Radians(), S1ChordAngle.Straight.Radians(), S2.DoubleError);
        }
コード例 #18
0
        public void Test_S2ClosestEdgeQueryBase_MaxDistance()
        {
            var index = MakeIndexOrDie("0:0 | 1:0 | 2:0 | 3:0 # #");
            FurthestEdgeQuery query = new(index);

            FurthestEdgeQuery.Options options = new();
            options.MaxResults = (1);
            FurthestPointTarget target = new(MakePointOrDie("4:0"));
            var results = query.FindClosestEdges(target, options);

            Assert.Single(results);
            Assert.Equal(0, results[0].ShapeId);
            Assert.Equal(0, results[0].EdgeId);
            Assert2.Near(4, results[0].Distance.ToS1ChordAngle().ToAngle().GetDegrees(), 1e-13);
        }
コード例 #19
0
        public void Test_S2Cell_GetMaxDistanceToCell()
        {
            for (int i = 0; i < 1000; i++)
            {
                S2Cell   cell                = new(S2Testing.GetRandomCellId());
                S2Cell   test_cell           = new(S2Testing.GetRandomCellId());
                S2CellId antipodal_leaf_id   = new(-test_cell.Center());
                S2Cell   antipodal_test_cell = new(antipodal_leaf_id.Parent(test_cell.Level));

                S1ChordAngle dist_from_min = S1ChordAngle.Straight -
                                             cell.Distance(antipodal_test_cell);
                S1ChordAngle dist_from_max = cell.MaxDistance(test_cell);
                Assert2.Near(dist_from_min.Radians(), dist_from_max.Radians(), 1e-8);
            }
        }
コード例 #20
0
    public void Test_S1ChordAngle_ArithmeticPrecision()
    {
        // Verifies that S1ChordAngle is capable of adding and subtracting angles
        // extremely accurately up to Pi/2 radians.  (Accuracy continues to be good
        // well beyond this value but degrades as angles approach Pi.)
        S1ChordAngle kEps        = S1ChordAngle.FromRadians(1e-15);
        S1ChordAngle k90         = S1ChordAngle.Right;
        S1ChordAngle k90MinusEps = k90 - kEps;
        S1ChordAngle k90PlusEps  = k90 + kEps;
        double       kMaxError   = 2 * S2.DoubleEpsilon;

        Assert2.Near(k90MinusEps.Radians(), S2.M_PI_2 - kEps.Radians(), kMaxError);
        Assert2.Near(k90PlusEps.Radians(), S2.M_PI_2 + kEps.Radians(), kMaxError);
        Assert2.Near((k90 - k90MinusEps).Radians(), kEps.Radians(), kMaxError);
        Assert2.Near((k90PlusEps - k90).Radians(), kEps.Radians(), kMaxError);
        Assert2.Near((k90MinusEps + kEps).Radians(), S2.M_PI_2, kMaxError);
    }
コード例 #21
0
        private void CheckMinMaxAvg(
            string label, int level, double count, double abs_error,
            double min_value, double max_value, double avg_value,
            S2.Metric min_metric,
            S2.Metric max_metric,
            S2.Metric avg_metric)
        {
            // All metrics are minimums, maximums, or averages of differential
            // quantities, and therefore will not be exact for cells at any finite
            // level.  The differential minimum is always a lower bound, and the maximum
            // is always an upper bound, but these minimums and maximums may not be
            // achieved for two different reasons.  First, the cells at each level are
            // sampled and we may miss the most extreme examples.  Second, the actual
            // metric for a cell is obtained by integrating the differential quantity,
            // which is notant across the cell.  Therefore cells at low levels
            // (bigger cells) have smaller variations.
            //
            // The "tolerance" below is an attempt to model both of these effects.
            // At low levels, error is dominated by the variation of differential
            // quantities across the cells, while at high levels error is dominated by
            // the effects of random sampling.
            double tolerance = (max_metric.GetValue(level) - min_metric.GetValue(level)) /
                               Math.Sqrt(Math.Min(count, 0.5 * (1 << level)));

            if (tolerance == 0)
            {
                tolerance = abs_error;
            }

            double min_error = min_value - min_metric.GetValue(level);
            double max_error = max_metric.GetValue(level) - max_value;
            double avg_error = Math.Abs(avg_metric.GetValue(level) - avg_value);

            _logger.WriteLine("%-10s (%6.0f samples, tolerance %8.3g) - min %9.4g (%9.3g : %9.3g) " +
                              "max %9.4g (%9.3g : %9.3g), avg %9.4g (%9.3g : %9.3g)",
                              label, count, tolerance,
                              min_value, min_error / min_value, min_error / tolerance,
                              max_value, max_error / max_value, max_error / tolerance,
                              avg_value, avg_error / avg_value, avg_error / tolerance);

            Assert.True(min_metric.GetValue(level) <= min_value + abs_error);
            Assert.True(min_metric.GetValue(level) >= min_value - tolerance);
            Assert.True(max_metric.GetValue(level) <= max_value + tolerance);
            Assert.True(max_metric.GetValue(level) >= max_value - abs_error);
            Assert2.Near(avg_metric.GetValue(level), avg_value, 10 * tolerance);
        }
コード例 #22
0
    public void Test_LoopTestBase_GetAreaAndCentroid()
    {
        Assert.Equal(S2.M_4_PI, S2.GetArea(full_));
        Assert.Equal(S2Point.Empty, S2.GetCentroid(full_));

        Assert2.DoubleEqual(S2.GetArea(north_hemi_), S2.M_2_PI);
        Assert2.Near(S2.M_2_PI, S2.GetArea(east_hemi_), 1e-12);

        // Construct spherical caps of random height, and approximate their boundary
        // with closely spaces vertices.  Then check that the area and centroid are
        // correct.
        for (int iter = 0; iter < 50; ++iter)
        {
            // Choose a coordinate frame for the spherical cap.
            S2Testing.GetRandomFrame(out var x, out var y, out var z);

            // Given two points at latitude phi and whose longitudes differ by dtheta,
            // the geodesic between the two points has a maximum latitude of
            // atan(tan(phi) / cos(dtheta/2)).  This can be derived by positioning
            // the two points at (-dtheta/2, phi) and (dtheta/2, phi).
            //
            // We want to position the vertices close enough together so that their
            // maximum distance from the boundary of the spherical cap is kMaxDist.
            // Thus we want Math.Abs(atan(tan(phi) / cos(dtheta/2)) - phi) <= kMaxDist.
            const double kMaxDist   = 1e-6;
            double       height     = 2 * S2Testing.Random.RandDouble();
            double       phi        = Math.Asin(1 - height);
            double       max_dtheta = 2 * Math.Acos(Math.Tan(Math.Abs(phi)) / Math.Tan(Math.Abs(phi) + kMaxDist));
            max_dtheta = Math.Min(Math.PI, max_dtheta);  // At least 3 vertices.

            S2PointLoopSpan loop = new();
            for (double theta = 0; theta < S2.M_2_PI;
                 theta += S2Testing.Random.RandDouble() * max_dtheta)
            {
                loop.Add(Math.Cos(theta) * Math.Cos(phi) * x +
                         Math.Sin(theta) * Math.Cos(phi) * y +
                         Math.Sin(phi) * z);
            }
            double  area          = S2.GetArea(loop);
            S2Point centroid      = S2.GetCentroid(loop);
            double  expected_area = S2.M_2_PI * height;
            Assert.True(Math.Abs(area - expected_area) <= S2.M_2_PI * kMaxDist);
            S2Point expected_centroid = expected_area * (1 - 0.5 * height) * z;
            Assert.True((centroid - expected_centroid).Norm() <= 2 * kMaxDist);
        }
    }
コード例 #23
0
    public void Test_S2_Interpolate()
    {
        // Choose test points designed to expose floating-point errors.
        S2Point p1 = new S2Point(0.1, 1e-30, 0.3).Normalize();
        S2Point p2 = new S2Point(-0.7, -0.55, -1e30).Normalize();

        // A zero-length edge.
        TestInterpolate(p1, p1, 0, p1);
        TestInterpolate(p1, p1, 1, p1);

        // Start, end, and middle of a medium-length edge.
        TestInterpolate(p1, p2, 0, p1);
        TestInterpolate(p1, p2, 1, p2);
        TestInterpolate(p1, p2, 0.5, 0.5 * (p1 + p2));

        // Test that interpolation is done using distances on the sphere rather than
        // linear distances.
        TestInterpolate(new S2Point(1, 0, 0), new S2Point(0, 1, 0), 1.0 / 3,
                        new S2Point(Math.Sqrt(3), 1, 0));
        TestInterpolate(new S2Point(1, 0, 0), new S2Point(0, 1, 0), 2.0 / 3,
                        new S2Point(1, Math.Sqrt(3), 0));

        // Test that interpolation is accurate on a long edge (but not so long that
        // the definition of the edge itself becomes too unstable).
        {
            double  kLng = Math.PI - 1e-2;
            S2Point a    = S2LatLng.FromRadians(0, 0).ToPoint();
            S2Point b    = S2LatLng.FromRadians(0, kLng).ToPoint();
            for (double f = 0.4; f > 1e-15; f *= 0.1)
            {
                TestInterpolate(a, b, f, S2LatLng.FromRadians(0, f * kLng).ToPoint());
                TestInterpolate(a, b, 1 - f, S2LatLng.FromRadians(0, (1 - f) * kLng).ToPoint());
            }
        }

        // Test that interpolation on a 180 degree edge (antipodal endpoints) yields
        // a result with the correct distance from each endpoint.
        for (double t = 0; t <= 1; t += 0.125)
        {
            S2Point actual = S2.Interpolate(p1, -p1, t);
            Assert2.Near(new S1Angle(actual, p1).Radians, t * Math.PI, 3e-15);
        }
    }
コード例 #24
0
ファイル: S2LatLngRectTests.cs プロジェクト: alas/s2geometry
    public void Test_S2LatLngRect_GetCentroid()
    {
        // Empty and full rectangles.
        Assert.Equal(new S2Point(), S2LatLngRect.Empty.Centroid());
        Assert.True(S2LatLngRect.Full.Centroid().Norm() <= 1e-15);

        // Rectangles that cover the full longitude range.
        for (int i = 0; i < 100; ++i)
        {
            double       lat1     = S2Testing.Random.UniformDouble(-S2.M_PI_2, S2.M_PI_2);
            double       lat2     = S2Testing.Random.UniformDouble(-S2.M_PI_2, S2.M_PI_2);
            S2LatLngRect r        = new(R1Interval.FromPointPair(lat1, lat2), S1Interval.Full);
            S2Point      centroid = r.Centroid();
            Assert2.Near(0.5 * (Math.Sin(lat1) + Math.Sin(lat2)) * r.Area(), centroid.Z, S2.DoubleError);
            Assert.True(new R2Point(centroid.X, centroid.Y).GetNorm() <= 1e-15);
        }

        // Rectangles that cover the full latitude range.
        for (int i = 0; i < 100; ++i)
        {
            double       lng1 = S2Testing.Random.UniformDouble(-Math.PI, Math.PI);
            double       lng2 = S2Testing.Random.UniformDouble(-Math.PI, Math.PI);
            S2LatLngRect r    = new(S2LatLngRect.FullLat,
                                    S1Interval.FromPointPair(lng1, lng2));
            S2Point centroid = r.Centroid();
            Assert.True(Math.Abs(centroid.Z) <= 1e-15);
            Assert2.Near(r.Lng.GetCenter(), new S2LatLng(centroid).LngRadians, S2.DoubleError);
            double alpha = 0.5 * r.Lng.GetLength();
            // TODO(Alas): the next Assert fails sometimes
            Assert2.Near(0.25 * Math.PI * Math.Sin(alpha) / alpha * r.Area(),
                         new R2Point(centroid.X, centroid.Y).GetNorm(), S2.DoubleError);
        }

        // Finally, verify that when a rectangle is recursively split into pieces,
        // the centroids of the pieces add to give the centroid of their parent.
        // To make the code simpler we avoid rectangles that cross the 180 degree
        // line of longitude.
        TestCentroidSplitting(
            new S2LatLngRect(S2LatLngRect.FullLat, new S1Interval(-3.14, 3.14)),
            10 /*splits_left*/);
    }
コード例 #25
0
        public void Test_S2ClosestPointQueryBase_MaxDistance()
        {
            S2PointIndex <int> index = new();
            var points = ParsePointsOrDie("0:0, 1:0, 2:0, 3:0");

            for (int i = 0; i < points.Count; ++i)
            {
                index.Add(points[i], i);
            }
            FurthestPointQuery <int> query = new(index);

            FurthestPointQuery <int> .Options options = new();
            options.MaxResults = (1);
            FurthestPointTarget target = new(MakePointOrDie("4:0"));
            var results = query.FindClosestPoints(target, options);

            Assert.Single(results);
            Assert.Equal(points[0], results[0].Point);
            Assert.Equal(0, results[0].Data);
            Assert2.Near(4, results[0].Distance.ToS1ChordAngle().ToAngle().GetDegrees(), 1e-13);
        }
コード例 #26
0
        public void Test_S2ClosestEdgeQuery_OptionsNotModified()
        {
            // Tests that FindClosestEdge(), GetDistance(), and IsDistanceLess() do not
            // modify query.Options_, even though all of these methods have their own
            // specific options requirements.
            S2ClosestEdgeQuery.Options options = new();
            options.MaxResults  = (3);
            options.MaxDistance = (Distance.FromDegrees(3));
            options.MaxError    = (Distance.FromDegrees(0.001));
            var index = MakeIndexOrDie("1:1 | 1:2 | 1:3 # #");
            S2ClosestEdgeQuery query = new(index, options);

            S2ClosestEdgeQuery.PointTarget target = new(MakePointOrDie("2:2"));
            Assert.Equal(1, query.FindClosestEdge(target).EdgeId);
            Assert2.Near(1.0, query.GetDistance(target).Degrees(), S2.DoubleError);
            Assert.True(query.IsDistanceLess(target, Distance.FromDegrees(1.5)));

            // Verify that none of the options above were modified.
            Assert.Equal(options.MaxResults, query.Options_.MaxResults);
            Assert.Equal(options.MaxDistance, query.Options_.MaxDistance);
            Assert.Equal(options.MaxError, query.Options_.MaxError);
        }
コード例 #27
0
 public void Test_ST_UV_Conversions()
 {
     // Check boundary conditions.
     for (double s = 0; s <= 1; s += 0.5)
     {
         /*volatile*/
         double u = S2.STtoUV(s);
         Assert.Equal(u, 2 * s - 1);
     }
     for (double u = -1; u <= 1; ++u)
     {
         /*volatile*/
         double s = S2.UVtoST(u);
         Assert.Equal(s, 0.5 * (u + 1));
     }
     // Check that UVtoST and STtoUV are inverses.
     for (double x = 0; x <= 1; x += 0.0001)
     {
         Assert2.Near(S2.UVtoST(S2.STtoUV(x)), x, S2.DoubleError);
         Assert2.Near(S2.STtoUV(S2.UVtoST(2 * x - 1)), 2 * x - 1, S2.DoubleError);
     }
 }
コード例 #28
0
 public void Test_S2Cell_GetDistanceToPoint()
 {
     S2Testing.Random.Reset(S2Testing.Random.RandomSeed);
     for (int iter = 0; iter < 1000; ++iter)
     {
         _logger.WriteLine($"Iteration {iter}");
         S2Cell  cell   = new(S2Testing.GetRandomCellId());
         S2Point target = S2Testing.RandomPoint();
         S1Angle expected_to_boundary =
             GetDistanceToPointBruteForce(cell, target).ToAngle();
         S1Angle expected_to_interior =
             cell.Contains(target) ? S1Angle.Zero : expected_to_boundary;
         S1Angle expected_max =
             GetMaxDistanceToPointBruteForce(cell, target).ToAngle();
         S1Angle actual_to_boundary = cell.BoundaryDistance(target).ToAngle();
         S1Angle actual_to_interior = cell.Distance(target).ToAngle();
         S1Angle actual_max         = cell.MaxDistance(target).ToAngle();
         // The error has a peak near Pi/2 for edge distance, and another peak near
         // Pi for vertex distance.
         Assert2.Near(expected_to_boundary.Radians,
                      actual_to_boundary.Radians, 1e-12);
         Assert2.Near(expected_to_interior.Radians,
                      actual_to_interior.Radians, 1e-12);
         Assert2.Near(expected_max.Radians,
                      actual_max.Radians, 1e-12);
         if (expected_to_boundary.Radians <= Math.PI / 3)
         {
             Assert2.Near(expected_to_boundary.Radians,
                          actual_to_boundary.Radians, S2.DoubleError);
             Assert2.Near(expected_to_interior.Radians,
                          actual_to_interior.Radians, S2.DoubleError);
         }
         if (expected_max.Radians <= Math.PI / 3)
         {
             Assert2.Near(expected_max.Radians, actual_max.Radians, S2.DoubleError);
         }
     }
 }
コード例 #29
0
ファイル: S2MeasuresTests.cs プロジェクト: alas/s2geometry
    public void Test_S2_AngleMethods()
    {
        S2Point pz   = new(0, 0, 1);
        S2Point p000 = new(1, 0, 0);
        S2Point p045 = new S2Point(1, 1, 0).Normalize();
        S2Point p090 = new(0, 1, 0);
        S2Point p180 = new(-1, 0, 0);

        Assert2.Near(S2.Angle(p000, pz, p045), S2.M_PI_4);
        Assert2.Near(S2.TurnAngle(p000, pz, p045), -3 * S2.M_PI_4);

        Assert2.Near(S2.Angle(p045, pz, p180), 3 * S2.M_PI_4);
        Assert2.Near(S2.TurnAngle(p045, pz, p180), -S2.M_PI_4);

        Assert2.Near(S2.Angle(p000, pz, p180), Math.PI);
        Assert2.Near(S2.TurnAngle(p000, pz, p180), 0);

        Assert2.Near(S2.Angle(pz, p000, p045), S2.M_PI_2);
        Assert2.Near(S2.TurnAngle(pz, p000, p045), S2.M_PI_2);

        Assert2.Near(S2.Angle(pz, p000, pz), 0);
        Assert2.Near(Math.Abs(S2.TurnAngle(pz, p000, pz)), Math.PI);
    }
コード例 #30
0
        public void Test_S2CellId_Continuity()
        {
            // Make sure that sequentially increasing cell ids form a continuous
            // path over the surface of the sphere, i.e. there are no
            // discontinuous jumps from one region to another.

            double   max_dist = S2.kMaxEdge.GetValue(kMaxWalkLevel);
            S2CellId end      = S2CellId.End(kMaxWalkLevel);
            S2CellId id       = S2CellId.Begin(kMaxWalkLevel);

            for (; id != end; id = id.Next())
            {
                Assert.True(id.ToPointRaw().Angle(id.NextWrap().ToPointRaw()) <= max_dist);
                Assert.Equal(id.NextWrap(), id.AdvanceWrap(1));
                Assert.Equal(id, id.NextWrap().AdvanceWrap(-1));

                // Check that the ToPointRaw() returns the center of each cell
                // in (s,t) coordinates.
                S2.XYZtoFaceUV(id.ToPointRaw(), out var u, out var v);
                Assert2.Near(Math.IEEERemainder(S2.UVtoST(u), 0.5 * kCellSize), 0.0, S2.DoubleError);
                Assert2.Near(Math.IEEERemainder(S2.UVtoST(v), 0.5 * kCellSize), 0.0, S2.DoubleError);
            }
        }