İki Enlem ve Boylam Coğrafi Koordinatları Arasındaki Mesafeyi Hesaplama


139

İki GeoCoordinate arasındaki mesafeyi hesaplıyorum. Uygulamamı diğer 3-4 uygulamada test ediyorum. Mesafeyi hesaplarken, diğer uygulamalar 3.5 mil alırken, hesaplama için ortalama 3.3 mil alıyorum. Yapmaya çalıştığım hesaplama için büyük bir fark var. Mesafeyi hesaplamak için orada iyi sınıf kütüphaneleri var mı? Ben C # olarak böyle hesaplıyorum:

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

Neyi yanlış yapabilirdim? Önce km cinsinden hesaplayıp mil mi dönüştürmeliyim?


1
Dünya ortalama yarıçapı = 6,371km = 3958,76 mil
Mitch Wheat


bu gis.stackexchange.com üzerinde olmamalı
Daniel Powell

Olabilir, ama sorum biraz farklı bir Windows Phone hesaplamak için daha fazla ilgilidir. Formül aynıdır, ancak DistanceTo yöntemi gibi daha yeni yöntem çağrıları zorunlu olarak mevcut değildir.
Jason N. Gaylord

1
Hesaplamayı tekrarlamaya devam etmek zorunda kalmamanız için pi / 180 saklamanızı öneririz.
Chris Caviness

Yanıtlar:


313

Coğrafi koordinat sınıfı (.NET Framework 4 ve üstü) zaten GetDistanceToyöntemini.

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

return sCoord.GetDistanceTo(eCoord);

Mesafe metre cinsindendir.

System.Device'e başvurmanız gerekir.


Nigel, DistanceTo yönteminin telefonda çalışacağından emin misin? WP7 için GeoCoordinate'in 2.0 sürümünü kullandığını düşündüm.
Jason N. Gaylord

1
Bunu kontrol ettim ve cihaz için GeoCordinate, ne başvurmuştunuz (ama yukarıda ne var değil) olan bir GetDistanceTo yöntemi vardır. Önemli değil. Yerleşik hesaplamanın daha iyi olup olmadığını görmek için bunu test edeceğim. Teşekkürler Nigel!
Jason N. Gaylord

1
Yanlış bir soru sorabilirim, ama sonuç hangi birimde? Mil mi, yoksa Kilometre mi. Onu hiç bir yerde bulamıyorum.
Saeed Neamati

3
@SaeedNeamati - msdn.microsoft.com/en-us/library/… 'a göre bunu da arıyordu - metre cinsindendir .
Andy Butland

Evet, GeoCoordinate.GetDistanceTo () değeri metre cinsinden döndürür. Benim için, ABD'de, 1610'dan azsa, onu ayaklara (metre * 3.28084) dönüştürürüm, aksi takdirde millere (metre * 0.000621371) dönüştürürüm. Doğruluk, benim amacım için yeterince iyi.
user3235770

110

GetDistance en iyi çözümdür , ancak çoğu durumda bu Yöntemi kullanamayız (örneğin Evrensel Uygulama)

  • Yalancı kod Algoritma için mesafe hesaplamak coorindates arasındaki:

    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;
    }
  • Genişletme Yöntemlerinden yararlanan Real World C # Uygulaması

    Kullanımı:

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

    Uygulama:

    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
Bu analiz için kullanılan formülü sağlayabilir misiniz, ya da belki de hangi satırın yaptığı hakkında yorum yapabilirsiniz? dönüştürmek zorunda kalmadan kilometreler yerine Km cinsinden elde edilen mesafeye doğrudan sahip olmak için ne değiştirmeliyim?
AlbertoFdzM

İyi bir çözüm için teşekkürler, şimdi Masaüstü uygulamamda kullanabilirim.
Jamshaid Kamran

GeoCoordinate'i kullanamayacağım UWP uygulamamda harika çalıştı.
Zach Green

1
hesaplama% 95 doğrudur. Aşağıdaki işlev% 100 doğrudur: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht

31

Ve burada, hala tatmin olmayanlar için, .NET-Frameworks GeoCoordinatesınıfındaki orijinal kod, bağımsız bir yöntemle yeniden düzenlendi:

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
Güzel cevap, ortaya çıkan mesafenin metre cinsinden olduğunu belirtmek isterim. Resmi belgelerde
LeviathanCode

Teşekkürler! GeoCoordinate sınıfında kullanılan gerçek dünya yarıçapını arıyordum.
KRoy

Küçük optimizasyon veya daha kolay okuma için pi / 180'i önceden hesaplayabilir double oneDegree = Math.PI / 180.0;mi?
brakeroo

1
@brakeroo Yanıtınız için teşekkürler. Cevabı olduğu gibi bırakmak istiyorum, çünkü bu orijinal .NET kodu. Tabii ki herkes önerinizi takip etmekte özgür olabilir.
Marc

17

İşte JavaScript sürümü çocuklar ve kızlar

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

Xamarin kullanan ve GeoCoordinate sınıfına erişimi olmayanlar için bunun yerine Android Konum sınıfını kullanabilirsiniz:

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

Bu işlevi kullanabilirsiniz:

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

Mükemmel çalışıyor! Teşekkürler!
Schnapz

3

Bu platformlar için bu GeoCoordinate kütüphanesi var :

  • Mono
  • .NET 4.5
  • .NET Core
  • Windows Phone 8.x
  • Evrensel Windows Platformu
  • Xamarin iOS
  • Xamarin Android

Kurulum NuGet üzerinden yapılır:

PM> Kurulum Paketi GeoCoordinate

kullanım

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

double distanceBetween = pin1.GetDistanceTo(pin2);

İki koordinat arasındaki mesafe, metre olarak .


3

Elliot Wood'un işlevine dayanarak ve herhangi biri C işleviyle ilgileniyorsa, bu çalışıyor ...

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

Bunu iki katına değiştirebilirsiniz . Değeri km olarak döndürür.


2

Enlem ve Boylam noktaları arasındaki Mesafeyi Hesaplama ...

        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

Bu eski bir soru, yine de cevaplar performans ve optimizasyon konusunda beni tatmin etmedi.

Burada benim optimize edilmiş C # varyantım (km cinsinden uzaklık, değişkenler ve gereksiz hesaplamalar olmadan, Haversine Formular'ın matematik ifadesine çok yakın https://en.wikipedia.org/wiki/Haversine_formula ).

İlham kaynağı: 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
    }
}

Vikipedi Haversine Formular


0

Bunu dene:

    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

Şunları kullanabilirsiniz 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);

iyi şanslar


0

CPU / matematik bilgi işlem gücü sınırlı olduğunda:

Hesaplama gücünün az olduğu (örneğin, kayan noktalı işlemci, küçük mikrodenetleyicilerle çalışan) zaman zaman (işimde olduğu gibi), bazı trig işlevlerinin aşırı derecede CPU zamanı alabileceği (ör. 3000+ saat döngüsü) zamanlar vardır. özellikle bir CPU'ya uzun bir süre bağlı kalmaması gerekiyorsa, CPU ek yükünü en aza indirmek için bunu kullanın:

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

Kredi https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt adresine gidiyor .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.