获取给定当前点、距离和方位的纬度/经度
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。
地球不是完全光滑的(高度变化)等等。
由于构造板块运动,地理点的纬度/经度位置可能每年变化数毫米(至少)。
因此,在各种几何模型中使用了许多不同的假设,它们的应用方式不同,具体取决于您所需的精度。因此,要回答这个问题,您需要考虑您希望得到的结果的准确性。
一些例子:
我只是在latitudes
0-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:) 这样的新手读者使用更多的说明 请查看我的答案并链接到它使用的公式以及我们可以预期的准确度。以上是关于获取给定当前点、距离和方位的纬度/经度的主要内容,如果未能解决你的问题,请参考以下文章