Wie kann ich in Java die Entfernung messen und einen Begrenzungsrahmen basierend auf zwei Längen- und Breitengraden erstellen?

75

Ich möchte den Abstand zwischen zwei verschiedenen Punkten finden. Ich weiß, dass dies mit der großen Kreisentfernung erreicht werden kann. http://www.meridianworlddata.com/Distance-calculation.asp

Sobald ich fertig bin, möchte ich mit einem Punkt und einer Entfernung den Punkt in dieser Entfernung nach Norden und in dieser Entfernung nach Osten finden, um eine Box um den Punkt zu erstellen.

werden
quelle
1
Siehe
Robin

Antworten:

21

Wir haben einige Erfolge mit OpenMap erzielt , um viele Positionsdaten zu zeichnen. Es gibt eine LatLonPoint- Klasse mit einigen grundlegenden Funktionen, einschließlich der Entfernung.

Brendan Cashman
quelle
5
Warnung an mögliche Anwender: Ich bin gerade auf ein großes Problem mit OpenMap gestoßen. Sie verwenden intern Floats für dezimale Lat / Lon, was die Genauigkeit in Abhängigkeit von der Nähe zum Äquator einschränkt. Sie planen, die Option für Doppel mit ihren OMGraphic-Klassen ab Version 4.7 zu unterstützen, aber der aktuelle Stall ist nur 4.6.5 (Stand März 2010). Quelle: openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.html
Marc
Beachten Sie auch, dass die aktuelle OpenMap-Softwarelizenzvereinbarung openmap.bbn.com/license.html als nicht kostenlos eingestuft wurde. web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Es wurden einige Diskussionen mit BBN geführt, um Lizenzen zu ändern, aber es ist noch nichts passiert.
Leif Gruenwoldt
3
Diese Antwort ist jetzt veraltet, da beide Links offline sind.
xivo
Ich habe gerade die Links zu der neuen Domain auf openmap-java.org repariert . Die Lizenz ist jetzt hier: openmap-java.org/License.html (keine Ahnung, ob sie seit dem Kommentar von @LeifGruenwoldt
Rüdiger Schulz
152

Hier ist eine Java-Implementierung der Haversine- Formel. Ich benutze dies in einem Projekt, um die Entfernung in Meilen zwischen Lat / Long zu berechnen.

public static double distFrom(double lat1, double lng1, double lat2, double lng2) {
    double earthRadius = 3958.75; // miles (or 6371.0 kilometers)
    double dLat = Math.toRadians(lat2-lat1);
    double dLng = Math.toRadians(lng2-lng1);
    double sindLat = Math.sin(dLat / 2);
    double sindLng = Math.sin(dLng / 2);
    double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2)
            * Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2));
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    double dist = earthRadius * c;

    return dist;
    }
Sean
quelle
23
Nur eine Anmerkung dazu, es wird die Entfernung in Meilen zurückgegeben (aufgrund der Einstellung earthRadius). Für andere Einheiten ändern Sie den earthRadius (siehe en.wikipedia.org/wiki/Earth_radius für mehr)
John Meagher
Gibt es einen Grund, warum Sie Floats anstelle von Doubles verwenden? Wenn ich das richtig verstehe, können Sie die Genauigkeit Ihrer Ergebnisse erhöhen, indem Sie einfach Ihre Eingabeparametertypen ändern
Hamy
Es ist auch nicht erforderlich, den Sinus der Hälfte der Deltas zweimal zu berechnen. Berechnen Sie es einmal und multiplizieren Sie es mit sich selbst. Es lohnt sich auf jeden Fall in einer engen Schleife.
Glenn Bech
1
Die Ableitung hierfür finden Sie unter mathforum.org/library/drmath/view/51879.html
Anand Sunderraman
6
Verwenden Sie earthRadius als 6371 , um das Ergebnis in Kilometern zu erhalten .
stevo.mit
45

Oder Sie könnten SimpleLatLng verwenden . Apache 2.0 ist lizenziert und wird in einem mir bekannten Produktionssystem verwendet: meinem.

Kurzgeschichte:

Ich suchte nach einer einfachen Geobibliothek und konnte keine finden, die meinen Anforderungen entsprach. Und wer möchte diese kleinen Geo-Tools in jeder Anwendung immer wieder schreiben, testen und debuggen? Es muss einen besseren Weg geben!

So wurde SimpleLatLng geboren, um Breiten- und Längengraddaten zu speichern, Entfernungsberechnungen durchzuführen und geformte Grenzen zu erstellen.

Ich weiß, dass ich zwei Jahre zu spät bin, um dem Originalplakat zu helfen, aber mein Ziel ist es, Menschen wie mir zu helfen, die diese Frage bei einer Suche finden. Ich würde es lieben, wenn einige Leute es benutzen und zum Testen und Sehen dieses kleinen, leichten Dienstprogramms beitragen.

JavadocMD
quelle
das könnte mir helfen! hast du es geschaffen Verwenden Sie die Haversine-Formel für Entfernungsberechnungen? Ich werde versuchen einzuspringen, wenn ich die Zeit finde!
Marsellus Wallace
Richtig, es verwendet Haversine für Entfernungsberechnungen mit Schwerpunkt auf (obwohl zugegebenermaßen keine Besessenheit) Geschwindigkeit und einem niedrigen Speicherprofil. Ich denke, es hat auch einige andere nette Eigenschaften im Umgang mit Zahlen, wie zum Beispiel die Berücksichtigung von Koordinaten, die "wirklich nahe" sind, um gleich zu sein.
JavadocMD
11

Für einen genaueren Abstand (0,5 mm) können Sie auch die Vincenty-Näherung verwenden:

/**
 * Calculates geodetic distance between two points specified by latitude/longitude using Vincenty inverse formula
 * for ellipsoids
 * 
 * @param lat1
 *            first point latitude in decimal degrees
 * @param lon1
 *            first point longitude in decimal degrees
 * @param lat2
 *            second point latitude in decimal degrees
 * @param lon2
 *            second point longitude in decimal degrees
 * @returns distance in meters between points with 5.10<sup>-4</sup> precision
 * @see <a href="http://www.movable-type.co.uk/scripts/latlong-vincenty.html">Originally posted here</a>
 */
public static double distVincenty(double lat1, double lon1, double lat2, double lon2) {
    double a = 6378137, b = 6356752.314245, f = 1 / 298.257223563; // WGS-84 ellipsoid params
    double L = Math.toRadians(lon2 - lon1);
    double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat1)));
    double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat2)));
    double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
    double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);

    double sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM;
    double lambda = L, lambdaP, iterLimit = 100;
    do {
        sinLambda = Math.sin(lambda);
        cosLambda = Math.cos(lambda);
        sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda)
                + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
        if (sinSigma == 0)
            return 0; // co-incident points
        cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
        sigma = Math.atan2(sinSigma, cosSigma);
        sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
        cosSqAlpha = 1 - sinAlpha * sinAlpha;
        cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
        if (Double.isNaN(cos2SigmaM))
            cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
        double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1 - C) * f * sinAlpha
                * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
    } while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);

    if (iterLimit == 0)
        return Double.NaN; // formula failed to converge

    double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
    double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
    double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
    double deltaSigma = B
            * sinSigma
            * (cos2SigmaM + B
                    / 4
                    * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM
                            * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
    double dist = b * A * (sigma - deltaSigma);

    return dist;
}

Dieser Code wurde frei von http://www.movable-type.co.uk/scripts/latlong-vincenty.html angepasst

Victor P.
quelle
6

Korrigierte Haversine Distance Formel ....

public static double HaverSineDistance(double lat1, double lng1, double lat2, double lng2) 
{
    // mHager 08-12-2012
    // http://en.wikipedia.org/wiki/Haversine_formula
    // Implementation

    // convert to radians
    lat1 = Math.toRadians(lat1);
    lng1 = Math.toRadians(lng1);
    lat2 = Math.toRadians(lat2);
    lng2 = Math.toRadians(lng2);

    double dlon = lng2 - lng1;
    double dlat = lat2 - lat1;

    double a = Math.pow((Math.sin(dlat/2)),2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon/2),2);

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

    return EARTH_RADIUS * c;
}   
Matthew Hager
quelle
Die Erde ist keine perfekte Kugel
Steve Kuo
Richtig, manchmal ist nah genug. IE: Warum Mr. Inman es geschaffen hat. Ich würde ihm sagen, dass er falsch liegt, aber er ist tot. : o (Wenn Sie die längliche Form der Welt berechnen müssen, gibt es dafür bessere Formeln. Es gibt auch einige großartige Apache-Bibliotheken, die Sie ebenfalls verwenden können. Wenn Sie nur etwas Einfaches benötigen, ist dies ein gutes schnelles Beispiel . :)
Matthew Hager
1
Ja, die Haversine-Formel basiert auf dem Prinzip „Schließen ist gut genug“. Zu der Zeit haben wir Entfernungen gemessen, die <50 Meilen waren, um die Nähe von einem Ort zum anderen als "Heuristik" zu bestimmen.
Matthew Hager
2

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

public static Double distanceBetweenTwoLocationsInKm(Double latitudeOne, Double longitudeOne, Double latitudeTwo, Double longitudeTwo) {
        if (latitudeOne == null || latitudeTwo == null || longitudeOne == null || longitudeTwo == null) {
            return null;
        }

        Double earthRadius = 6371.0;
        Double diffBetweenLatitudeRadians = Math.toRadians(latitudeTwo - latitudeOne);
        Double diffBetweenLongitudeRadians = Math.toRadians(longitudeTwo - longitudeOne);
        Double latitudeOneInRadians = Math.toRadians(latitudeOne);
        Double latitudeTwoInRadians = Math.toRadians(latitudeTwo);
        Double a = Math.sin(diffBetweenLatitudeRadians / 2) * Math.sin(diffBetweenLatitudeRadians / 2) + Math.cos(latitudeOneInRadians) * Math.cos(latitudeTwoInRadians) * Math.sin(diffBetweenLongitudeRadians / 2)
                * Math.sin(diffBetweenLongitudeRadians / 2);
        Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return (earthRadius * c);
    }
Rija Ramampiandra
quelle
1

Sie können die Java Geodesy Library für GPS verwenden . Sie verwendet die Vincenty-Formeln, die die Krümmung der Erdoberfläche berücksichtigen.

Die Implementierung sieht folgendermaßen aus:

import org.gavaghan.geodesy.*;
...
GeodeticCalculator geoCalc = new GeodeticCalculator();
Ellipsoid reference = Ellipsoid.WGS84;
GlobalPosition pointA = new GlobalPosition(latitude, longitude, 0.0);
GlobalPosition userPos = new GlobalPosition(userLat, userLon, 0.0);
double distance = geoCalc.calculateGeodeticCurve(reference, userPos, pointA).getEllipsoidalDistance();

Die resultierende Entfernung ist in Metern.

Oscar Salguero
quelle
1

Ich weiß, dass es viele Antworten gibt, aber bei einigen Recherchen zu diesem Thema habe ich festgestellt, dass die meisten Antworten hier die Haversine-Formel verwenden, aber die Vincenty-Formel ist tatsächlich genauer. Es gab einen Beitrag, der die Berechnung aus einer Javascript-Version angepasst hat, aber er ist sehr unhandlich. Ich habe eine Version gefunden, die überlegen ist, weil:

  1. Es hat auch eine offene Lizenz.
  2. Es verwendet OOP-Prinzipien.
  3. Es hat eine größere Flexibilität bei der Auswahl des Ellipsoids, das Sie verwenden möchten.
  4. Es verfügt über mehr Methoden, um in Zukunft unterschiedliche Berechnungen zu ermöglichen.
  5. Es ist gut dokumentiert.

VincentyDistanceCalculator

mikeho
quelle
1

Diese Methode hilft Ihnen dabei, die Entfernung zwischen dem geografischen Standort in km zu ermitteln.

private double getDist(double lat1, double lon1, double lat2, double lon2)
{
    int R = 6373; // radius of the earth in kilometres
    double lat1rad = Math.toRadians(lat1);
    double lat2rad = Math.toRadians(lat2);
    double deltaLat = Math.toRadians(lat2-lat1);
    double deltaLon = Math.toRadians(lon2-lon1);

    double a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
            Math.cos(lat1rad) * Math.cos(lat2rad) *
            Math.sin(deltaLon/2) * Math.sin(deltaLon/2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

    double d = R * c;
    return d;
}
Ad infinitum
quelle
1

Kotlin-Version der Haversine-Formel. Zurückgegebenes Ergebnis in Metern. Getestet unter https://www.vcalc.com/wiki/vCalc/Haversine+-+Distance

const val EARTH_RADIUS_IN_METERS = 6371007.177356707

fun distance(lat1: Double, lng1: Double, lat2: Double, lng2: Double): Double {
    val latDiff = Math.toRadians(abs(lat2 - lat1))
    val lngDiff = Math.toRadians(abs(lng2 - lng1))
    val a = sin(latDiff / 2) * sin(latDiff / 2) +
        cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
        sin(lngDiff / 2) * sin(lngDiff / 2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return EARTH_RADIUS_IN_METERS * c
}
Binakot
quelle
Hey, also kann ich das als Geofence verwenden, oder ???? Der Geofence-Code auf Android funktioniert aus einem unbekannten Grund nicht für mich und ich dachte, dies als Alternative zu verwenden
Pemba Tamang
0

Normalerweise verwende ich MATLAB mit der Mapping Toolbox und verwende dann den Code in meinem Java mit MATLAB Builder JA. Es macht mein Leben viel einfacher. Da die meisten Schulen freien Zugang für Schüler haben, können Sie es ausprobieren (oder die Testversion herunterladen, um Ihre Arbeit zu erledigen).

Chetan Rawal
quelle