Javascript 如何计算与另一个点相距一定距离的点的纬度?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2637023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:15:36  来源:igfitidea点击:

How to calculate the latlng of a point a certain distance away from another?

javascriptgoogle-maps

提问by Rene Saarsoo

To draw a circle on map I have a center GLatLng (A) and a radius (r) in meters.

为了在地图上画一个圆,我有一个中心 GLatLng (A) 和一个以米为单位的半径 (r)。

Here's a diagram:

这是一个图表:

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

How to calculate the GLatLng at position B? Assuming that r is parallel to the equator.

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

Getting the radius when A and B is given is trivial using the GLatLng.distanceFrom() method - but doing it the other way around not so. Seems that I need to do some heavier math.

使用 GLatLng.distanceFrom() 方法在给定 A 和 B 时获取半径是微不足道的 - 但反过来则不然。似乎我需要做一些更重的数学运算。

回答by Daniel Vassallo

We will need a method that returns the destination point when given a bearing and the distance travelled from a source point. Luckily, there is a very good JavaScript implementation by Chris Veness at Calculate distance, bearing and more between Latitude/Longitude points.

我们需要一个方法,当给定方位角和从源点行进的距离时返回目标点。幸运的是,Chris Veness 在计算纬度/经度点之间的距离、方位等方面有一个非常好的 JavaScript 实现。

The following has been adapted to work with the google.maps.LatLngclass:

以下内容已经过调整以适用于google.maps.LatLng该类:

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

You would simply use it as follows:

您只需按如下方式使用它:

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

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

Here is a complete example using Google Maps API v3:

这是使用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>

Screenshot:

截屏:

Google Maps Geometry

谷歌地图几何

UPDATE:

更新:

In reply to Paul'scomment below, this is what happens when the circle wraps around one of the poles.

作为对下面保罗评论的回复,这就是当圆圈环绕其中一个极点时会发生的情况。

Plotting pointAnear the north pole, with a radius of 1,000km:

pointA在北极附近绘制,半径为 1,000 公里:

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

Screenshot for pointA.destinationPoint(90, radius):

截图pointA.destinationPoint(90, radius)

Close to north pole

靠近北极

回答by Fredrik Harloff

To calculate a lat,long point at a given bearing and distance from another you can use google′s JavaScript implementation:

要计算给定方位的纬度和经度点以及与另一个的距离,您可以使用谷歌的 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);

See https://developers.google.com/maps/documentation/javascript/reference#sphericalFor documentation

请参阅https://developers.google.com/maps/documentation/javascript/reference#sphereal有关文档

回答by Paul Tomblin

The answer to this question and more can be found here: http://www.edwilliams.org/avform.htm

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

回答by Dan Brough

If you are after the distance between 2 lat/lng points across the earths surface then you can find the javascript here:

如果您在地球表面上 2 个纬度/经度点之间的距离之后,那么您可以在此处找到 javascript:

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

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

This is the same formula used in android API at android.location.Location::distanceTo

这与 android API 中使用的公式相同 android.location.Location::distanceTo

You can easily convert the code from javascript to java.

您可以轻松地将代码从 javascript 转换为 java。

If you want to calculate the destination point given the start point, bearing and distance, then you need this method:

如果要根据起点、方位和距离计算目标点,则需要此方法:

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

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

Here are the formulae in java:

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

}

回答by cffk

Javascript for many geodesic calculations (direct & inverse problems, area calculations, etc). is available at

用于许多测地线计算(正反问题、面积计算等)的 Javascript。可在

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

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

Sample usage is shown in

示例用法显示在

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

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

An interface to google maps is provided at

谷歌地图的界面提供在

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

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

This includes plotting a geodesic (blue), geodesic circle (green) and the geodesic envelope (red).

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

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

采样测地线始于新西兰并环绕世界 1.5 圈

回答by Flyview

Here's @Daniel Vassallo's answer adapter for Android (java) and using meters instead of km for distances:

这是@Daniel Vassallo 的 Android (java) 应答适配器,并使用米而不是公里来表示距离:

private LatLng getDestinationPoint (LatLng pointStart, double bearing, double distance) {
    distance = distance / 6371000;
    bearing = getRad(bearing);

    double lat1 = getRad(pointStart.latitude);
    double lon1 = getRad(pointStart.longitude);

    double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distance) +
            Math.cos(lat1) * Math.sin(distance) * Math.cos(bearing));

    double lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(distance) *
                    Math.cos(lat1),
            Math.cos(distance) - Math.sin(lat1) *
                    Math.sin(lat2));

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

    return new LatLng(getDeg(lat2), getDeg(lon2));
}

private double getRad(double degrees) {
    return degrees * Math.PI / 180;
}

private double getDeg(double rad) {
    return rad * 180 / Math.PI;
}