/** * 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)); }
/** * Indexing structure to efficiently clipEdge() of a polygon. This is an * abstract class because we need to use if for both polygons (for * initToIntersection() and friends) and for sets of lists of points (for * initToSimplified()). * * Usage -- in your subclass, create an array of vertex counts for each loop * in the loop sequence and pass it to this constructor. Overwrite * edgeFromTo(), calling decodeIndex() and use the resulting two indices to * access your accessing vertices. */ private static void AddIntersection(S2Point a0, S2Point a1, S2Point b0, S2Point b1, bool addSharedEdges, int crossing, System.Collections.Generic.ICollection <ParametrizedS2Point> intersections) { if (crossing > 0) { // There is a proper edge crossing. var x = S2EdgeUtil.GetIntersection(a0, a1, b0, b1); var t = S2EdgeUtil.GetDistanceFraction(x, a0, a1); intersections.Add(new ParametrizedS2Point(t, x)); } else if (S2EdgeUtil.VertexCrossing(a0, a1, b0, b1)) { // There is a crossing at one of the vertices. The basic rule is simple: // if a0 equals one of the "b" vertices, the crossing occurs at t=0; // otherwise, it occurs at t=1. // // This has the effect that when two symmetric edges are encountered (an // edge an its reverse), neither one is included in the output. When two // duplicate edges are encountered, both are included in the output. The // "addSharedEdges" flag allows one of these two copies to be removed by // changing its intersection parameter from 0 to 1. double t = (a0 == b0 || a0 == b1) ? 0 : 1; if (!addSharedEdges && a1 == b1) { t = 1; } intersections.Add(new ParametrizedS2Point(t, t == 0 ? a0 : a1)); } }
// 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); }
/** * Given a point p and the index of the start point of an edge of this polyline, * returns the point on that edge that is closest to p. */ public S2Point ProjectToEdge(S2Point point, int index) { Preconditions.CheckState(NumVertices > 0, "Empty polyline"); Preconditions.CheckState(NumVertices == 1 || index < NumVertices - 1, "Invalid edge index"); if (NumVertices == 1) { // If there is only one vertex, it is always closest to any given point. return(Vertex(0)); } return(S2EdgeUtil.GetClosestPoint(point, Vertex(index), Vertex(index + 1))); }
/** * 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); }
/** * This method is equivalent to the S2EdgeUtil.edgeOrVertexCrossing() method * defined below. It is similar to robustCrossing, but handles cases where * two vertices are identical in a way that makes it easy to implement * point-in-polygon containment tests. */ public bool EdgeOrVertexCrossing(S2Point d) { // We need to copy c since it is clobbered by robustCrossing(). var c2 = new S2Point(c[0], c[1], c[2]); var crossing = RobustCrossing(d); if (crossing < 0) { return(false); } if (crossing > 0) { return(true); } return(S2EdgeUtil.VertexCrossing(a, b, c2, d)); }
/** * 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))))); }