Menghitung Jarak antara dua Koordinat Lintang dan Bujur

144

Saya sedang menghitung jarak antara dua GeoCoordinate. Saya menguji aplikasi saya terhadap 3-4 aplikasi lain. Saat saya menghitung jarak, saya cenderung mendapatkan rata-rata 3,3 mil untuk perhitungan saya sedangkan aplikasi lain mendapatkan 3,5 mil. Ini perbedaan besar untuk kalkulasi yang saya coba lakukan. Apakah ada perpustakaan kelas yang bagus di luar sana untuk menghitung jarak? Saya menghitungnya seperti ini di 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;
}

Apa yang bisa saya lakukan salah? Haruskah saya menghitungnya dalam km dulu, baru kemudian mengubahnya menjadi mil?

Jason N. Gaylord
sumber
1
Radius rata-rata bumi = 6.371km = 3958.76 mil
Mitch Wheat
seharusnya ini tidak ada di gis.stackexchange.com
Daniel Powell
Bisa saja, tetapi pertanyaan saya lebih berkaitan dengan menghitung ini di Windows Phone yang sedikit berbeda. Rumusnya sama, tetapi panggilan metode yang lebih baru seperti metode DistanceTo belum tentu tersedia.
Jason N.Gaylord
1
Sarankan Anda menyimpan pi / 180 sehingga Anda tidak perlu terus-menerus mengulangi perhitungan.
Chris Caviness

Jawaban:

321

Kelas GeoCoordinate (.NET Framework 4 dan lebih tinggi) sudah memiliki GetDistanceTometode.

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

return sCoord.GetDistanceTo(eCoord);

Jaraknya dalam meter.

Anda perlu mereferensikan System.Device.

Nigel Sampson
sumber
Nigel, apakah Anda yakin bahwa metode DistanceTo akan berfungsi di telepon? Saya pikir itu menggunakan GeoCoordinate versi 2.0 untuk WP7.
Jason N.Gaylord
1
Saya sudah memeriksa ini dan GeoCordinate untuk perangkat memiliki metode GetDistanceTo yang telah Anda referensikan (tetapi bukan yang Anda miliki di atas). Bukan masalah besar. Saya akan menguji ini untuk melihat apakah penghitungan bawaan lebih baik. Terima kasih Nigel!
Jason N.Gaylord
1
Saya mungkin akan menanyakan pertanyaan yang salah, tetapi dalam unit apa hasilnya? Apakah Miles, atau Kilometer. Saya tidak dapat menemukannya di mana pun.
Saeed Neamati
3
@SaeedNeamati - mencari ini juga, menurut msdn.microsoft.com/en-us/library/… - ini dalam meter.
Andy Butland
Ya, GeoCoordinate.GetDistanceTo () mengembalikan nilai dalam meter. Bagi saya, di AS, jika kurang dari 1610, saya mengubahnya menjadi kaki (meter * 3,28084) jika tidak saya ubah ke mil (meter * 0,000621371). Akurasi lebih dari cukup untuk tujuan saya.
buzzard51
115

GetDistance adalah solusi terbaik , tetapi dalam banyak kasus kami tidak dapat menggunakan Metode ini (mis. Aplikasi Universal)

  • Pseudocode dari Algoritma untuk menghitung jarak antara koordinat:

    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;
    }
    
  • Implementasi C # Dunia Nyata , yang menggunakan Metode Ekstensi

    Pemakaian:

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

    Penerapan:

    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
sumber
1
Dapatkah Anda memberikan rumus yang digunakan untuk kalkulus ini atau mungkin beberapa komentar tentang baris apa? apa yang harus saya ubah agar langsung mendapatkan jarak yang dihasilkan dalam Km alih-alih mil tanpa harus mengonversi?
AlbertoFdzM
Terima kasih atas solusi yang bagus, sekarang saya dapat menggunakannya di aplikasi Desktop saya.
Jamshaid Kamran
Bekerja dengan baik di aplikasi UWP saya di mana saya tidak dapat menggunakan GeoCoordinate.
Zach Green
1
Perhitungannya 95% benar. fungsi di bawah ini 100% akurat: stackoverflow.com/a/51839058/3736063
Malek Tubaisaht
44

Dan di sini, bagi mereka yang masih belum puas, kode asli dari GeoCoordinatekelas .NET-Frameworks , diubah menjadi metode mandiri:

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
sumber
10
Jawaban yang bagus, saya ingin menunjukkan bahwa jarak yang dihasilkan adalah dalam meter. sebagaimana dinyatakan dalam dokumentasi resmi
LeviathanCode
Terima kasih! Saya sedang mencari radius bumi sebenarnya yang digunakan di kelas GeoCoordinate.
KRoy
Pengoptimalan kecil, atau untuk pembacaan yang lebih mudah, dapatkah menghitung sebelumnya pi / 180 double oneDegree = Math.PI / 180.0;?
brakeroo
1
@brakeroo Terima kasih atas balasan Anda. Saya ingin membiarkan jawabannya apa adanya, karena ini adalah kode NET asli. Siapapun dapat merasa bebas untuk mengikuti saran Anda, tentu saja.
Marc
17

Ini adalah versi JavaScript guys and gals

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
sumber
10

Bagi mereka yang menggunakan Xamarin dan tidak memiliki akses ke kelas GeoCoordinate, Anda dapat menggunakan kelas Lokasi Android sebagai gantinya:

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
sumber
3

Anda dapat menggunakan fungsi ini:

Sumber: 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
sumber
Bekerja dengan sempurna! Terima kasih!
Schnapz
3

Ada pustaka GeoCoordinate untuk platform ini:

  • Mono
  • .NET 4.5
  • .NET Core
  • Windows Phone 8.x.
  • Platform Windows Universal
  • Xamarin iOS
  • Xamarin Android

Instalasi dilakukan melalui NuGet:

PM> Instal-Paket GeoCoordinate

Pemakaian

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

double distanceBetween = pin1.GetDistanceTo(pin2);

Jarak antara dua koordinat, dalam meter .

A. Morel
sumber
3

Berdasarkan fungsi Elliot Wood, dan jika ada yang tertarik dengan fungsi C, yang ini berfungsi ...

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

Anda dapat mengubahnya menjadi dua kali lipat . Ini mengembalikan nilai dalam km.

Santiago Villafuerte
sumber
2

Menghitung Jarak antara titik Lintang dan Bujur ...

        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
sumber
1

Ini adalah pertanyaan lama, namun jawabannya tidak memuaskan saya terkait kinerja dan pengoptimalan.

Di sini varian C # saya yang dioptimalkan (jarak dalam km, tanpa variabel dan perhitungan yang berlebihan, sangat dekat dengan ekspresi matematika dari Haversine Formular https://en.wikipedia.org/wiki/Haversine_formula ).

Terinspirasi oleh: 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 dari Wikipedia

JanW
sumber
0

Coba ini:

    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
sumber
0

Anda dapat menggunakan 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);

semoga berhasil

Danilo Garro
sumber
0

Ketika daya komputasi CPU / matematika terbatas:

Ada kalanya (seperti dalam pekerjaan saya) ketika daya komputasi langka (misalnya tidak ada prosesor floating point, bekerja dengan mikrokontroler kecil) di mana beberapa fungsi trigonometri dapat mengambil jumlah waktu CPU yang selangit (misalnya 3000+ siklus jam), jadi ketika saya hanya perlu perkiraan, terutama jika CPU tidak boleh diikat dalam waktu lama, saya menggunakan ini untuk meminimalkan overhead 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. */
}

Kredit diberikan ke https://github.com/kristianmandrup/geo_vectors/blob/master/Distance%20calc%20notes.txt .

V. Wheeler
sumber