Beispiel #1
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 #2
0
        /**
         * Return true if the edge AB intersects the given edge of constant latitude.
         */

        private static bool IntersectsLatEdge(S2Point a, S2Point b, double lat,
                                              S1Interval lng)
        {
            // Return true if the segment AB intersects the given edge of constant
            // latitude. Unfortunately, lines of constant latitude are curves on
            // the sphere. They can intersect a straight edge in 0, 1, or 2 points.
            // assert (S2.isUnitLength(a) && S2.isUnitLength(b));

            // First, compute the normal to the plane AB that points vaguely north.
            var z = S2Point.Normalize(S2.RobustCrossProd(a, b));

            if (z.Z < 0)
            {
                z = -z;
            }

            // Extend this to an orthonormal frame (x,y,z) where x is the direction
            // where the great circle through AB achieves its maximium latitude.
            var y = S2Point.Normalize(S2.RobustCrossProd(z, new S2Point(0, 0, 1)));
            var x = S2Point.CrossProd(y, z);
            // assert (S2.isUnitLength(x) && x.z >= 0);

            // Compute the angle "theta" from the x-axis (in the x-y plane defined
            // above) where the great circle intersects the given line of latitude.
            var sinLat = Math.Sin(lat);

            if (Math.Abs(sinLat) >= x.Z)
            {
                return(false); // The great circle does not reach the given latitude.
            }
            // assert (x.z > 0);
            var cosTheta = sinLat / x.Z;
            var sinTheta = Math.Sqrt(1 - cosTheta * cosTheta);
            var theta    = Math.Atan2(sinTheta, cosTheta);

            // The candidate intersection points are located +/- theta in the x-y
            // plane. For an intersection to be valid, we need to check that the
            // intersection point is contained in the interior of the edge AB and
            // also that it is contained within the given longitude interval "lng".

            // Compute the range of theta values spanned by the edge AB.
            var abTheta = S1Interval.FromPointPair(Math.Atan2(
                                                       a.DotProd(y), a.DotProd(x)), Math.Atan2(b.DotProd(y), b.DotProd(x)));

            if (abTheta.Contains(theta))
            {
                // Check if the intersection point is also in the given "lng" interval.
                var isect = (x * cosTheta) + (y * sinTheta);
                if (lng.Contains(Math.Atan2(isect.Y, isect.X)))
                {
                    return(true);
                }
            }
            if (abTheta.Contains(-theta))
            {
                // Check if the intersection point is also in the given "lng" interval.
                var intersection = (x * cosTheta) - (y * sinTheta);
                if (lng.Contains(Math.Atan2(intersection.Y, intersection.X)))
                {
                    return(true);
                }
            }
            return(false);
        }
Beispiel #3
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 #4
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 #5
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)));
        }