Calcolo della distanza tra due coordinate geografiche di latitudine e longitudine


139

Sto calcolando la distanza tra due GeoCoordinate. Sto testando la mia app con altre 3-4 app. Quando sto calcolando la distanza, tendo a ottenere una media di 3,3 miglia per il mio calcolo, mentre altre app ottengono 3,5 miglia. È una grande differenza per il calcolo che sto cercando di eseguire. Esistono librerie di buone classi là fuori per il calcolo della distanza? Lo sto calcolando in questo modo in 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;
}

Cosa potrei fare di sbagliato? Devo prima calcolarlo in km e poi convertirlo in miglia?


1
Raggio medio terrestre = 6.371km = 3958.76 miglia
Mitch Wheat,


non dovrebbe essere su gis.stackexchange.com
Daniel Powell,

Potrebbe esserlo, ma la mia domanda riguarda di più il calcolo di questo su un Windows Phone che è un po 'diverso. La formula è la stessa, ma le chiamate di metodo più recenti come il metodo DistanceTo non sono necessariamente disponibili.
Jason N. Gaylord,

1
Suggerire di memorizzare pi / 180 in modo da non dover ripetere il calcolo.
Chris Caviness,

Risposte:


313

La classe GeoCoordinate (.NET Framework 4 e versioni successive) ha già un GetDistanceTometodo.

var sCoord = new GeoCoordinate(sLatitude, sLongitude);
var eCoord = new GeoCoordinate(eLatitude, eLongitude);

return sCoord.GetDistanceTo(eCoord);

La distanza è in metri.

Devi fare riferimento a System.Device.


Nigel, sei sicuro che il metodo DistanceTo funzionerà al telefono? Ho pensato che usasse la versione 2.0 di GeoCoordinate per WP7.
Jason N. Gaylord,

1
Ho controllato questo e GeoCordinate per il dispositivo ha un metodo GetDistanceTo che è ciò a cui hai fatto riferimento (ma non quello che hai sopra). Nessun grosso problema. Lo testerò per vedere se il calcolo integrato è migliore. Grazie Nigel!
Jason N. Gaylord,

1
Potrei fare una domanda sbagliata, ma in quale unità è il risultato? Sono miglia o chilometri. Non riesco a trovarlo da nessuna parte.
Saeed Neamati,

3
@SaeedNeamati - cercavo anche questo, secondo msdn.microsoft.com/en-us/library/… - è in metri.
Andy Butland,

Sì, GeoCoordinate.GetDistanceTo () restituisce il valore in metri. Per me, negli Stati Uniti, se è inferiore a 1610, lo converto in piedi (metri * 3.28084), altrimenti converto in miglia (metri * 0.000621371). La precisione è più che sufficiente per i miei scopi.
user3235770

110

GetDistance è la soluzione migliore , ma in molti casi non è possibile utilizzare questo metodo (ad es. Universal App)

  • Pseudocodice dell'algoritmo per calcolare la distanza tra coorindora:

    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 , che utilizza metodi di estensione

    Uso:

    var distance = new Coordinates(48.672309, 15.695585)
                    .DistanceTo(
                        new Coordinates(48.237867, 16.389477),
                        UnitOfLength.Kilometers
                    );

    Implementazione:

    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;
        }
    } 

1
Puoi fornire la formula utilizzata per questo calcolo o forse alcuni commenti su quale riga fa? cosa dovrei cambiare per avere direttamente la distanza risultante in Km invece di miglia senza dover convertire?
AlbertoFdzM,

Grazie per una buona soluzione, ora posso usarlo nella mia applicazione desktop.
Jamshaid Kamran

Ha funzionato alla grande nella mia app UWP dove non posso usare GeoCoordinate.
Zach Green

1
il calcolo è vero al 95%. la funzione di seguito è accurato al 100%: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht

31

E qui, per coloro che non sono ancora soddisfatti, il codice originale della GeoCoordinateclasse .NET-Frameworks , trasformato in un metodo autonomo:

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)));
}

8
Bella risposta, vorrei sottolineare che la distanza risultante è in metri. come dichiarato nella documentazione ufficiale
LeviathanCode

Grazie! Stavo cercando il raggio di terra effettivo utilizzato nella classe GeoCoordinate.
KRoy

L'ottimizzazione minore o per una lettura più semplice potrebbe pre-calcolare pi / 180 double oneDegree = Math.PI / 180.0;?
brakeroo,

1
@brakeroo Grazie per la risposta. Vorrei lasciare la risposta così com'è, perché questo è il codice .NET originale. Ovviamente, chiunque può sentirsi libero di seguire il tuo suggerimento.
Marc

17

Ecco la versione JavaScript ragazzi e ragazze

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
}

10

Per coloro che utilizzano Xamarin e non hanno accesso alla classe GeoCoordinate, puoi invece utilizzare la classe Posizione Android:

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);
        }

3

Puoi usare questa funzione:

Fonte: 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"));

Funziona perfettamente! Grazie!
Schnapz,

3

C'è questa libreria GeoCoordinate per queste piattaforme:

  • Mono
  • .NET 4.5
  • .NET Core
  • Windows Phone 8.x
  • Piattaforma universale di Windows
  • Xamarin iOS
  • Xamarin Android

L'installazione avviene tramite NuGet:

PM> GeoCoordinate pacchetto di installazione

uso

GeoCoordinate pin1 = new GeoCoordinate(lat, lng);
GeoCoordinate pin2 = new GeoCoordinate(lat, lng);

double distanceBetween = pin1.GetDistanceTo(pin2);

La distanza tra le due coordinate, in metri .


3

Basato sulla funzione di Elliot Wood, e se qualcuno è interessato a una funzione C, questa funziona ...

#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);
}

Puoi cambiarlo in doppio . Restituisce il valore in km.


2

Calcolo della distanza tra i punti di latitudine e longitudine ...

        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;

1

Questa è una vecchia domanda, tuttavia le risposte non mi hanno soddisfatto per quanto riguarda le prestazioni e l'ottimizzazione.

Ecco la mia variante C # ottimizzata (distanza in km, senza variabili e calcoli ridondanti, molto vicina all'espressione matematica di Haversine Formular https://en.wikipedia.org/wiki/Haversine_formula ).

Ispirato da: 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 da Wikipedia


0

Prova questo:

    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);
    }

0

Puoi usare 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);

in bocca al lupo


0

Quando la potenza di calcolo della CPU / matematica è limitata:

Ci sono momenti (come nel mio lavoro) in cui la potenza di calcolo è scarsa (ad es. Nessun processore a virgola mobile, lavorando con piccoli microcontrollori) in cui alcune funzioni di trigger possono richiedere una quantità esorbitante di tempo della CPU (ad esempio 3000+ cicli di clock), quindi quando ho solo bisogno di un'approssimazione, specialmente se se la CPU non deve essere legata per molto tempo, la uso per minimizzare il sovraccarico della 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. */
}

Il credito va a https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.