// 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); }
/** * 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))))); }
public void InitToIntersectionSloppy( S2Polygon a, S2Polygon b, S1Angle vertexMergeRadius) { Preconditions.CheckState(NumLoops == 0); if (!a._bound.Intersects(b._bound)) { return; } // We want the boundary of A clipped to the interior of B, // plus the boundary of B clipped to the interior of A, // plus one copy of any directed edges that are in both boundaries. var options = S2PolygonBuilderOptions.DirectedXor; options.MergeDistance = vertexMergeRadius; var builder = new S2PolygonBuilder(options); ClipBoundary(a, false, b, false, false, true, builder); ClipBoundary(b, false, a, false, false, false, builder); if (!builder.AssemblePolygon(this, null)) { // TODO (andriy): do something more meaningful here. Debug.WriteLine("Bad directed edges"); } }
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])))); }
/** * 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. }
/** * 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)); }
private S2PolygonBuilderOptions(bool undirectedEdges, bool xorEdges) { UndirectedEdges = undirectedEdges; XorEdges = xorEdges; Validate = false; _mergeDistance = S1Angle.FromRadians(0); }
/** * Create a cap given its axis and the cap opening angle, i.e. maximum angle * between the axis and a point on the cap. 'axis' should be a unit-length * vector, and 'angle' should be between 0 and 180 degrees. */ public static S2Cap FromAxisAngle(S2Point axis, S1Angle angle) { // The height of the cap can be computed as 1-cos(angle), but this isn't // very accurate for angles close to zero (where cos(angle) is almost 1). // Computing it as 2*(sin(angle/2)**2) gives much better precision. // assert (S2.isUnitLength(axis)); var d = Math.Sin(0.5 * angle.Radians); return(new S2Cap(axis, 2 * d * d)); }
/** * 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 a rectangle that contains the convolution of this rectangle with a * cap of the given angle. This expands the rectangle by a fixed distance (as * opposed to growing the rectangle in latitude-longitude space). The returned * rectangle includes all points whose minimum distance to the original * rectangle is at most the given angle. */ public S2LatLngRect ConvolveWithCap(S1Angle angle) { // The most straightforward approach is to build a cap centered on each // vertex and take the union of all the bounding rectangles (including the // original rectangle; this is necessary for very large rectangles). // Optimization: convert the angle to a height exactly once. var cap = S2Cap.FromAxisAngle(new S2Point(1, 0, 0), angle); var r = this; for (var k = 0; k < 4; ++k) { var vertexCap = S2Cap.FromAxisHeight(GetVertex(k).ToPoint(), cap.Height); r = r.Union(vertexCap.RectBound); } return(r); }
/** * 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); }
/** * Expand the cell union such that it contains all points whose distance to * the cell union is at most minRadius, but do not use cells that are more * than maxLevelDiff levels higher than the largest cell in the input. The * second parameter controls the tradeoff between accuracy and output size * when a large region is being expanded by a small amount (e.g. expanding * Canada by 1km). * * For example, if maxLevelDiff == 4, the region will always be expanded by * approximately 1/16 the width of its largest cell. Note that in the worst * case, the number of cells in the output can be up to 4 * (1 + 2 ** * maxLevelDiff) times larger than the number of cells in the input. */ public void Expand(S1Angle minRadius, int maxLevelDiff) { var minLevel = S2CellId.MaxLevel; foreach (var id in this) { minLevel = Math.Min(minLevel, id.Level); } // Find the maximum level such that all cells are at least "min_radius" // wide. var radiusLevel = S2Projections.MinWidth.GetMaxLevel(minRadius.Radians); if (radiusLevel == 0 && minRadius.Radians > S2Projections.MinWidth.GetValue(0)) { // The requested expansion is greater than the width of a face cell. // The easiest way to handle this is to expand twice. Expand(0); } Expand(Math.Min(minLevel + maxLevelDiff, radiusLevel)); }
public static S2LatLng FromDegrees(double latDegrees, double lngDegrees) { return(new S2LatLng(S1Angle.FromDegrees(latDegrees), S1Angle.FromDegrees(lngDegrees))); }
/** * 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 S2LatLng FromE6(long latE6, long lngE6) { return(new S2LatLng(S1Angle.E6(latE6), S1Angle.E6(lngE6))); }
public static S2LatLng FromE7(long latE7, long lngE7) { return(new S2LatLng(S1Angle.E7(latE7), S1Angle.E7(lngE7))); }
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]))); }
/** * Return a polygon which is the union of the given polygons; combines * vertices that form edges that are almost identical, as defined by * vertexMergeRadius. Note: clears the List! */ public static S2Polygon DestructiveUnionSloppy(System.Collections.Generic.ICollection <S2Polygon> polygons, S1Angle vertexMergeRadius) { // Effectively create a priority queue of polygons in order of number of // vertices. Repeatedly union the two smallest polygons and add the result // to the queue until we have a single polygon to return. // map: # of vertices -> polygon //TreeMultimap<Integer, S2Polygon> queue = TreeMultimap.create(); var queue = new MultiMap <int, S2Polygon>(); foreach (var polygon in polygons) { queue.Add(polygon.NumVertices, polygon); } polygons.Clear(); // Java uses a live-view that maps to the underlying structure //Set<Map.Entry<Integer, S2Polygon>> queueSet = queue.entries(); var enumer = queue.SortedValues; while (queue.CountIsAtLeast(2)) { // Pop two simplest polygons from queue. // queueSet = queue.entries(); //Iterator<Map.Entry<Integer, S2Polygon>> smallestIter = queueSet.iterator(); var smallestTwo = enumer.Take(2).ToList(); //Map.Entry<Integer, S2Polygon> smallest = smallestIter.next(); //int aSize = smallest.getKey().intValue(); //S2Polygon aPolygon = smallest.getValue(); //smallestIter.remove(); //smallest = smallestIter.next(); //int bSize = smallest.getKey().intValue(); //S2Polygon bPolygon = smallest.getValue(); //smallestIter.remove(); foreach (var item in smallestTwo) { queue.Remove(item); } // Union and add result back to queue. var unionPolygon = new S2Polygon(); unionPolygon.InitToUnionSloppy(smallestTwo[0].Value, smallestTwo[1].Value, vertexMergeRadius); var unionSize = smallestTwo[0].Key + smallestTwo[1].Key; queue.Add(unionSize, unionPolygon); // We assume that the number of vertices in the union polygon is the // sum of the number of vertices in the original polygons, which is not // always true, but will almost always be a decent approximation, and // faster than recomputing. } if (queue.Count == 0) { return(new S2Polygon()); } else { //return queue.get(queue.asMap().firstKey()).first(); return(queue.SortedValues.First().Value); } }
/** * Basic constructor. The latitude and longitude must be within the ranges * allowed by is_valid() below. * * TODO(dbeaumont): Make this a static factory method (fromLatLng() ?). */ public S2LatLng(S1Angle lat, S1Angle lng) : this(lat.Radians, lng.Radians) { }
public static S2LatLng FromE5(long latE5, long lngE5) { return(new S2LatLng(S1Angle.E5(latE5), S1Angle.E5(lngE5))); }