Calcolare la distanza tra due punti di latitudine e longitudine? (Formula Haversine)


908

Come posso calcolare la distanza tra due punti specificati da latitudine e longitudine?

Per chiarimenti, vorrei la distanza in chilometri; i punti usano il sistema WGS84 e mi piacerebbe capire la precisione relativa degli approcci disponibili.


Per una migliore precisione - vedi stackoverflow.com/questions/1420045/…
Lior Kogan,

3
Nota che non puoi applicare una formula Haversine su un ellissoide di rivoluzione come WGS 84. Puoi applicare questo metodo solo su una sfera con un raggio.
Mike T,

3
La maggior parte delle risposte qui utilizza una semplice trigonometria sferica, quindi i risultati sono piuttosto rozzi rispetto alle distanze ellissoidali WGS84 utilizzate nel sistema GPS. Alcune delle risposte fanno riferimento alla formula di Vincenty per gli ellissoidi, ma quell'algoritmo è stato progettato per l'uso su calcolatrici da tavolo dell'era degli anni '60 e presenta problemi di stabilità e precisione; ora abbiamo hardware e software migliori. Consultare GeographicLib per una libreria di alta qualità con implementazioni in varie lingue.
PM 2Ring

@MikeT - vero anche se molte delle risposte qui sembrano utili su piccole distanze : se prendi lat / long da WGS 84 e applichi Haversine come se fossero punti su una sfera, non ottieni risposte i cui errori sono dovuti solo a il fattore di appiattimento della terra, quindi forse entro l'1% di una formula più accurata? Con l'avvertenza che queste sono piccole distanze, diciamo all'interno di una singola città.
ToolmakerSteve

1
Per questi formati: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Piattaforma Windows universale / Xamarin iOS / Xamarin Android vedi stackoverflow.com/a/54296314/2736742
A. Morel

Risposte:


1148

Questo link potrebbe esserti utile, in quanto descrive in dettaglio l'uso della formula Haversine per calcolare la distanza.

Estratto:

Questo script [in Javascript] calcola le distanze del grande cerchio tra i due punti - cioè la distanza più breve sulla superficie terrestre - usando la formula "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)
}

51
Questo calcolo / metodo spiega che la Terra è uno sferoide (non una sfera perfetta)? La domanda originale chiedeva la distanza tra i punti su un globo WGS84. Non sono sicuro di quanto errore si insinua usando una sfera perfetta, ma sospetto che possa dipendere molto da dove si trovano i punti sul globo, quindi vale la pena tenere presente la distinzione.
redcalx

15
La formula di Haversine non tiene conto del fatto che la Terra sia uno sferoide, quindi avrai qualche errore introdotto a causa di questo fatto. Non può essere garantito corretto a migliore dello 0,5%. Tuttavia, potrebbe essere un livello accettabile di errore.
Brandon,

24
C'è qualche motivo da usare al Math.atan2(Math.sqrt(a), Math.sqrt(1-a))posto di Math.asin(Math.sqrt(h)), quale sarebbe l'implementazione diretta della formula che utilizza l'articolo di Wikipedia? È più efficiente e / o numericamente stabile?
musiphil,

16
@UsmanMutawakil Bene, le 38 miglia che ottieni è la distanza sulla strada. Questo algoritmo calcola una distanza in linea retta sulla superficie terrestre. Google Maps ha uno strumento di distanza (in basso a sinistra, "Labs") che fa lo stesso, usalo per confrontare.
Pascal,

4
@ Forte_201092: Perché non è necessario - come (sin(x))²uguale(sin(-x))²
Jean Hominal,

360

Avevo bisogno di calcolare molte distanze tra i punti per il mio progetto, quindi sono andato avanti e ho cercato di ottimizzare il codice, che ho trovato qui. In media in diversi browser la mia nuova implementazione viene eseguita 2 volte più veloce della risposta più votata.

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
}

Puoi giocare con il mio jsPerf e vedere i risultati qui .

Di recente ho dovuto fare lo stesso in Python, quindi ecco un'implementazione di Python :

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

E per completezza: Haversine su wiki.


13
@AngularM e è molto probabile che google calcoli la distanza se prenderai alcune strade e non una linea retta.
Salvador Dali,

3
Google calcola la distanza in auto, questo calcola "in linea d'aria"
Hobbyist

4
@Ouadie e migliorerà la velocità? Molto probabilmente no, ma finirò con un sacco di "le tue cose non funzionano" per le persone che copypaste nei vecchi browser
Salvador Dali,

4
bene sì, ma cosa // 2 * R; R = 6371 kmsignifica? e l'attuale metodo fornisce una risposta in km o miglia? necessita di una migliore documentazione. Grazie
Khalil Khalaf il

20
@KhalilKhalaf stai scherzando o stai cercando di troll qui? km sta per chilometri. Cosa pensi che R rappresenti (specialmente se parliamo di shpere)? Indovina in quali unità la risposta sarà se vedi già il km. Che tipo di documentazione stai cercando qui: ci sono letteralmente 4 righe lì.
Salvador Dali,

69

Ecco un'implementazione 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;
    }

}

14
Stai usando il raggio equatoriale, ma dovresti usare il raggio medio, che è 6371 km
Philippe Leybaert,

7
Non dovrebbe essere questo double dlon = Radians(lon2 - lon1);edouble dlat = Radians(lat2 - lat1);
Chris Marisic,

Sono d'accordo con Chris Marisic. Ho usato il codice originale e i calcoli erano sbagliati. Ho aggiunto la chiamata per convertire i delta in radianti e ora funziona correttamente. Ho inviato una modifica e sto aspettando che venga rivista.
Bryan Bedard il

Ho inviato un'altra modifica perché anche lat1 e lat2 devono essere convertiti in radianti. Ho anche rivisto la formula per il compito in modo che corrisponda alla formula e al codice trovati qui: movable-type.co.uk/scripts/latlong.html
Bryan Bedard

il RADIUSvalore deve essere 6371 come nelle altre risposte?
Chris Hayes,

66

Ecco un'implementazione Java della formula Haversine.

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

Si noti che qui stiamo arrotondando la risposta al km più vicino.


2
Se volessimo calcolare la distanza tra due punti in metri, quale sarebbe il modo più preciso? Da usare 6371000come raggio della terra? (il raggio medio di terra è di 6371000 metri) o converti chilometri in metri dalla tua funzione?
Micro

se vuoi miglia, moltiplica il risultato per0.621371
lasec0203

42

Grazie mille per tutto questo. Ho usato il seguente codice nella mia app per iPhone 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;
}

Latitudine e Longitudine sono in decimali. Non ho usato min () per la chiamata asin () poiché le distanze che sto usando sono così piccole che non lo richiedono.

Ha dato risposte errate fino a quando non ho passato i valori in Radianti - ora è praticamente lo stesso dei valori ottenuti dall'app Mappa di Apple :-)

Aggiornamento extra:

Se stai utilizzando iOS4 o versioni successive, Apple fornisce alcuni metodi per farlo in modo da ottenere le stesse funzionalità con:

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

    MKMapPoint  start, finish;


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

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}


Penso che la parentesi pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))non sia corretta. Rimuovi quelli e il risultato corrisponde a quello che ottengo quando uso altre implementazioni su questa pagina o implemento la formula Haversine da Wikipedia da zero.
zanedp,

Usando le coordinate (40.7127837, -74.0059413) per New York e (34.052234, -118.243685) per Los Angeles, con il valore di ()quella somma, ottengo 3869.75. Senza di loro, ottengo 3935.75, che è praticamente ciò che una ricerca web rivela.
zanedp,

40

Questa è una semplice funzione PHP che fornirà un'approssimazione molto ragionevole (margine di errore inferiore a +/- 1%).

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

Come detto prima; la terra NON è una sfera. È come un vecchio vecchio baseball con cui Mark McGwire ha deciso di esercitarsi - è pieno di ammaccature e dossi. I calcoli più semplici (come questo) lo trattano come una sfera.

Diversi metodi possono essere più o meno precisi a seconda di dove ti trovi in ​​questo ovoide irregolare E quanto distanti i tuoi punti (più vicini sono, minore è il margine di errore assoluto). Più precisa è la tua aspettativa, più complessa è la matematica.

Per maggiori informazioni: distanza geografica Wikipedia


4
Funziona perfettamente! Ho appena aggiunto $ distance_miles = $ km * 0.621371; e questo è tutto ciò di cui avevo bisogno per una distanza approssimativa in miglia! Grazie Tony.

31

Pubblico qui il mio esempio di lavoro.

Elenca tutti i punti nella tabella con distanza tra un punto designato (usiamo un punto casuale - lat: 45.20327, long: 23.7806) meno di 50 KM, con latitudine e longitudine, in MySQL (i campi della tabella sono coord_lat e coord_long):

Elenco di tutti con DISTANZA <50, in chilometri (considerato raggio terrestre 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

L'esempio sopra è stato testato in MySQL 5.0.95 e 5.5.16 (Linux).


Penso che un buon approccio potrebbe essere pre-filtrare i risultati usando un approssimazione, quindi la formula pesante viene applicata solo in alcuni casi. Utile specialmente se hai altre condizioni. Lo sto usando per l'aprox iniziale: stackoverflow.com/questions/1253499/…
Pato

28

Nell'altra risposta un'implementazione in manca.

Il calcolo della distanza tra due punti è abbastanza semplice con la distmfunzione dal geospherepacchetto:

distm(p1, p2, fun = distHaversine)

dove:

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

Poiché la terra non è perfettamente sferica, la formula Vincenty per gli ellissoidi è probabilmente il modo migliore per calcolare le distanze. Quindi nel geospherepacchetto che usi quindi:

distm(p1, p2, fun = distVincentyEllipsoid)

Ovviamente non devi necessariamente usare il geospherepacchetto, puoi anche calcolare la distanza in base Rcon una funzione:

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

Per essere sicuro di essere chiaro su ciò che hai detto: Il codice che dai alla fine del post: È un'implementazione della formula Vincenty? Per quanto ne sai, dovrebbe dare la stessa risposta come chiamare Vincenty in geosfera? [Non ho geosfera o altra biblioteca; sto solo cercando del codice da includere in un'app multipiattaforma. Ovviamente verificherei alcuni casi di test con un buon calcolatore noto.]
ToolmakerSteve

1
@ToolmakerSteve la funzione alla fine della mia risposta è un'implementazione del metodo Haversine
Jaap

Ciao @Jaap potrei chiedere qual è l'unità di misura per la formula? È in metri?
Jackson,

11

L'haversine è sicuramente una buona formula per la maggior parte dei casi, altre risposte lo includono già, quindi non ho intenzione di occupare lo spazio. Ma è importante notare che non importa quale formula viene utilizzata (sì, non solo una). A causa della vasta gamma di precisione possibile e del tempo di calcolo richiesto. La scelta della formula richiede un po 'più di pensiero di una semplice risposta senza sforzo.

Questo post di una persona della NASA è il migliore che ho trovato discutendo delle opzioni

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

Ad esempio, se stai solo ordinando le righe per distanza in un raggio di 100 miglia. La formula della terra piatta sarà molto più veloce di quella di haversine.

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;

Nota che c'è solo un coseno e una radice quadrata. Vs 9 di loro sulla formula Haversine.


È una bella possibilità. Basta essere consapevoli del fatto che la distanza massima raccomandata nella discussione è di 12 miglia, non di 100 , e che anche così, gli errori potrebbero strisciare fino a 30 metri (100 piedi), a seconda della posizione del globo.
Eric Wu,

7

È possibile utilizzare la build in CLLocationDistance per calcolare questo:

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

Nel tuo caso, se vuoi chilometri dividi solo per 1000.


7

Non mi piace aggiungere ancora un'altra risposta, ma l'API di Google Maps v.3 ha una geometria sferica (e altro). Dopo aver convertito il tuo WGS84 in gradi decimali puoi farlo:

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

Nessuna parola su quanto siano accurati i calcoli di Google o persino quale modello viene utilizzato (anche se dice "sferico" piuttosto che "geoide". A proposito, la distanza della "linea retta" sarà ovviamente diversa dalla distanza se si viaggia sulla superficie della terra che è ciò che tutti sembrano presumere.


la distanza è in metri. in alternativa si può usare computeLength ()
electrobabe il

7

L'implementazione del pitone L'origine è il centro dei contigui Stati Uniti.

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

Per ottenere la risposta in chilometri è sufficiente impostare miglia = falso.


1
Stai importando un pacchetto non standard che fa tutto il lavoro. Non so se sia così utile.
Teepeemm,

Il pacchetto si trova in PyPI, Python Package Index, come pacchetto python 3 insieme a numpy e scikit-learn. Non sono sicuro del motivo per cui si è apposti ai pacchetti. Tendono ad essere abbastanza utili. Come open source, si potrebbe anche esaminare i metodi contenuti. Penso che molti troverebbero utile questo pacchetto, quindi lascerò il post nonostante il downvote. Saluti. :)
invoketheshell,

7

Potrebbe esserci una soluzione più semplice e più corretta: il perimetro della terra è di 40.000 Km all'equatore, circa 37.000 nel ciclo di Greenwich (o di qualsiasi longitudine). Così:

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

Concordo sul fatto che dovrebbe essere messo a punto in quanto, io stesso ho detto che si tratta di un ellissoide, quindi il raggio da moltiplicare per il coseno varia. Ma è un po 'più preciso. Rispetto a Google Maps e ha ridotto l'errore in modo significativo.


Questa funzione è la distanza di ritorno in km?
Wikki,

Lo è, solo perché l'equatore e i cicli di longitudine sono in Km. Per miglia, basta dividere 40000 e 37000 per 1,6. Sentendosi geniale, puoi convertirlo in Ris, moltiplicandolo per circa 7 o in parasang, dividendo per 2,2 ;-)
Meymann

Questa sembra essere la migliore risposta offerta qui. Vorrei usarlo ma mi chiedo solo se esiste un modo per verificare la correttezza di questo algoritmo. Ho provato f (50,5,58,3). Dà 832 km, mentre movable-type.co.uk/scripts/latlong.html usando la formula 'haversine' dà 899 km. C'è una differenza così grande?
Chong Lip Phang,

Inoltre, penso che il valore restituito dal codice sopra sia in m, e non in km.
Chong Lip Phang,

@ChongLipPhang - ATTENZIONE: il teorema di Pitagora è un'approssimazione ragionevole solo per piccole aree , poiché questo teorema presuppone che la terra sia piatta. Come caso estremo, inizia dall'equatore e sposta di 90 gradi est e 90 gradi nord. Il risultato finale ovviamente è il polo nord, ed è lo stesso che spostarsi di 0 gradi est e 90 gradi nord; così facendo sqrt (sqr (dx) + sqr (dy)) sarà selvaggiamente fuori nel primo caso. ~ sqrt (10km sqr + 10km sqr) ~ = 14,4 km vs distanza corretta ~ 10km.
ToolmakerSteve

7

Tutte le risposte di cui sopra presuppongono che la terra sia una sfera. Tuttavia, un'approssimazione più accurata sarebbe quella di uno sferoide oblato.

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

Ecco l'implementazione SQL per calcolare la distanza in 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;

Per ulteriori dettagli sull'implementazione programmando langugage, puoi semplicemente leggere lo script php qui fornito


5

Ecco un'implementazione dattiloscritta della formula di 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;
}

5

Come sottolineato, un calcolo accurato dovrebbe tener conto del fatto che la terra non è una sfera perfetta. Ecco alcuni confronti dei vari algoritmi offerti qui:

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

Su piccole distanze, l'algoritmo di Keerthana sembra coincidere con quello di Google Maps. Google Maps non sembra seguire alcun semplice algoritmo, suggerendo che qui potrebbe essere il metodo più accurato.

Comunque, ecco un'implementazione Javascript dell'algoritmo di 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));
}

4

Questo script [in PHP] calcola le distanze tra i due punti.

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

Implementazione Java secondo la formula 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
}

3

Per calcolare la distanza tra due punti su una sfera è necessario eseguire il calcolo del Grande Cerchio .

Esistono diverse librerie C / C ++ per aiutare con la proiezione delle mappe su MapTools se è necessario riproiettare le distanze su una superficie piana. Per fare ciò avrai bisogno della stringa di proiezione dei vari sistemi di coordinate.

È inoltre possibile trovare MapWindow uno strumento utile per visualizzare i punti. Anche come open source è una guida utile su come utilizzare la libreria proj.dll, che sembra essere la libreria di proiezione open source principale.


3

Ecco l'implementazione della risposta accettata portata su Java nel caso qualcuno ne abbia bisogno.

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

Ecco l'implementazione VB.NET, questa implementazione ti darà il risultato in KM o Miglia in base al valore Enum che passi.

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

Nel calcolare "a", hai scritto Math.Sin ( dLat ..) due volte per errore?
Marco Ottina,

2

Ho ridotto il calcolo semplificando la formula.

Eccolo in 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

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

La soluzione di Chuck, valida anche per miglia.


2

Ecco la mia implementazione Java per la distanza di calcolo in gradi decimali dopo alcune ricerche. Ho usato il raggio medio del mondo (da Wikipedia) in km. Se si desidera ottenere miglia risultanti, utilizzare il raggio mondiale in miglia.

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

In Mysql usare la seguente funzione passare i parametri come usando 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

ecco un esempio in postgres sql (in km, per la versione in miglia, sostituire 1.609344 con la versione 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;

2

Ecco un altro codice convertito in 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

1

c'è un buon esempio qui per calcolare la distanza con 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;
     }
 }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.