Calcolare la distanza tra due punti, utilizzando la latitudine longitudine?


94

Ecco il mio tentativo, è solo uno snippet del mio codice:

final double RADIUS = 6371.01;
double temp = Math.cos(Math.toRadians(latA))
            * Math.cos(Math.toRadians(latB))
            * Math.cos(Math.toRadians((latB) - (latA)))
            + Math.sin(Math.toRadians(latA))
            * Math.sin(Math.toRadians(latB));
    return temp * RADIUS * Math.PI / 180;

Sto usando queste formule per ottenere la latitudine e la longitudine:

x = Deg + (Min + Sec / 60) / 60)

Risposte:


217

Il codice Java fornito da Dommer sopra fornisce risultati leggermente errati ma i piccoli errori si sommano se stai elaborando, ad esempio, una traccia GPS. Ecco un'implementazione del metodo Haversine in Java che tiene conto anche delle differenze di altezza tra due punti.

/**
 * Calculate distance between two points in latitude and longitude taking
 * into account height difference. If you are not interested in height
 * difference pass 0.0. Uses Haversine method as its base.
 * 
 * lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters
 * el2 End altitude in meters
 * @returns Distance in Meters
 */
public static double distance(double lat1, double lat2, double lon1,
        double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}

7
Perché non Math.toRadians () invece di deg2rad ()? Sarebbe davvero autosufficiente.
Aron Lorincz

2
@Bala - Colpa mia, è nel commento sul codice sul mio computer ma manca qui. Distanza in metri.
David George

4
@ ÁronNemmondommegavezetéknevem Ho aggiornato il metodo per utilizzare il tuo ottimo suggerimento.
David George


Qualche rilevanza particolare per i nomi delle variabili ae c? Sono questi i lati di un triangolo rettangolo con le etichette tradizionali a, be c?
AlainD

76

Ecco una funzione Java che calcola la distanza tra due punti latitudine / longitudine , pubblicata di seguito, nel caso in cui scompaia di nuovo.

    private double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
      double theta = lon1 - lon2;
      double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
      dist = Math.acos(dist);
      dist = rad2deg(dist);
      dist = dist * 60 * 1.1515;
      if (unit == 'K') {
        dist = dist * 1.609344;
      } else if (unit == 'N') {
        dist = dist * 0.8684;
        }
      return (dist);
    }
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts decimal degrees to radians             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    private double deg2rad(double deg) {
      return (deg * Math.PI / 180.0);
    }
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts radians to decimal degrees             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    private double rad2deg(double rad) {
      return (rad * 180.0 / Math.PI);
    }
    
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'M') + " Miles\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'K') + " Kilometers\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'N') + " Nautical Miles\n");

1
Google Map mostra 200 Kms per 12.915700, 77.632046, 11.665154, 78.145657 dove come il codice sopra mostra 149.82 Kms. Qualcosa è ancora sbagliato.
Samy

1
@Samy la funzione di cui sopra ti dà la distanza in linea retta.
Rahul_Pawar

1
Il mio regno per gli sviluppatori per etichettare le loro variabili con le Unità . double dist => double distInMiles (o simile) (non sto bussando alla persona che ha pubblicato questa risposta, sto votando ....... ma piuttosto l'implementatore originale del codice)
granadaCoder

13

Nota: questa soluzione funziona solo per brevi distanze.

Ho provato a utilizzare la formula pubblicata di Dommer per un'applicazione e ho scoperto che funzionava bene per le lunghe distanze, ma nei miei dati stavo usando tutte le distanze molto brevi e il post di Dommer ha funzionato molto male. Avevo bisogno di velocità e i calcoli geografici più complessi funzionavano bene ma erano troppo lenti. Quindi, nel caso in cui tu abbia bisogno di velocità e tutti i calcoli che stai facendo sono brevi (forse <100m circa). Ho trovato che questa piccola approssimazione funziona alla grande. presume che il mondo sia piatto, intendiamoci, quindi non usatelo per lunghe distanze, funziona approssimando la distanza di una singola latitudine e longitudine alla latitudine data e restituendo la distanza pitagorica in metri.

public class FlatEarthDist {
    //returns distance in meters
    public static double distance(double lat1, double lng1, 
                                      double lat2, double lng2){
     double a = (lat1-lat2)*FlatEarthDist.distPerLat(lat1);
     double b = (lng1-lng2)*FlatEarthDist.distPerLng(lat1);
     return Math.sqrt(a*a+b*b);
    }

    private static double distPerLng(double lat){
      return 0.0003121092*Math.pow(lat, 4)
             +0.0101182384*Math.pow(lat, 3)
                 -17.2385140059*lat*lat
             +5.5485277537*lat+111301.967182595;
    }

    private static double distPerLat(double lat){
            return -0.000000487305676*Math.pow(lat, 4)
                -0.0033668574*Math.pow(lat, 3)
                +0.4601181791*lat*lat
                -1.4558127346*lat+110579.25662316;
    }
}

13

Futuri lettori che si imbatteranno in questo articolo SOF.

Ovviamente, la domanda è stata posta nel 2010 e ora è nel 2019. Ma viene fuori presto in una ricerca su Internet. La domanda originale non esclude l'uso di librerie di terze parti (quando ho scritto questa risposta).

public double calculateDistanceInMeters(double lat1, double long1, double lat2,
                                     double long2) {


    double dist = org.apache.lucene.util.SloppyMath.haversinMeters(lat1, long1, lat2, long2);
    return dist;
}

e

<dependency>
  <groupId>org.apache.lucene</groupId>
  <artifactId>lucene-spatial</artifactId>
  <version>8.2.0</version>
</dependency>

https://mvnrepository.com/artifact/org.apache.lucene/lucene-spatial/8.2.0

Si prega di leggere la documentazione su "SloppyMath" prima di immergersi!

https://lucene.apache.org/core/8_2_0/core/org/apache/lucene/util/SloppyMath.html


5

Ecco una pagina con esempi javascript per vari calcoli sferici. Il primo sulla pagina dovrebbe darti ciò di cui hai bisogno.

http://www.mobly-type.co.uk/scripts/latlong.html

Ecco il codice Javascript

var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
        Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c;

Dove 'd' manterrà la distanza.


"A" può mai essere negativo?
Xi Wei

1
package distanceAlgorithm;

public class CalDistance {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
    CalDistance obj=new CalDistance();
    /*obj.distance(38.898556, -77.037852, 38.897147, -77.043934);*/
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "M") + " Miles\n");
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "K") + " Kilometers\n");
        System.out.println(obj.distance(32.9697, -96.80322, 29.46786, -98.53506, "N") + " Nautical Miles\n");       
    }   
    public double distance(double lat1, double lon1, double lat2, double lon2, String sr) {


          double theta = lon1 - lon2;
          double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
          dist = Math.acos(dist);
          dist = rad2deg(dist);
          dist = dist * 60 * 1.1515;
          if (sr.equals("K")) {
            dist = dist * 1.609344;
          } else if (sr.equals("N")) {
            dist = dist * 0.8684;
            }
          return (dist);
        }
    public double deg2rad(double deg) {
          return (deg * Math.PI / 180.0);
        }
    public double rad2deg(double rad) {
          return (rad * 180.0 / Math.PI);
        }


    }

1

sono state fornite molte ottime risposte, tuttavia ho riscontrato alcune carenze nelle prestazioni, quindi lasciatemi offrire una versione con le prestazioni in mente. Ogni costante è precalcolata e vengono introdotte le variabili x, y per evitare di calcolare due volte lo stesso valore. Spero che sia d'aiuto

    private static final double r2d = 180.0D / 3.141592653589793D;
    private static final double d2r = 3.141592653589793D / 180.0D;
    private static final double d2km = 111189.57696D * r2d;
    public static double meters(double lt1, double ln1, double lt2, double ln2) {
        final double x = lt1 * d2r;
        final double y = lt2 * d2r;
        return Math.acos( Math.sin(x) * Math.sin(y) + Math.cos(x) * Math.cos(y) * Math.cos(d2r * (ln1 - ln2))) * d2km;
    }

0

Risposta leggermente aggiornata da @David George:

public static double distance(double lat1, double lat2, double lon1,
                              double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}

public static double distanceBetweenLocations(Location l1, Location l2) {
    if(l1.hasAltitude() && l2.hasAltitude()) {
        return distance(l1.getLatitude(), l2.getLatitude(), l1.getLongitude(), l2.getLongitude(), l1.getAltitude(), l2.getAltitude());
    }
    return l1.distanceTo(l2);
}

la funzione distanza è la stessa, ma ho creato una piccola funzione wrapper, che accetta 2 oggetti Location . Grazie a questo, utilizzo la funzione distanza solo se entrambe le posizioni hanno effettivamente un'altitudine, perché a volte no. E può portare a risultati strani (se la posizione non conosce la sua altitudine verrà restituito 0). In questo caso, torno alla classica funzione distanceTo .


-1

Questo articolo di wikipedia fornisce le formule e un esempio. Il testo è in tedesco, ma i calcoli parlano da soli.

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.