Oblicz odległość między dwoma punktami szerokość-długość geograficzna? (Formuła Haversine)

905

Jak obliczyć odległość między dwoma punktami określonymi przez szerokość i długość geograficzną?

Dla wyjaśnienia chciałbym odległość w kilometrach; punkty wykorzystują system WGS84 i chciałbym zrozumieć względne dokładności dostępnych podejść.

Robin Minto
źródło
Dla większej dokładności - patrz stackoverflow.com/questions/1420045/…
Lior Kogan
3
Zauważ, że nie możesz zastosować formuły Haversine'a do elipsoidy obrotu, takiej jak WGS 84. Możesz zastosować tę metodę tylko na kuli o promieniu.
Mike T
3
Większość odpowiedzi tutaj wykorzystuje prostą sferyczną trygonometrię, więc wyniki są raczej przybliżone w porównaniu z odległościami elipsoidy WGS84 stosowanymi w systemie GPS. Niektóre odpowiedzi odnoszą się do wzoru Vincenta na elipsoidy, ale algorytm ten został zaprojektowany do użycia w kalkulatorach biurkowych z lat 60. XX wieku i ma problemy ze stabilnością i dokładnością; mamy teraz lepszy sprzęt i oprogramowanie. Proszę zobaczyć GeographicLib dla wysokiej jakości biblioteki z wdrożeń w różnych językach.
PM
@MikeT - prawda, choć wiele odpowiedzi tutaj wydaje się użytecznych na małych odległościach : jeśli weźmiesz W / W 84 na długość / szerokość i zastosujesz Haversine tak, jakby to były punkty na kuli, nie otrzymujesz odpowiedzi, których błędy są spowodowane tylko współczynnik spłaszczenia ziemi, więc może w granicach 1% dokładniejszej formuły? Z zastrzeżeniem, że są to niewielkie odległości, powiedzmy w obrębie jednego miasta.
ToolmakerSteve,
1
Dla tych form płyt: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Universal Windows Platform / Xamarin iOS / Xamarin Android patrz stackoverflow.com/a/54296314/2736742
A. Morel

Odpowiedzi:

1145

Ten link może być dla Ciebie pomocny, ponieważ zawiera szczegółowe informacje na temat użycia formuły Haversine do obliczania odległości.

Fragment:

Ten skrypt [w Javascripcie] oblicza odległości wielkiego koła między dwoma punktami - czyli najkrótszą odległością nad powierzchnią ziemi - przy użyciu wzoru „Haversine”.

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)
}
Głaskanie pod brodę
źródło
51
Czy te obliczenia / metody uwzględniają fakt, że Ziemia jest sferoidą (nie idealną kulą)? Pierwotne pytanie dotyczyło odległości między punktami na kuli ziemskiej WGS84. Nie jestem pewien, jak dużo błędów wkrada się przy użyciu idealnej kuli, ale podejrzewam, że może to być całkiem sporo w zależności od tego, gdzie znajdują się punkty na kuli ziemskiej, dlatego warto pamiętać o tym rozróżnieniu.
redcalx,
15
Formuła Haversine nie uwzględnia faktu, że Ziemia jest sferoidą, więc z tego powodu pojawi się błąd. Nie można zagwarantować poprawności do wartości większej niż 0,5%. To może, ale nie musi być akceptowalny poziom błędu.
Brandon
24
Czy istnieje jakikolwiek powód, aby użyć Math.atan2(Math.sqrt(a), Math.sqrt(1-a))zamiast tego Math.asin(Math.sqrt(h)), która byłaby bezpośrednią implementacją formuły, której używa artykuł z Wikipedii? Czy jest bardziej wydajny i / lub bardziej stabilny numerycznie?
musiphil
16
@UsmanMutawakil Cóż, 38 mil, które dostajesz, to odległość na drodze. Algorytm ten oblicza odległość linii prostej na powierzchni ziemi. Mapy Google mają narzędzie do pomiaru odległości (lewy dolny „Labs”), które robi to samo, użyj go do porównania.
Pascal
4
@ Forte_201092: Ponieważ nie jest to konieczne - jak (sin(x))²równa(sin(-x))²
Jean Hominal
359

Musiałem obliczyć wiele odległości między punktami dla mojego projektu, więc poszedłem dalej i spróbowałem zoptymalizować kod, który znalazłem tutaj. Przeciętnie w różnych przeglądarkach moja nowa implementacja działa 2 razy szybciej niż najbardziej pozytywna odpowiedź.

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
}

Możesz grać z moim jsPerf i zobaczyć wyniki tutaj .

Ostatnio musiałem zrobić to samo w Pythonie, więc oto implementacja Pythona :

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...

I dla kompletności: Haversine na wiki.

Salvador Dali
źródło
13
@AngularM i istnieje duże prawdopodobieństwo, że Google obliczy odległość, jeśli będziesz jechał niektórymi drogami, a nie linią prostą.
Salvador Dali
3
Google oblicza odległość jazdy, to oblicza „w linii prostej”
hobbysta
4
@Ouadie i czy poprawi prędkość? Najprawdopodobniej nie, ale skończę z dużą ilością „twoich rzeczy nie działa” dla ludzi, którzy kopiują to w starych przeglądarkach
Salvador Dali
4
no tak, ale co to // 2 * R; R = 6371 kmoznacza? a obecna metoda zapewnia odpowiedź w km lub milach? potrzebuje lepszej dokumentacji. Dzięki
Khalil Khalaf
20
@KhalilKhalaf żartujesz lub próbujesz tu trollować? km oznacza kilometry. Jak myślisz, co oznacza R (zwłaszcza jeśli mówimy o shpere)? Zgadnij, w jakich jednostkach będzie odpowiedź, jeśli już widzisz km. Jakiej dokumentacji szukasz tutaj: są tam dosłownie 4 wiersze.
Salvador Dali
69

Oto implementacja C #:

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

}
jaircazarin-old-account
źródło
14
Korzystasz z promienia równikowego, ale powinieneś używać średniego promienia, który wynosi 6371 km
Philippe Leybaert
7
Nie powinno być double dlon = Radians(lon2 - lon1);idouble dlat = Radians(lat2 - lat1);
Chris Marisic
Zgadzam się z Chrisem Marisiciem. Użyłem oryginalnego kodu i obliczenia były nieprawidłowe. Dodałem wezwanie do konwersji delt na radiany i teraz działa poprawnie. Przesłałem edycję i czekam na sprawdzenie.
Bryan Bedard,
Przesłałem inną zmianę, ponieważ lat1 i lat2 również muszą zostać przekonwertowane na radiany. Zmieniłem
Bryan Bedard
czy RADIUSwartość musi wynosić 6371, jak w innych odpowiedziach?
Chris Hayes,
66

Oto implementacja formuły Haversine w Javie.

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

Zauważ, że tutaj zaokrąglamy odpowiedź do najbliższego kilometra.

Whostolebenfrog
źródło
2
Gdybyśmy chcieli obliczyć odległość między dwoma punktami w metrach, jaki byłby bardziej dokładny sposób? Używać 6371000jako promienia ziemi? (średni promień ziemi wynosi 6371000 metrów) lub przeliczasz kilometry na metry z Twojej funkcji?
Micro
jeśli chcesz mile, 0.621371
pomnóż
42

Dziękuję bardzo za wszystko. Użyłem następującego kodu w mojej aplikacji na iPhone'a Objective-C:

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

Szerokość i długość geograficzna są w systemie dziesiętnym. Nie użyłem min () do wywołania asin (), ponieważ odległości, których używam, są tak małe, że nie wymagają tego.

Dał niepoprawne odpowiedzi, dopóki nie podałem wartości w radianach - teraz jest prawie taki sam jak wartości uzyskane z aplikacji Apple's Map :-)

Dodatkowa aktualizacja:

Jeśli korzystasz z iOS4 lub nowszego, Apple udostępnia kilka metod, aby to zrobić, aby uzyskać tę samą funkcjonalność:

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

    MKMapPoint  start, finish;


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

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}
Stephen Watson
źródło
1
iOS SDK ma własną implementację: developer.apple.com/library/ios/documentation/CoreLocation/… :
tuler
Myślę, że nawias wokół pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))jest niepoprawny. Usuń je, a wynik będzie zgodny z tym, co otrzymam, gdy użyję innych implementacji na tej stronie lub od podstaw zastosuję formułę Haversine z Wikipedii .
zanedp
Używając współrzędnych (40,7127837, -74.0059413) dla NYC i (34.052234, -118,243685) dla LA, z ()tą sumą, otrzymuję 3869.75. Bez nich dostaję 3935,75, czyli prawie to, co pojawia się w wyszukiwarce.
zanedp
40

Jest to prosta funkcja PHP, która da bardzo rozsądne przybliżenie (poniżej +/- 1% marginesu błędu).

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

Jak powiedziano wcześniej; ziemia NIE jest kulą. To jak stary, stary baseball, z którym Mark McGwire postanowił ćwiczyć - jest pełen wgnieceń i nierówności. Prostsze obliczenia (takie jak to) traktują to jak kulę.

Różne metody mogą być mniej lub bardziej precyzyjne w zależności od tego, gdzie znajdujesz się na tym nieregularnym owalnym ORAZ i jak daleko od siebie są twoje punkty (im bliżej, tym mniejszy margines błędu bezwzględnego). Im bardziej precyzyjne są twoje oczekiwania, tym bardziej złożona jest matematyka.

Aby uzyskać więcej informacji: wikipedia odległość geograficzna

Tony Gil
źródło
4
To działa idealnie! Właśnie dodałem $ distance_miles = $ km * 0,621371; i to wszystko, czego potrzebowałem do przybliżonej odległości w milach! Dzięki Tony.
31

Zamieszczam tutaj mój przykład działania.

Wymień wszystkie punkty w tabeli posiadające odległość między wyznaczonym punktem (używamy losowego punktu - lat: 45,20327, długi: 23,7806) mniej niż 50 KM, z szerokością i długością geograficzną, w MySQL (pola tabeli to: współrzędna_lokalna i współrzędna_długa):

Wymień wszystkie posiadające ODLEGŁOŚĆ <50, w Kilometrach (rozpatrywany promień Ziemi 6371 KM):

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

Powyższy przykład został przetestowany w MySQL 5.0.95 i 5.5.16 (Linux).

conualfy
źródło
Myślę, że dobrym rozwiązaniem może być wstępne filtrowanie wyników za pomocą aproksymacji, więc ciężka formuła jest stosowana tylko w niektórych przypadkach. Szczególnie przydatne, jeśli masz inne warunki. Używam tego do początkowej aprox: stackoverflow.com/questions/1253499/...
Pato
28

W pozostałych odpowiedziach implementacja w brakuje.

Obliczanie odległości między dwoma punktami jest dość proste dzięki distmfunkcji z geospherepakietu:

distm(p1, p2, fun = distHaversine)

gdzie:

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

Ponieważ Ziemia nie jest idealnie kulista, formuła Vincenta dla elipsoidów jest prawdopodobnie najlepszym sposobem obliczania odległości. Zatem w geospherepakiecie, którego używasz, to:

distm(p1, p2, fun = distVincentyEllipsoid)

Oczywiście nie musisz koniecznie używać geospherepakietu, możesz również obliczyć odległość w bazie Rza pomocą funkcji:

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)
}
Jaap
źródło
Aby upewnić się, że mam jasność co do tego, co powiedziałeś: Kod podany na końcu postu: Czy to implementacja formuły Vincenty? O ile wiesz, powinna dać tę samą odpowiedź, co nazywanie Vincentego w geosferze? [Nie mam geosfery ani innej biblioteki; po prostu szukam kodu do dołączenia do aplikacji międzyplatformowej. Oczywiście sprawdziłbym niektóre przypadki testowe na podstawie znanego dobrego kalkulatora.]
ToolmakerSteve
1
@ToolmakerSteve funkcją na końcu mojej odpowiedzi jest implementacja metody Haversine
Jaap
Cześć @Jaap, czy mogę zapytać, jaka jest jednostka miary dla formuły? Czy to w metrach?
Jackson,
11

Herweryna jest zdecydowanie dobrą formułą dla prawdopodobnie większości przypadków, inne odpowiedzi już ją zawierają, więc nie zamierzam zajmować miejsca. Należy jednak pamiętać, że bez względu na zastosowaną formułę (tak, nie tylko jedną). Ze względu na ogromny możliwy zakres dokładności, a także wymagany czas obliczeń. Wybór formuły wymaga nieco więcej przemyślenia niż prostej odpowiedzi bez zastanowienia.

Ten post od osoby z nasa jest najlepszy, jaki znalazłem podczas omawiania opcji

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

Na przykład, jeśli sortujesz wiersze według odległości w promieniu 100 mil. Formuła płaskiej ziemi będzie znacznie szybsza niż haverine.

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;

Zauważ, że jest tylko jeden cosinus i jeden pierwiastek kwadratowy. Wersety 9 z nich dotyczące formuły Haversine.

Arturo Hernandez
źródło
To niezła możliwość. Pamiętaj tylko, że zalecana maksymalna odległość w dyskusji to 12 mil, a nie 100 , a mimo to błędy mogą pełzać nawet do 30 metrów (100 stóp), w zależności od położenia globu.
Eric Wu,
7

Możesz użyć kompilacji w CLLocationDistance do obliczenia tego:

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

W twoim przypadku, jeśli chcesz kilometry po prostu podziel przez 1000.

Andre Cytryn
źródło
7

Nie lubię dodawać kolejnej odpowiedzi, ale interfejs API Google Maps v.3 ma geometrię sferyczną (i więcej). Po konwersji WGS84 na stopnie dziesiętne możesz to zrobić:

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

Żadnego słowa o tym, jak dokładne są obliczenia Google, a nawet o tym, jaki model jest używany (choć mówi raczej „sferyczny” niż „geoidowy”. Nawiasem mówiąc, odległość „linii prostej” będzie oczywiście różna od odległości, jeśli podróżujemy powierzchnia ziemi, którą wszyscy przypuszczają.

Steven Christenson
źródło
odległość jest w metrach. Alternatywnie można stosować computeLength ()
electrobabe
7

Implikacja Pythona Origin jest centrum przyległych Stanów Zjednoczonych.

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

Aby uzyskać odpowiedź w kilometrach, po prostu ustaw mile = fałsz.

invoketheshell
źródło
1
Importujesz niestandardowy pakiet, który wykonuje całą pracę. Nie wiem czy to wszystko jest przydatne.
Teepeemm,
Pakiet znajduje się w PyPI, Python Package Index, jako pakiet Python 3 wraz z numpy i scikit-learn. Nie jestem pewien, dlaczego stosuje się go do pakietów. Zwykle są bardzo przydatne. Jako open source można również zbadać zawarte w nim metody. Myślę, że wielu uznałoby ten pakiet za przydatny, więc zostawię ten post pomimo głosowania negatywnego. Twoje zdrowie. :)
invoketheshell
7

Może być prostsze i bardziej poprawne rozwiązanie: obwód Ziemi wynosi 40 000 km na równiku, około 37 000 w cyklu Greenwich (lub dowolnej długości geograficznej). A zatem:

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

Zgadzam się, że powinien on być dostrojony, ponieważ sam powiedziałem, że jest to elipsoida, więc promień, który należy pomnożyć przez cosinus, jest różny. Ale to jest trochę dokładniejsze. W porównaniu z Mapami Google znacznie zmniejszyło błąd.

Meymann
źródło
Czy ta funkcja zwraca odległość w km?
Wikki
Jest tak tylko dlatego, że cykle równika i długości geograficznej są w km. W przypadku mil po prostu podziel 40000 i 37000 przez 1,6. Czując się naukowo, możesz przekonwertować go na Ris, pomnożony przez około 7 lub na parasang, dzieląc przez 2,2 ;-)
Meymann,
To wydaje się być najlepszą odpowiedzią na to pytanie. Chciałbym go użyć, ale zastanawiam się tylko, czy istnieje sposób na sprawdzenie poprawności tego algorytmu. Testowałem f (50,5,58,3). Daje 832 km, podczas gdy movable-type.co.uk/scripts/latlong.html przy użyciu formuły „haversine” daje 899 km. Czy jest taka duża różnica?
Chong Lip Phang,
Co więcej, myślę, że wartość zwrócona przez powyższy kod jest wm, a nie w km.
Chong Lip Phang,
@ChongLipPhang - UWAGA: Twierdzenie Pitagorasa jest rozsądnym przybliżeniem tylko dla małych obszarów , ponieważ zakłada ono, że ziemia jest płaska. W skrajnym przypadku zacznij od równika i przesuń się o 90 stopni na wschód i 90 stopni na północ. Końcowym rezultatem jest oczywiście biegun północny i jest taki sam, jak przesunięcie 0 stopni na wschód i 90 stopni na północ; więc zrobienie sqrt (sqr (dx) + sqr (dy)) będzie w pierwszym przypadku szalone. ~ sqrt (10 km sqr + 10 km sqr) ~ = 14,4 km vs właściwa odległość ~ 10 km.
ToolmakerSteve
7

Wszystkie powyższe odpowiedzi zakładają, że Ziemia jest kulą. Jednak dokładniejszym przybliżeniem byłoby przybliżenie sferoidy spłaszczonej.

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
Keerthana Gopalakrishnan
źródło
6

Oto implementacja SQL do obliczania odległości w km,

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;

Aby uzyskać więcej informacji na temat implementacji poprzez programowanie języka, możesz po prostu przejść przez skrypt php podany tutaj

Kiran Maniya
źródło
5

Oto implementacja maszynopisu formuły Haversine

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;
}
Sel
źródło
5

Jak wskazano, dokładne obliczenia powinny uwzględniać, że ziemia nie jest idealną kulą. Oto kilka porównań różnych algorytmów tutaj oferowanych:

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

Na małych odległościach algorytm Keerthany wydaje się pokrywać z algorytmem Google Maps. Mapy Google nie wydają się stosować żadnego prostego algorytmu, co sugeruje, że może to być najdokładniejsza metoda tutaj.

Tak czy inaczej, oto implementacja JavaScript algorytmu Keerthana:

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));
}
Chong Lip Phang
źródło
4

Ten skrypt [w PHP] oblicza odległości między dwoma punktami.

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;
        }
    }
Er.Subhendu Kumar Pati
źródło
4

Implementacja Java w formule Haversine

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
}
ak-j
źródło
3

Aby obliczyć odległość między dwoma punktami na kuli, musisz wykonać obliczenia Wielkiego Kręgu .

Istnieje wiele bibliotek C / C ++, które pomagają w rzutowaniu mapy w MapTools, jeśli chcesz zmienić odległość na płaską powierzchnię. Aby to zrobić, potrzebujesz ciągu rzutowego różnych układów współrzędnych.

MapWindow może być również przydatnym narzędziem do wizualizacji punktów. Również jako open source jest przydatnym przewodnikiem po tym, jak korzystać z biblioteki proj.dll, która wydaje się być podstawową biblioteką projekcji open source.


źródło
3

Oto implementacja zaakceptowanej odpowiedzi przeniesiona na Javę na wypadek, gdyby ktoś jej potrzebował.

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);
}
Eduardo Naveda
źródło
2

Oto implementacja VB.NET, ta implementacja da ci wynik w KM lub Milach na podstawie przekazywanej wartości Enum.

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
Taiseer Joudeh
źródło
Czy po obliczeniu „a” przez pomyłkę napisałeś Math.Sin ( dLat ..) dwa razy?
Marco Ottina,
2

Skondensowałem obliczenia, upraszczając formułę.

Oto w Ruby:

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
Kache
źródło
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;
}}

Rozwiązanie Chucka, ważne także dla mil.

MPaulo
źródło
2

Oto moja implementacja Java do obliczania odległości w stopniach dziesiętnych po pewnym wyszukiwaniu. Użyłem średniego promienia świata (z wikipedii) w km. Jeśli chcesz mile wynikowe, użyj promienia świata w milach.

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

źródło
2

W Mysql użyj poniższej funkcji, aby przekazać parametry jak za pomocą 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;
shanavascet
źródło
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}
));
Raphael C.
źródło
2

oto przykład w postgres sql (w km, dla wersji mil, zamień 1.609344 na wersję 0.8684)

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;
skarb państwa
źródło
2

Oto kolejny przekonwertowany na kod Ruby :

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
aldrien.h
źródło
1

jest tutaj dobry przykład do obliczania odległości za pomocą PHP 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;
     }
 }
ayalcinkaya
źródło