获取给定当前点、距离和方位的纬度/经度

Posted

技术标签:

【中文标题】获取给定当前点、距离和方位的纬度/经度【英文标题】:Get lat/long given current point, distance and bearing 【发布时间】:2011-11-05 13:09:16 【问题描述】:

给定一个现有的纬度/经度点、距离(公里)和方位角(度数转换为弧度),我想计算新的纬度/经度。 This 网站一遍又一遍地出现,但我就是无法让公式为我工作。

上面链接的公式是:

lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))

lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))

以上公式适用于MSExcel,其中-

asin          = arc sin()   
d             = distance (in any unit)   
R             = Radius of the earth (in the same unit as above)  
and hence d/r = is the angular distance (in radians)  
atan2(a,b)    = arc tan(b/a)  
θ is the bearing (in radians, clockwise from north);  

这是我在 Python 中得到的代码。

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = 52.20472 * (math.pi * 180) #Current lat point converted to radians
lon1 = 0.14056 * (math.pi * 180) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
             math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
                     math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

print(lat2)
print(lon2)

我明白了

lat2 = 0.472492248844 
lon2 = 79.4821662373

【问题讨论】:

@GWW 我得到了一个没有意义的答案。它没有意义的原因是因为我没有将答案转换回度数。代码已更改并作为编辑包含在原始帖子中。 您应该简单地将您的编辑作为答案提交并接受该答案,以更清楚地表明您解决了自己的问题。否则,SO 会因您留下未解决的问题而受到处罚,从而使未来的用户更有可能不会费心回答您的问题。 如果你使用 numpy 对象,你会得到更好的精度和结果。 @Cerin - 感谢您的建议。 不应该是“lat1 = 52.20472 * (math.pi */180)”吗? 【参考方案1】:

需要将答案从弧度转换回度数。工作代码如下:

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = math.radians(52.20472) #Current lat point converted to radians
lon1 = math.radians(0.14056) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
     math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
             math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

lat2 = math.degrees(lat2)
lon2 = math.degrees(lon2)

print(lat2)
print(lon2)

【讨论】:

对我来说也是同样的结果 谢谢implemented that snippet in Kotlin。 我注意到如果原纬度为0,原经度为-179,方位角为270度(1.5pi弧度),距离1500km,得到的经度为-192.4,即地图上不存在。 谢谢你用 C# 实现了一个 sn-p gist.github.com/BicycleMark/3e1a2152febaa2935e4c8cfcea7e061b 我验证了代码输出使用:fcc.gov/media/radio/find-terminal-coordinates【参考方案2】:

geopy 库支持这一点:

import geopy
from geopy.distance import VincentyDistance

# given: lat1, lon1, b = bearing in degrees, d = distance in kilometers

origin = geopy.Point(lat1, lon1)
destination = VincentyDistance(kilometers=d).destination(origin, b)

lat2, lon2 = destination.latitude, destination.longitude

通过https://***.com/a/4531227/37610找到

【讨论】:

这个库有一些距离问题有待解决:github.com/geopy/geopy/pull/144 请注意,API 自 v2.0.0 起已更改。而是使用geopy.distance.geodesic:***.com/a/62866744/4717384【参考方案3】:

这个问题在geodesy的研究中被称为直接问题

这确实是一个非常受欢迎的问题,也是一个经常引起混淆的问题。原因是大多数人都在寻找一个简单直接的答案。但是没有,因为大多数问这个问题的人没有提供足够的信息,仅仅是因为他们没有意识到:

    地球不是一个完美的球体,因为它被两极压扁/压缩 因为 (1) 地球没有恒定的半径,R。见here。 地球不是完全光滑的(高度变化)等等。 由于构造板块运动,地理点的纬度/经度位置可能每年变化数毫米(至少)。

因此,在各种几何模型中使用了许多不同的假设,它们的应用方式不同,具体取决于您所需的精度。因此,要回答这个问题,您需要考虑您希望得到的结果的准确性

一些例子:

我只是在latitudes0-70 deg之间寻找小( 100 公里)距离的最近几公里的大致位置N|S。 (地球是~平面模型。) 我想要一个在全球任何地方都很好的答案,但只能精确到几米左右 我想要一个超精确的定位,它在nanometers [nm] 的原子尺度下有效。 我想要快速且易于计算且计算量不大的答案。

因此,您可以有多种选择使用哪种算法。此外,每种编程语言都有自己的实现或“包”乘以模型的数量和模型开发人员的特定需求。出于所有实际目的,除了javascript 之外,忽略任何其他语言都是值得的,因为它本质上非常类似于伪代码。因此,它可以很容易地转换为任何其他语言,只需极少的更改。

那么主要的型号有:

Euclidian/Flat earth model: 适用于 10 公里以下的短距离 Spherical model:适用于较大的纵向距离,但纬度差异较小。流行型号: Haversine: [km] 刻度精度,非常简单的代码。 Ellipsoidal models:在任何纬度/经度和距离上最准确,但仍然是一个数值近似值,取决于您需要的准确度。一些流行的模型是: Lambert:~10 米 精度超过 1000 的 km。 Paul D.Thomas: Andoyer-Lambert 近似 Vincenty:毫米精度和计算效率 Kerney:纳米精度

参考资料:

https://en.wikipedia.org/wiki/Reference_ellipsoid https://en.wikipedia.org/wiki/Haversine_formula https://en.wikipedia.org/wiki/Earth_ellipsoid https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid https://en.wikipedia.org/wiki/Vincenty%27s_formulae https://geographiclib.sourceforge.io/scripts/geod-calc.html

【讨论】:

【参考方案4】:

回答可能有点晚,但在测试其他答案后,它们似乎无法正常工作。这是我们用于系统的 php 代码。全方位工作。

PHP 代码:

lat1 = 起点的纬度,以度为单位

long1 = 起点的经度,单位为度

d = 以公里为单位的距离

角度 = 方位角

function get_gps_distance($lat1,$long1,$d,$angle)

    # Earth Radious in KM
    $R = 6378.14;

    # Degree to Radian
    $latitude1 = $lat1 * (M_PI/180);
    $longitude1 = $long1 * (M_PI/180);
    $brng = $angle * (M_PI/180);

    $latitude2 = asin(sin($latitude1)*cos($d/$R) + cos($latitude1)*sin($d/$R)*cos($brng));
    $longitude2 = $longitude1 + atan2(sin($brng)*sin($d/$R)*cos($latitude1),cos($d/$R)-sin($latitude1)*sin($latitude2));

    # back to degrees
    $latitude2 = $latitude2 * (180/M_PI);
    $longitude2 = $longitude2 * (180/M_PI);

    # 6 decimal for Leaflet and other system compatibility
   $lat2 = round ($latitude2,6);
   $long2 = round ($longitude2,6);

   // Push in array and get back
   $tab[0] = $lat2;
   $tab[1] = $long2;
   return $tab;
 

【讨论】:

看起来不错,但我认为请求者希望在 python 中有一些东西。错了吗? 最好命名为get_gps_coord 或类似名称。你没有得到距离,你将它提供给函数。但谢谢你,这正是我想要的。许多搜索返回计算坐标之间的距离(误报)。谢谢! 太棒了!感谢您的贡献! 6,378.14 km 似乎是地球的最大半径。平均值约为6,371.0 km,这样可以进行更准确的计算。 感谢您为我节省了一点时间。【参考方案5】:

我将 Brad 的答案移植到 vanilla JS 答案,没有 Bing 地图依赖

https://jsfiddle.net/kodisha/8a3hcjtd/

    // ----------------------------------------
    // Calculate new Lat/Lng from original points
    // on a distance and bearing (angle)
    // ----------------------------------------
    let llFromDistance = function(latitude, longitude, distance, bearing) 
      // taken from: https://***.com/a/46410871/13549 
      // distance in KM, bearing in degrees
    
      const R = 6378.1; // Radius of the Earth
      const brng = bearing * Math.PI / 180; // Convert bearing to radian
      let lat = latitude * Math.PI / 180; // Current coords to radians
      let lon = longitude * Math.PI / 180;
    
      // Do the math magic
      lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
      lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance / R) - Math.sin(lat) * Math.sin(lat));
    
      // Coords back to degrees and return
      return [(lat * 180 / Math.PI), (lon * 180 / Math.PI)];
    
    
    
    let pointsOnMapCircle = function(latitude, longitude, distance, numPoints) 
      const points = [];
      for (let i = 0; i <= numPoints - 1; i++) 
        const bearing = Math.round((360 / numPoints) * i);
        console.log(bearing, i);
        const newPoints = llFromDistance(latitude, longitude, distance, bearing);
        points.push(newPoints);
      
      return points;
    
    
    const points = pointsOnMapCircle(41.890242042122836, 12.492358982563019, 0.2, 8);
    let geoJSON = 
      "type": "FeatureCollection",
      "features": []
    ;
    points.forEach((p) => 
      geoJSON.features.push(
        "type": "Feature",
        "properties": ,
        "geometry": 
          "type": "Point",
          "coordinates": [
            p[1],
            p[0]
          ]
        
      );
    );
    
    document.getElementById('res').innerHTML = JSON.stringify(geoJSON, true, 2);

此外,我添加了geoJSON 导出,因此您只需将生成的geoJSON 粘贴到:http://geojson.io/#map=17/41.89017/12.49171 即可立即查看结果。

结果:

【讨论】:

geojson 的地图对我在地图中定位位置很有帮助 谢谢@kodisha,你的小提琴帮了我很多! 与我在上一个答案中的评论相同,我认为经度计算的最后一部分可能是错误的,因为变量lat 在计算lon 之前已经更新,即术语Math.sin(lat) * Math.sin(lat)实际上并没有分别使用旧纬度和新纬度。【参考方案6】:

使用 geopy 的快速方法

from geopy import distance
#distance.distance(unit=15).destination((lat,lon),bering) 
#Exemples
distance.distance(nautical=15).destination((-24,-42),90) 
distance.distance(miles=15).destination((-24,-42),90)
distance.distance(kilometers=15).destination((-24,-42),90) 

【讨论】:

不说你计算的方法,答案基本没用。 @not2qubit @plinio-bueno-andrade-silva 是否知道,geopy.distance.distance currently uses geodesic. geopy 更具体地说,默认使用的椭球模型是 WGS-84 椭球,"这是全球最准确的。”【参考方案7】:

lon1 和 lat1 度数

brng = 方位角

d = 距离(公里)

R = 地球半径,单位为千米

lat2 = math.degrees((d/R) * math.cos(brng)) + lat1
long2 = math.degrees((d/(R*math.sin(math.radians(lat2)))) * math.sin(brng)) + long1

我用 PHP 实现了您的算法和我的算法,并对其进行了基准测试。这个版本在大约 50% 的时间内运行。生成的结果是相同的,因此在数学上似乎是等价的。

我没有测试上面的python代码,所以可能有语法错误。

【讨论】:

不工作。从北到南,结果是正确的,但在“东西”方向上是错误的。【参考方案8】:

我将 Python 移植到了 Javascript。这将返回一个 Bing Maps Location 对象,您可以更改为任何您喜欢的对象。

getLocationXDistanceFromLocation: function(latitude, longitude, distance, bearing) 
    // distance in KM, bearing in degrees

    var R = 6378.1,                         // Radius of the Earth
        brng = Math.radians(bearing)       // Convert bearing to radian
        lat = Math.radians(latitude),       // Current coords to radians
        lon = Math.radians(longitude);

    // Do the math magic
    lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
    lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance/R)-Math.sin(lat)*Math.sin(lat));

    // Coords back to degrees and return
    return new Microsoft.Maps.Location(Math.degrees(lat), Math.degrees(lon));

,

【讨论】:

请发布功能代码,包括运行所需的代码。 IE。这似乎依赖于 Microsoft.Maps。在哪里可以找到/如何安装它? 如果您的程序使用 Bing 地图,您将只使用 Bing (Microsoft) Maps。只需获取 Math.degrees(lat)Math.degrees(lon) 值,然后为您的应用程序做任何您需要的事情。【参考方案9】:

也迟了,但对于那些可能会发现这一点的人,您将使用geographiclib 库获得更准确的结果。查看测地线问题描述和 JavaScript 示例,以轻松介绍如何使用来回答主题问题以及许多其他问题。包括 Python 在内的多种语言的实现。如果您关心准确性,则比自己编写代码要好得多;比早期“使用库”建议中的 VincentyDistance 更好。正如文档所说:“重点是返回准确的结果,误差接近四舍五入(大约 5-15 纳米)。”

【讨论】:

【参考方案10】:

只需交换 atan2(y,x) 函数中的值。不是 atan2(x,y)!

【讨论】:

【参考方案11】:

如果有人想要这个,我将@David M 的答案移植到 java...我确实得到了一个略有不同的结果 52.20462299620793, 0.360433887489931

    double R = 6378.1;  //Radius of the Earth
    double brng = 1.57;  //Bearing is 90 degrees converted to radians.
    double d = 15;  //Distance in km

    double lat2 = 52.20444; // - the lat result I'm hoping for
    double lon2 = 0.36056; // - the long result I'm hoping for.

    double lat1 = Math.toRadians(52.20472); //Current lat point converted to radians
    double lon1 = Math.toRadians(0.14056); //Current long point converted to radians

    lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
            Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));

    lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
            Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

    lat2 = Math.toDegrees(lat2);
    lon2 = Math.toDegrees(lon2);

    System.out.println(lat2 + ", " + lon2);

【讨论】:

这可能是最正确的答案,因为在计算lon2 表达式的最后一项,即Math.sin(lat1)*Math.sin(lat2) 时,它分别正确地使用了旧纬度和新纬度。因此结果略有不同。【参考方案12】:

感谢@kodisha,这是一个 Swift 版本,但对地球半径进行了改进和更精确的计算:

extension CLLocationCoordinate2D 
  
  func earthRadius() -> CLLocationDistance 
    let earthRadiusInMetersAtSeaLevel = 6378137.0
    let earthRadiusInMetersAtPole = 6356752.314
    
    let r1 = earthRadiusInMetersAtSeaLevel
    let r2 = earthRadiusInMetersAtPole
    let beta = latitude
    
    let earthRadiuseAtGivenLatitude = (
      ( pow(pow(r1, 2) * cos(beta), 2) + pow(pow(r2, 2) * sin(beta), 2) ) /
        ( pow(r1 * cos(beta), 2) + pow(r2 * sin(beta), 2) )
    )
    .squareRoot()
    
    return earthRadiuseAtGivenLatitude
  
  
  func locationByAdding(
    distance: CLLocationDistance,
    bearing: CLLocationDegrees
  ) -> CLLocationCoordinate2D 
    let latitude = self.latitude
    let longitude = self.longitude
    
    let earthRadiusInMeters = self.earthRadius()
    let brng = bearing.degreesToRadians
    var lat = latitude.degreesToRadians
    var lon = longitude.degreesToRadians
    
    lat = asin(
      sin(lat) * cos(distance / earthRadiusInMeters) +
        cos(lat) * sin(distance / earthRadiusInMeters) * cos(brng)
    )
    lon += atan2(
      sin(brng) * sin(distance / earthRadiusInMeters) * cos(lat),
      cos(distance / earthRadiusInMeters) - sin(lat) * sin(lat)
    )
    
    let newCoordinate = CLLocationCoordinate2D(
      latitude: lat.radiansToDegrees,
      longitude: lon.radiansToDegrees
    )
    
    return newCoordinate
  


extension FloatingPoint 
  var degreesToRadians: Self  self * .pi / 180 
  var radiansToDegrees: Self  self * 180 / .pi 

【讨论】:

我认为经度计算的最后一部分可能是错误的,因为变量 lat 在计算 lon 之前已经更新,即术语 sin(lat) * sin(lat) 实际上并没有同时使用旧的和旧的新的纬度。【参考方案13】:

这是一个基于 Ed Williams Aviation Formulary 的 PHP 版本。模数在 PHP 中的处理方式略有不同。这对我有用。

function get_new_waypoint ( $lat, $lon, $radial, $magvar, $range )


   // $range in nm.
   // $radial is heading to or bearing from    
   // $magvar for local area.

   $range = $range * pi() /(180*60);
   $radial = $radial - $magvar ;

   if ( $radial < 1 )
     
        $radial = 360 + $radial - $magvar; 
     
   $radial =  deg2rad($radial);
   $tmp_lat = deg2rad($lat);
   $tmp_lon = deg2rad($lon);
   $new_lat = asin(sin($tmp_lat)* cos($range) + cos($tmp_lat) * sin($range) * cos($radial));
   $new_lat = rad2deg($new_lat);
   $new_lon = $tmp_lon - asin(sin($radial) * sin($range)/cos($new_lat))+ pi() % 2 * pi() -  pi();
   $new_lon = rad2deg($new_lon);

   return $new_lat." ".$new_lon;


【讨论】:

您能解释几个变量吗? $range 和 $magvar 可以为像 (me:) 这样的新手读者使用更多的说明 请查看我的答案并链接到它使用的公式以及我们可以预期的准确度。

以上是关于获取给定当前点、距离和方位的纬度/经度的主要内容,如果未能解决你的问题,请参考以下文章

给定初始纬度 lng,距离和方位角,在 php 中查找纬度经度点

使用经度和纬度查找给定距离内的所有附近客户

在一定距离处查找经纬度四个方向的点

从另一点以某个方位行驶一段距离后如何获得纬度和经度?

已知基点的经纬度,根据方位角和运动距离求另外一点的经纬度

当经纬度为已知点时计算100米距离