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))); } }
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)); }
public void Test_GetArea_TinyShellAndHole() { // A square about 20 um on each side with a hole 10 um on each side. double side = S1Angle.FromDegrees(1e-10).Radians; Assert.Equal(3 * side * side, S2.GetArea(MakeLaxPolygonOrDie( "0:0, 0:2e-10, 2e-10:2e-10, 2e-10:0; " + "0.5e-10:0.5e-10, 1.5e-10:0.5e-10, 1.5e-10:1.5e-10, 0.5e-10:1.5e-10"))); }
public void Test_GetArea_TwoTinyShells() { // Two small squares with sides about 10 um (micrometers) long. double side = S1Angle.FromDegrees(1e-10).Radians; Assert.Equal(2 * side * side, S2.GetArea(MakeLaxPolygonOrDie( "0:0, 0:1e-10, 1e-10:1e-10, 1e-10:0; " + "0:0, 0:-1e-10, -1e-10:-1e-10, -1e-10:0"))); }
private static void TestAreaConsistentWithCurvature(S2PointLoopSpan loop) { // Check that the area computed using GetArea() is consistent with the loop // curvature. According to the Gauss-Bonnet theorem, the area of the loop // equals 2*Pi minus its curvature. double area = S2.GetArea(loop); double gauss_area = S2.M_2_PI - S2.GetCurvature(loop); // The error bound below is sufficient for current tests but not guaranteed. _ = loop.ToDebugString(); Assert.True(Math.Abs(area - gauss_area) <= 1e-14); }
// Returns the total area of all polygons in the index. Returns zero if no // polygons are present. This method has good relative accuracy for both very // large and very small regions. Note that the result may exceed 4*Pi if the // index contains overlapping polygons. // // All edges are modeled as spherical geodesics. The result can be converted // to an area on the Earth's surface (with a worst-case error of 0.900% near // the poles) using the functions in s2earth.h. public static double GetArea(S2ShapeIndex index) { double area = 0; for (int i = 0; i < index.NumShapeIds(); ++i) { var shape = index.Shape(i); if (shape != null) { area += S2.GetArea(shape); } } return(area); }
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); } }
public void Test_GetArea_FullPolygon() { Assert.Equal(4 * Math.PI, S2.GetArea(MakeLaxPolygonOrDie("full"))); Assert.Equal(4 * Math.PI, S2.GetArea(new S2Polygon.OwningShape(MakePolygonOrDie("full")))); }
public void Test_GetArea_EmptyPolygon() { Assert.Equal(0.0, S2.GetArea(MakeLaxPolygonOrDie("empty"))); }
public void Test_GetArea_WrongDimension() { Assert.Equal(0.0, S2.GetArea(MakeIndexOrDie("0:0 # #").Shape(0))); Assert.Equal(0.0, S2.GetArea(MakeLaxPolylineOrDie("0:0, 0:1, 1:0"))); }