private static S1Angle BruteForceDistance(S2LatLngRect a, S2LatLngRect b) { if (a.Intersects(b)) { return(S1Angle.FromRadians(0)); } // Compare every point in 'a' against every latitude edge and longitude edge // in 'b', and vice-versa, for a total of 16 point-vs-latitude-edge tests and // 16 point-vs-longitude-edge tests. var pnt_a = new S2LatLng[4]; var pnt_b = new S2LatLng[4]; pnt_a[0] = new S2LatLng(a.LatLo(), a.LngLo()); pnt_a[1] = new S2LatLng(a.LatLo(), a.LngHi()); pnt_a[2] = new S2LatLng(a.LatHi(), a.LngHi()); pnt_a[3] = new S2LatLng(a.LatHi(), a.LngLo()); pnt_b[0] = new S2LatLng(b.LatLo(), b.LngLo()); pnt_b[1] = new S2LatLng(b.LatLo(), b.LngHi()); pnt_b[2] = new S2LatLng(b.LatHi(), b.LngHi()); pnt_b[3] = new S2LatLng(b.LatHi(), b.LngLo()); // Make arrays containing the lo/hi latitudes and the lo/hi longitude edges. var lat_a = new S1Angle[2] { a.LatLo(), a.LatHi() }; var lat_b = new S1Angle[2] { b.LatLo(), b.LatHi() }; var lng_edge_a = new S2Point[][] { new S2Point[] { pnt_a[0].ToPoint(), pnt_a[3].ToPoint() }, new S2Point[] { pnt_a[1].ToPoint(), pnt_a[2].ToPoint() } }; var lng_edge_b = new S2Point[][] { new S2Point[] { pnt_b[0].ToPoint(), pnt_b[3].ToPoint() }, new S2Point[] { pnt_b[1].ToPoint(), pnt_b[2].ToPoint() } }; S1Angle min_distance = S1Angle.FromDegrees(180.0); for (int i = 0; i < 4; ++i) { // For each point in a and b. var current_a = pnt_a[i]; var current_b = pnt_b[i]; for (int j = 0; j < 2; ++j) { // Get distances to latitude and longitude edges. S1Angle a_to_lat = GetDistance(current_a, lat_b[j], b.Lng); S1Angle b_to_lat = GetDistance(current_b, lat_a[j], a.Lng); S1Angle a_to_lng = S2.GetDistance(current_a.ToPoint(), lng_edge_b[j][0], lng_edge_b[j][1]); S1Angle b_to_lng = S2.GetDistance(current_b.ToPoint(), lng_edge_a[j][0], lng_edge_a[j][1]); min_distance = new[] { min_distance, a_to_lat, b_to_lat, a_to_lng, b_to_lng }.Min(); } } return(min_distance); }
private static S1Angle BruteForceRectPointDistance(S2LatLngRect a, S2LatLng b) { if (a.Contains(b)) { return(S1Angle.FromRadians(0)); } S1Angle b_to_lo_lat = GetDistance(b, a.LatLo(), a.Lng); S1Angle b_to_hi_lat = GetDistance(b, a.LatHi(), a.Lng); S1Angle b_to_lo_lng = S2.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo(), a.LngLo()).ToPoint(), new S2LatLng(a.LatHi(), a.LngLo()).ToPoint()); S1Angle b_to_hi_lng = S2.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo(), a.LngHi()).ToPoint(), new S2LatLng(a.LatHi(), a.LngHi()).ToPoint()); return(new[] { b_to_lo_lat, b_to_hi_lat, b_to_lo_lng, b_to_hi_lng }.Min()); }
public void Test_S2LatLngRect_Accessors() { // Check various accessor methods. S2LatLngRect d1 = RectFromDegrees(-90, 0, -45, 180); Assert2.DoubleEqual(d1.LatLo().GetDegrees(), -90); Assert2.DoubleEqual(d1.LatHi().GetDegrees(), -45); Assert2.DoubleEqual(d1.LngLo().GetDegrees(), 0); Assert2.DoubleEqual(d1.LngHi().GetDegrees(), 180); Assert.Equal(d1.Lat, new(-S2.M_PI_2, -S2.M_PI_4)); Assert.Equal(d1.Lng, new(0, Math.PI)); }
static void TestSubdivide(S2Cell cell) { GatherStats(cell); if (cell.IsLeaf()) { return; } var children = new S2Cell[4]; Assert.True(cell.Subdivide(children)); S2CellId child_id = cell.Id.ChildBegin(); double exact_area = 0; double approx_area = 0; double average_area = 0; for (int i = 0; i < 4; ++i, child_id = child_id.Next()) { exact_area += children[i].ExactArea(); approx_area += children[i].ApproxArea(); average_area += children[i].AverageArea(); // Check that the child geometry is consistent with its cell ID. Assert.Equal(child_id, children[i].Id); Assert.True(S2.ApproxEquals(children[i].Center(), child_id.ToPoint())); S2Cell direct = new(child_id); Assert.Equal(direct.Face, children[i].Face); Assert.Equal(direct.Level, children[i].Level); Assert.Equal(direct.Orientation, children[i].Orientation); Assert.Equal(direct.CenterRaw(), children[i].CenterRaw()); for (int k = 0; k < 4; ++k) { Assert.Equal(direct.VertexRaw(k), children[i].VertexRaw(k)); Assert.Equal(direct.EdgeRaw(k), children[i].EdgeRaw(k)); } // Test Contains() and MayIntersect(). Assert.True(cell.Contains(children[i])); Assert.True(cell.MayIntersect(children[i])); Assert.False(children[i].Contains(cell)); Assert.True(cell.Contains(children[i].CenterRaw())); for (int j = 0; j < 4; ++j) { Assert.True(cell.Contains(children[i].VertexRaw(j))); if (j != i) { Assert.False(children[i].Contains(children[j].CenterRaw())); Assert.False(children[i].MayIntersect(children[j])); } } // Test GetCapBound and GetRectBound. S2Cap parent_cap = cell.GetCapBound(); S2LatLngRect parent_rect = cell.GetRectBound(); if (cell.Contains(new S2Point(0, 0, 1)) || cell.Contains(new S2Point(0, 0, -1))) { Assert.True(parent_rect.Lng.IsFull()); } S2Cap child_cap = children[i].GetCapBound(); S2LatLngRect child_rect = children[i].GetRectBound(); Assert.True(child_cap.Contains(children[i].Center())); Assert.True(child_rect.Contains(children[i].CenterRaw())); Assert.True(parent_cap.Contains(children[i].Center())); Assert.True(parent_rect.Contains(children[i].CenterRaw())); for (int j = 0; j < 4; ++j) { Assert.True(child_cap.Contains(children[i].Vertex(j))); Assert.True(child_rect.Contains(children[i].Vertex(j))); Assert.True(child_rect.Contains(children[i].VertexRaw(j))); Assert.True(parent_cap.Contains(children[i].Vertex(j))); Assert.True(parent_rect.Contains(children[i].Vertex(j))); Assert.True(parent_rect.Contains(children[i].VertexRaw(j))); if (j != i) { // The bounding caps and rectangles should be tight enough so that // they exclude at least two vertices of each adjacent cell. int cap_count = 0; int rect_count = 0; for (int k = 0; k < 4; ++k) { if (child_cap.Contains(children[j].Vertex(k))) { ++cap_count; } if (child_rect.Contains(children[j].VertexRaw(k))) { ++rect_count; } } Assert.True(cap_count <= 2); if (child_rect.LatLo().Radians > -S2.M_PI_2 && child_rect.LatHi().Radians < S2.M_PI_2) { // Bounding rectangles may be too large at the poles because the // pole itself has an arbitrary fixed longitude. Assert.True(rect_count <= 2); } } } // Check all children for the first few levels, and then sample randomly. // We also always subdivide the cells containing a few chosen points so // that we have a better chance of sampling the minimum and maximum metric // values. kMaxSizeUV is the absolute value of the u- and v-coordinate // where the cell size at a given level is maximal. double kMaxSizeUV = 0.3964182625366691; R2Point[] special_uv = { new R2Point(S2.DoubleEpsilon, S2.DoubleEpsilon), // Face center new R2Point(S2.DoubleEpsilon, 1), // Edge midpoint new R2Point(1, 1), // Face corner new R2Point(kMaxSizeUV, kMaxSizeUV), // Largest cell area new R2Point(S2.DoubleEpsilon, kMaxSizeUV), // Longest edge/diagonal }; bool force_subdivide = false; foreach (R2Point uv in special_uv) { if (children[i].BoundUV.Contains(uv)) { force_subdivide = true; } } var debugFlag = #if s2debug true; #else false; #endif if (force_subdivide || cell.Level < (debugFlag ? 5 : 6) || S2Testing.Random.OneIn(debugFlag ? 5 : 4)) { TestSubdivide(children[i]); } } // Check sum of child areas equals parent area. // // For ExactArea(), the best relative error we can expect is about 1e-6 // because the precision of the unit vector coordinates is only about 1e-15 // and the edge length of a leaf cell is about 1e-9. // // For ApproxArea(), the areas are accurate to within a few percent. // // For AverageArea(), the areas themselves are not very accurate, but // the average area of a parent is exactly 4 times the area of a child. Assert.True(Math.Abs(Math.Log(exact_area / cell.ExactArea())) <= Math.Abs(Math.Log((1 + 1e-6)))); Assert.True(Math.Abs(Math.Log((approx_area / cell.ApproxArea()))) <= Math.Abs(Math.Log((1.03)))); Assert.True(Math.Abs(Math.Log((average_area / cell.AverageArea()))) <= Math.Abs(Math.Log((1 + 1e-15)))); }