Example #1
0
        /// <summary>
        /// Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment
        /// (lat1, lng1) to (lat2, lng2).
        /// Longitudes are offset by -lng1; the implicit lng1 becomes 0.
        /// </summary>
        /// <param name="lat1"></param>
        /// <param name="lat2"></param>
        /// <param name="lng2"></param>
        /// <param name="lat3"></param>
        /// <param name="lng3"></param>
        /// <param name="geodesic"></param>
        /// <returns></returns>
        private static bool Intersects(double lat1, double lat2, double lng2,
                                       double lat3, double lng3, bool geodesic)
        {
            // Both ends on the same side of lng3.
            if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2))
            {
                return(false);
            }
            // Point is South Pole.
            if (lat3 <= -Math.PI / 2)
            {
                return(false);
            }
            // Any segment end is a pole.
            if (lat1 <= -Math.PI / 2 || lat2 <= -Math.PI / 2 || lat1 >= Math.PI / 2 || lat2 >= Math.PI / 2)
            {
                return(false);
            }
            if (lng2 <= -Math.PI)
            {
                return(false);
            }
            double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;

            // Northern hemisphere and point under lat-lng line.
            if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat)
            {
                return(false);
            }
            // Southern hemisphere and point above lat-lng line.
            if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat)
            {
                return(true);
            }
            // North Pole.
            if (lat3 >= Math.PI / 2)
            {
                return(true);
            }
            // Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
            // Compare through a strictly-increasing function (Math.Tan() or mercator()) as convenient.
            return(geodesic ?
                   Math.Tan(lat3) >= TanLatGC(lat1, lat2, lng2, lng3) :
                   GmsMathUtils.Mercator(lat3) >= MercatorLatRhumb(lat1, lat2, lng2, lng3));
        }
Example #2
0
        /// <summary>
        /// Returns sin(initial bearing from (lat1,lng1) to (lat3,lng3) minus initial bearing
        /// from (lat1, lng1) to (lat2,lng2)).
        /// </summary>
        /// <param name="lat1"></param>
        /// <param name="lng1"></param>
        /// <param name="lat2"></param>
        /// <param name="lng2"></param>
        /// <param name="lat3"></param>
        /// <param name="lng3"></param>
        /// <returns></returns>
        private static double SinDeltaBearing(double lat1, double lng1, double lat2, double lng2,
                                              double lat3, double lng3)
        {
            double sinLat1 = Math.Sin(lat1);
            double cosLat2 = Math.Cos(lat2);
            double cosLat3 = Math.Cos(lat3);
            double lat31   = lat3 - lat1;
            double lng31   = lng3 - lng1;
            double lat21   = lat2 - lat1;
            double lng21   = lng2 - lng1;
            double a       = Math.Sin(lng31) * cosLat3;
            double c       = Math.Sin(lng21) * cosLat2;
            double b       = Math.Sin(lat31) + 2 * sinLat1 * cosLat3 * GmsMathUtils.Hav(lng31);
            double d       = Math.Sin(lat21) + 2 * sinLat1 * cosLat2 * GmsMathUtils.Hav(lng21);
            double denom   = (a * a + b * b) * (c * c + d * d);

            return(denom <= 0 ? 1 : (a * d - b * c) / Math.Sqrt(denom));
        }
Example #3
0
        private static bool IsOnSegmentGC(double lat1, double lng1, double lat2, double lng2,
                                          double lat3, double lng3, double havTolerance)
        {
            double havDist13 = GmsMathUtils.HavDistance(lat1, lat3, lng1 - lng3);

            if (havDist13 <= havTolerance)
            {
                return(true);
            }
            double havDist23 = GmsMathUtils.HavDistance(lat2, lat3, lng2 - lng3);

            if (havDist23 <= havTolerance)
            {
                return(true);
            }
            double sinBearing    = SinDeltaBearing(lat1, lng1, lat2, lng2, lat3, lng3);
            double sinDist13     = GmsMathUtils.SinFromHav(havDist13);
            double havCrossTrack = GmsMathUtils.HavFromSin(sinDist13 * sinBearing);

            if (havCrossTrack > havTolerance)
            {
                return(false);
            }
            double havDist12 = GmsMathUtils.HavDistance(lat1, lat2, lng1 - lng2);
            double term      = havDist12 + havCrossTrack * (1 - 2 * havDist12);

            if (havDist13 > term || havDist23 > term)
            {
                return(false);
            }
            if (havDist12 < 0.74)
            {
                return(true);
            }
            double cosCrossTrack    = 1 - 2 * havCrossTrack;
            double havAlongTrack13  = (havDist13 - havCrossTrack) / cosCrossTrack;
            double havAlongTrack23  = (havDist23 - havCrossTrack) / cosCrossTrack;
            double sinSumAlongTrack = GmsMathUtils.SinSumFromHav(havAlongTrack13, havAlongTrack23);

            return(sinSumAlongTrack > 0);  // Compare with half-circle == PI using sign of sin().
        }
Example #4
0
        /// <summary>
        /// Computes whether the given point lies inside the specified polygon.
        /// The polygon is always cosidered closed, regardless of whether the last point equals
        /// the first or not.
        /// Inside is defined as not containing the South Pole -- the South Pole is always outside.
        ///  The polygon is formed of great circle segments if geodesic is true, and of rhumb
        /// (loxodromic) segments otherwise.
        /// </summary>
        /// <param name="point"></param>
        /// <param name="polygon"></param>
        /// <param name="geodesic"></param>
        /// <returns></returns>
        public static bool ContainsLocation(Position point, IEnumerable <Position> polygon, bool geodesic)
        {
            int size = polygon.Count();

            if (size == 0)
            {
                return(false);
            }

            double   lat3       = point.Latitude.ToRadian();
            double   lng3       = point.Longitude.ToRadian();
            Position prev       = polygon.Last();
            double   lat1       = prev.Latitude.ToRadian();
            double   lng1       = prev.Longitude.ToRadian();
            int      nIntersect = 0;

            foreach (var point2 in polygon)
            {
                double dLng3 = GmsMathUtils.Wrap(lng3 - lng1, -Math.PI, Math.PI);
                // Special case: point equal to vertex is inside.
                if (lat3 == lat1 && dLng3 == 0)
                {
                    return(true);
                }
                double lat2 = point2.Latitude.ToRadian();
                double lng2 = point2.Longitude.ToRadian();
                // Offset longitudes by -lng1.
                if (Intersects(lat1, lat2, GmsMathUtils.Wrap(lng2 - lng1, -Math.PI, Math.PI), lat3, dLng3, geodesic))
                {
                    ++nIntersect;
                }
                lat1 = lat2;
                lng1 = lng2;
            }
            return((nIntersect & 1) != 0);
        }
Example #5
0
 /// <summary>
 /// Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0.
 /// </summary>
 /// <param name="lat1"></param>
 /// <param name="lat2"></param>
 /// <param name="lng2"></param>
 /// <param name="lng3"></param>
 /// <returns></returns>
 private static double MercatorLatRhumb(double lat1, double lat2, double lng2, double lng3)
 {
     return((GmsMathUtils.Mercator(lat1) * (lng2 - lng3) + GmsMathUtils.Mercator(lat2) * lng3) / lng2);
 }
Example #6
0
        private static bool IsLocationOnEdgeOrPath(Position point, IEnumerable <Position> poly, bool closed,
                                                   bool geodesic, double toleranceEarth)
        {
            int size = poly.Count();

            if (size == 0)
            {
                return(false);
            }

            double   tolerance    = toleranceEarth / GmsMathUtils.EarthRadius;
            double   havTolerance = GmsMathUtils.Hav(tolerance);
            double   lat3         = point.Latitude.ToRadian();
            double   lng3         = point.Longitude.ToRadian();
            Position prev         = poly.ElementAt(closed ? size - 1 : 0);
            double   lat1         = prev.Latitude.ToRadian();
            double   lng1         = prev.Longitude.ToRadian();

            if (geodesic)
            {
                foreach (var point2 in poly)
                {
                    double lat2 = point2.Latitude.ToRadian();
                    double lng2 = point2.Longitude.ToRadian();
                    if (IsOnSegmentGC(lat1, lng1, lat2, lng2, lat3, lng3, havTolerance))
                    {
                        return(true);
                    }
                    lat1 = lat2;
                    lng1 = lng2;
                }
            }
            else
            {
                // We project the points to mercator space, where the Rhumb segment is a straight line,
                // and compute the geodesic distance between point3 and the closest point on the
                // segment. This method is an approximation, because it uses "closest" in mercator
                // space which is not "closest" on the sphere -- but the error is small because
                // "tolerance" is small.
                double   minAcceptable = lat3 - tolerance;
                double   maxAcceptable = lat3 + tolerance;
                double   y1            = GmsMathUtils.Mercator(lat1);
                double   y3            = GmsMathUtils.Mercator(lat3);
                double[] xTry          = new double[3];

                foreach (var point2 in poly)
                {
                    double lat2 = point2.Latitude.ToRadian();
                    double y2   = GmsMathUtils.Mercator(lat2);
                    double lng2 = point2.Longitude.ToRadian();
                    if (Math.Max(lat1, lat2) >= minAcceptable && Math.Min(lat1, lat2) <= maxAcceptable)
                    {
                        // We offset longitudes by -lng1; the implicit x1 is 0.
                        double x2     = GmsMathUtils.Wrap(lng2 - lng1, -Math.PI, Math.PI);
                        double x3Base = GmsMathUtils.Wrap(lng3 - lng1, -Math.PI, Math.PI);
                        xTry[0] = x3Base;
                        // Also explore wrapping of x3Base around the world in both directions.
                        xTry[1] = x3Base + 2 * Math.PI;
                        xTry[2] = x3Base - 2 * Math.PI;

                        foreach (var x3 in xTry)
                        {
                            double dy         = y2 - y1;
                            double len2       = x2 * x2 + dy * dy;
                            double t          = len2 <= 0 ? 0 : GmsMathUtils.Clamp((x3 * x2 + (y3 - y1) * dy) / len2, 0, 1);
                            double xClosest   = t * x2;
                            double yClosest   = y1 + t * dy;
                            double latClosest = GmsMathUtils.InverseMercator(yClosest);
                            double havDist    = GmsMathUtils.HavDistance(lat3, latClosest, x3 - xClosest);
                            if (havDist < havTolerance)
                            {
                                return(true);
                            }
                        }
                    }
                    lat1 = lat2;
                    lng1 = lng2;
                    y1   = y2;
                }
            }
            return(false);
        }