Je calcule la distance entre deux GeoCoordinates. Je teste mon application contre 3 ou 4 autres applications. Lorsque je calcule la distance, j'ai tendance à obtenir une moyenne de 3,3 milles alors que les autres applications obtiennent 3,5 milles. C'est une grande différence pour le calcul que j'essaye de faire. Existe-t-il de bonnes bibliothèques de classes pour calculer la distance? Je le calcule comme ça en C #:
public static double Calculate(double sLatitude,double sLongitude, double eLatitude,
double eLongitude)
{
var radiansOverDegrees = (Math.PI / 180.0);
var sLatitudeRadians = sLatitude * radiansOverDegrees;
var sLongitudeRadians = sLongitude * radiansOverDegrees;
var eLatitudeRadians = eLatitude * radiansOverDegrees;
var eLongitudeRadians = eLongitude * radiansOverDegrees;
var dLongitude = eLongitudeRadians - sLongitudeRadians;
var dLatitude = eLatitudeRadians - sLatitudeRadians;
var result1 = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) +
Math.Cos(sLatitudeRadians) * Math.Cos(eLatitudeRadians) *
Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);
// Using 3956 as the number of miles around the earth
var result2 = 3956.0 * 2.0 *
Math.Atan2(Math.Sqrt(result1), Math.Sqrt(1.0 - result1));
return result2;
}
Que pourrais-je faire de mal? Devrais-je d'abord calculer en km, puis convertir en miles?
La classe GeoCoordinate (.NET Framework 4 et versions supérieures) a déjà la méthode GetDistanceTo
.
var sCoord = new GeoCoordinate(sLatitude, sLongitude);
var eCoord = new GeoCoordinate(eLatitude, eLongitude);
return sCoord.GetDistanceTo(eCoord);
La distance est en mètres.
Vous devez référencer System.Device.
GetDistance est la meilleure solution}, mais dans de nombreux cas, nous ne pouvons pas utiliser cette méthode (par exemple, Universal App)
Pseudocode de l'algorithme à calcule la distance entre pour coordonner:
public static double DistanceTo(double lat1, double lon1, double lat2, double lon2, char unit = 'K')
{
double rlat1 = Math.PI*lat1/180;
double rlat2 = Math.PI*lat2/180;
double theta = lon1 - lon2;
double rtheta = Math.PI*theta/180;
double dist =
Math.Sin(rlat1)*Math.Sin(rlat2) + Math.Cos(rlat1)*
Math.Cos(rlat2)*Math.Cos(rtheta);
dist = Math.Acos(dist);
dist = dist*180/Math.PI;
dist = dist*60*1.1515;
switch (unit)
{
case 'K': //Kilometers -> default
return dist*1.609344;
case 'N': //Nautical Miles
return dist*0.8684;
case 'M': //Miles
return dist;
}
return dist;
}
Real World C # Implementation, qui utilise un méthode d'extension
Utilisation:
var distance = new Coordinates(48.672309, 15.695585)
.DistanceTo(
new Coordinates(48.237867, 16.389477),
UnitOfLength.Kilometers
);
La mise en oeuvre:
public class Coordinates
{
public double Latitude { get; private set; }
public double Longitude { get; private set; }
public Coordinates(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
}
public static class CoordinatesDistanceExtensions
{
public static double DistanceTo(this Coordinates baseCoordinates, Coordinates targetCoordinates)
{
return DistanceTo(baseCoordinates, targetCoordinates, UnitOfLength.Kilometers);
}
public static double DistanceTo(this Coordinates baseCoordinates, Coordinates targetCoordinates, UnitOfLength unitOfLength)
{
var baseRad = Math.PI * baseCoordinates.Latitude / 180;
var targetRad = Math.PI * targetCoordinates.Latitude/ 180;
var theta = baseCoordinates.Longitude - targetCoordinates.Longitude;
var thetaRad = Math.PI * theta / 180;
double dist =
Math.Sin(baseRad) * Math.Sin(targetRad) + Math.Cos(baseRad) *
Math.Cos(targetRad) * Math.Cos(thetaRad);
dist = Math.Acos(dist);
dist = dist * 180 / Math.PI;
dist = dist * 60 * 1.1515;
return unitOfLength.ConvertFromMiles(dist);
}
}
public class UnitOfLength
{
public static UnitOfLength Kilometers = new UnitOfLength(1.609344);
public static UnitOfLength NauticalMiles = new UnitOfLength(0.8684);
public static UnitOfLength Miles = new UnitOfLength(1);
private readonly double _fromMilesFactor;
private UnitOfLength(double fromMilesFactor)
{
_fromMilesFactor = fromMilesFactor;
}
public double ConvertFromMiles(double input)
{
return input*_fromMilesFactor;
}
}
Voici la version JavaScript les gars et les filles
function distanceTo(lat1, lon1, lat2, lon2, unit) {
var rlat1 = Math.PI * lat1/180
var rlat2 = Math.PI * lat2/180
var rlon1 = Math.PI * lon1/180
var rlon2 = Math.PI * lon2/180
var theta = lon1-lon2
var rtheta = Math.PI * theta/180
var dist = Math.sin(rlat1) * Math.sin(rlat2) + Math.cos(rlat1) * Math.cos(rlat2) * Math.cos(rtheta);
dist = Math.acos(dist)
dist = dist * 180/Math.PI
dist = dist * 60 * 1.1515
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
return dist
}
Pour ceux qui utilisent Xamarin et n’ont pas accès à la classe GeoCoordinate, vous pouvez utiliser la classe Android Location à la place:
public static double GetDistanceBetweenCoordinates (double lat1, double lng1, double lat2, double lng2) {
var coords1 = new Location ("");
coords1.Latitude = lat1;
coords1.Longitude = lng1;
var coords2 = new Location ("");
coords2.Latitude = lat2;
coords2.Longitude = lng2;
return coords1.DistanceTo (coords2);
}
Et ici, pour ceux qui ne sont toujours pas satisfaits, le code original de .NET-Frameworks GeoCoordinate
class, refactored en une méthode autonome:
public double GetDistance(double longitude, double latitude, double otherLongitude, double otherLatitude)
{
var d1 = latitude * (Math.PI / 180.0);
var num1 = longitude * (Math.PI / 180.0);
var d2 = otherLatitude * (Math.PI / 180.0);
var num2 = otherLongitude * (Math.PI / 180.0) - num1;
var d3 = Math.Pow(Math.Sin((d2 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d2) * Math.Pow(Math.Sin(num2 / 2.0), 2.0);
return 6376500.0 * (2.0 * Math.Atan2(Math.Sqrt(d3), Math.Sqrt(1.0 - d3)));
}
Basée sur la fonction d'Elliot Wood, cette fonction C fonctionne ...
#define SIM_Degree_to_Radian(x) ((float)x * 0.017453292F)
#define SIM_PI_VALUE (3.14159265359)
float GPS_Distance(float lat1, float lon1, float lat2, float lon2)
{
float theta;
float dist;
theta = lon1 - lon2;
lat1 = SIM_Degree_to_Radian(lat1);
lat2 = SIM_Degree_to_Radian(lat2);
theta = SIM_Degree_to_Radian(theta);
dist = (sin(lat1) * sin(lat2)) + (cos(lat1) * cos(lat2) * cos(theta));
dist = acos(dist);
// dist = dist * 180.0 / SIM_PI_VALUE;
// dist = dist * 60.0 * 1.1515;
// /* Convert to km */
// dist = dist * 1.609344;
dist *= 6370.693486F;
return (dist);
}
Vous pouvez le changer en double . Il retourne la valeur en km.
Calcul de la distance entre les points de latitude et de longitude ...
double Lat1 = Convert.ToDouble(latitude);
double Long1 = Convert.ToDouble(longitude);
double Lat2 = 30.678;
double Long2 = 45.786;
double circumference = 40000.0; // Earth's circumference at the equator in km
double distance = 0.0;
double latitude1Rad = DegreesToRadians(Lat1);
double latititude2Rad = DegreesToRadians(Lat2);
double longitude1Rad = DegreesToRadians(Long1);
double longitude2Rad = DegreesToRadians(Long2);
double logitudeDiff = Math.Abs(longitude1Rad - longitude2Rad);
if (logitudeDiff > Math.PI)
{
logitudeDiff = 2.0 * Math.PI - logitudeDiff;
}
double angleCalculation =
Math.Acos(
Math.Sin(latititude2Rad) * Math.Sin(latitude1Rad) +
Math.Cos(latititude2Rad) * Math.Cos(latitude1Rad) * Math.Cos(logitudeDiff));
distance = circumference * angleCalculation / (2.0 * Math.PI);
return distance;
Rayon moyen de la Terre = 6 371 km = 3958,76 milles
Plutôt que d'utiliser var
, je vous suggère d'utiliser double
, juste pour être explicite.
Vous pouvez utiliser cette fonction:
Source: https://www.geodatasource.com/developers/c-sharp
private double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
if ((lat1 == lat2) && (lon1 == lon2)) {
return 0;
}
else {
double theta = lon1 - lon2;
double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
dist = Math.Acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
if (unit == 'K') {
dist = dist * 1.609344;
} else if (unit == 'N') {
dist = dist * 0.8684;
}
return (dist);
}
}
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//:: This function converts decimal degrees to radians :::
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
private double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//:: This function converts radians to decimal degrees :::
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
private double rad2deg(double rad) {
return (rad / Math.PI * 180.0);
}
Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "M"));
Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "K"));
Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "N"));
Vous pouvez utiliser System.device.Location
:
System.device.Location.GeoCoordinate gc = new System.device.Location.GeoCoordinate(){
Latitude = yourLatitudePt1,
Longitude = yourLongitudePt1
};
System.device.Location.GeoCoordinate gc2 = new System.device.Location.GeoCoordinate(){
Latitude = yourLatitudePt2,
Longitude = yourLongitudePt2
};
Double distance = gc2.getDistanceTo(gc);
bonne chance
Essaye ça:
public double getDistance(GeoCoordinate p1, GeoCoordinate p2)
{
double d = p1.Latitude * 0.017453292519943295;
double num3 = p1.Longitude * 0.017453292519943295;
double num4 = p2.Latitude * 0.017453292519943295;
double num5 = p2.Longitude * 0.017453292519943295;
double num6 = num5 - num3;
double num7 = num4 - d;
double num8 = Math.Pow(Math.Sin(num7 / 2.0), 2.0) + ((Math.Cos(d) * Math.Cos(num4)) * Math.Pow(Math.Sin(num6 / 2.0), 2.0));
double num9 = 2.0 * Math.Atan2(Math.Sqrt(num8), Math.Sqrt(1.0 - num8));
return (6376500.0 * num9);
}
Il y a cette bibliothèque GeoCoordinate pour ces plateformes:
L'installation se fait via NuGet:
PM> Install-Package GeoCoordinate
Utilisation
GeoCoordinate pin1 = new GeoCoordinate(lat, lng);
GeoCoordinate pin2 = new GeoCoordinate(lat, lng);
double distanceBetween = pin1.GetDistanceTo(pin2);
La distance entre les deux coordonnées, en metres.