2008-09-23 7 views
74

Ich möchte die Entfernung zwischen zwei verschiedenen Punkten finden. Das weiß ich kann man mit der Großkreisdistanz erreichen. Wie kann ich die Entfernung messen und eine Begrenzungsbox basierend auf zwei Breiten- und Längengradpunkten in Java erstellen?

Sobald getan, mit einem Punkt und Abstand möchte ich den Punkt finden, dass die Entfernung Norden, und diese Entfernung Osten, um eine Box um den Punkt zu erstellen.

+1

in diesem Blog finden Sie http://xebee.xebia.in/2010/10/28/working-with-geolocations/ – Robin

Antwort

20

Wir hatten Erfolg mit OpenMap, um viele Positionsdaten zu plotten. Es gibt eine Klasse LatLonPoint, die einige grundlegende Funktionen enthält, einschließlich der Entfernung.

+5

auf mögliche Adopters Warnung: Ich lief in ein großes Problem mit OpenMap; Sie verwenden Floats intern für das dezimale lat/lon, was die Genauigkeit in Abhängigkeit davon begrenzt, wie nahe am Äquator Sie sich befinden. Sie planen, die Option für Doubles mit ihren OMGraphic-Klassen ab Version 4.7 zu unterstützen, aber der aktuelle Stable ist nur 4.6.5 (Stand März 2010). Quelle: http://openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.html – Marc

+0

Beachten Sie auch, dass die aktuelle OpenMap Software-Lizenzvereinbarung http://openmap.bbn.com/license.html hat als nicht frei angesehen. http://web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Einige Diskussionen mit BBN ist aufgetreten, um Lizenzen zu ändern, aber noch ist nichts passiert. –

+0

LatLonPoint link is broken – Petriborg

7

Eine schnelle Google-Suche erscheint GeoTools, die wahrscheinlich die Art von Funktionen haben, die Sie suchen.

140

Hier ist eine Java-Implementierung von Haversine Formel. Ich verwende das in einem Projekt, um die Entfernung in Meilen zwischen Breitengrad und Länge 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; 
    } 
+22

Nur ein Hinweis darauf, es wird die Entfernung in Meilen zurückgeben (wegen der Einstellung von earthRadius). Für andere Einheiten ändern Sie den earthRadius (siehe http://en.wikipedia.org/wiki/Earth_radius für mehr) –

+0

Gibt es einen Grund, warum Sie Floats statt Doubles verwenden? Wenn ich das richtig verstanden habe, können Sie die Genauigkeit Ihrer Ergebnisse erhöhen, indem Sie einfach Ihre Eingangsparameter ändern – Hamy

+0

Es ist auch nicht notwendig, den Sinus der Hälfte der Deltas zweimal zu berechnen. Berechne es einmal und multipliziere es mit sich selbst. Es lohnt sich definitiv in einer engen Schleife. –

43

Oder Sie könnten SimpleLatLng verwenden. Apache 2.0 lizenziert und in einem Produktionssystem verwendet, das ich kenne: meins.

Kurzgeschichte:

ich für eine einfache geo Bibliothek gesucht und nicht eine meine Bedürfnisse finden konnten, passen. Und wer möchte diese kleinen Geo-Tools in jeder Anwendung immer wieder schreiben und testen? Es muss einen besseren Weg geben!

Also wurde SimpleLatLng als eine Möglichkeit geboren, um Längen- und Breitengraddaten zu speichern, Distanzberechnungen durchzuführen und geformte Grenzen zu erstellen.

Ich weiß, dass ich zwei Jahre zu spät bin, um dem ursprünglichen Poster zu helfen, aber mein Ziel ist es, Leuten wie mir zu helfen, die diese Frage bei einer Suche finden. Ich würde es sehr gerne haben, wenn einige Leute es benutzen und zur Erprobung und Vision dieses kleinen, leichten Hilfsprogramms beitragen würden.

+0

das könnte mir helfen! hast du es erschaffen? Verwenden Sie die Haversine Formel für Entfernungsberechnungen?Ich werde versuchen hineinzuspringen, wenn ich die Zeit finde! – Gevorg

+0

Richtig, es verwendet Haversine für Entfernungsberechnungen mit einem Schwerpunkt auf (obwohl zugegebenermaßen keine Obsession mit) Geschwindigkeit und einem niedrigen Speicherprofil. Ich denke, es hat auch einige andere nette Handlingeigenschaften, wie zum Beispiel die Berücksichtigung von Koordinaten, die "wirklich nahe" sind, um gleich zu sein. – JavadocMD

1

Sie können die Java Geodesy Library for GPS verwenden, verwendet die Vincenty's formulae, die die Erdoberfläche Krümmung berücksichtigt.

Umsetzung geht so:

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

Der resultierende Abstand in Metern.

11

Für einen genaueren Abstand (0,5 mm) auch die Annäherung Vincenty 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 frei von http://www.movable-type.co.uk/scripts/latlong-vincenty.html

6

Korrigierte Haversine Entfernung Formel ....

angepasst wurde
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; 
} 
+0

Die Erde ist keine perfekte Kugel –

+0

Korrekt, manchmal ** schließen ** ist gut genug. IE: Warum Mr. Inman es erstellt hat. Ich würde ihm sagen, dass er falsch liegt, aber er ist tot. : o (Wenn Sie für die längliche Form der Welt berechnen müssen, gibt es bessere Formeln dafür. Es gibt auch einige großartige Apache-Bibliotheken, die Sie auch verwenden können. Wenn Sie nur etwas einfaches brauchen, ist dies ein gutes schnelles Beispiel . :) –

+0

Ya, Haversine Formel ist auf dem Prinzip 'nahe ist gut genug' aufgebaut. Zu der Zeit, als wir Entfernungen von <50 Meilen gemessen haben, um die Nähe von einem Ort zum anderen als "Heuristik" zu bestimmen. –

0

ich verwende in der Regel MATLAB mit den Mapping Toolbox, und dann den Code in meinen Java verwenden mit MATLAB Builder JA. Es hat mein Leben einfacher viel macht. Angesichts der Tatsache, dass die meisten Schulen kostenlosen Zugang zu den Studenten haben, können Sie es ausprobieren (oder erhalten Sie die Testversion, um über Ihre Arbeit zu kommen).

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

Ich weiß, dass es viele Antworten, aber einige der Forschung zu diesem Thema, fand ich, dass die meisten Antworten hier die Haversine Formel verwenden, aber die Vincenty Formel ist eigentlich genauer. Es gab einen Beitrag, der die Berechnung von einer Javascript-Version anpasste, aber es ist sehr unhandlich. Ich fand eine Version, die besser ist, weil:

  1. Es hat auch eine offene Lizenz.
  2. Es verwendet OOP-Prinzipien.
  3. Es ist flexibler, das Ellipsoid zu wählen, das Sie verwenden möchten.
  4. Es hat mehr Methoden, um verschiedene Berechnungen in der Zukunft zu ermöglichen.
  5. Es ist gut dokumentiert.

VincentyDistanceCalculator

1

dieser Methode können Sie den Abstand zwischen der geographischen Lage in km finden würde helfen.

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