public void Test_ExpandedByDistance_NegativeDistanceLatWithSouthPoleAndLngFull() { S2LatLngRect rect = RectFromDegrees(-90.0, -180.0, 0.0, 180.0) .ExpandedByDistance(-S1Angle.FromDegrees(5.0)); Assert.True(rect.ApproxEquals(RectFromDegrees(-90.0, -180.0, -5.0, 180.0))); }
public List <S2CellId> GetS2CellIds(ushort level, int maxCells) { //var geofence = Geofence.FromMultiPolygon(this); var bbox = GetBoundingBox(); var regionCoverer = new S2RegionCoverer { MinLevel = level, MaxLevel = level, MaxCells = maxCells, }; var region = new S2LatLngRect( S2LatLng.FromDegrees(bbox.MinimumLatitude, bbox.MinimumLongitude), S2LatLng.FromDegrees(bbox.MaximumLatitude, bbox.MaximumLongitude) ); var coverage = new List <S2CellId>(); regionCoverer.GetCovering(region, coverage); var result = new List <S2CellId>(); foreach (var cellId in coverage) { var cell = new S2Cell(cellId); for (var i = 0; i <= 3; i++) { var vertex = cell.GetVertex(i); var coord = new S2LatLng(new S2Point(vertex.X, vertex.Y, vertex.Z)); //if (geofence.Intersects(coord.LatDegrees, coord.LngDegrees)) if (GeofenceService.InPolygon(this, coord.LatDegrees, coord.LngDegrees)) { result.Add(cellId); } } } return(result); }
/// <summary> /// Find the cell of the given level that covers the given Geoposition. /// This method uses the library given S2RegionCoverer to find the leaf cell containing the given Geoposition /// and uses binary operation on the leaf cell's Id to find the higher level cell containing the leaf. /// This is somehow more accurate than using the S2RegionCoverer for the higher level cell, where some errors occured. /// </summary> /// <param name="pos">Position in the cell</param> /// <param name="level">Level of the cell</param> /// <returns>The cell covering the given position that matches the specifications</returns> private S2Cell FindExactCell(BasicGeoposition pos, int level) { var point = S2LatLngRect.FromPoint(S2LatLng.FromDegrees(pos.Latitude, pos.Longitude)); var cells = new List <S2CellId>(); // find leaf cell var coverer = new S2RegionCoverer() { MinLevel = 30, MaxLevel = 30, MaxCells = 1 }; coverer.GetCovering(point, cells); var leaf = new S2Cell(cells[0]); int shift = 64 - (3 + level * 2 + 1); ulong id = leaf.Id.Id & ulong.MaxValue << shift; id |= 0 | ((ulong)1 << shift); return(new S2Cell(new S2CellId(id))); }
public void testIntervalOps(S2LatLngRect x, S2LatLngRect y, String expectedRelation, S2LatLngRect expectedUnion, S2LatLngRect expectedIntersection) { // Test all of the interval operations on the given pair of intervals. // "expected_relation" is a sequence of "T" and "F" characters corresponding // to the expected results of Contains(), InteriorContains(), Intersects(), // and InteriorIntersects() respectively. assertEquals(x.Contains(y), expectedRelation[0] == 'T'); assertEquals(x.InteriorContains(y), expectedRelation[1] == 'T'); assertEquals(x.Intersects(y), expectedRelation[2] == 'T'); assertEquals(x.InteriorIntersects(y), expectedRelation[3] == 'T'); assertEquals(x.Contains(y), x.Union(y).Equals(x)); assertEquals(x.Intersects(y), !x.Intersection(y).IsEmpty); assertTrue(x.Union(y).Equals(expectedUnion)); assertTrue(x.Intersection(y).Equals(expectedIntersection)); if (y.Size == S2LatLng.FromRadians(0, 0)) { var r = x.AddPoint(y.Lo); assertTrue(r == expectedUnion); } }
private static List <Coordinate> GetS2Cells(BoundingBox bbox) { var regionCoverer = new S2RegionCoverer { MinLevel = 15, MaxLevel = 15, //MaxCells = 100, }; var region = new S2LatLngRect( S2LatLng.FromDegrees(bbox.MinimumLatitude, bbox.MinimumLongitude), S2LatLng.FromDegrees(bbox.MaximumLatitude, bbox.MaximumLongitude) ); var cellIds = regionCoverer.GetCovering(region); var list = new List <Coordinate>(); foreach (var cellId in cellIds) { var center = cellId.ToLatLng(); list.Add(new Coordinate(center.LatDegrees, center.LngDegrees)); } // TODO: Check if point is within geofence //var filtered = FilterCoordinates(coordinates); //return filtered; return(list); }
private static void TestIntervalOps(S2LatLngRect x, S2LatLngRect y, string expected_relation, S2LatLngRect expected_union, S2LatLngRect expected_intersection) { // Test all of the interval operations on the given pair of intervals. // "expected_relation" is a sequence of "T" and "F" characters corresponding // to the expected results of Contains(), InteriorContains(), Intersects(), // and InteriorIntersects() respectively. Assert.Equal(x.Contains(y), expected_relation[0] == 'T'); Assert.Equal(x.InteriorContains(y), expected_relation[1] == 'T'); Assert.Equal(x.Intersects(y), expected_relation[2] == 'T'); Assert.Equal(x.InteriorIntersects(y), expected_relation[3] == 'T'); Assert.Equal(x.Contains(y), x.Union(y) == x); Assert.Equal(x.Intersects(y), !x.Intersection(y).IsEmpty()); Assert.Equal(x.Union(y), expected_union); Assert.Equal(x.Intersection(y), expected_intersection); if (y.Size() == S2LatLng.FromRadians(0, 0)) { S2LatLngRect r = x; r.AddPoint(y.Lo()); Assert.Equal(r, expected_union); } }
public void Test_ExpandedByDistance_NegativeDistanceLngFull() { S2LatLngRect rect = RectFromDegrees(0.0, -180.0, 30.0, 180.0) .ExpandedByDistance(-S1Angle.FromDegrees(5.0)); Assert.True(rect.ApproxEquals(RectFromDegrees(5.0, -180.0, 25.0, 180.0))); }
/** * 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) { var distance1 = bruteForceDistance(a, b); var distance2 = a.GetDistance(b); assertEquals(distance1.Radians, distance2.Radians, 1e-10); }
public void Test_BoundaryIntersects_RectCrossingAntiMeridian() { S2LatLngRect rect = RectFromDegrees(20, 170, 40, -170); Assert.True(rect.Contains(MakePointOrDie("30:180"))); // Check that crossings of all four sides are detected. Assert.True(rect.BoundaryIntersects( MakePointOrDie("25:160"), MakePointOrDie("25:180"))); Assert.True(rect.BoundaryIntersects( MakePointOrDie("25:-160"), MakePointOrDie("25:-180"))); Assert.True(rect.BoundaryIntersects( MakePointOrDie("15:175"), MakePointOrDie("30:175"))); Assert.True(rect.BoundaryIntersects( MakePointOrDie("45:175"), MakePointOrDie("30:175"))); // Check that the edges on the opposite side of the sphere but at the same // latitude do not intersect the rectangle boundary. Assert.False(rect.BoundaryIntersects( MakePointOrDie("25:-20"), MakePointOrDie("25:0"))); Assert.False(rect.BoundaryIntersects( MakePointOrDie("25:20"), MakePointOrDie("25:0"))); Assert.False(rect.BoundaryIntersects( MakePointOrDie("15:-5"), MakePointOrDie("30:-5"))); Assert.False(rect.BoundaryIntersects( MakePointOrDie("45:-5"), MakePointOrDie("30:-5"))); }
public void Test_ExpandedByDistance_NegativeDistanceLatResultEmpty() { S2LatLngRect rect = RectFromDegrees(0.0, 0.0, 9.9, 90.0) .ExpandedByDistance(-S1Angle.FromDegrees(5.0)); Assert.True(rect.IsEmpty()); }
public void Test_S2LatLngRect_GetDirectedHausdorffDistanceRandomPairs() { // Test random pairs. int kIters = 1000; for (int i = 0; i < kIters; ++i) { S2LatLngRect a = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); S2LatLngRect b = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); // a and b are *minimum* bounding rectangles of two random points, in // particular, their Voronoi diagrams are always of the same topology. We // take the "complements" of a and b for more thorough testing. S2LatLngRect a2 = new(a.Lat, a.Lng.Complement()); S2LatLngRect b2 = new(b.Lat, b.Lng.Complement()); // Note that "a" and "b" come from the same distribution, so there is no // need to test pairs such as (b, a), (b, a2), etc. VerifyGetDirectedHausdorffDistance(a, b); VerifyGetDirectedHausdorffDistance(a, b2); VerifyGetDirectedHausdorffDistance(a2, b); VerifyGetDirectedHausdorffDistance(a2, b2); } }
/** * 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) { var distance1 = bruteForceRectPointDistance(a, p.Normalized); var distance2 = a.GetDistance(p.Normalized); assertEquals(distance1.Radians, distance2.Radians, 1e-10); }
public static S2CellUnion FindCellIds(S2LatLngRect latLngRect) { var queue = new ConcurrentQueue <S2CellId>(); var cellIds = new List <S2CellId>(); for (var c = S2CellId.Begin(0); !c.Equals(S2CellId.End(0)); c = c.Next) { if (ContainsGeodataToFind(c, latLngRect)) { queue.Enqueue(c); } } ProcessQueue(queue, cellIds, latLngRect); Debug.Assert(queue.Count == 0); queue = null; if (cellIds.Count > 0) { var cellUnion = new S2CellUnion(); cellUnion.InitFromCellIds(cellIds); // This normalize the cells. // cellUnion.initRawCellIds(cellIds); // This does not normalize the cells. cellIds = null; return(cellUnion); } return(null); }
public void Test_S2LatLngRect_FromPoint() { S2LatLng p = S2LatLng.FromDegrees(23, 47); Assert.Equal(S2LatLngRect.FromPoint(p), new S2LatLngRect(p, p)); Assert.True(S2LatLngRect.FromPoint(p).IsPoint()); }
public void Test_ExpandedByDistance_NegativeDistanceLatWithNorthPole() { S2LatLngRect rect = RectFromDegrees(0.0, -90.0, 90.0, 180.0) .ExpandedByDistance(-S1Angle.FromDegrees(5.0)); Assert.True(rect.ApproxEquals(RectFromDegrees(5.0, 0.0, 85.0, 90.0))); }
// This function assumes that GetDirectedHausdorffDistance() always returns // a distance from some point in a to b. So the function mainly tests whether // the returned distance is large enough, and only does a weak test on whether // it is small enough. private static void VerifyGetDirectedHausdorffDistance(S2LatLngRect a, S2LatLngRect b) { S1Angle hausdorff_distance = a.GetDirectedHausdorffDistance(b); const double kResolution = 0.1; S1Angle max_distance = S1Angle.Zero; int sample_size_on_lat = (int)(a.Lat.GetLength() / kResolution) + 1; int sample_size_on_lng = (int)(a.Lng.GetLength() / kResolution) + 1; double delta_on_lat = a.Lat.GetLength() / sample_size_on_lat; double delta_on_lng = a.Lng.GetLength() / sample_size_on_lng; double lng = a.Lng.Lo; for (int i = 0; i <= sample_size_on_lng; ++i, lng += delta_on_lng) { double lat = a.Lat.Lo; for (int j = 0; j <= sample_size_on_lat; ++j, lat += delta_on_lat) { S2LatLng latlng = S2LatLng.FromRadians(lat, lng).Normalized(); S1Angle distance_to_b = b.GetDistance(latlng); if (distance_to_b >= max_distance) { max_distance = distance_to_b; } } } Assert.True(max_distance.Radians <= hausdorff_distance.Radians + 1e-10); Assert.True(max_distance.Radians >= hausdorff_distance.Radians - kResolution); }
// 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); }
// 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); }
public void Test_S2LatLngRect_GetDirectHausdorffDistancePointToRect() { // The Hausdorff distance from a point to a rect should be the same as its // distance to the rect. S2LatLngRect a1 = PointRectFromDegrees(5, 8); S2LatLngRect a2 = PointRectFromDegrees(90, 10); // north pole S2LatLngRect b = RectFromDegrees(-85, -50, -80, 10); Assert2.DoubleEqual(a1.GetDirectedHausdorffDistance(b).Radians, a1.GetDistance(b).Radians); Assert2.DoubleEqual(a2.GetDirectedHausdorffDistance(b).Radians, a2.GetDistance(b).Radians); b = RectFromDegrees(4, -10, 80, 10); Assert2.DoubleEqual(a1.GetDirectedHausdorffDistance(b).Radians, a1.GetDistance(b).Radians); Assert2.DoubleEqual(a2.GetDirectedHausdorffDistance(b).Radians, a2.GetDistance(b).Radians); b = RectFromDegrees(70, 170, 80, -170); Assert2.DoubleEqual(a1.GetDirectedHausdorffDistance(b).Radians, a1.GetDistance(b).Radians); Assert2.DoubleEqual(a2.GetDirectedHausdorffDistance(b).Radians, a2.GetDistance(b).Radians); }
public void Test_ExpandedByDistance_NegativeDistanceNorthEast() { S2LatLngRect in_rect = RectFromDegrees(0.0, 0.0, 30.0, 90.0); S1Angle distance = S1Angle.FromDegrees(5.0); S2LatLngRect out_rect = in_rect.ExpandedByDistance(distance).ExpandedByDistance(-distance); Assert.True(out_rect.ApproxEquals(in_rect)); }
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_BoundaryIntersects_FullRectangle() { S2LatLngRect rect = S2LatLngRect.Full; S2Point lo = rect.Lo().ToPoint(); var hi = rect.Hi().ToPoint(); Assert.False(rect.BoundaryIntersects(lo, lo)); Assert.False(rect.BoundaryIntersects(lo, hi)); }
public S2LatLngRect GetRectBound() { S2LatLngRect result = S2LatLngRect.Full; for (int i = 0; i < Regions.Length; ++i) { result = result.Intersection(Region(i).GetRectBound()); } return(result); }
private static S2LatLngRect getEdgeBound(double x1, double y1, double z1, double x2, double y2, double z2) { return(S2LatLngRect.FromEdge( S2Point.Normalize(new S2Point(x1, y1, z1)), S2Point.Normalize(new S2Point(x2, y2, z2)))); }
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); }
public void Test_S2LatLngRect_GetDirectedHausdorffDistanceRectToPoint() { S2LatLngRect a = RectFromDegrees(1, -8, 10, 20); VerifyGetDirectedHausdorffDistance(a, PointRectFromDegrees(5, 8)); VerifyGetDirectedHausdorffDistance(a, PointRectFromDegrees(-6, -100)); // south pole VerifyGetDirectedHausdorffDistance(a, PointRectFromDegrees(-90, -20)); // north pole VerifyGetDirectedHausdorffDistance(a, PointRectFromDegrees(90, 0)); }
public void Test_ExpandedByDistance_NegativeDistanceLngResultEmpty() { S2LatLngRect rect = RectFromDegrees(0.0, 0.0, 30.0, 11.0) .ExpandedByDistance(-S1Angle.FromDegrees(5.0)); // The cap center is at latitude 30 - 5 = 25 degrees. The length of the // latitude 25 degree line is 0.906 times the length of the equator. Thus the // cap whose radius is 5 degrees covers the rectangle whose latitude interval // is 11 degrees. Assert.True(rect.IsEmpty()); }
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)); }
public void Test_S2LatLngRect_GetDirectedHausdorffDistanceRectToRectNearPole() { // Tests near south pole. S2LatLngRect a = RectFromDegrees(-87, 0, -85, 3); VerifyGetDirectedHausdorffDistance(a, RectFromDegrees(-89, 1, -88, 2)); VerifyGetDirectedHausdorffDistance(a, RectFromDegrees(-84, 1, -83, 2)); VerifyGetDirectedHausdorffDistance(a, RectFromDegrees(-88, 90, -86, 91)); VerifyGetDirectedHausdorffDistance(a, RectFromDegrees(-84, -91, -83, -90)); VerifyGetDirectedHausdorffDistance(a, RectFromDegrees(-90, 181, -89, 182)); VerifyGetDirectedHausdorffDistance(a, RectFromDegrees(-84, 181, -83, 182)); }
public void Test_S2LatLngRect_EncodeDecode() { S2LatLngRect r = RectFromDegrees(-20, -80, 10, 20); Encoder encoder = new(); r.Encode(encoder); var decoder = encoder.Decoder(); var(success, decoded_rect) = S2LatLngRect.Decode(decoder); Assert.True(success); Assert.Equal(r, decoded_rect); }
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. S2LatLng[] pntA = { new S2LatLng(a.LatLo, a.LngLo), new S2LatLng(a.LatLo, a.LngHi), new S2LatLng(a.LatHi, a.LngHi), new S2LatLng(a.LatHi, a.LngLo) }; S2LatLng[] pntB = { new S2LatLng(b.LatLo, b.LngLo), new S2LatLng(b.LatLo, b.LngHi), new S2LatLng(b.LatHi, b.LngHi), new S2LatLng(b.LatHi, b.LngLo) }; // Make arrays containing the lo/hi latitudes and the lo/hi longitude edges. S1Angle[] latA = {a.LatLo, a.LatHi}; S1Angle[] latB = {b.LatLo, b.LatHi}; S2Point[][] lng_edge_a = {new[] {pntA[0].ToPoint(), pntA[3].ToPoint()}, new[] {pntA[1].ToPoint(), pntA[2].ToPoint()}}; S2Point[][] lng_edge_b = {new[] {pntB[0].ToPoint(), pntB[3].ToPoint()}, new[] {pntB[1].ToPoint(), pntB[2].ToPoint()}}; var minDistance = S1Angle.FromDegrees(180.0); for (var i = 0; i < 4; ++i) { // For each point in a and b. var currentA = pntA[i]; var currentB = pntB[i]; for (var j = 0; j < 2; ++j) { // Get distances to latitude and longitude edges. var aToLat = getDistance(currentA, latB[j], b.Lng); var bToLat = getDistance(currentB, latA[j], a.Lng); var aToLng = S2EdgeUtil.GetDistance(currentA.ToPoint(), lng_edge_b[j][0], lng_edge_b[j][1]); var bToLng = S2EdgeUtil.GetDistance(currentB.ToPoint(), lng_edge_a[j][0], lng_edge_a[j][1]); minDistance = S1Angle.Min( minDistance, S1Angle.Min(aToLat, S1Angle.Min(bToLat, S1Angle.Min(aToLng, bToLng)))); } } return minDistance; }
public void testCellOps(S2LatLngRect r, S2Cell cell, int level) { // Test the relationship between the given rectangle and cell: // 0 == no intersection, 1 == MayIntersect, 2 == Intersects, // 3 == Vertex Containment, 4 == Contains var vertexContained = false; for (var i = 0; i < 4; ++i) { if (r.Contains(cell.GetVertexRaw(i)) || (!r.IsEmpty && cell.Contains(r.GetVertex(i).ToPoint()))) { vertexContained = true; } } assertEquals(r.MayIntersect(cell), level >= 1); assertEquals(r.Intersects(cell), level >= 2); assertEquals(vertexContained, level >= 3); assertEquals(r.Contains(cell), level >= 4); }
public void AddPoint(S2Point b) { // assert (S2.isUnitLength(b)); var bLatLng = new S2LatLng(b); if (bound.IsEmpty) { bound = bound.AddPoint(bLatLng); } else { // We can't just call bound.addPoint(bLatLng) here, since we need to // ensure that all the longitudes between "a" and "b" are included. bound = bound.Union(S2LatLngRect.FromPointPair(aLatLng, bLatLng)); // Check whether the Min/Max latitude occurs in the edge interior. // We find the normal to the plane containing AB, and then a vector // "dir" in this plane that also passes through the equator. We use // RobustCrossProd to ensure that the edge normal is accurate even // when the two points are very close together. var aCrossB = S2.RobustCrossProd(a, b); var dir = S2Point.CrossProd(aCrossB, new S2Point(0, 0, 1)); var da = dir.DotProd(a); var db = dir.DotProd(b); if (da*db < 0) { // Minimum/maximum latitude occurs in the edge interior. This affects // the latitude bounds but not the longitude bounds. var absLat = Math.Acos(Math.Abs(aCrossB[2]/aCrossB.Norm)); var lat = bound.Lat; if (da < 0) { // It's possible that absLat < lat.lo() due to numerical errors. lat = new R1Interval(lat.Lo, Math.Max(absLat, bound.Lat.Hi)); } else { lat = new R1Interval(Math.Min(-absLat, bound.Lat.Lo), lat.Hi); } bound = new S2LatLngRect(lat, bound.Lng); } } a = b; aLatLng = bLatLng; }
public void testBasic() { // Most of the S2LatLngRect methods have trivial implementations that // use the R1Interval and S1Interval classes, so most of the testing // is done in those unit tests. // Test basic properties of empty and full caps. var empty = S2LatLngRect.Empty; var full = S2LatLngRect.Full; assertTrue(empty.IsValid); assertTrue(empty.IsEmpty); assertTrue(full.IsValid); assertTrue(full.IsFull); // assertTrue various constructors and accessor methods. var d1 = rectFromDegrees(-90, 0, -45, 180); assertDoubleNear(d1.LatLo.Degrees, -90); assertDoubleNear(d1.LatHi.Degrees, -45); assertDoubleNear(d1.LngLo.Degrees, 0); assertDoubleNear(d1.LngHi.Degrees, 180); assertTrue(d1.Lat.Equals(new R1Interval(-S2.PiOver2, -S2.PiOver4))); assertTrue(d1.Lng.Equals(new S1Interval(0, S2.Pi))); // FromCenterSize() assertTrue( S2LatLngRect.FromCenterSize(S2LatLng.FromDegrees(80, 170), S2LatLng.FromDegrees(40, 60)) .ApproxEquals(rectFromDegrees(60, 140, 90, -160))); assertTrue(S2LatLngRect .FromCenterSize(S2LatLng.FromDegrees(10, 40), S2LatLng.FromDegrees(210, 400)).IsFull); assertTrue( S2LatLngRect.FromCenterSize(S2LatLng.FromDegrees(-90, 180), S2LatLng.FromDegrees(20, 50)) .ApproxEquals(rectFromDegrees(-90, 155, -80, -155))); // FromPoint(), FromPointPair() assertEquals(S2LatLngRect.FromPoint(d1.Lo), new S2LatLngRect(d1.Lo, d1.Lo)); assertEquals( S2LatLngRect.FromPointPair(S2LatLng.FromDegrees(-35, -140), S2LatLng.FromDegrees(15, 155)), rectFromDegrees(-35, 155, 15, -140)); assertEquals( S2LatLngRect.FromPointPair(S2LatLng.FromDegrees(25, -70), S2LatLng.FromDegrees(-90, 80)), rectFromDegrees(-90, -70, 25, 80)); // GetCenter(), GetVertex(), Contains(S2LatLng), InteriorContains(S2LatLng). var eqM180 = S2LatLng.FromRadians(0, -S2.Pi); var northPole = S2LatLng.FromRadians(S2.PiOver2, 0); var r1 = new S2LatLngRect(eqM180, northPole); assertEquals(r1.Center, S2LatLng.FromRadians(S2.PiOver4, -S2.PiOver2)); assertEquals(r1.GetVertex(0), S2LatLng.FromRadians(0, S2.Pi)); assertEquals(r1.GetVertex(1), S2LatLng.FromRadians(0, 0)); assertEquals(r1.GetVertex(2), S2LatLng.FromRadians(S2.PiOver2, 0)); assertEquals(r1.GetVertex(3), S2LatLng.FromRadians(S2.PiOver2, S2.Pi)); assertTrue(r1.Contains(S2LatLng.FromDegrees(30, -45))); assertTrue(!r1.Contains(S2LatLng.FromDegrees(30, 45))); assertTrue(!r1.InteriorContains(eqM180) && !r1.InteriorContains(northPole)); assertTrue(r1.Contains(new S2Point(0.5, -0.3, 0.1))); assertTrue(!r1.Contains(new S2Point(0.5, 0.2, 0.1))); // Make sure that GetVertex() returns vertices in CCW order. for (var i = 0; i < 4; ++i) { var lat = S2.PiOver4*(i - 2); var lng = S2.PiOver2*(i - 2) + 0.2; var r = new S2LatLngRect(new R1Interval(lat, lat + S2.PiOver4), new S1Interval( Math.IEEERemainder(lng, 2*S2.Pi), Math.IEEERemainder(lng + S2.PiOver2, 2*S2.Pi))); for (var k = 0; k < 4; ++k) { assertTrue( S2.SimpleCcw(r.GetVertex((k - 1) & 3).ToPoint(), r.GetVertex(k).ToPoint(), r.GetVertex((k + 1) & 3).ToPoint())); } } // Contains(S2LatLngRect), InteriorContains(S2LatLngRect), // Intersects(), InteriorIntersects(), Union(), Intersection(). // // Much more testing of these methods is done in s1interval_unittest // and r1interval_unittest. var r1Mid = rectFromDegrees(45, -90, 45, -90); var reqM180 = new S2LatLngRect(eqM180, eqM180); var rNorthPole = new S2LatLngRect(northPole, northPole); testIntervalOps(r1, r1Mid, "TTTT", r1, r1Mid); testIntervalOps(r1, reqM180, "TFTF", r1, reqM180); testIntervalOps(r1, rNorthPole, "TFTF", r1, rNorthPole); assertTrue(r1.Equals(rectFromDegrees(0, -180, 90, 0))); testIntervalOps(r1, rectFromDegrees(-10, -1, 1, 20), "FFTT", rectFromDegrees(-10, -180, 90, 20), rectFromDegrees(0, -1, 1, 0)); testIntervalOps(r1, rectFromDegrees(-10, -1, 0, 20), "FFTF", rectFromDegrees(-10, -180, 90, 20), rectFromDegrees(0, -1, 0, 0)); testIntervalOps(r1, rectFromDegrees(-10, 0, 1, 20), "FFTF", rectFromDegrees(-10, -180, 90, 20), rectFromDegrees(0, 0, 1, 0)); testIntervalOps(rectFromDegrees(-15, -160, -15, -150), rectFromDegrees(20, 145, 25, 155), "FFFF", rectFromDegrees(-15, 145, 25, -150), empty); testIntervalOps(rectFromDegrees(70, -10, 90, -140), rectFromDegrees(60, 175, 80, 5), "FFTT", rectFromDegrees(60, -180, 90, 180), rectFromDegrees(70, 175, 80, 5)); // assertTrue that the intersection of two rectangles that overlap in // latitude // but not longitude is valid, and vice versa. testIntervalOps(rectFromDegrees(12, 30, 60, 60), rectFromDegrees(0, 0, 30, 18), "FFFF", rectFromDegrees(0, 0, 60, 60), empty); testIntervalOps(rectFromDegrees(0, 0, 18, 42), rectFromDegrees(30, 12, 42, 60), "FFFF", rectFromDegrees(0, 0, 42, 60), empty); // AddPoint() var p = S2LatLngRect.Empty; p = p.AddPoint(S2LatLng.FromDegrees(0, 0)); p = p.AddPoint(S2LatLng.FromRadians(0, -S2.PiOver2)); p = p.AddPoint(S2LatLng.FromRadians(S2.PiOver4, -S2.Pi)); p = p.AddPoint(new S2Point(0, 0, 1)); assertTrue(p.Equals(r1)); // Expanded() assertTrue( rectFromDegrees(70, 150, 80, 170).Expanded(S2LatLng.FromDegrees(20, 30)).ApproxEquals( rectFromDegrees(50, 120, 90, -160))); assertTrue(S2LatLngRect.Empty.Expanded(S2LatLng.FromDegrees(20, 30)).IsEmpty); assertTrue(S2LatLngRect.Full.Expanded(S2LatLng.FromDegrees(20, 30)).IsFull); assertTrue( rectFromDegrees(-90, 170, 10, 20).Expanded(S2LatLng.FromDegrees(30, 80)).ApproxEquals( rectFromDegrees(-90, -180, 40, 180))); // ConvolveWithCap() var llr1 = new S2LatLngRect(S2LatLng.FromDegrees(0, 170), S2LatLng.FromDegrees(0, -170)) .ConvolveWithCap(S1Angle.FromDegrees(15)); var llr2 = new S2LatLngRect(S2LatLng.FromDegrees(-15, 155), S2LatLng.FromDegrees(15, -155)); assertTrue(llr1.ApproxEquals(llr2)); llr1 = new S2LatLngRect(S2LatLng.FromDegrees(60, 150), S2LatLng.FromDegrees(80, 10)) .ConvolveWithCap(S1Angle.FromDegrees(15)); llr2 = new S2LatLngRect(S2LatLng.FromDegrees(45, -180), S2LatLng.FromDegrees(90, 180)); assertTrue(llr1.ApproxEquals(llr2)); // GetCapBound(), bounding cap at center is smaller: assertTrue(new S2LatLngRect(S2LatLng.FromDegrees(-45, -45), S2LatLng.FromDegrees(45, 45)).CapBound.ApproxEquals(S2Cap.FromAxisHeight(new S2Point(1, 0, 0), 0.5))); // GetCapBound(), bounding cap at north pole is smaller: assertTrue(new S2LatLngRect(S2LatLng.FromDegrees(88, -80), S2LatLng.FromDegrees(89, 80)).CapBound.ApproxEquals(S2Cap.FromAxisAngle(new S2Point(0, 0, 1), S1Angle.FromDegrees(2)))); // GetCapBound(), longitude span > 180 degrees: assertTrue( new S2LatLngRect(S2LatLng.FromDegrees(-30, -150), S2LatLng.FromDegrees(-10, 50)).CapBound .ApproxEquals(S2Cap.FromAxisAngle(new S2Point(0, 0, -1), S1Angle.FromDegrees(80)))); // Contains(S2Cell), MayIntersect(S2Cell), Intersects(S2Cell) // Special cases. testCellOps(empty, S2Cell.FromFacePosLevel(3, (byte)0, 0), 0); testCellOps(full, S2Cell.FromFacePosLevel(2, (byte)0, 0), 4); testCellOps(full, S2Cell.FromFacePosLevel(5, (byte)0, 25), 4); // This rectangle includes the first quadrant of face 0. It's expanded // slightly because cell bounding rectangles are slightly conservative. var r4 = rectFromDegrees(-45.1, -45.1, 0.1, 0.1); testCellOps(r4, S2Cell.FromFacePosLevel(0, (byte)0, 0), 3); testCellOps(r4, S2Cell.FromFacePosLevel(0, (byte)0, 1), 4); testCellOps(r4, S2Cell.FromFacePosLevel(1, (byte)0, 1), 0); // This rectangle intersects the first quadrant of face 0. var r5 = rectFromDegrees(-10, -45, 10, 0); testCellOps(r5, S2Cell.FromFacePosLevel(0, (byte)0, 0), 3); testCellOps(r5, S2Cell.FromFacePosLevel(0, (byte)0, 1), 3); testCellOps(r5, S2Cell.FromFacePosLevel(1, (byte)0, 1), 0); // Rectangle consisting of a single point. testCellOps(rectFromDegrees(4, 4, 4, 4), S2Cell.FromFacePosLevel(0, (byte)0, 0), 3); // Rectangles that intersect the bounding rectangle of a face // but not the face itself. testCellOps(rectFromDegrees(41, -87, 42, -79), S2Cell.FromFacePosLevel(2, (byte)0, 0), 1); testCellOps(rectFromDegrees(-41, 160, -40, -160), S2Cell.FromFacePosLevel(5, (byte)0, 0), 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. var cell0tr = new S2Cell(new S2Point(1 + 1e-12, 1, 1)); var bound0tr = cell0tr.RectBound; var v0 = new S2LatLng(cell0tr.GetVertexRaw(0)); testCellOps( rectFromDegrees(v0.Lat.Degrees - 1e-8, v0.Lng.Degrees - 1e-8, v0.Lat.Degrees - 2e-10, v0.Lng.Degrees + 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.FromFacePosLevel(5, (byte)0, 0), 2); { // These two intersect like a diamond and a square. var cell202 = S2Cell.FromFacePosLevel(2, (byte)0, 2); var bound202 = cell202.RectBound; testCellOps( rectFromDegrees(bound202.Lo.Lat.Degrees + 3, bound202.Lo.Lng.Degrees + 3, bound202.Hi.Lat.Degrees - 3, bound202.Hi.Lng.Degrees - 3), cell202, 2); } }
public RectBounder() { bound = S2LatLngRect.Empty; }
private static S1Angle bruteForceRectPointDistance(S2LatLngRect a, S2LatLng b) { if (a.Contains(b)) { return S1Angle.FromRadians(0); } var bToLoLat = getDistance(b, a.LatLo, a.Lng); var bToHiLat = getDistance(b, a.LatHi, a.Lng); var bToLoLng = S2EdgeUtil.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo, a.LngLo).ToPoint(), new S2LatLng(a.LatHi, a.LngLo).ToPoint()); var bToHiLng = S2EdgeUtil.GetDistance(b.ToPoint(), new S2LatLng(a.LatLo, a.LngHi).ToPoint(), new S2LatLng(a.LatHi, a.LngHi).ToPoint()); return S1Angle.Min(bToLoLat, S1Angle.Min(bToHiLat, S1Angle.Min(bToLoLng, bToHiLng))); }