public void Test_S2LatLngRect_CellOps() { // Contains(S2Cell), MayIntersect(S2Cell), Intersects(S2Cell) // Special cases. TestCellOps(S2LatLngRect.Empty, S2Cell.FromFacePosLevel(3, 0, 0), 0); TestCellOps(S2LatLngRect.Full, S2Cell.FromFacePosLevel(2, 0, 0), 4); TestCellOps(S2LatLngRect.Full, S2Cell.FromFacePosLevel(5, 0, 25), 4); // This rectangle includes the first quadrant of face 0. It's expanded // slightly because cell bounding rectangles are slightly conservative. S2LatLngRect r4 = RectFromDegrees(-45.1, -45.1, 0.1, 0.1); TestCellOps(r4, S2Cell.FromFacePosLevel(0, 0, 0), 3); TestCellOps(r4, S2Cell.FromFacePosLevel(0, 0, 1), 4); TestCellOps(r4, S2Cell.FromFacePosLevel(1, 0, 1), 0); // This rectangle intersects the first quadrant of face 0. S2LatLngRect r5 = RectFromDegrees(-10, -45, 10, 0); TestCellOps(r5, S2Cell.FromFacePosLevel(0, 0, 0), 3); TestCellOps(r5, S2Cell.FromFacePosLevel(0, 0, 1), 3); TestCellOps(r5, S2Cell.FromFacePosLevel(1, 0, 1), 0); // Rectangle consisting of a single point. TestCellOps(RectFromDegrees(4, 4, 4, 4), S2Cell.FromFace(0), 3); // Rectangles that intersect the bounding rectangle of a face // but not the face itself. TestCellOps(RectFromDegrees(41, -87, 42, -79), S2Cell.FromFace(2), 1); TestCellOps(RectFromDegrees(-41, 160, -40, -160), S2Cell.FromFace(5), 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. S2Cell cell0tr = new(new S2Point(1 + 1e-12, 1, 1)); _ = cell0tr.GetRectBound(); S2LatLng v0 = new(cell0tr.VertexRaw(0)); TestCellOps(RectFromDegrees(v0.Lat().GetDegrees() - 1e-8, v0.Lng().GetDegrees() - 1e-8, v0.Lat().GetDegrees() - 2e-10, v0.Lng().GetDegrees() + 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.FromFace(5), 2); // These two intersect like a diamond and a square. S2Cell cell202 = S2Cell.FromFacePosLevel(2, 0, 2); S2LatLngRect bound202 = cell202.GetRectBound(); TestCellOps(RectFromDegrees(bound202.Lo().Lat().GetDegrees() + 3, bound202.Lo().Lng().GetDegrees() + 3, bound202.Hi().Lat().GetDegrees() - 3, bound202.Hi().Lng().GetDegrees() - 3), cell202, 2); }
public void Test_S2RegionUnionTest_Basic() { S2RegionUnion ru_empty = new(new List <IS2Region>()); Assert.Equal(0, ru_empty.Count()); Assert.Equal(S2Cap.Empty, ru_empty.GetCapBound()); Assert.Equal(S2LatLngRect.Empty, ru_empty.GetRectBound()); var empty_clone = (S2RegionUnion)ru_empty.CustomClone(); var two_point_region = new List <IS2Region> { new S2PointRegion(S2LatLng.FromDegrees(35, 40).ToPoint()), new S2PointRegion(S2LatLng.FromDegrees(-35, -40).ToPoint()) }; var two_points_orig = new S2RegionUnion(two_point_region); // two_point_region is in a valid, but unspecified, state. // Check that Clone() returns a deep copy. var two_points = (S2RegionUnion)two_points_orig.CustomClone(); // The bounds below may not be exactly equal because the S2PointRegion // version converts each S2LatLng value to an S2Point and back. Assert.True(MakeLatLngRectOrDie("-35:-40,35:40") !.Value .ApproxEquals(two_points.GetRectBound())); S2Cell face0 = S2Cell.FromFace(0); Assert.True(two_points.MayIntersect(face0)); Assert.False(two_points.Contains(face0)); Assert.True(two_points.Contains(S2LatLng.FromDegrees(35, 40).ToPoint())); Assert.True(two_points.Contains(S2LatLng.FromDegrees(-35, -40).ToPoint())); Assert.False(two_points.Contains(S2LatLng.FromDegrees(0, 0).ToPoint())); // Check that we can Add() another region. var three_points = (S2RegionUnion)two_points.CustomClone(); Assert.False(three_points.Contains(S2LatLng.FromDegrees(10, 10).ToPoint())); three_points.Add(new S2RegionUnion(new List <IS2Region> { new S2PointRegion(S2LatLng.FromDegrees(10, 10).ToPoint()) })); Assert.True(three_points.Contains(S2LatLng.FromDegrees(10, 10).ToPoint())); var coverer = new S2RegionCoverer(); coverer.Options_.MaxCells = 1; coverer.GetCovering(two_points, out var covering); Assert.Single(covering); Assert.Equal(face0.Id, covering[0]); }
public void Test_S2RegionEncodeDecodeTest_S2Cell() { S2Cell cell_from_point = new(new S2Point(1, 2, 3)); S2Cell cell_from_latlng = new(S2LatLng.FromDegrees(39.0, -120.0)); S2Cell cell_from_face_pos_lvl = S2Cell.FromFacePosLevel(3, 0x12345678, S2.kMaxCellLevel - 4); S2Cell cell_from_from_face = S2Cell.FromFace(0); var cell = TestEncodeDecode(kEncodedCellFromPoint, cell_from_point); Assert.Equal(cell_from_point, cell); cell = TestEncodeDecode(kEncodedCellFromLatLng, cell_from_latlng); Assert.Equal(cell_from_latlng, cell); cell = TestEncodeDecode(kEncodedCellFromFacePosLevel, cell_from_face_pos_lvl); Assert.Equal(cell_from_face_pos_lvl, cell); cell = TestEncodeDecode(kEncodedCellFace0, cell_from_from_face); Assert.Equal(cell_from_from_face, cell); }
public void Test_S2Cell_TestSubdivide() { // Only test a sample of faces to reduce the runtime. TestSubdivide(S2Cell.FromFace(0)); TestSubdivide(S2Cell.FromFace(3)); TestSubdivide(S2Cell.FromFace(5)); #region Print Header // This table is useful in evaluating the quality of the various S2 // projections. // // The maximum edge *ratio* is the ratio of the longest edge of any cell to // the shortest edge of any cell at the same level (and similarly for the // maximum diagonal ratio). // // The maximum edge *aspect* is the maximum ratio of the longest edge of a // cell to the shortest edge of that same cell (and similarly for the // maximum diagonal aspect). _logger.WriteLine( @"Ratio: (Max value for any cell) / (Min value for any cell) Aspect: (Max value / min value) for any cell Edge Diag Approx Area/ Avg Area/ Area Length Length Exact Area Exact Area Level Ratio Ratio Aspect Ratio Aspect Min Max Min Max --------------------------------------------------------------------"); #endregion for (int i = 0; i <= S2.kMaxCellLevel; ++i) { var s = level_stats[i]; if (s.Count > 0) { s.Avg_area /= s.Count; s.Avg_width /= s.Count; s.Avg_edge /= s.Count; s.Avg_diag /= s.Count; s.Avg_angle_span /= s.Count; } _logger.WriteLine($"{i:d5} {s.Max_area / s.Min_area:f6.3} {s.Max_edge / s.Min_edge:f6.3} {s.Max_edge_aspect:f6.3} {s.Max_diag / s.Min_diag:f6.3} {s.Max_diag_aspect:f6.3} {s.Min_approx_ratio:f6.3} {s.Max_approx_ratio:f6.3} {S2Cell.AverageArea(i) / s.Max_area:f6.3} {S2Cell.AverageArea(i) / s.Min_area:f6.3}"); } // Now check the validity of the S2 length and area metrics. for (int i = 0; i <= S2.kMaxCellLevel; ++i) { var s = level_stats[i]; if (s.Count == 0) { continue; } _logger.WriteLine($"Level {i:2d} - metric value (error/actual : error/tolerance)"); // The various length calculations are only accurate to 1e-15 or so, // so we need to allow for this amount of discrepancy with the theoretical // minimums and maximums. The area calculation is accurate to about 1e-15 // times the cell width. CheckMinMaxAvg("area", i, s.Count, 1e-15 * s.Min_width, s.Min_area, s.Max_area, s.Avg_area, S2.kMinArea, S2.kMaxArea, S2.kAvgArea); CheckMinMaxAvg("width", i, s.Count, 1e-15, s.Min_width, s.Max_width, s.Avg_width, S2.kMinWidth, S2.kMaxWidth, S2.kAvgWidth); CheckMinMaxAvg("edge", i, s.Count, 1e-15, s.Min_edge, s.Max_edge, s.Avg_edge, S2.kMinEdge, S2.kMaxEdge, S2.kAvgEdge); CheckMinMaxAvg("diagonal", i, s.Count, 1e-15, s.Min_diag, s.Max_diag, s.Avg_diag, S2.kMinDiag, S2.kMaxDiag, S2.kAvgDiag); CheckMinMaxAvg("angle span", i, s.Count, 1e-15, s.Min_angle_span, s.Max_angle_span, s.Avg_angle_span, S2.kMinAngleSpan, S2.kMaxAngleSpan, S2.kAvgAngleSpan); // The aspect ratio calculations are ratios of lengths and are therefore // less accurate at higher subdivision levels. Assert.True(s.Max_edge_aspect <= S2.kMaxEdgeAspect + 1e-15 * (1 << i)); Assert.True(s.Max_diag_aspect <= S2.kMaxDiagAspect + 1e-15 * (1 << i)); } }
public void Test_S2Cap_S2CellMethods() { // For each cube face, we construct some cells on // that face and some caps whose positions are relative to that face, // and then check for the expected intersection/containment results. for (var face = 0; face < 6; ++face) { // The cell consisting of the entire face. S2Cell root_cell = S2Cell.FromFace(face); // A leaf cell at the midpoint of the v=1 edge. S2Cell edge_cell = new(S2.FaceUVtoXYZ(face, 0, 1 - kEps)); // A leaf cell at the u=1, v=1 corner. S2Cell corner_cell = new(S2.FaceUVtoXYZ(face, 1 - kEps, 1 - kEps)); // Quick check for full and empty caps. Assert.True(S2Cap.Full.Contains(root_cell)); Assert.False(S2Cap.Empty.MayIntersect(root_cell)); // Check intersections with the bounding caps of the leaf cells that are // adjacent to 'corner_cell' along the Hilbert curve. Because this corner // is at (u=1,v=1), the curve stays locally within the same cube face. S2CellId first = corner_cell.Id.Advance(-3); S2CellId last = corner_cell.Id.Advance(4); for (S2CellId id = first; id < last; id = id.Next()) { S2Cell cell = new(id); Assert.Equal(id == corner_cell.Id, cell.GetCapBound().Contains(corner_cell)); Assert.Equal(id.Parent().Contains(corner_cell.Id), cell.GetCapBound().MayIntersect(corner_cell)); } var anti_face = (face + 3) % 6; // Opposite face. for (var cap_face = 0; cap_face < 6; ++cap_face) { // A cap that barely contains all of 'cap_face'. S2Point center = S2.GetNorm(cap_face); S2Cap covering = new(center, S1Angle.FromRadians(kFaceRadius + kEps)); Assert.Equal(cap_face == face, covering.Contains(root_cell)); Assert.Equal(cap_face != anti_face, covering.MayIntersect(root_cell)); Assert.Equal(center.DotProd(edge_cell.Center()) > 0.1, covering.Contains(edge_cell)); Assert.Equal(covering.MayIntersect(edge_cell), covering.Contains(edge_cell)); Assert.Equal(cap_face == face, covering.Contains(corner_cell)); Assert.Equal(center.DotProd(corner_cell.Center()) > 0, covering.MayIntersect(corner_cell)); // A cap that barely intersects the edges of 'cap_face'. S2Cap bulging = new(center, S1Angle.FromRadians(S2.M_PI_4 + kEps)); Assert.False(bulging.Contains(root_cell)); Assert.Equal(cap_face != anti_face, bulging.MayIntersect(root_cell)); Assert.Equal(cap_face == face, bulging.Contains(edge_cell)); Assert.Equal(center.DotProd(edge_cell.Center()) > 0.1, bulging.MayIntersect(edge_cell)); Assert.False(bulging.Contains(corner_cell)); Assert.False(bulging.MayIntersect(corner_cell)); // A singleton cap. S2Cap singleton = new(center, S1Angle.Zero); Assert.Equal(cap_face == face, singleton.MayIntersect(root_cell)); Assert.False(singleton.MayIntersect(edge_cell)); Assert.False(singleton.MayIntersect(corner_cell)); } } }