如何计算距离另一点一定距离的点的纬度?

问题描述:

在地图上画一个圆我有一个以米为单位的中心GLatLng(A)和半径(r)。如何计算距离另一点一定距离的点的纬度?

这里有一个图:

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

如何计算的GLatLng位置B?假设r与赤道平行。

使用GLatLng.distanceFrom()方法获得A和B时的半径是微不足道的 - 但以相反的方式进行。似乎我需要做一些更重的数学。

+0

@Rene :使我的答案适应GMaps API v2应该很简单。我相信这只是用'GLatLng'替换'google.maps.LatLng'的问题。如果您发现任何困难,请告诉我。 – 2010-04-14 12:20:57

+0

谢谢,这里没有任何困难:) – 2010-04-14 12:34:31

我们需要给出一个轴承和从源点移动的距离时,返回目标点的方法。幸运的是,Chris Veness在Calculate distance, bearing and more between Latitude/Longitude points上有一个很好的JavaScript实现。

下已经适应与google.maps.LatLng类工作:

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

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

下面是使用Google Maps API v3一个完整的例子:

<!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> 

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

只需将如下使用

屏幕截图:

Google Maps Geometry

UPDATE:

在回答Paul's下面的评论,这是当圆圈环绕的一极会发生什么。

绘制pointA北极附近,与千公里半径:

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

截图供pointA.destinationPoint(90, radius)

Close to north pole

+1

那么这总是会让目的地位于东方吗? – Nirmal 2010-04-14 12:06:36

+4

@Nirmal:不,它取决于你传递给'destinationPoint()'的第一个参数。 90为东,但您可以使用任何轴承,从0 =北开始,顺时针方向移动。 – 2010-04-14 12:09:48

+0

谢谢,那很完美。 – 2010-04-14 12:31:28

的这个问题的答案,更可以在这里找到:http://www.edwilliams.org/avform.htm

+0

我在页面上看到很多acos,sin,tan等。但我会坚持丹尼尔的回答。感谢您的帮助。 – 2010-04-14 12:32:29

+0

@保罗,谢谢你这样一个不错的链接... – aProgrammer 2011-09-16 09:01:45

+0

链接现在被打破了。 – 2018-01-27 17:18:02

如果你是2纬度/经度点之间的距离后,穿过地球表面,那么你可以在这里找到javascript:

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

这是Android的API用于在android.location.Location::distanceTo

您可以轻松地从JavaScript将代码转换成Java相同的公式。

如果要计算目标点给出的起点,方位和距离, 那么你需要这个方法:

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

下面是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

请注意:Vincenty的公式精确到0.5mm或0.000015“(!)以内,正在使用的椭球体。基于球形模型的计算,如(更简单)Haversine,精确到0.3%左右。所以以前的JavaScript解决方案可能是大多数人需要的。 – 2012-12-13 19:39:19

用于许多测地计算的Javascript(直接&反问题,面积计算等)。可在

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

示例用法是

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

Google地图的界面处所示

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

这包括绘制测地线(蓝色提供),测地圆(绿色)和测地线包络(红色)。

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

为了从另一个给定的方位和距离计算纬度,经度点,你可以使用google's JavaScript实现:

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 对于文档