/** * Return the center of the rectangle in latitude-longitude space (in general * this is not the center of the region on the sphere). */ /** * Return the minimum distance (measured along the surface of the sphere) * from a given point to the rectangle (both its boundary and its interior). * The latLng must be valid. */ public S1Angle GetDistance(S2LatLng p) { // The algorithm here is the same as in getDistance(S2LagLngRect), only // with simplified calculations. var a = this; Preconditions.CheckState(!a.IsEmpty); Preconditions.CheckArgument(p.IsValid); if (a.Lng.Contains(p.Lng.Radians)) { return(S1Angle.FromRadians(Math.Max(0.0, Math.Max(p.Lat.Radians - a.Lat.Hi, a.Lat.Lo - p.Lat.Radians)))); } var interval = new S1Interval(a.Lng.Hi, a.Lng.Complement.Center); var aLng = a.Lng.Lo; if (interval.Contains(p.Lng.Radians)) { aLng = a.Lng.Hi; } var lo = S2LatLng.FromRadians(a.Lat.Lo, aLng).ToPoint(); var hi = S2LatLng.FromRadians(a.Lat.Hi, aLng).ToPoint(); var loCrossHi = S2LatLng.FromRadians(0, aLng - S2.PiOver2).Normalized.ToPoint(); return(S2EdgeUtil.GetDistance(p.ToPoint(), lo, hi, loCrossHi)); }
/** * Returns true if the edge (v0, v1) intersects the given longitude * interval, and then saves 'v1' to be used as the next 'v0'. */ public bool Intersects(S2Point v1) { var lng1 = S2LatLng.Longitude(v1).Radians; var result = interval.Intersects(S1Interval.FromPointPair(lng0, lng1)); lng0 = lng1; return(result); }
/** *'interval' is the longitude interval to be tested against, and 'v0' is * the first vertex of edge chain. */ public LongitudePruner(S1Interval interval, S2Point v0) { this.interval = interval; lng0 = S2LatLng.Longitude(v0).Radians; }
/** * Return true if the edge AB intersects the given edge of constant latitude. */ private static bool IntersectsLatEdge(S2Point a, S2Point b, double lat, S1Interval lng) { // Return true if the segment AB intersects the given edge of constant // latitude. Unfortunately, lines of constant latitude are curves on // the sphere. They can intersect a straight edge in 0, 1, or 2 points. // assert (S2.isUnitLength(a) && S2.isUnitLength(b)); // First, compute the normal to the plane AB that points vaguely north. var z = S2Point.Normalize(S2.RobustCrossProd(a, b)); if (z.Z < 0) { z = -z; } // Extend this to an orthonormal frame (x,y,z) where x is the direction // where the great circle through AB achieves its maximium latitude. var y = S2Point.Normalize(S2.RobustCrossProd(z, new S2Point(0, 0, 1))); var x = S2Point.CrossProd(y, z); // assert (S2.isUnitLength(x) && x.z >= 0); // Compute the angle "theta" from the x-axis (in the x-y plane defined // above) where the great circle intersects the given line of latitude. var sinLat = Math.Sin(lat); if (Math.Abs(sinLat) >= x.Z) { return(false); // The great circle does not reach the given latitude. } // assert (x.z > 0); var cosTheta = sinLat / x.Z; var sinTheta = Math.Sqrt(1 - cosTheta * cosTheta); var theta = Math.Atan2(sinTheta, cosTheta); // The candidate intersection points are located +/- theta in the x-y // plane. For an intersection to be valid, we need to check that the // intersection point is contained in the interior of the edge AB and // also that it is contained within the given longitude interval "lng". // Compute the range of theta values spanned by the edge AB. var abTheta = S1Interval.FromPointPair(Math.Atan2( a.DotProd(y), a.DotProd(x)), Math.Atan2(b.DotProd(y), b.DotProd(x))); if (abTheta.Contains(theta)) { // Check if the intersection point is also in the given "lng" interval. var isect = (x * cosTheta) + (y * sinTheta); if (lng.Contains(Math.Atan2(isect.Y, isect.X))) { return(true); } } if (abTheta.Contains(-theta)) { // Check if the intersection point is also in the given "lng" interval. var intersection = (x * cosTheta) - (y * sinTheta); if (lng.Contains(Math.Atan2(intersection.Y, intersection.X))) { return(true); } } return(false); }
/** * Returns true if this rectangle intersects the given cell. (This is an exact * test and may be fairly expensive, see also MayIntersect below.) */ public bool Intersects(S2Cell cell) { // First we eliminate the cases where one region completely contains the // other. Once these are disposed of, then the regions will intersect // if and only if their boundaries intersect. if (IsEmpty) { return(false); } if (Contains(cell.Center)) { return(true); } if (cell.Contains(Center.ToPoint())) { return(true); } // Quick rejection test (not required for correctness). if (!Intersects(cell.RectBound)) { return(false); } // Now check whether the boundaries intersect. Unfortunately, a // latitude-longitude rectangle does not have straight edges -- two edges // are curved, and at least one of them is concave. // Precompute the cell vertices as points and latitude-longitudes. var cellV = new S2Point[4]; var cellLl = new S2LatLng[4]; for (var i = 0; i < 4; ++i) { cellV[i] = cell.GetVertex(i); // Must be normalized. cellLl[i] = new S2LatLng(cellV[i]); if (Contains(cellLl[i])) { return(true); // Quick acceptance test. } } for (var i = 0; i < 4; ++i) { var edgeLng = S1Interval.FromPointPair( cellLl[i].Lng.Radians, cellLl[(i + 1) & 3].Lng.Radians); if (!_lng.Intersects(edgeLng)) { continue; } var a = cellV[i]; var b = cellV[(i + 1) & 3]; if (edgeLng.Contains(_lng.Lo)) { if (IntersectsLngEdge(a, b, _lat, _lng.Lo)) { return(true); } } if (edgeLng.Contains(_lng.Hi)) { if (IntersectsLngEdge(a, b, _lat, _lng.Hi)) { return(true); } } if (IntersectsLatEdge(a, b, _lat.Lo, _lng)) { return(true); } if (IntersectsLatEdge(a, b, _lat.Hi, _lng)) { return(true); } } return(false); }
/** Construct a rectangle from latitude and longitude intervals. */ public S2LatLngRect(R1Interval lat, S1Interval lng) { _lat = lat; _lng = lng; // assert (isValid()); }
/** * Return the minimum distance (measured along the surface of the sphere) to * the given S2LatLngRect. Both S2LatLngRects must be non-empty. */ public S1Angle GetDistance(S2LatLngRect other) { var a = this; var b = other; Preconditions.CheckState(!a.IsEmpty); Preconditions.CheckArgument(!b.IsEmpty); // First, handle the trivial cases where the longitude intervals overlap. if (a.Lng.Intersects(b.Lng)) { if (a.Lat.Intersects(b.Lat)) { return(S1Angle.FromRadians(0)); // Intersection between a and b. } // We found an overlap in the longitude interval, but not in the latitude // interval. This means the shortest path travels along some line of // longitude connecting the high-latitude of the lower rect with the // low-latitude of the higher rect. S1Angle lo, hi; if (a.Lat.Lo > b.Lat.Hi) { lo = b.LatHi; hi = a.LatLo; } else { lo = a.LatHi; hi = b.LatLo; } return(S1Angle.FromRadians(hi.Radians - lo.Radians)); } // The longitude intervals don't overlap. In this case, the closest points // occur somewhere on the pair of longitudinal edges which are nearest in // longitude-space. S1Angle aLng, bLng; var loHi = S1Interval.FromPointPair(a.Lng.Lo, b.Lng.Hi); var hiLo = S1Interval.FromPointPair(a.Lng.Hi, b.Lng.Lo); if (loHi.Length < hiLo.Length) { aLng = a.LngLo; bLng = b.LngHi; } else { aLng = a.LngHi; bLng = b.LngLo; } // The shortest distance between the two longitudinal segments will include // at least one segment endpoint. We could probably narrow this down further // to a single point-edge distance by comparing the relative latitudes of the // endpoints, but for the sake of clarity, we'll do all four point-edge // distance tests. var aLo = new S2LatLng(a.LatLo, aLng).ToPoint(); var aHi = new S2LatLng(a.LatHi, aLng).ToPoint(); var aLoCrossHi = S2LatLng.FromRadians(0, aLng.Radians - S2.PiOver2).Normalized.ToPoint(); var bLo = new S2LatLng(b.LatLo, bLng).ToPoint(); var bHi = new S2LatLng(b.LatHi, bLng).ToPoint(); var bLoCrossHi = S2LatLng.FromRadians(0, bLng.Radians - S2.PiOver2).Normalized.ToPoint(); return(S1Angle.Min(S2EdgeUtil.GetDistance(aLo, bLo, bHi, bLoCrossHi), S1Angle.Min(S2EdgeUtil.GetDistance(aHi, bLo, bHi, bLoCrossHi), S1Angle.Min(S2EdgeUtil.GetDistance(bLo, aLo, aHi, aLoCrossHi), S2EdgeUtil.GetDistance(bHi, aLo, aHi, aLoCrossHi))))); }
/** * Construct a rectangle from minimum and maximum latitudes and longitudes. If * lo.Lng > hi.Lng, the rectangle spans the 180 degree longitude line. */ public S2LatLngRect(S2LatLng lo, S2LatLng hi) { _lat = new R1Interval(lo.Lat.Radians, hi.Lat.Radians); _lng = new S1Interval(lo.Lng.Radians, hi.Lng.Radians); // assert (isValid()); }
/** * Convenience method to construct the minimal bounding rectangle containing * the two given points. This is equivalent to starting with an empty * rectangle and calling AddPoint() twice. Note that it is different than the * S2LatLngRect(lo, hi) constructor, where the first point is always used as * the lower-left corner of the resulting rectangle. */ public static S2LatLngRect FromPointPair(S2LatLng p1, S2LatLng p2) { // assert (p1.isValid() && p2.isValid()); return(new S2LatLngRect(R1Interval.FromPointPair(p1.Lat.Radians, p2.Lat.Radians), S1Interval.FromPointPair(p1.Lng.Radians, p2.Lng.Radians))); }