İki enlem-boylam noktası arasındaki mesafeyi hesapla? (Haversine formülü)


905

Enlem ve boylam ile belirtilen iki nokta arasındaki mesafeyi nasıl hesaplayabilirim?

Açıklığa kavuşturmak için, kilometre cinsinden mesafeyi istiyorum; puanlar WGS84 sistemini kullanıyor ve mevcut yaklaşımların göreceli doğruluklarını anlamak istiyorum.


Daha iyi doğruluk için - bkz. Stackoverflow.com/questions/1420045/…
Lior Kogan

3
Haversine formülünü WGS 84 gibi bir elipsoid devrimine uygulayamayacağınızı unutmayın. Bu yöntemi yalnızca yarıçaplı bir küreye uygulayabilirsiniz.
Mike T

3
Buradaki cevapların çoğu basit küresel trigonometri kullanıyor, bu nedenle sonuçlar GPS sisteminde kullanılan WGS84 elipsoid mesafelerine kıyasla oldukça kaba. Cevapların bazıları Vincenty'nin elipsoid formülüne atıfta bulunuyor, ancak bu algoritma 1960'ların dönem masası hesap makinelerinde kullanılmak üzere tasarlandı ve kararlılık ve doğruluk sorunları var; şimdi daha iyi donanım ve yazılıma sahibiz. Çeşitli dillerde uygulamaları olan yüksek kaliteli bir kütüphane için lütfen GeographicLib'e bakınız .
PM 2Ring

@MikeT - doğru olsa da, buradaki cevapların çoğu küçük mesafelerde faydalı görünüyor : WGS 84'den enlem / boylam alır ve Haversine'i bir kürenin üzerinde noktalarmış gibi uygularsanız , hataları yalnızca belki de daha doğru bir formülün% 1'inde? Bunların küçük mesafeler olduğunu söyleyerek, tek bir kasabada söyleyin.
ToolmakerSteve

1
Bu plaka formları için: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Evrensel Windows Platformu / Xamarin iOS / Xamarin Android bkz. Stackoverflow.com/a/54296314/2736742
A. Morel

Yanıtlar:


1145

Mesafeyi hesaplamak için Haversine formülünün kullanımını detaylandırdığı için bu bağlantı size yardımcı olabilir .

Alıntı:

Bu komut dosyası [Javascript'te], 'Haversine' formülünü kullanarak iki nokta arasındaki (yani, dünya yüzeyi üzerindeki en kısa mesafe) büyük daire mesafelerini hesaplar.

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

51
Bu hesaplama / yöntem Dünya'nın bir küremsi olduğunu (mükemmel bir küre değil) açıklıyor mu? Orijinal soru WGS84 dünyasındaki noktalar arasındaki mesafeyi istedi. Mükemmel bir küre kullanarak ne kadar hata sürüntüğünden emin değilim, ancak noktaların dünyanın neresinde olduğuna bağlı olarak oldukça fazla olabileceğinden şüpheleniyorum, bu nedenle ayrım akılda tutulmaya değer.
redcalx

15
Haversine formülü, Dünya'nın bir sferoid olduğunu hesaba katmaz, bu nedenle bu gerçek nedeniyle bazı hatalar alırsınız. % 0,5'ten daha iyi doğru garanti edilemez. Bu kabul edilebilir bir hata seviyesi olabilir veya olmayabilir.
Brandon

24
Kullanmak için herhangi bir neden var mı Math.atan2(Math.sqrt(a), Math.sqrt(1-a))yerine Math.asin(Math.sqrt(h))formülü olduğunu Wikipedia makalesi kullandığı doğrudan uygulanması olacaktır? Daha verimli ve / veya sayısal olarak daha kararlı mı?
musiphil

16
@UsmanMutawakil Peki, aldığınız 38 mil yoldaki mesafedir. Bu algoritma dünya yüzeyinde düz bir çizgi mesafesi hesaplar. Google Haritalar'da aynı şeyi yapan bir mesafe aracı (sol altta, "Labs") vardır, bunu karşılaştırmak için kullanın.
Pascal

4
@ Forte_201092: Çünkü bu gerekli değil - (sin(x))²eşittir(sin(-x))²
Jean Hominal

359

Projem için noktalar arasında çok fazla mesafe hesaplamamız gerekiyordu, bu yüzden devam ettim ve kodu optimize etmeye çalıştım, burada buldum. Ortalama olarak farklı tarayıcılarda yeni uygulamam en çok oylanan cevaptan 2 kat daha hızlı çalışıyor .

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

Benim jsPerf ile oynamak ve sonuçları burada görebilirsiniz .

Son zamanlarda python da aynısını yapmam gerekiyordu, işte bir python uygulaması :

from math import cos, asin, sqrt, pi

def distance(lat1, lon1, lat2, lon2):
    p = pi/180
    a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

Ve bütünlük uğruna: Wiki'de Haversine .


13
@AngularM ve düz bir çizgi değil, bazı yollar alacaksanız google'ın mesafeyi hesaplaması büyük olasılıktır.
Salvador Dali

3
Google, sürüş mesafesini hesaplar, bu "karga uçar gibi" hesaplar
Hobbyist

4
@Ouadie ve hızı artıracak mı? Büyük olasılıkla hayır, ancak eski tarayıcılara kopyalayan insanlar için bir sürü 'öğeniz çalışmıyor'
Salvador Dali

4
iyi evet ama ne anlama geliyor // 2 * R; R = 6371 km? ve mevcut yöntem km veya mil olarak cevap verir? daha iyi belgelere ihtiyaç duyar. Teşekkürler
Khalil Khalaf

20
@KhalilKhalaf şaka mı yapıyorsun yoksa burada trol etmeye mi çalışıyorsun? km kilometredir. Sence R ne anlama geliyor (özellikle bir shpere hakkında konuşursak)? Km'yi zaten görürseniz cevabın hangi birimlerde olacağını tahmin edin. Burada ne tür belgeler arıyorsunuz: tam anlamıyla 4 satır var.
Salvador Dali

69

İşte bir C # Uygulaması:

static class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIUS = 6378.16;

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon = Radians(lon2 - lon1);
        double dlat = Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return angle * RADIUS;
    }

}

14
Ekvatoral yarıçapı kullanıyorsunuz, ancak 6371 km olan ortalama yarıçapı kullanmalısınız
Philippe Leybaert

7
Bu olmamalı double dlon = Radians(lon2 - lon1);vedouble dlat = Radians(lat2 - lat1);
Chris Marisic

Chris Marisic ile aynı fikirdeyim. Orijinal kodu kullandım ve hesaplamalar yanlıştı. Deltaları radyana dönüştürme çağrısını ekledim ve şimdi düzgün çalışıyor. Bir düzenleme gönderdim ve akran tarafından incelenmesini bekliyorum.
Bryan Bedard

Lat1 ve lat2'nin de radyana dönüştürülmesi gerektiğinden başka bir düzenleme daha gönderdim. Ayrıca burada bulunan formül ve kodla eşleşecek bir a atama formülünü de gözden geçirdim: movable-type.co.uk/scripts/latlong.html
Bryan Bedard

does RADIUSdiğer yanıtlar olarak 6371 olmak değeri gerekir?
Chris Hayes

66

İşte Haversine formülünün java uygulaması.

public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
  double venueLat, double venueLng) {

    double latDistance = Math.toRadians(userLat - venueLat);
    double lngDistance = Math.toRadians(userLng - venueLng);

    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
      + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
      * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}

Burada cevabı en yakın km'ye yuvarladığımızı unutmayın.


2
İki nokta arasındaki mesafeyi metre cinsinden hesaplamak isteseydik, daha doğru bir yol ne olurdu? 6371000Dünyanın yarıçapı olarak kullanmak için? (ortalama dünya yarıçapı 6371000 metredir) veya fonksiyonunuzdan kilometreyi metreye dönüştürür?
Mikro

mil istiyorsanız, sonucu birden fazla olarak0.621371
lasec0203

42

Tüm bunlar için çok teşekkürler. Objective-C iPhone uygulamamda aşağıdaki kodu kullandım:

const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km

double convertToRadians(double val) {

   return val * PIx / 180;
}

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

        double dlon = convertToRadians(place2.longitude - place1.longitude);
        double dlat = convertToRadians(place2.latitude - place1.latitude);

        double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
        double angle = 2 * asin(sqrt(a));

        return angle * RADIO;
}

Enlem ve Boylam ondalıktadır. Asin () çağrısı için min () kullanmadım, kullandığım mesafeler gerektirmediği kadar küçük.

Radyan cinsinden değerleri geçene kadar yanlış cevaplar verdi - şimdi Apple'ın Harita uygulamasından elde edilen değerlerle hemen hemen aynı :-)

Ekstra güncelleme:

İOS4 veya üstünü kullanıyorsanız, Apple bunu yapmak için bazı yöntemler sağlar, böylece aynı işlevsellik elde edilir:

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

    MKMapPoint  start, finish;


    start = MKMapPointForCoordinate(place1);
    finish = MKMapPointForCoordinate(place2);

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}


Bence parantez pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))yanlış. O çıkarın ve sonuç maçları bu sayfadaki diğer uygulamaları kullanırken ne elde veya gelen Haversine formülü uygulamak Wikipedia sıfırdan.
zanedp

NYC için koordinatları (40.7127837, -74.0059413) ve LA için (34.052234, -118.243685) koordinatlarını kullanarak ()toplamı 3869.75 elde ediyorum. Onlar olmadan, 3935.75 alıyorum, bu bir web aramasının ortaya çıktığı şey.
zanedp

40

Bu çok makul bir yaklaşım (+ / -% 1 hata payı altında) verecek basit bir PHP fonksiyonudur.

<?php
function distance($lat1, $lon1, $lat2, $lon2) {

    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lon1 *= $pi80;
    $lat2 *= $pi80;
    $lon2 *= $pi80;

    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlon = $lon2 - $lon1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;

    //echo '<br/>'.$km;
    return $km;
}
?>

Daha önce de belirtildiği gibi; dünya bir küre DEĞİLDİR. Mark McGwire'ın uygulamaya karar verdiği eski, eski bir beyzbol topu gibidir - ezik ve çarpmalarla doludur. Daha basit hesaplamalar (böyle) bir küre gibi davranır.

Bu düzensiz ovalde ve puanlarınızın ne kadar uzakta olduğuna göre farklı yöntemler az çok kesin olabilir (ne kadar yakınsa, mutlak hata payı o kadar küçüktür). Beklentiniz ne kadar kesin olursa, matematik de o kadar karmaşık olur.

Daha fazla bilgi için: wikipedia coğrafi mesafesi


4
Mükemmel çalışıyor! Az önce $ distance_miles = $ km * 0.621371 ekledim; ve mil olarak yaklaşık mesafe için ihtiyacım olan şey bu! Teşekkürler Tony.

31

Buraya çalışma örneğimi gönderiyorum.

Tablodaki tüm noktaları, MySQL'de (enlem ve boylam ile, 50 KM'den daha az, enlem ve boylam ile belirlenmiş bir nokta (rastgele bir nokta kullanıyoruz - lat: 45.20327, uzun: 23.7806):

Toplamı DISTANCE <50 olan Kilometre cinsinden (Dünya yarıçapı 6371 KM olarak kabul edilir):

SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta 
FROM obiective 
WHERE coord_lat<>'' 
    AND coord_long<>'' 
HAVING distanta<50 
ORDER BY distanta desc

Yukarıdaki örnek MySQL 5.0.95 ve 5.5.16'da (Linux) test edilmiştir.


İyi bir yaklaşımın sonuçları bir yakınlaştırma kullanarak önceden filtrelemek olabilir, bu nedenle ağır formül sadece bazı durumlarda uygulanır. Başka koşullarınız varsa özellikle yararlıdır. Bunu ilk aproks için kullanıyorum: stackoverflow.com/questions/1253499/…
Pato

28

Diğer cevaplarda ise kayıp.

İki nokta arasındaki mesafeyi hesaplamak, paketten distmgelen işlevle oldukça basittir geosphere:

distm(p1, p2, fun = distHaversine)

nerede:

p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid 

Dünya mükemmel bir şekilde küresel olmadığından, elipsoidler için Vincenty formülü muhtemelen mesafeleri hesaplamanın en iyi yoludur. Böylece geospherekullandığınız pakette:

distm(p1, p2, fun = distVincentyEllipsoid)

Tabii ki geospherepaketi kullanmak zorunda değilsiniz , ayrıca Rbir fonksiyonla tabandaki mesafeyi de hesaplayabilirsiniz :

hav.dist <- function(long1, lat1, long2, lat2) {
  R <- 6371
  diff.long <- (long2 - long1)
  diff.lat <- (lat2 - lat1)
  a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
  b <- 2 * asin(pmin(1, sqrt(a))) 
  d = R * b
  return(d)
}

Söylediklerinizin net olduğundan emin olmak için: Mesajın sonunda verdiğiniz kod: Bu Vincenty formülünün bir uygulaması mı? Bildiğiniz kadarıyla, jeosferde Vincenty'yi aramakla aynı cevabı vermeli mi? [Geosphere veya başka bir kütüphanem yok; sadece bir çapraz platform uygulamasına dahil etmek için bazı kodlar arıyorum. Elbette bilinen iyi bir hesap makinesine karşı bazı test durumlarını doğrularım.]
ToolmakerSteve

1
Cevabımın sonundaki fonksiyon Haversine yönteminin bir uygulaması
Jaap

Merhaba @Jaap formül için ölçü biriminin ne olduğunu sorabilir miyim? Metre olarak mı?
Jackson

11

Haversine kesinlikle çoğu durumda iyi bir formüldür, diğer cevaplar zaten içerir, bu yüzden yer almayacağım. Ancak, hangi formül kullanılırsa kullanılsın (evet sadece bir tane değil) not etmek önemlidir. Çok büyük doğruluk aralığı ve gerekli hesaplama süresi nedeniyle. Formül seçimi, basit bir beyinsiz cevaptan biraz daha fazla düşünmeyi gerektirir.

Nasa'daki bir kişiden gelen bu gönderi, seçenekleri tartışırken bulduğum en iyisi

http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

Örneğin, satırları yalnızca 100 mil yarıçapında mesafeye göre sıralıyorsanız. Düz toprak formülü haversinden çok daha hızlı olacaktır.

HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/

a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;

Sadece bir kosinüs ve bir kare kök olduğuna dikkat edin. Bunlardan 9'u Haversine formülünde.


Güzel bir olasılık. Tartışmada önerilen maksimum mesafenin 100 değil 12 mil olduğunu ve bu durumda bile hataların dünyanın konumuna bağlı olarak 30 metreye (100 ft) kadar çıkabileceğini unutmayın.
Eric Wu

7

Bunu hesaplamak için CLLocationDistance içindeki derlemeyi kullanabilirsiniz:

CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]

- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
    CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
    return distanceInMeters;
}

Sizin durumunuzda kilometre istiyorsanız 1000'e bölün.


7

Başka bir cevap eklemekten hoşlanmıyorum, ancak Google Maps API v.3'ün küresel geometrisi (ve daha fazlası) var. WGS84'ünüzü ondalık dereceye dönüştürdükten sonra bunu yapabilirsiniz:

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script>  

distance = google.maps.geometry.spherical.computeDistanceBetween(
    new google.maps.LatLng(fromLat, fromLng), 
    new google.maps.LatLng(toLat, toLng));

Google'ın hesaplamalarının ne kadar doğru olduğu ve hatta hangi modelin kullanıldığı hakkında hiçbir kelime ("jeoid" yerine "küresel" diyor olsa da) Bu arada, "düz çizgi" mesafesi, herkesin varsaydığı gibi görünen yüzey.


mesafe metre cinsindendir. alternatif olarak computeLength ()
electrobabe

7

Python implimentation Origin bitişik ABD'nin merkezidir.

from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)

Cevabı kilometre olarak almak için mil = yanlış ayarlamanız yeterli.


1
Tüm işi yapan standart dışı bir paketi içe aktarıyorsunuz. Bu kadar faydalı olup olmadığını bilmiyorum.
Teepeemm

Paket PyPI, Python Paket İndeksi'nde, numpy ve scikit-learn ile birlikte bir python 3 paketi olarak bulunmaktadır. Birinin neden paketlere uygulandığından emin değilim. Oldukça yararlı olma eğilimindedirler. Açık kaynak olarak, içerilen yöntemler de incelenebilir. Sanırım birçok kişi bu paketi faydalı bulacaktır, bu yüzden düşüşe rağmen yazıyı bırakacağım. Şerefe. :)
invoketheshell

7

Daha basit ve daha doğru bir çözüm olabilir: Dünyanın çevresi ekvatorda 40.000Km, Greenwich (veya herhangi bir boylam) döngüsünde yaklaşık 37.000. Böylece:

pythagoras = function (lat1, lon1, lat2, lon2) {
   function sqr(x) {return x * x;}
   function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);}

   var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0);
   var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0;
   var dy = 37000000.0 * (lat1 - lat2) / 360.0;

   return Math.sqrt(sqr(dx) + sqr(dy));
};

Ben bunun bir elipsoid olduğunu söyledim, bu yüzden kosinüs ile çarpılacak yarıçapın değiştiği gibi ince ayar yapılması gerektiğini kabul ediyorum. Ama bu biraz daha doğru. Google Haritalar ile karşılaştırıldığında hatayı önemli ölçüde azalttı.


Bu işlev dönüş mesafesi km olarak mı?
Wikki

Bunun nedeni ekvator ve boylam döngülerinin Km. Mil için 40000 ve 37000'i 1,6'ya bölün. Geeky hissederek, Ris'e dönüştürebilirsiniz, yaklaşık 7 ile çarparak veya parasang'a dönüşerek 2.2 ;-)
Meymann

Bu, burada sunulan en iyi cevap gibi görünüyor. Kullanmak istiyorum ama sadece bu algoritmanın doğruluğunu doğrulamanın bir yolu olup olmadığını merak ediyorum. F'yi (50,5,58,3) test ettim. 832km verirken, movable-type.co.uk/scripts/latlong.html 'haversine' formülünü kullanarak 899km verir. Böyle büyük bir fark var mı?
Chong Lip Phang

Ayrıca, yukarıdaki kod tarafından döndürülen değeri km değil, m olduğunu düşünüyorum.
Chong Lip Phang

@ChongLipPhang - DİKKAT: Pisagor teoremi, küçük alanlar için sadece makul bir yaklaşımdır , çünkü bu teorem dünyanın düz olduğunu varsayar. Aşırı bir durum olarak, ekvatordan başlayın ve 90 derece doğuya ve 90 derece kuzeye gidin. Tabii ki sonuç kuzey kutbudur ve 0 derece doğu ve 90 derece kuzeye hareket etmekle aynıdır; böylece sqrt (sqr (dx) + sqr (dy)) yapmak ilk durumda çılgınca kapalı olacaktır. ~ sqrt (10km sqr + 10km sqr) ~ = 14.4 km vs doğru mesafe ~ 10km.
ToolmakerSteve

7

Yukarıdaki tüm cevaplar dünyanın bir küre olduğunu varsayar. Bununla birlikte, daha doğru bir yaklaşım, oblate bir küreninki olacaktır.

a= 6378.137#equitorial radius in km
b= 6356.752#polar radius in km

def Distance(lat1, lons1, lat2, lons2):
    lat1=math.radians(lat1)
    lons1=math.radians(lons1)
    R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1
    x1=R*math.cos(lat1)*math.cos(lons1)
    y1=R*math.cos(lat1)*math.sin(lons1)
    z1=R*math.sin(lat1)

    lat2=math.radians(lat2)
    lons2=math.radians(lons2)
    R1=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2
    x2=R*math.cos(lat2)*math.cos(lons2)
    y2=R*math.cos(lat2)*math.sin(lons2)
    z2=R*math.sin(lat2)

    return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5

6

İşte km cinsinden mesafeyi hesaplamak için SQL Uygulaması,

SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * 
cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * 
sin( radians(latitude) ) ) ) AS distance FROM user HAVING
distance < 5  ORDER BY distance LIMIT 0 , 5;

Languages ​​programlama ile uygulama hakkında daha fazla bilgi için, sadece burada verilen php betiği geçebilirsiniz


5

İşte Haversine formülünün daktilo ile uygulanması

static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
    var deg2Rad = deg => {
        return deg * Math.PI / 180;
    }

    var r = 6371; // Radius of the earth in km
    var dLat = deg2Rad(lat2 - lat1);   
    var dLon = deg2Rad(lon2 - lon1);
    var a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = r * c; // Distance in km
    return d;
}

5

Belirtildiği gibi, doğru bir hesaplama, dünyanın mükemmel bir küre olmadığını dikkate almalıdır. Burada sunulan çeşitli algoritmaların bazı karşılaştırmaları:

geoDistance(50,5,58,3)
Haversine: 899 km
Maymenn: 833 km
Keerthana: 897 km
google.maps.geometry.spherical.computeDistanceBetween(): 900 km

geoDistance(50,5,-58,-3)
Haversine: 12030 km
Maymenn: 11135 km
Keerthana: 10310 km
google.maps.geometry.spherical.computeDistanceBetween(): 12044 km

geoDistance(.05,.005,.058,.003)
Haversine: 0.9169 km
Maymenn: 0.851723 km
Keerthana: 0.917964 km
google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km

geoDistance(.05,80,.058,80.3)
Haversine: 33.37 km
Maymenn: 33.34 km
Keerthana: 33.40767 km
google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km

Küçük mesafelerde Keerthana'nın algoritması Google Haritalar'ın algoritmasına denk geliyor. Google Haritalar herhangi bir basit algoritmayı takip etmiyor ve buradaki en doğru yöntem olabileceğini düşündürüyor.

Her neyse, işte Keerthana algoritmasının Javascript uygulaması:

function geoDistance(lat1, lng1, lat2, lng2){
    const a = 6378.137; // equitorial radius in km
    const b = 6356.752; // polar radius in km

    var sq = x => (x*x);
    var sqr = x => Math.sqrt(x);
    var cos = x => Math.cos(x);
    var sin = x => Math.sin(x);
    var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat))));

    lat1 = lat1 * Math.PI / 180;
    lng1 = lng1 * Math.PI / 180;
    lat2 = lat2 * Math.PI / 180;
    lng2 = lng2 * Math.PI / 180;

    var R1 = radius(lat1);
    var x1 = R1*cos(lat1)*cos(lng1);
    var y1 = R1*cos(lat1)*sin(lng1);
    var z1 = R1*sin(lat1);

    var R2 = radius(lat2);
    var x2 = R2*cos(lat2)*cos(lng2);
    var y2 = R2*cos(lat2)*sin(lng2);
    var z2 = R2*sin(lat2);

    return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2));
}

4

Bu komut dosyası [PHP'de] iki nokta arasındaki mesafeleri hesaplar.

public static function getDistanceOfTwoPoints($source, $dest, $unit='K') {
        $lat1 = $source[0];
        $lon1 = $source[1];
        $lat2 = $dest[0];
        $lon2 = $dest[1];

        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        $unit = strtoupper($unit);

        if ($unit == "K") {
            return ($miles * 1.609344);
        }
        else if ($unit == "M")
        {
            return ($miles * 1.609344 * 1000);
        }
        else if ($unit == "N") {
            return ($miles * 0.8684);
        } 
        else {
            return $miles;
        }
    }

4

Haversine formülüne göre Java uygulaması

double calculateDistance(double latPoint1, double lngPoint1, 
                         double latPoint2, double lngPoint2) {
    if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) {
        return 0d;
    }

    final double EARTH_RADIUS = 6371.0; //km value;

    //converting to radians
    latPoint1 = Math.toRadians(latPoint1);
    lngPoint1 = Math.toRadians(lngPoint1);
    latPoint2 = Math.toRadians(latPoint2);
    lngPoint2 = Math.toRadians(lngPoint2);

    double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) 
            + Math.cos(latPoint1) * Math.cos(latPoint2)
            * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2);
    distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance));

    return distance; //km value
}

3

Bir küredeki iki nokta arasındaki mesafeyi hesaplamak için Büyük Daire hesaplaması yapmanız gerekir .

Mesafelerinizi düz bir yüzeye yeniden yansıtmanız gerekirse , MapTools'ta harita projeksiyonuna yardımcı olacak bir dizi C / C ++ kütüphanesi vardır . Bunu yapmak için çeşitli koordinat sistemlerinin projeksiyon dizesine ihtiyacınız olacaktır.

MapWindow'u noktaları görselleştirmek için kullanışlı bir araç olarak da bulabilirsiniz . Ayrıca açık kaynak olarak, çekirdek açık kaynak projeksiyon kütüphanesi gibi görünen proj.dll kütüphanesinin nasıl kullanılacağı konusunda kullanışlı bir rehberdir.


3

İşte herkesin ihtiyacı olması durumunda Java'ya taşınan kabul edilen cevap uygulaması.

package com.project529.garage.util;


/**
 * Mean radius.
 */
private static double EARTH_RADIUS = 6371;

/**
 * Returns the distance between two sets of latitudes and longitudes in meters.
 * <p/>
 * Based from the following JavaScript SO answer:
 * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
 * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
 */
public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) {
    double dLat = toRadians(lat2 - lat1);
    double dLon = toRadians(lon2 - lon1);

    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
                    Math.sin(dLon / 2) * Math.sin(dLon / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double d = EARTH_RADIUS * c;

    return d;
}

public double toRadians(double degrees) {
    return degrees * (Math.PI / 180);
}

2

İşte uygulama VB.NET, bu uygulama size geçtiğiniz bir Enum değerine dayalı olarak KM veya Mil sonucunu verecektir.

Public Enum DistanceType
    Miles
    KiloMeters
End Enum

Public Structure Position
    Public Latitude As Double
    Public Longitude As Double
End Structure

Public Class Haversine

    Public Function Distance(Pos1 As Position,
                             Pos2 As Position,
                             DistType As DistanceType) As Double

        Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371)

        Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude)

        Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude)

        Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)

        Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)))

        Dim result As Double = R * c

        Return result

    End Function

    Private Function toRadian(val As Double) As Double

        Return (Math.PI / 180) * val

    End Function

End Class

"A" hesaplandığında, yanlışlıkla Math.Sin ( dLat ..) yazdınız mı?
Marco Ottina

2

Formülü basitleştirerek hesaplamayı yoğunlaştırdım.

İşte Ruby'de:

include Math
earth_radius_mi = 3959
radians = lambda { |deg| deg * PI / 180 }
coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } }

# from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) }
def haversine_distance(from, to)
  from, to = coord_radians[from], coord_radians[to]
  cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng])
  sines_product = sin(to[:lat]) * sin(from[:lat])
  return earth_radius_mi * acos(cosines_product + sines_product)
end

2
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; 
  var miles = d / 1.609344; 

if ( units == 'km' ) {  
return d; 
 } else {
return miles;
}}

Chuck'ın çözümü, kilometrelerce de geçerlidir.


2

İşte bazı arama sonra ondalık derece üzerinden hesaplama mesafesi için benim java uygulama. Km olarak dünyanın ortalama yarıçapını (wikipedia'dan) kullandım. Sonuç milleri istiyorsanız mil cinsinden dünya yarıçapını kullanın.

public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) 
{
  double earthRadius = 6371.0d; // KM: use mile here if you want mile result

  double dLat = toRadian(lat2 - lat1);
  double dLng = toRadian(lng2 - lng1);

  double a = Math.pow(Math.sin(dLat/2), 2)  + 
          Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * 
          Math.pow(Math.sin(dLng/2), 2);

  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

  return earthRadius * c; // returns result kilometers
}

public static double toRadian(double degrees) 
{
  return (degrees * Math.PI) / 180.0d;
}

2

Mysql'de aşağıdaki işlevi kullanarak parametreleri kullanarak geçirin POINT(LONG,LAT)

CREATE FUNCTION `distance`(a POINT, b POINT)
 RETURNS double
    DETERMINISTIC
BEGIN

RETURN

GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters

END;

2
function getDistanceFromLatLonInKm(position1, position2) {
    "use strict";
    var deg2rad = function (deg) { return deg * (Math.PI / 180); },
        R = 6371,
        dLat = deg2rad(position2.lat - position1.lat),
        dLng = deg2rad(position2.lng - position1.lng),
        a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
            + Math.cos(deg2rad(position1.lat))
            * Math.cos(deg2rad(position1.lat))
            * Math.sin(dLng / 2) * Math.sin(dLng / 2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
}

console.log(getDistanceFromLatLonInKm(
    {lat: 48.7931459, lng: 1.9483572},
    {lat: 48.827167, lng: 2.2459745}
));

2

İşte postgres sql bir örnek (km olarak, mil sürümü için, 1.609344 yerine 0.8684 sürümü)

CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat  

float, blng  float)
  RETURNS float AS
$BODY$
DECLARE
    v_distance float;
BEGIN

    v_distance = asin( sqrt(
            sin(radians(blat-alat)/2)^2 
                + (
                    (sin(radians(blng-alng)/2)^2) *
                    cos(radians(alat)) *
                    cos(radians(blat))
                )
          )
        ) * cast('7926.3352' as float) * cast('1.609344' as float) ;


    RETURN v_distance;
END 
$BODY$
language plpgsql VOLATILE SECURITY DEFINER;
alter function geodistance(alat float, alng float, blat float, blng float)
owner to postgres;

2

Ruby koduna dönüştürülen başka bir kod:

include Math
#Note: from/to = [lat, long]

def get_distance_in_km(from, to)
  radians = lambda { |deg| deg * Math.PI / 180 }
  radius = 6371 # Radius of the earth in kilometer
  dLat = radians[to[0]-from[0]]
  dLon = radians[to[1]-from[1]]

  cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2)

  c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product)) 
  return radius * c # Distance in kilometer
end

1

burada PHP ile mesafeyi hesaplamak için iyi bir örnek var http://www.geodatasource.com/developers/php :

 function distance($lat1, $lon1, $lat2, $lon2, $unit) {

     $theta = $lon1 - $lon2;
     $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
     $dist = acos($dist);
     $dist = rad2deg($dist);
     $miles = $dist * 60 * 1.1515;
     $unit = strtoupper($unit);

     if ($unit == "K") {
         return ($miles * 1.609344);
     } else if ($unit == "N") {
          return ($miles * 0.8684);
     } else {
          return $miles;
     }
 }
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.