/// <summary> /// Calculates distance between two geographic locations on equirectangular map projection /// <para>Using Pythagoras’ theorem </para> /// </summary> public static GeoDistance GetDistanceAppx(IGeoLocatable origin, IGeoLocatable destination, double radius = GeoGlobal.Earths.Radius) { origin = origin.ToRadians(); destination = destination.ToRadians(); var dLat = (destination.Latitude - origin.Latitude); var dLon = (destination.Longitude - origin.Longitude); var x = (dLon) * Math.Cos((origin.Latitude + destination.Latitude) / 2); var y = (dLat); var distance = Math.Sqrt(x * x + y * y) * radius; return(new GeoDistance { Kilometers = distance }); //return distance; //var yMin = Math.Min(origin.Latitude, destination.Latitude); //var yMax = Math.Max(origin.Latitude, destination.Latitude); //var xMin = Math.Min(origin.Longitude, destination.Longitude); //var xMax = Math.Max(origin.Longitude, destination.Longitude); //var yDelta = (yMax - yMin) * (yMax - yMin); //var xDelta = (xMax - xMin) * (xMax - xMin); //var distance = Math.Sqrt(xDelta + yDelta); //return new GeoDistance { Degrees = distance }; }
/// <summary> /// Calculates the maximum latitude of a great circle path from origin location in direction of bearing angle /// <para>using Clairaut’s formula</para> /// </summary> /// <param name="origin">origin location in geographic degrees</param> /// <param name="bearing">bearing from origin in geographic degrees</param> public static double GetLatitudeMax(IGeoLocatable origin, double bearing) { origin = origin.ToRadians(); bearing = bearing.ToRadians(); var latMax = Math.Acos(Math.Abs(Math.Sin(bearing) * Math.Cos(origin.Latitude))); return(latMax); }
/// <summary> /// Calculates the initial bearing from origin location in direction of destination location, in degrees from true North /// <para> North = 0, East = 90, South = 180, West = 270 </para> /// </summary> /// <param name="origin">origin location in geographic degrees</param> /// <param name="destination">destination location in geographic degrees</param> public static double GetBearing(IGeoLocatable origin, IGeoLocatable destination) { origin = origin.ToRadians(); destination = destination.ToRadians(); var dLat = (destination.Latitude - origin.Latitude); var dLon = (destination.Longitude - origin.Longitude); var y = Math.Sin(dLon) * Math.Cos(destination.Latitude); var x = Math.Cos(origin.Latitude) * Math.Sin(destination.Latitude) - Math.Sin(origin.Latitude) * Math.Cos(destination.Latitude) * Math.Cos(dLon); var angle = Math.Atan2(y, x); return(angle.ToDegreesNormalized()); }
/// <summary> /// Calculates distance between two geographic locations on the Great Circle /// <para>Using the Spherical law of cosines </para> /// </summary> /// <param name="origin">origin location in geographic degrees</param> /// <param name="destination">destination location in geographic degrees</param> /// <param name="radius">radius of a geographic sphere, in kilometers</param> /// <remarks>radius defaults to Earth's mean radius</remarks> public static GeoDistance GetDistance(IGeoLocatable origin, IGeoLocatable destination, double radius = GeoGlobal.Earths.Radius) { origin = origin.ToRadians(); destination = destination.ToRadians(); var sinProd = Math.Sin(origin.Latitude) * Math.Sin(destination.Latitude); var cosProd = Math.Cos(origin.Latitude) * Math.Cos(destination.Latitude); var dLon = (destination.Longitude - origin.Longitude); var angle = Math.Acos(sinProd + cosProd * Math.Cos(dLon)); var distance = angle * radius; return(new GeoDistance { Kilometers = distance }); }
/// <summary> /// Calculates mid point between two geographic locations on the Great Circle /// <para>Using the Spherical law of cosines </para> /// </summary> /// <param name="origin">origin location in geographic degrees</param> /// <param name="destination">destination location in geographic degrees</param> public static GeoPoint GetMidpoint(IGeoLocatable origin, IGeoLocatable destination) { origin = origin.ToRadians(); destination = destination.ToRadians(); var dLat = (destination.Latitude - origin.Latitude); var dLon = (destination.Longitude - origin.Longitude); var bx = Math.Cos(destination.Latitude) * Math.Cos(dLon); var by = Math.Cos(destination.Latitude) * Math.Sin(dLon); var lat = Math.Atan2(Math.Sin(origin.Latitude) + Math.Sin(destination.Latitude), Math.Sqrt((Math.Cos(origin.Latitude) + bx) * (Math.Cos(origin.Latitude) + bx) + by * by)); var lon = origin.Longitude + Math.Atan2(by, Math.Cos(origin.Latitude) + bx); lon = (lon + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180º return(new GeoPoint(lon.ToDegrees(), lat.ToDegrees())); }
/// <summary> /// Calculates the destination location at distance and in direction of bearing from origin location /// <para>Using the Spherical law of cosines </para> /// </summary> /// <param name="origin">location in geographic degrees </param> /// <param name="bearing">bearing in geographic degrees from origin</param> /// <param name="distance">distance in km</param> /// <param name="radius">radius in km</param> /// <remarks>radius defaults to Earth's mean radius</remarks> public static GeoPoint GetDestination(IGeoLocatable origin, double bearing, double distance, double radius = GeoGlobal.Earths.Radius) { origin = origin.ToRadians(); bearing = bearing.ToRadians(); distance = distance / radius; // angular distance in radians var latitude = Math.Asin(Math.Sin(origin.Latitude) * Math.Cos(distance) + Math.Cos(origin.Latitude) * Math.Sin(distance) * Math.Cos(bearing)); var x = Math.Sin(bearing) * Math.Sin(distance) * Math.Cos(origin.Latitude); var y = Math.Cos(distance) - Math.Sin(origin.Latitude) * Math.Sin(origin.Latitude); var longitude = origin.Longitude + Math.Atan2(x, y); longitude = (longitude + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180º var destination = new GeoPoint(longitude.ToDegrees(), latitude.ToDegrees()); return(destination); }
/// <summary> /// Calculates distance between two geographic locations on the Great Circle /// <para>Using the Haversine formula</para> /// <remarks>"Virtues of the Haversine" by R. W. Sinnott, Sky and Telescope, vol 68, no 2, 1984</remarks> /// </summary> /// <param name="origin">origin location in geographic degrees</param> /// <param name="destination">destination location in geographic degrees</param> /// <param name="radius">radius of a geographic sphere, in kilometers</param> /// <remarks>radius defaults to Earth's mean radius</remarks> public static GeoDistance GetDistanceH(IGeoLocatable origin, IGeoLocatable destination, double radius = GeoGlobal.Earths.Radius) { origin = origin.ToRadians(); destination = destination.ToRadians(); var dLat = (destination.Latitude - origin.Latitude); var dLon = (destination.Longitude - origin.Longitude); var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(origin.Latitude) * Math.Cos(destination.Latitude); var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); var distance = radius * c; return(new GeoDistance { Kilometers = distance }); }
/// <summary> /// Calculates intersection point of paths from two geographic locations /// </summary> /// <param name="origin1">origin of first location in geographic degrees</param> /// <param name="origin2">origin of second location in geographic degrees</param> /// <param name="bearing1">bearing from first location in geographic degrees</param> /// <param name="bearing2">bearing from second location in geographic degrees</param> /// <param name="radius">radius of a geographic sphere, in kilometers</param> /// <remarks>radius defaults to Earth's mean radius</remarks> public static GeoPoint GetIntersection( IGeoLocatable origin1, double bearing1, IGeoLocatable origin2, double bearing2, double radius = GeoGlobal.Earths.Radius) { origin1 = origin1.ToRadians(); origin2 = origin2.ToRadians(); var brng13 = bearing1.ToRadians(); var brng23 = bearing2.ToRadians(); var dLat = (origin2.Latitude - origin1.Latitude); var dLon = (origin2.Longitude - origin1.Longitude); var dist12 = 2 * Math.Asin(Math.Sqrt(Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(origin1.Latitude) * Math.Cos(origin2.Latitude) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2))); if (dist12 == 0) { return(GeoPoint.Invalid); } // initial/final bearings between points var brngA = Math.Acos((Math.Sin(origin2.Latitude) - Math.Sin(origin1.Latitude) * Math.Cos(dist12)) / (Math.Sin(dist12) * Math.Cos(origin1.Latitude))); if (double.IsNaN(brngA)) { brngA = 0; // protect against rounding } var brngB = Math.Acos((Math.Sin(origin1.Latitude) - Math.Sin(origin2.Latitude) * Math.Cos(dist12)) / (Math.Sin(dist12) * Math.Cos(origin2.Latitude))); double brng12, brng21; if (Math.Sin(dLon) > 0) { brng12 = brngA; brng21 = 2 * Math.PI - brngB; } else { brng12 = 2 * Math.PI - brngA; brng21 = brngB; } var alpha1 = (brng13 - brng12 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 2-1-3 var alpha2 = (brng21 - brng23 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 1-2-3 if (Math.Sin(alpha1) == 0 && Math.Sin(alpha2) == 0) { return(GeoPoint.Invalid); // infinite intersections } if (Math.Sin(alpha1) * Math.Sin(alpha2) < 0) { return(GeoPoint.Invalid); // ambiguous intersection } var alpha3 = Math.Acos(-Math.Cos(alpha1) * Math.Cos(alpha2) + Math.Sin(alpha1) * Math.Sin(alpha2) * Math.Cos(dist12)); var dist13 = Math.Atan2(Math.Sin(dist12) * Math.Sin(alpha1) * Math.Sin(alpha2), Math.Cos(alpha2) + Math.Cos(alpha1) * Math.Cos(alpha3)); var lat3 = Math.Asin(Math.Sin(origin1.Latitude) * Math.Cos(dist13) + Math.Cos(origin1.Latitude) * Math.Sin(dist13) * Math.Cos(brng13)); var dLon13 = Math.Atan2(Math.Sin(brng13) * Math.Sin(dist13) * Math.Cos(origin1.Latitude), Math.Cos(dist13) - Math.Sin(origin1.Latitude) * Math.Sin(lat3)); var lon3 = origin1.Longitude + dLon13; lon3 = (lon3 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180º return(new GeoPoint(lat3.ToDegrees(), lon3.ToDegrees())); }