2010-04-14 9 views
41

Um einen Kreis auf der Karte zeichnet ich habe ein Zentrum GLatLng (A) und einen Radius (r) in Metern.Wie berechnet man den Abstand eines Punktes in einer bestimmten Entfernung von einem anderen?

Hier ist ein Diagramm:

  ----------- 
     --/   \-- 
     -/     \- 
    /     \ 
    /      \ 
/     r  \ 
    |   *-------------* 
    \    A   /B 
    \      /
    \     /
     -\     /- 
     --\   /-- 
      ----------- 

Wie die GLatLng an Position B berechnen? Angenommen, dass r parallel zum Äquator ist.

Erhalten des Radius, wenn A und B gegeben ist trivial, die GLatLng.distanceFrom() Methode verwendet - aber umgekehrt nicht zu tun. Scheint, dass ich etwas schwerere Mathe machen muss.

+0

@Rene : Die Anpassung meiner Antwort an die GMaps API v2 sollte einfach sein. Ich glaube, es ist nur eine Frage von 'google.maps.LatLng' mit' GLatLng' zu ersetzen. Lassen Sie es mich wissen, wenn Sie Schwierigkeiten haben. –

+0

Danke, keine Schwierigkeiten hier :) –

Antwort

85

Wir werden eine Methode benötigen, die den Zielpunkt zurückkehrt, wenn ein Lager und die von einem Quellpunkt zurückgelegte Strecke gegeben. Zum Glück gibt es eine sehr gute JavaScript-Implementierung von Chris Veness unter Calculate distance, bearing and more between Latitude/Longitude points.

Nachfolgend wurde angepasst, um mit der google.maps.LatLng Klasse zu arbeiten:

Number.prototype.toRad = function() { 
    return this * Math.PI/180; 
} 

Number.prototype.toDeg = function() { 
    return this * 180/Math.PI; 
} 

google.maps.LatLng.prototype.destinationPoint = function(brng, dist) { 
    dist = dist/6371; 
    brng = brng.toRad(); 

    var lat1 = this.lat().toRad(), lon1 = this.lng().toRad(); 

    var lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) + 
         Math.cos(lat1) * Math.sin(dist) * Math.cos(brng)); 

    var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(dist) * 
           Math.cos(lat1), 
           Math.cos(dist) - Math.sin(lat1) * 
           Math.sin(lat2)); 

    if (isNaN(lat2) || isNaN(lon2)) return null; 

    return new google.maps.LatLng(lat2.toDeg(), lon2.toDeg()); 
} 

Sie einfach es benutzen würde, wie folgt:

var pointA = new google.maps.LatLng(25.48, -71.26); 
var radiusInKm = 10; 

var pointB = pointA.destinationPoint(90, radiusInKm); 

hier ein komplettes Beispiel ist Google Maps API v3 mit:

<!DOCTYPE html> 
<html> 
<head> 
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 
    <title>Google Maps Geometry</title> 
    <script src="http://maps.google.com/maps/api/js?sensor=false" 
      type="text/javascript"></script> 
</head> 
<body> 
    <div id="map" style="width: 400px; height: 300px"></div> 

    <script type="text/javascript"> 
     Number.prototype.toRad = function() { 
     return this * Math.PI/180; 
     } 

     Number.prototype.toDeg = function() { 
     return this * 180/Math.PI; 
     } 

     google.maps.LatLng.prototype.destinationPoint = function(brng, dist) { 
     dist = dist/6371; 
     brng = brng.toRad(); 

     var lat1 = this.lat().toRad(), lon1 = this.lng().toRad(); 

     var lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) + 
           Math.cos(lat1) * Math.sin(dist) * Math.cos(brng)); 

     var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(dist) * 
             Math.cos(lat1), 
             Math.cos(dist) - Math.sin(lat1) * 
             Math.sin(lat2)); 

     if (isNaN(lat2) || isNaN(lon2)) return null; 

     return new google.maps.LatLng(lat2.toDeg(), lon2.toDeg()); 
     } 

     var pointA = new google.maps.LatLng(40.70, -74.00); // Circle center 
     var radius = 10;          // 10km 

     var mapOpt = { 
     mapTypeId: google.maps.MapTypeId.TERRAIN, 
     center: pointA, 
     zoom: 10 
     }; 

     var map = new google.maps.Map(document.getElementById("map"), mapOpt); 

     // Draw the circle 
     new google.maps.Circle({ 
     center: pointA, 
     radius: radius * 1000,  // Convert to meters 
     fillColor: '#FF0000', 
     fillOpacity: 0.2, 
     map: map 
     }); 

     // Show marker at circle center 
     new google.maps.Marker({ 
     position: pointA, 
     map: map 
     }); 

     // Show marker at destination point 
     new google.maps.Marker({ 
     position: pointA.destinationPoint(90, radius), 
     map: map 
     }); 
    </script> 
</body> 
</html> 

Screenshot:

Google Maps Geometry

UPDATE:

In Antwort auf Paul's Kommentar unten, das ist, was passiert, wenn der Kreis wickelt sich um einen der Pole.

Plotten pointA in der Nähe des Nordpols mit einem Radius von 1.000 km:

var pointA = new google.maps.LatLng(85, 0); // Close to north pole 
    var radius = 1000;       // 1000km 

Screenshots für pointA.destinationPoint(90, radius):

Close to north pole

+1

Gibt es also immer den im Osten liegenden Zielpunkt? – Nirmal

+4

@Nirmal: Nein, es hängt vom ersten Parameter ab, den Sie an 'destinationPoint()' übergeben. 90 ist Osten, aber Sie können jedes Lager verwenden, beginnend mit 0 = Norden, im Uhrzeigersinn. –

+0

Danke, das ist perfekt. –

3

Die Antwort auf diese Frage und mehr ist hier zu finden: http://www.edwilliams.org/avform.htm

+0

Ich sehe eine Menge von Acos, Sünde, Bräune, etc. auf dieser Seite. Aber ich bleibe bei der Antwort von Daniel. Danke fürs Helfen. –

+0

@Paul, danke für so einen netten Link ... – aProgrammer

+0

Der Link ist mittlerweile kaputt. –

3

Wenn Sie nach dem Abstand zwischen 2 lat/lng Punkten dann können Sie die Javascript finden Sie hier auf der Erdoberfläche:

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

Dies ist die gleiche Formel in Android-API verwendet, um android.location.Location::distanceTo

Sie können ganz einfach den Code aus Javascript Java umwandeln.

Wenn Sie den Zielpunkt angesichts der Startpunkt, Peilung und Entfernung berechnen wollen, dann müssen Sie diese Methode:

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

Hier sind die Formeln in java:

public class LatLngUtils { 

    /** 
    * @param lat1 
    *   Initial latitude 
    * @param lon1 
    *   Initial longitude 
    * @param lat2 
    *   destination latitude 
    * @param lon2 
    *   destination longitude 
    * @param results 
    *   To be populated with the distance, initial bearing and final 
    *   bearing 
    */ 

    public static void computeDistanceAndBearing(double lat1, double lon1, 
     double lat2, double lon2, double results[]) { 
    // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf 
    // using the "Inverse Formula" (section 4) 

    int MAXITERS = 20; 
    // Convert lat/long to radians 
    lat1 *= Math.PI/180.0; 
    lat2 *= Math.PI/180.0; 
    lon1 *= Math.PI/180.0; 
    lon2 *= Math.PI/180.0; 

    double a = 6378137.0; // WGS84 major axis 
    double b = 6356752.3142; // WGS84 semi-major axis 
    double f = (a - b)/a; 
    double aSqMinusBSqOverBSq = (a * a - b * b)/(b * b); 

    double L = lon2 - lon1; 
    double A = 0.0; 
    double U1 = Math.atan((1.0 - f) * Math.tan(lat1)); 
    double U2 = Math.atan((1.0 - f) * Math.tan(lat2)); 

    double cosU1 = Math.cos(U1); 
    double cosU2 = Math.cos(U2); 
    double sinU1 = Math.sin(U1); 
    double sinU2 = Math.sin(U2); 
    double cosU1cosU2 = cosU1 * cosU2; 
    double sinU1sinU2 = sinU1 * sinU2; 

    double sigma = 0.0; 
    double deltaSigma = 0.0; 
    double cosSqAlpha = 0.0; 
    double cos2SM = 0.0; 
    double cosSigma = 0.0; 
    double sinSigma = 0.0; 
    double cosLambda = 0.0; 
    double sinLambda = 0.0; 

    double lambda = L; // initial guess 
    for (int iter = 0; iter < MAXITERS; iter++) { 
     double lambdaOrig = lambda; 
     cosLambda = Math.cos(lambda); 
     sinLambda = Math.sin(lambda); 
     double t1 = cosU2 * sinLambda; 
     double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda; 
     double sinSqSigma = t1 * t1 + t2 * t2; // (14) 
     sinSigma = Math.sqrt(sinSqSigma); 
     cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15) 
     sigma = Math.atan2(sinSigma, cosSigma); // (16) 
     double sinAlpha = (sinSigma == 0) ? 0.0 : cosU1cosU2 * sinLambda 
     /sinSigma; // (17) 
     cosSqAlpha = 1.0 - sinAlpha * sinAlpha; 
     cos2SM = (cosSqAlpha == 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2 
     /cosSqAlpha; // (18) 

     double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn 
     A = 1 + (uSquared/16384.0) * // (3) 
      (4096.0 + uSquared * (-768 + uSquared * (320.0 - 175.0 * uSquared))); 
     double B = (uSquared/1024.0) * // (4) 
      (256.0 + uSquared * (-128.0 + uSquared * (74.0 - 47.0 * uSquared))); 
     double C = (f/16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10) 
     double cos2SMSq = cos2SM * cos2SM; 
     deltaSigma = B 
      * sinSigma 
      * // (6) 
      (cos2SM + (B/4.0) 
       * (cosSigma * (-1.0 + 2.0 * cos2SMSq) - (B/6.0) * cos2SM 
        * (-3.0 + 4.0 * sinSigma * sinSigma) 
        * (-3.0 + 4.0 * cos2SMSq))); 

     lambda = L 
      + (1.0 - C) 
      * f 
      * sinAlpha 
      * (sigma + C * sinSigma 
       * (cos2SM + C * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM))); // (11) 

     double delta = (lambda - lambdaOrig)/lambda; 
     if (Math.abs(delta) < 1.0e-12) { 
     break; 
     } 
    } 

    double distance = (b * A * (sigma - deltaSigma)); 
    results[0] = distance; 
    if (results.length > 1) { 
     double initialBearing = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2 
      - sinU1 * cosU2 * cosLambda); 
     initialBearing *= 180.0/Math.PI; 
     results[1] = initialBearing; 
     if (results.length > 2) { 
     double finalBearing = Math.atan2(cosU1 * sinLambda, -sinU1 * cosU2 
      + cosU1 * sinU2 * cosLambda); 
     finalBearing *= 180.0/Math.PI; 
     results[2] = finalBearing; 
     } 
    } 
    } 

    /* 
    * Vincenty Direct Solution of Geodesics on the Ellipsoid (c) Chris Veness 
    * 2005-2012 
    * 
    * from: Vincenty direct formula - T Vincenty, "Direct and Inverse Solutions 
    * of Geodesics on the Ellipsoid with application of nested equations", Survey 
    * Review, vol XXII no 176, 1975 http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf 
    */ 

    /** 
    * Calculates destination point and final bearing given given start point, 
    * bearing & distance, using Vincenty inverse formula for ellipsoids 
    * 
    * @param lat1 
    *   start point latitude 
    * @param lon1 
    *   start point longitude 
    * @param brng 
    *   initial bearing in decimal degrees 
    * @param dist 
    *   distance along bearing in metres 
    * @returns an array of the desination point coordinates and the final bearing 
    */ 

    public static void computeDestinationAndBearing(double lat1, double lon1, 
     double brng, double dist, double results[]) { 
    double a = 6378137, b = 6356752.3142, f = 1/298.257223563; // WGS-84 
                   // ellipsiod 
    double s = dist; 
    double alpha1 = toRad(brng); 
    double sinAlpha1 = Math.sin(alpha1); 
    double cosAlpha1 = Math.cos(alpha1); 

    double tanU1 = (1 - f) * Math.tan(toRad(lat1)); 
    double cosU1 = 1/Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1; 
    double sigma1 = Math.atan2(tanU1, cosAlpha1); 
    double sinAlpha = cosU1 * sinAlpha1; 
    double cosSqAlpha = 1 - sinAlpha * sinAlpha; 
    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 sinSigma = 0, cosSigma = 0, deltaSigma = 0, cos2SigmaM = 0; 
    double sigma = s/(b * A), sigmaP = 2 * Math.PI; 

    while (Math.abs(sigma - sigmaP) > 1e-12) { 
     cos2SigmaM = Math.cos(2 * sigma1 + sigma); 
     sinSigma = Math.sin(sigma); 
     cosSigma = Math.cos(sigma); 
     deltaSigma = B 
      * sinSigma 
      * (cos2SigmaM + B 
      /4 
       * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B/6 
        * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) 
        * (-3 + 4 * cos2SigmaM * cos2SigmaM))); 
     sigmaP = sigma; 
     sigma = s/(b * A) + deltaSigma; 
    } 

    double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1; 
    double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, 
     (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)); 
    double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 
     * sinSigma * cosAlpha1); 
    double C = f/16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); 
    double L = lambda 
     - (1 - C) 
     * f 
     * sinAlpha 
     * (sigma + C * sinSigma 
      * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); 
    double lon2 = (toRad(lon1) + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise 
                      // to 
                      // -180...+180 

    double revAz = Math.atan2(sinAlpha, -tmp); // final bearing, if required 

    results[0] = toDegrees(lat2); 
    results[1] = toDegrees(lon2); 
    results[2] = toDegrees(revAz); 

    } 

    private static double toRad(double angle) { 
    return angle * Math.PI/180; 
    } 

    private static double toDegrees(double radians) { 
    return radians * 180/Math.PI; 
    } 

} 
+0

Beachten Sie, dass Vincentys Formel auf dem verwendeten Ellipsoid innerhalb von 0,5 mm oder 0,000015 "(!) Genau ist. Berechnungen, die auf einem sphärischen Modell basieren, wie dem (viel einfacheren) Haversine, sind auf etwa 0,3% genau. Also die vorherige Javascript-Lösung ist wahrscheinlich das, was die meisten Leute brauchen. –

2

Javascript für viele geodätische Berechnungen (direkte & inverse Probleme, Flächenberechnungen, etc). bei

http://geographiclib.sourceforge.net/scripts/geographiclib.js

Verwendungsbeispiel verfügbar ist, wird in

http://geographiclib.sourceforge.net/scripts/geod-calc.html

gezeigt

Eine Schnittstelle Google Maps wird bei

bereitgestellt

http://geographiclib.sourceforge.net/scripts/geod-google.html

Dazu gehört eine geodätische Plotten (blau), geodätischer Kreis (grün) und die geodätische Hülle (rot).

sample geodesic starting in New Zealand and wrapping 1.5 times round the world

0

Um einen lat, long Punkt bei einer gegebenen Peilung und Entfernung von einem anderen zu berechnen Sie Googles verwenden können JavaScript-Implementierung:

var pointA = new google.maps.LatLng(25.48, -71.26); 
var distance = 10; // 10 metres 
var bearing 90; // 90 degrees 
var pointB = google.maps.geometry.spherical.computeOffset(pointA, distance, bearing); 

https://developers.google.com/maps/documentation/javascript/reference#spherical Zur Dokumentation Siehe

Verwandte Themen