Obliczanie odległości między dwoma punktami przy użyciu szerokości i długości geograficznej?


95

Oto moja próba, to tylko fragment mojego kodu:

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;

Używam tych formuł, aby uzyskać szerokość i długość geograficzną:

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

Odpowiedzi:


222

Kod Java podany przez Dommera powyżej daje nieco niepoprawne wyniki, ale drobne błędy sumują się, jeśli przetwarzasz, na przykład ścieżkę GPS. Oto implementacja metody Haversine w Javie, która uwzględnia również różnice wysokości między dwoma punktami.

/**
 * 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
Dlaczego nie Math.toRadians () zamiast deg2rad ()? Byłby naprawdę samowystarczalny.
Aron Lorincz,

2
@Bala - Moja wina, jest w komentarzu do kodu na moim komputerze, ale brakuje go tutaj. Odległość w metrach.
David George

4
@ ÁronNemmondommegavezetéknevem Zaktualizowałem metodę, aby wykorzystać Twoją bardzo dobrą sugestię.
David George


Jakieś szczególne znaczenie dla nazw zmiennych ai c? Czy to są boki trójkąta prostokątnego z tradycyjnych etykiet a, bi c?
AlainD

76

Oto funkcja Java, która oblicza odległość między dwoma długimi i długimi punktami , zamieszczona poniżej, na wypadek, gdyby znowu zniknęła.

    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
Mapa Google pokazuje 200 Kms dla 12.915700, 77.632046, 11.665154, 78.145657, gdzie zgodnie z powyższym kodem pokazuje 149,82 Kms. Coś jest nadal nie tak.
Samy

1
@Samy powyższa funkcja podaje odległość w linii prostej.
Rahul_Pawar,

1
Moje królestwo dla programistów, aby oznaczali swoje zmienne jednostkami . podwójne dist => podwójne distInMiles (lub podobny) (nie jestem pukanie osobę, która opublikowała tę odpowiedź, mam upvoting ....... ale raczej oryginalny realizator kodu)
granadaCoder

14

Uwaga: to rozwiązanie działa tylko na krótkie odległości.

Próbowałem użyć opublikowanej formuły dommera dla aplikacji i stwierdziłem, że radziła sobie dobrze na długich dystansach, ale w moich danych użyłem wszystkich bardzo krótkich odległości, a post dommera wypadł bardzo słabo. Potrzebowałem szybkości, a bardziej złożone obliczenia geograficzne działały dobrze, ale były zbyt wolne. Tak więc w przypadku, gdy potrzebujesz prędkości, a wszystkie obliczenia, które wykonujesz, są krótkie (może <100 m lub więcej). Uważam, że to małe przybliżenie działa świetnie. zakłada, że ​​świat jest płaski, więc nie używaj go na duże odległości, działa poprzez przybliżenie odległości jednej szerokości i długości geograficznej na danej szerokości geograficznej i zwrócenie odległości pitagorejskiej w metrach.

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

14

Przyszli czytelnicy, którzy natkną się na ten artykuł SOF.

Oczywiście pytanie padło w 2010 r., A teraz jest w 2019 r. Ale pojawia się na początku wyszukiwania w Internecie. Oryginalne pytanie nie pomija korzystania z biblioteki zewnętrznej (kiedy pisałem tę odpowiedź).

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

i

<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

Przeczytaj dokumentację o „SloppyMath” przed przystąpieniem do nurkowania!

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


Czy na pewno to prawda? ten plik jar nie ma „.util”, właśnie to sprawdziłem.
Adnan Ali

Dlatego zawsze publikuję wersję z odpowiedzią. Upewnij się, że otrzymujesz 8.2.0. 13 głosów poparcia sugeruje również, że odpowiedź jest trafna.
granadaCoder

5

Oto strona z przykładami javascript do różnych obliczeń sferycznych. Pierwsza na stronie powinna dać Ci to, czego potrzebujesz.

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

Oto kod 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;

Gdzie „d” utrzyma dystans.


Czy „a” może kiedykolwiek być negatywne?
Xi Wei

2

udzielono wielu świetnych odpowiedzi, jednak znalazłem pewne niedociągnięcia w wydajności, więc pozwól mi zaoferować wersję z myślą o wydajności. Każda stała jest wstępnie obliczana i wprowadzane są zmienne x, y, aby uniknąć podwójnego obliczania tej samej wartości. Mam nadzieję, że to pomoże

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

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

Nieznacznie ulepszona odpowiedź od @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);
}

Funkcja odległości jest taka sama, ale utworzyłem małą funkcję opakowującą, która przyjmuje 2 obiekty Location . Dzięki temu z funkcji odległości korzystam tylko wtedy, gdy obie lokalizacje faktycznie mają wysokość, bo czasami tak nie jest. Może to prowadzić do dziwnych wyników (jeśli lokalizacja nie zna jej wysokości, zostanie zwrócona 0). W tym przypadku wracam do klasycznej funkcji distanceTo .


-1

Ten artykuł na Wikipedii zawiera wzory i przykład. Tekst jest w języku niemieckim, ale obliczenia mówią same za siebie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.