Beispiel #1
0
        public string ToDegreesString()
        {
            var s2LatLng = new S2LatLng(this);

            return("(" + s2LatLng.LatDegrees + ", "
                   + s2LatLng.LngDegrees + ")");
        }
Beispiel #2
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));
        }
Beispiel #3
0
        /**
         * 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);
        }
Beispiel #4
0
        // Increase the size of the bounding rectangle to include the given point.
        // The rectangle is expanded by the minimum amount possible.
        public S2LatLngRect AddPoint(S2LatLng ll)
        {
            // assert (ll.isValid());
            var newLat = _lat.AddPoint(ll.Lat.Radians);
            var newLng = _lng.AddPoint(ll.Lng.Radians);

            return(new S2LatLngRect(newLat, newLng));
        }
Beispiel #5
0
        /**
         * Return true if the edge AB intersects the given edge of constant longitude.
         */

        private static bool IntersectsLngEdge(S2Point a, S2Point b,
                                              R1Interval lat, double lng)
        {
            // Return true if the segment AB intersects the given edge of constant
            // longitude. The nice thing about edges of constant longitude is that
            // they are straight lines on the sphere (geodesics).

            return(S2.SimpleCrossing(a, b, S2LatLng.FromRadians(lat.Lo, lng)
                                     .ToPoint(), S2LatLng.FromRadians(lat.Hi, lng).ToPoint()));
        }
Beispiel #6
0
        /**
         * Return a rectangle that contains all points whose latitude distance from
         * this rectangle is at most margin.Lat, and whose longitude distance from
         * this rectangle is at most margin.Lng. In particular, latitudes are
         * clamped while longitudes are wrapped. Note that any expansion of an empty
         * interval remains empty, and both components of the given margin must be
         * non-negative.
         *
         * NOTE: If you are trying to grow a rectangle by a certain *distance* on the
         * sphere (e.g. 5km), use the ConvolveWithCap() method instead.
         */

        public S2LatLngRect Expanded(S2LatLng margin)
        {
            // assert (margin.Lat.radians() >= 0 && margin.Lng.radians() >= 0);
            if (IsEmpty)
            {
                return(this);
            }
            return(new S2LatLngRect(_lat.Expanded(margin.Lat.Radians).Intersection(
                                        FullLat), _lng.Expanded(margin.Lng.Radians)));
        }
Beispiel #7
0
        public void AddPoint(S2Point b)
        {
            // assert (S2.isUnitLength(b));

            var bLatLng = new S2LatLng(b);

            if (bound.IsEmpty)
            {
                bound = bound.AddPoint(bLatLng);
            }
            else
            {
                // We can't just call bound.addPoint(bLatLng) here, since we need to
                // ensure that all the longitudes between "a" and "b" are included.
                bound = bound.Union(S2LatLngRect.FromPointPair(aLatLng, bLatLng));

                // Check whether the Min/Max latitude occurs in the edge interior.
                // We find the normal to the plane containing AB, and then a vector
                // "dir" in this plane that also passes through the equator. We use
                // RobustCrossProd to ensure that the edge normal is accurate even
                // when the two points are very close together.
                var aCrossB = S2.RobustCrossProd(a, b);
                var dir     = S2Point.CrossProd(aCrossB, new S2Point(0, 0, 1));
                var da      = dir.DotProd(a);
                var db      = dir.DotProd(b);

                if (da * db < 0)
                {
                    // Minimum/maximum latitude occurs in the edge interior. This affects
                    // the latitude bounds but not the longitude bounds.
                    var absLat = Math.Acos(Math.Abs(aCrossB[2] / aCrossB.Norm));
                    var lat    = bound.Lat;
                    if (da < 0)
                    {
                        // It's possible that absLat < lat.lo() due to numerical errors.
                        lat = new R1Interval(lat.Lo, Math.Max(absLat, bound.Lat.Hi));
                    }
                    else
                    {
                        lat = new R1Interval(Math.Min(-absLat, bound.Lat.Lo), lat.Hi);
                    }
                    bound = new S2LatLngRect(lat, bound.Lng);
                }
            }
            a       = b;
            aLatLng = bLatLng;
        }
Beispiel #8
0
        /**
         * Return true if the rectangle is valid, which essentially just means that
         * the latitude bounds do not exceed Pi/2 in absolute value and the longitude
         * bounds do not exceed Pi in absolute value.
         *
         */

        /** Return the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order. */

        public S2LatLng GetVertex(int k)
        {
            // Return the points in CCW order (SW, SE, NE, NW).
            switch (k)
            {
            case 0:
                return(S2LatLng.FromRadians(_lat.Lo, _lng.Lo));

            case 1:
                return(S2LatLng.FromRadians(_lat.Lo, _lng.Hi));

            case 2:
                return(S2LatLng.FromRadians(_lat.Hi, _lng.Hi));

            case 3:
                return(S2LatLng.FromRadians(_lat.Hi, _lng.Lo));

            default:
                throw new ArgumentException("Invalid vertex index.");
            }
        }
Beispiel #9
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 #10
0
        /**
         *'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;
        }
Beispiel #11
0
        /**
         * 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);
        }
Beispiel #12
0
        /**
         * More efficient version of InteriorContains() that accepts a S2LatLng rather
         * than an S2Point.
         */

        public bool InteriorContains(S2LatLng ll)
        {
            // assert (ll.isValid());
            return(_lat.InteriorContains(ll.Lat.Radians) && _lng
                   .InteriorContains(ll.Lng.Radians));
        }
Beispiel #13
0
 public S2Cell(S2LatLng ll)
 {
     Init(S2CellId.FromLatLng(ll));
 }
Beispiel #14
0
        /**
         * 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());
        }
Beispiel #15
0
        /**
         * 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)));
        }
Beispiel #16
0
        /** Convenience method to construct a rectangle containing a single point. */

        public static S2LatLngRect FromPoint(S2LatLng p)
        {
            // assert (p.isValid());
            return(new S2LatLngRect(p, p));
        }
Beispiel #17
0
        /** The canonical empty rectangle */

        /**
         * Construct a rectangle from a center point (in lat-lng space) and size in
         * each dimension. If size.Lng is greater than 360 degrees it is clamped,
         * and latitudes greater than +/- 90 degrees are also clamped. So for example,
         * FromCenterSize((80,170),(20,20)) -> (lo=(60,150),hi=(90,-170)).
         */

        public static S2LatLngRect FromCenterSize(S2LatLng center, S2LatLng size)
        {
            return(FromPoint(center).Expanded(size * 0.5));
        }
Beispiel #18
0
        /** Return the leaf cell containing the given S2LatLng. */

        public static S2CellId FromLatLng(S2LatLng ll)
        {
            return(FromPoint(ll.ToPoint()));
        }