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()); }
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)); } }
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); }
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)); }
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); } }
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); }
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))); }