// 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);
        }
Beispiel #2
0
        /**
         * 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");
            }
        }
Beispiel #4
0
 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]))));
 }
Beispiel #5
0
        /**
         * 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.
        }
Beispiel #6
0
        /**
         * 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);
 }
Beispiel #8
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));
        }
Beispiel #9
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);
        }
Beispiel #10
0
        /**
         * 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);
        }
Beispiel #12
0
        /**
         * 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));
        }
Beispiel #13
0
 public static S2LatLng FromDegrees(double latDegrees, double lngDegrees)
 {
     return(new S2LatLng(S1Angle.FromDegrees(latDegrees), S1Angle.FromDegrees(lngDegrees)));
 }
Beispiel #14
0
        /**
         * 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)))));
        }
Beispiel #15
0
 public static S2LatLng FromE6(long latE6, long lngE6)
 {
     return(new S2LatLng(S1Angle.E6(latE6), S1Angle.E6(lngE6)));
 }
Beispiel #16
0
 public static S2LatLng FromE7(long latE7, long lngE7)
 {
     return(new S2LatLng(S1Angle.E7(latE7), S1Angle.E7(lngE7)));
 }
Beispiel #17
0
 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);
            }
        }
Beispiel #19
0
        /**
         * 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)
        {
        }
Beispiel #20
0
 public static S2LatLng FromE5(long latE5, long lngE5)
 {
     return(new S2LatLng(S1Angle.E5(latE5), S1Angle.E5(lngE5)));
 }