Berechnung der Entfernung zwischen zwei Breiten- und Längengrad-Geokoordinaten


139

Ich berechne den Abstand zwischen zwei GeoCoordinates. Ich teste meine App gegen 3-4 andere Apps. Wenn ich die Entfernung berechne, erhalte ich für meine Berechnung in der Regel durchschnittlich 3,3 Meilen, während andere Apps 3,5 Meilen erhalten. Es ist ein großer Unterschied für die Berechnung, die ich durchführen möchte. Gibt es gute Klassenbibliotheken zur Berechnung der Entfernung? Ich berechne es so 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;
}

Was könnte ich falsch machen? Soll ich es zuerst in km berechnen und dann in Meilen umrechnen?


1
Mittlerer Erdradius = 6.371 km = 3958,76 Meilen
Mitch Wheat


sollte dies nicht auf gis.stackexchange.com sein
Daniel Powell

Es hätte sein können, aber meine Frage bezieht sich eher auf die Berechnung auf einem Windows Phone, das etwas anders ist. Die Formel ist dieselbe, aber neuere Methodenaufrufe wie die DistanceTo-Methode sind nicht unbedingt verfügbar.
Jason N. Gaylord

1
Schlagen Sie vor, pi / 180 zu speichern, damit Sie die Berechnung nicht ständig wiederholen müssen.
Chris Caviness

Antworten:


313

Die GeoCoordinate- Klasse (.NET Framework 4 und höher) verfügt bereits über eine GetDistanceToMethode.

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

return sCoord.GetDistanceTo(eCoord);

Die Entfernung ist in Metern.

Sie müssen auf System.Device verweisen.


Nigel, sind Sie sicher, dass die DistanceTo-Methode auf dem Telefon funktioniert? Ich dachte, es wird die 2.0-Version von GeoCoordinate für WP7 verwendet.
Jason N. Gaylord

1
Ich habe dies überprüft und der GeoCordinate für das Gerät verfügt über eine GetDistanceTo-Methode, auf die Sie verwiesen haben (aber nicht auf die oben genannten). Keine große Sache. Ich werde dies testen, um festzustellen, ob die eingebaute Berechnung besser ist. Danke Nigel!
Jason N. Gaylord

1
Ich könnte eine falsche Frage stellen, aber in welcher Einheit ist das Ergebnis? Ist es Meilen oder Kilometer. Ich kann es nirgendwo finden.
Saeed Neamati

3
@SaeedNeamati - hat laut msdn.microsoft.com/en-us/library/… auch danach gesucht - es ist in Metern.
Andy Butland

Ja, GeoCoordinate.GetDistanceTo () gibt den Wert in Metern zurück. Wenn es in den USA weniger als 1610 ist, rechne ich es in Fuß (Meter * 3,28084) um, andernfalls rechne ich es in Meilen (Meter * 0,000621371) um. Die Genauigkeit ist für meine Zwecke mehr als gut genug.
user3235770

110

GetDistance ist die beste Lösung , aber in vielen Fällen können wir diese Methode nicht verwenden (z. B. Universal App).

  • Pseudocode des Algorithmus zur Berechnung des Abstands zwischen den Koorindaten:

    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 # -Implementierung , bei der Erweiterungsmethoden verwendet werden

    Verwendung:

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

    Implementierung:

    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
Können Sie die für diesen Kalkül verwendete Formel oder einige Kommentare dazu angeben, welche Zeile funktioniert? Was müsste ich ändern, um die resultierende Entfernung in km anstelle von Meilen direkt zu erhalten, ohne konvertieren zu müssen?
AlbertoFdzM

Vielen Dank für eine gute Lösung, die ich jetzt in meiner Desktop-Anwendung verwenden kann.
Jamshaid Kamran

Funktionierte hervorragend in meiner UWP-App, in der ich GeoCoordinate nicht verwenden kann.
Zach Green

1
Berechnung ist 95% wahr. Die folgende Funktion ist 100% genau: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht

31

Und hier, für diejenigen, die noch nicht zufrieden sind, der ursprüngliche Code aus der .NET-Frameworks- GeoCoordinateKlasse, der in eine eigenständige Methode umgewandelt wurde:

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
Schöne Antwort, ich möchte darauf hinweisen, dass die resultierende Entfernung in Metern ist. wie in der offiziellen Dokumentation angegeben
LeviathanCode

Vielen Dank! Ich habe nach dem tatsächlichen Erdradius gesucht, der in der GeoCoordinate-Klasse verwendet wird.
KRoy

Eine geringfügige Optimierung oder zum leichteren Lesen könnte pi / 180 vorberechnen double oneDegree = Math.PI / 180.0;?
Brakeroo

1
@brakeroo Danke für deine Antwort. Ich möchte die Antwort so lassen, wie sie ist, da dies der ursprüngliche .NET-Code ist. Natürlich kann jeder Ihrem Vorschlag folgen.
Marc

17

Hier ist die JavaScript-Version Jungs und Mädels

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

Für diejenigen, die Xamarin verwenden und keinen Zugriff auf die GeoCoordinate-Klasse haben, können Sie stattdessen die Android Location-Klasse verwenden:

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

Sie können diese Funktion verwenden:

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

Funktioniert perfekt! Vielen Dank!
Schnapz

3

Es gibt diese Bibliothek GeoCoordinate für diese Plattformen:

  • Mono
  • .NET 4.5
  • .NET Core
  • Windows Phone 8.x.
  • Universelle Windows-Plattform
  • Xamarin iOS
  • Xamarin Android

Die Installation erfolgt über NuGet:

PM> Install-Package GeoCoordinate

Verwendung

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

double distanceBetween = pin1.GetDistanceTo(pin2);

Der Abstand zwischen den beiden Koordinaten in Metern .


3

Basierend auf Elliot Woods Funktion und wenn jemand an einer C-Funktion interessiert ist, funktioniert diese ...

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

Sie können es in double ändern . Es gibt den Wert in km zurück.


2

Berechnung der Entfernung zwischen Breiten- und Längengradpunkten ...

        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

Dies ist eine alte Frage, dennoch haben mich die Antworten hinsichtlich Leistung und Optimierung nicht befriedigt.

Hier meine optimierte C # -Variante (Entfernung in km, ohne Variablen und redundante Berechnungen, sehr nahe am mathematischen Ausdruck des Haversine Formulars https://en.wikipedia.org/wiki/Haversine_formula ).

Inspiriert von: 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 aus Wikipedia


0

Versuche dies:

    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

Sie können verwenden 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);

Viel Glück


0

Wenn die CPU / Mathe-Rechenleistung begrenzt ist:

Es gibt Zeiten (wie in meiner Arbeit), in denen die Rechenleistung knapp ist (z. B. kein Gleitkommaprozessor, der mit kleinen Mikrocontrollern arbeitet), in denen einige Triggerfunktionen eine exorbitante Menge an CPU-Zeit in Anspruch nehmen können (z. B. mehr als 3000 Taktzyklen) Ich brauche nur eine Annäherung, besonders wenn die CPU nicht lange gebunden sein muss. Ich verwende dies, um den CPU-Overhead zu minimieren:

/**------------------------------------------------------------------------
 * \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. */
}

Das Guthaben geht an https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.