Beispiel #1
0
        /**
         * Return a latitude-longitude rectangle that contains the edge from "a" to
         * "b". Both points must be unit-length. Note that the bounding rectangle of
         * an edge can be larger than the bounding rectangle of its endpoints.
         */

        public static S2LatLngRect FromEdge(S2Point a, S2Point b)
        {
            // assert (S2.isUnitLength(a) && S2.isUnitLength(b));
            var r = FromPointPair(new S2LatLng(a), new S2LatLng(b));

            // 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 ab  = S2.RobustCrossProd(a, b);
            var dir = S2Point.CrossProd(ab, new S2Point(0, 0, 1));
            var da  = dir.DotProd(a);
            var db  = dir.DotProd(b);

            if (da * db >= 0)
            {
                // Minimum and maximum latitude are attained at the vertices.
                return(r);
            }
            // Minimum/maximum latitude occurs in the edge interior. This affects the
            // latitude bounds but not the longitude bounds.
            var absLat = Math.Acos(Math.Abs(ab.Z / ab.Norm));

            if (da < 0)
            {
                return(new S2LatLngRect(new R1Interval(r.Lat.Lo, absLat), r.Lng));
            }
            else
            {
                return(new S2LatLngRect(new R1Interval(-absLat, r.Lat.Hi), r.Lng));
            }
        }
Beispiel #2
0
        /*
         * Given two edges AB and CD such that robustCrossing() is true, return their
         * intersection point. Useful properties of getIntersection (GI):
         *
         * (1) GI(b,a,c,d) == GI(a,b,d,c) == GI(a,b,c,d) (2) GI(c,d,a,b) ==
         * GI(a,b,c,d)
         *
         * The returned intersection point X is guaranteed to be close to the edges AB
         * and CD, but if the edges intersect at a very small angle then X may not be
         * close to the true mathematical intersection point P. See the description of
         * "DEFAULT_INTERSECTION_TOLERANCE" below for details.
         */

        public static S2Point GetIntersection(S2Point a0, S2Point a1, S2Point b0, S2Point b1)
        {
            Preconditions.CheckArgument(RobustCrossing(a0, a1, b0, b1) > 0,
                                        "Input edges a0a1 and b0b1 muct have a true robustCrossing.");

            // We use robustCrossProd() to get accurate results even when two endpoints
            // are close together, or when the two line segments are nearly parallel.
            var aNorm = S2Point.Normalize(S2.RobustCrossProd(a0, a1));
            var bNorm = S2Point.Normalize(S2.RobustCrossProd(b0, b1));
            var x     = S2Point.Normalize(S2.RobustCrossProd(aNorm, bNorm));

            // Make sure the intersection point is on the correct side of the sphere.
            // Since all vertices are unit length, and edges are less than 180 degrees,
            // (a0 + a1) and (b0 + b1) both have positive dot product with the
            // intersection point. We use the sum of all vertices to make sure that the
            // result is unchanged when the edges are reversed or exchanged.
            if (x.DotProd(a0 + a1 + b0 + b1) < 0)
            {
                x = -x;
            }

            // The calculation above is sufficient to ensure that "x" is within
            // DEFAULT_INTERSECTION_TOLERANCE of the great circles through (a0,a1) and
            // (b0,b1).
            // However, if these two great circles are very close to parallel, it is
            // possible that "x" does not lie between the endpoints of the given line
            // segments. In other words, "x" might be on the great circle through
            // (a0,a1) but outside the range covered by (a0,a1). In this case we do
            // additional clipping to ensure that it does.

            if (S2.OrderedCcw(a0, x, a1, aNorm) && S2.OrderedCcw(b0, x, b1, bNorm))
            {
                return(x);
            }

            // Find the acceptable endpoint closest to x and return it. An endpoint is
            // acceptable if it lies between the endpoints of the other line segment.
            var r = new CloserResult(10, x);

            if (S2.OrderedCcw(b0, a0, b1, bNorm))
            {
                r.ReplaceIfCloser(x, a0);
            }
            if (S2.OrderedCcw(b0, a1, b1, bNorm))
            {
                r.ReplaceIfCloser(x, a1);
            }
            if (S2.OrderedCcw(a0, b0, a1, aNorm))
            {
                r.ReplaceIfCloser(x, b0);
            }
            if (S2.OrderedCcw(a0, b1, a1, aNorm))
            {
                r.ReplaceIfCloser(x, b1);
            }
            return(r.Vmin);
        }
Beispiel #3
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 #4
0
        /**
         * Returns the point on edge AB closest to X. x, a and b must be of unit
         * length. Throws IllegalArgumentException if this is not the case.
         *
         */

        public static S2Point GetClosestPoint(S2Point x, S2Point a, S2Point b)
        {
            Preconditions.CheckArgument(S2.IsUnitLength(x));
            Preconditions.CheckArgument(S2.IsUnitLength(a));
            Preconditions.CheckArgument(S2.IsUnitLength(b));

            var crossProd = S2.RobustCrossProd(a, b);
            // Find the closest point to X along the great circle through AB.
            var p = x - (crossProd * x.DotProd(crossProd) / crossProd.Norm2);

            // If p is on the edge AB, then it's the closest point.
            if (S2.SimpleCcw(crossProd, a, p) && S2.SimpleCcw(p, b, crossProd))
            {
                return(S2Point.Normalize(p));
            }
            // Otherwise, the closest point is either A or B.
            return((x - a).Norm2 <= (x - b).Norm2 ? a : b);
        }
Beispiel #5
0
        /**
         * Return the minimum distance from X to any point on the edge AB. The result
         * is very accurate for small distances but may have some numerical error if
         * the distance is large (approximately Pi/2 or greater). The case A == B is
         * handled correctly. Note: x, a and b must be of unit length. Throws
         * IllegalArgumentException if this is not the case.
         */

        public static S1Angle GetDistance(S2Point x, S2Point a, S2Point b)
        {
            return(GetDistance(x, a, b, S2.RobustCrossProd(a, b)));
        }
Beispiel #6
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);
        }