Calcul de la distance entre deux coordonnées géographiques de latitude et de longitude

139

Je calcule la distance entre deux GeoCoordinates. Je teste mon application avec 3-4 autres applications. Lorsque je calcule la distance, j'ai tendance à obtenir une moyenne de 3,3 miles pour mon calcul, tandis que d'autres applications obtiennent 3,5 miles. C'est une grande différence pour le calcul que j'essaie d'effectuer. 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? Dois-je d'abord le calculer en km, puis le convertir en miles?

Jason N. Gaylord
la source
1
Rayon moyen de la Terre = 6371 km = 3958,76 miles
Mitch Wheat
cela ne devrait-il pas être sur gis.stackexchange.com
Daniel Powell
Cela aurait pu, mais ma question concerne davantage le calcul sur un Windows Phone, ce qui est un peu différent. La formule est la même, mais les appels de méthode plus récents comme la méthode DistanceTo ne sont pas nécessairement disponibles.
Jason N.Gaylord
1
Suggère de stocker pi / 180 pour ne pas avoir à répéter le calcul.
Chris Caviness

Réponses:

313

La classe GeoCoordinate (.NET Framework 4 et supérieur) a déjà une GetDistanceTométhode.

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.

Nigel Sampson
la source
Nigel, êtes-vous sûr que la méthode DistanceTo fonctionnera sur le téléphone? Je pensais qu'il utilisait la version 2.0 de GeoCoordinate pour WP7.
Jason N.Gaylord
1
J'ai vérifié cela et le GeoCordinate pour l'appareil a une méthode GetDistanceTo qui est ce que vous aviez référencé (mais pas ce que vous avez ci-dessus). Pas grand-chose. Je vais tester cela pour voir si le calcul intégré est meilleur. Merci Nigel!
Jason N.Gaylord
1
Je pourrais poser une mauvaise question, mais dans quelle unité est le résultat? Est-ce des miles ou des kilomètres. Je ne peux le trouver nul part.
Saeed Neamati
3
@SaeedNeamati - recherchait cela aussi, selon msdn.microsoft.com/en-us/library / ... - c'est en mètres.
Andy Butland
Oui, GeoCoordinate.GetDistanceTo () renvoie la valeur en mètres. Pour moi, aux USA, s'il est inférieur à 1610, je le convertis en pieds (mètres * 3,28084) sinon je le convertis en miles (mètres * 0,000621371). La précision est plus que suffisante pour mes besoins.
user3235770 le
110

GetDistance est la meilleure solution , mais dans de nombreux cas, nous ne pouvons pas utiliser cette méthode (par exemple, application universelle)

  • Pseudocode de l'algorithme pour calculer la distance entre les coorindates:

    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;
    }
  • Implémentation Real World C # , qui utilise des méthodes d'extension

    Usage:

    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;
        }
    } 
David Leitner
la source
1
Pouvez-vous fournir la formule utilisée pour ce calcul ou peut-être quelques commentaires sur ce que fait la ligne? que devrais-je changer pour avoir directement la distance résultante en Km au lieu de miles sans avoir à convertir?
AlbertoFdzM
Merci pour une bonne solution, je peux maintenant l'utiliser dans mon application Desktop.
Jamshaid Kamran
Fonctionne très bien dans mon application UWP où je ne peux pas utiliser GeoCoordinate.
Zach Green
1
le calcul est vrai à 95%. la fonction ci-dessous est précise à 100%: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht
31

Et voici, pour ceux qui ne sont toujours pas satisfaits, le code original de la GeoCoordinateclasse .NET-Frameworks , refactorisé 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)));
}
Marc
la source
8
Belle réponse, je tiens à souligner que la distance résultante est en mètres. comme indiqué dans la documentation officielle
LeviathanCode
Merci! Je cherchais le rayon réel de la terre utilisé dans la classe GeoCoordinate.
KRoy le
Une optimisation mineure, ou pour une lecture plus facile, pourrait pré-calculer pi / 180 double oneDegree = Math.PI / 180.0;?
brakeroo le
1
@brakeroo Merci pour votre réponse. Je voudrais laisser la réponse telle quelle, car il s'agit du code .NET d'origine. N'importe qui peut se sentir libre de suivre votre suggestion, bien sûr.
Marc
17

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
}
Elliot Wood
la source
10

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);
        }
Justin
la source
3

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"));
Yanga
la source
Fonctionne parfaitement! Merci!
Schnapz
3

Il existe cette bibliothèque GeoCoordinate pour ces plates-formes:

  • Mono
  • .NET 4.5
  • .NET Core
  • Windows Phone 8.x
  • Plateforme Windows universelle
  • Xamarin iOS
  • Xamarin Android

L'installation se fait via NuGet:

PM> GeoCoordinate du package d'installation

Usage

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 mètres .

A. Morel
la source
3

Basé sur la fonction d'Elliot Wood, et si quelqu'un est intéressé par une fonction C, celle-ci 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 pour doubler . Il renvoie la valeur en km.

Santiago Villafuerte
la source
2

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;
Manish Sharma
la source
1

C'est une vieille question, néanmoins les réponses ne m'ont pas satisfait en termes de performances et d'optimisation.

Voici ma variante C # optimisée (distance en km, sans variables et calculs redondants, très proche de l'expression mathématique de Haversine Formular https://en.wikipedia.org/wiki/Haversine_formula ).

Inspiré par: https://rosettacode.org/wiki/Haversine_formula#C.23

public static class Haversine
{
    public static double Calculate(double lat1, double lon1, double lat2, double lon2)
    {
        double rad(double angle) => angle * 0.017453292519943295769236907684886127d; // = angle * Math.Pi / 180.0d
        double havf(double diff) => Math.Pow(Math.Sin(rad(diff) / 2d), 2); // = sin²(diff / 2)
        return 12745.6 * Math.Asin(Math.Sqrt(havf(lat2 - lat1) + Math.Cos(rad(lat1)) * Math.Cos(rad(lat2)) * havf(lon2 - lon1))); // earth radius 6.372,8‬km x 2 = 12745.6
    }
}

Haversine Formular de Wikipedia

JanW
la source
0

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);
    }
Hamzeh Soboh
la source
0

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

Danilo Garro
la source
0

Lorsque la puissance de calcul CPU / math est limitée:

Il y a des moments (comme dans mon travail) où la puissance de calcul est rare (par exemple pas de processeur à virgule flottante, travaillant avec de petits microcontrôleurs) où certaines fonctions trigonométriques peuvent prendre une quantité exorbitante de temps CPU (par exemple plus de 3000 cycles d'horloge), donc quand je seulement besoin d'une approximation, surtout si si le CPU ne doit pas être lié pendant une longue période, je l'utilise pour minimiser la surcharge du CPU:

/**------------------------------------------------------------------------
 * \brief  Great Circle distance approximation in km over short distances.
 *
 * Can be off by as much as 10%.
 *
 * approx_distance_in_mi = sqrt(x * x + y * y)
 *
 * where x = 69.1 * (lat2 - lat1)
 * and y = 69.1 * (lon2 - lon1) * cos(lat1/57.3)
 *//*----------------------------------------------------------------------*/
double    ApproximateDisatanceBetweenTwoLatLonsInKm(
                  double lat1, double lon1,
                  double lat2, double lon2
                  ) {
    double  ldRadians, ldCosR, x, y;

    ldRadians = (lat1 / 57.3) * 0.017453292519943295769236907684886;
    ldCosR = cos(ldRadians);
    x = 69.1 * (lat2 - lat1);
    y = 69.1 * (lon2 - lon1) * ldCosR;

    return sqrt(x * x + y * y) * 1.609344;  /* Converts mi to km. */
}

Le crédit va à https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt .

V. Wheeler
la source