/** * A slightly more efficient version of getDistance() where the cross product * of the two endpoints has been precomputed. The cross product does not need * to be normalized, but should be computed using S2.robustCrossProd() for the * most accurate results. */ public static S1Angle GetDistance(S2Point x, S2Point a, S2Point b, S2Point aCrossB) { Preconditions.CheckArgument(S2.IsUnitLength(x)); Preconditions.CheckArgument(S2.IsUnitLength(a)); Preconditions.CheckArgument(S2.IsUnitLength(b)); // There are three cases. If X is located in the spherical wedge defined by // A, B, and the axis A x B, then the closest point is on the segment AB. // Otherwise the closest point is either A or B; the dividing line between // these two cases is the great circle passing through (A x B) and the // midpoint of AB. if (S2.SimpleCcw(aCrossB, a, x) && S2.SimpleCcw(x, b, aCrossB)) { // The closest point to X lies on the segment AB. We compute the distance // to the corresponding great circle. The result is accurate for small // distances but not necessarily for large distances (approaching Pi/2). var sinDist = Math.Abs(x.DotProd(aCrossB)) / aCrossB.Norm; return(S1Angle.FromRadians(Math.Asin(Math.Min(1.0, sinDist)))); } // Otherwise, the closest point is either A or B. The cheapest method is // just to compute the minimum of the two linear (as opposed to spherical) // distances and convert the result to an angle. Again, this method is // accurate for small but not large distances (approaching Pi). var linearDist2 = Math.Min((x - a).Norm2, (x - b).Norm2); return(S1Angle.FromRadians(2 * Math.Asin(Math.Min(1.0, 0.5 * Math.Sqrt(linearDist2))))); }
/** * 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)); }
// S2Region interface (see {@code S2Region} for details): /** Return a bounding spherical cap. */ /** * Given a point, returns the index of the start point of the (first) edge on * the polyline that is closest to the given point. The polyline must have at * least one vertex. Throws IllegalStateException if this is not the case. */ public int GetNearestEdgeIndex(S2Point point) { Preconditions.CheckState(NumVertices > 0, "Empty polyline"); if (NumVertices == 1) { // If there is only one vertex, the "edge" is trivial, and it's the only one return(0); } // Initial value larger than any possible distance on the unit sphere. var minDistance = S1Angle.FromRadians(10); var minIndex = -1; // Find the line segment in the polyline that is closest to the point given. for (var i = 0; i < NumVertices - 1; ++i) { var distanceToSegment = S2EdgeUtil.GetDistance(point, Vertex(i), Vertex(i + 1)); if (distanceToSegment < minDistance) { minDistance = distanceToSegment; minIndex = i; } } return(minIndex); }
/** * Return the distance (measured along the surface of the sphere) to the given * point. */ public S1Angle GetDistance(S2LatLng o) { // This implements the Haversine formula, which is numerically stable for // small distances but only gets about 8 digits of precision for very large // distances (e.g. antipodal points). Note that 8 digits is still accurate // to within about 10cm for a sphere the size of the Earth. // // This could be fixed with another sin() and cos() below, but at that point // you might as well just convert both arguments to S2Points and compute the // distance that way (which gives about 15 digits of accuracy for all // distances). var lat1 = Lat.Radians; var lat2 = o.Lat.Radians; var lng1 = Lng.Radians; var lng2 = o.Lng.Radians; var dlat = Math.Sin(0.5 * (lat2 - lat1)); var dlng = Math.Sin(0.5 * (lng2 - lng1)); var x = dlat * dlat + dlng * dlng * Math.Cos(lat1) * Math.Cos(lat2); return(S1Angle.FromRadians(2 * Math.Atan2(Math.Sqrt(x), Math.Sqrt(Math.Max(0.0, 1.0 - x))))); // Return the distance (measured along the surface of the sphere) to the // given S2LatLng. This is mathematically equivalent to: // // S1Angle::FromRadians(ToPoint().Angle(o.ToPoint()) // // but this implementation is slightly more efficient. }
public static S1Angle Latitude(S2Point p) { // We use atan2 rather than asin because the input vector is not necessarily // unit length, and atan2 is much more accurate than asin near the poles. return(S1Angle.FromRadians( Math.Atan2(p[2], Math.Sqrt(p[0] * p[0] + p[1] * p[1])))); }
private S2PolygonBuilderOptions(bool undirectedEdges, bool xorEdges) { UndirectedEdges = undirectedEdges; XorEdges = xorEdges; Validate = false; _mergeDistance = S1Angle.FromRadians(0); }
/** * Returns the shortest distance from a point P to this loop, given as the * angle formed between P, the origin and the nearest point on the loop to P. * This angle in radians is equivalent to the arclength along the unit sphere. */ public S1Angle GetDistance(S2Point p) { var normalized = S2Point.Normalize(p); // The furthest point from p on the sphere is its antipode, which is an // angle of PI radians. This is an upper bound on the angle. var minDistance = S1Angle.FromRadians(Math.PI); for (var i = 0; i < NumVertices; i++) { minDistance = S1Angle.Min(minDistance, S2EdgeUtil.GetDistance(normalized, Vertex(i), Vertex(i + 1))); } return(minDistance); }
/** * Return the area of the polygon interior, i.e. the region on the left side * of an odd number of loops (this value return value is between 0 and 4*Pi) * and the true centroid of the polygon multiplied by the area of the polygon * (see s2.h for details on centroids). Note that the centroid may not be * contained by the polygon. */ /** * Returns the shortest distance from a point P to this polygon, given as the * angle formed between P, the origin and the nearest point on the polygon to * P. This angle in radians is equivalent to the arclength along the unit * sphere. * * If the point is contained inside the polygon, the distance returned is 0. */ public S1Angle GetDistance(S2Point p) { if (Contains(p)) { return(S1Angle.FromRadians(0)); } // The furthest point from p on the sphere is its antipode, which is an // angle of PI radians. This is an upper bound on the angle. var minDistance = S1Angle.FromRadians(Math.PI); for (var i = 0; i < NumLoops; i++) { minDistance = S1Angle.Min(minDistance, Loop(i).GetDistance(p)); } return(minDistance); }
/** * 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))))); }
public static S1Angle Longitude(S2Point p) { // Note that atan2(0, 0) is defined to be zero. return(S1Angle.FromRadians(Math.Atan2(p[1], p[0]))); }