计算两个纬度/经度点之间的角度
Posted
技术标签:
【中文标题】计算两个纬度/经度点之间的角度【英文标题】:Calculate angle between two Latitude/Longitude points 【发布时间】:2010-10-14 11:09:15 【问题描述】:有没有办法?
我想要实现的是了解用户的前进方向。例如,用户正在向北、向南、……向东南等方向前进。
但我只有两点(Lng/Ltd)
谢谢
【问题讨论】:
我认为您正在寻找的词是方位而不是角度。方位是从 a 点移动到 b 点时罗盘上显示的行进方向。 【参考方案1】:使用this引用计算角度:
private double angleFromCoordinate(double lat1, double long1, double lat2,
double long2)
double dLon = (long2 - long1);
double y = Math.sin(dLon) * Math.cos(lat2);
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
* Math.cos(lat2) * Math.cos(dLon);
double brng = Math.atan2(y, x);
brng = Math.toDegrees(brng);
brng = (brng + 360) % 360;
brng = 360 - brng; // count degrees counter-clockwise - remove to make clockwise
return brng;
【讨论】:
为我工作,没有 brng = 360 - brng; 由于纬度和经度通常以度数表示,所以在使用此功能之前不要忘记将它们转换为弧度。 cos(lat2) 可以重构出来,留下 tan(lat2) 而不是 sin(lat2) @Michael:你能详细说明一下吗?你的意思是我们可以简单地将所有出现的 cos(lat2) 替换为 1 并将 sin(lat2) 替换为 tan(lat2)? 对我不起作用:angleFromCoordinate(Math.toRadians(0f), Math.toRadians(0f), Math.toRadians(0f), Math.toRadians(45f));结果是270,这是错误的!有什么帮助吗?【参考方案2】:你可以使用谷歌地图计算标题:
var point1 = new google.maps.LatLng(lat1, lng1);
var point2 = new google.maps.LatLng(lat2, lng2);
var heading = google.maps.geometry.spherical.computeHeading(point1,point2);
【讨论】:
使用谷歌地图进行简单的触发似乎有点矫枉过正。 不适用于无法访问谷歌地图的车载系统(例如Arduino项目)【参考方案3】:计算两点间夹角(方位角)的一般公式如下:
θ = atan2(sin(Δlong)*cos(lat2), cos(lat1)*sin(lat2) − sin(lat1)*cos(lat2)*cos(Δlong))
请注意,在使用此公式之前,角度(θ)应转换为弧度,并且 Δlong = long2 - long1。
atan2 是几乎所有编程语言(主要在 Math 包/库中)中都可以找到的常用函数。通常也有角度和弧度之间转换的函数(也在数学包/库中)。
请记住,atan2 返回的值在 -π ... +π 范围内,要将结果转换为指南针方位,您需要将 θ 乘以 180/π,然后使用 (θ+360) % 360,其中 %是模除法运算,返回除法的余数。
以下链接是涉及纬度和经度的公式的一个很好的资源。他们还提供其公式的 javascript 实现。事实上,这个答案是基于这个页面的信息:
http://www.yourhomenow.com/house/haversine.html
【讨论】:
cos(lat2) 可以重构出来,留下 tan(lat2) 而不是 sin(lat2) 链接在 2019 年 12 月失效【参考方案4】:在 Javascript 中,我创建了一个函数名称 angleFromCoordinate
,在其中传递了两个 lat/lng。这个函数将返回两个纬度/经度之间的角度
function angleFromCoordinate(lat1,lon1,lat2,lon2)
var p1 =
x: lat1,
y: lon1
;
var p2 =
x: lat2,
y: lon2
;
// angle in radians
var angleRadians = Math.atan2(p2.y - p1.y, p2.x - p1.x);
// angle in degrees
var angleDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
console.log(angleDeg);
return angleDeg;
工作代码片段
function angleFromCoordinate(lat1,lon1,lat2,lon2)
var p1 =
x: lat1,
y: lon1
;
var p2 =
x: lat2,
y: lon2
;
// angle in radians
var angleRadians = Math.atan2(p2.y - p1.y, p2.x - p1.x);
// angle in degrees
var angleDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
document.getElementById('rotation').innerHTML ="Rotation : "+ angleDeg;
return angleDeg;
angleFromCoordinate(37.330604,-122.028947,37.3322109,-122.0329665);
<html>
<p id="rotation">Rotation : </p>
</html>
【讨论】:
你确定你的答案是正确的,因为对我来说它显示了一些其他的答案。对于相同的坐标,我所做的函数给了我 63.52 的东西,而你的答案是 -68.20。对吗? 为了更好的帮助,你能同时提供 lat lng 吗? @YashasviBhatt【参考方案5】:根据 Nayanesh Gupte 的回答,如果有人需要,这里是一个 Python 实现,说明如何计算由纬度和经度定义的两点之间的角度:
def angleFromCoordinate(lat1, long1, lat2, long2):
dLon = (long2 - long1)
y = math.sin(dLon) * math.cos(lat2)
x = math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(dLon)
brng = math.atan2(y, x)
brng = math.degrees(brng)
brng = (brng + 360) % 360
brng = 360 - brng # count degrees clockwise - remove to make counter-clockwise
return brng
0 度角表示向北方向。
【讨论】:
【参考方案6】:如果您使用的是谷歌地图(android),有一个简单的方法 - 使用 SphericalUtil
double angle = SphericalUtil.computeHeading(fromLatLng, toLatLng);
假设我们有 2 个点及其 lat 和 lng 然后创建它的 Latlng 对象
LatLng latlng = new LatLng(latValue, lngValue);
得到2点的Latlng后,使用sperical util获取角度
//import com.google.maps.android.SphericalUtil;
double sphericalValue = SphericalUtil.computeHeading(latLng1, latLng2);
SpericalValue 是角度。 假设您有一个汽车图标,并将其相应地转向其前进的方向。这里是从 latLng1 到 latLng2 然后
Bitmap vehiclePin = rotateIconBitmap(sphericalValue);
mMap.addMarker(new MarkerOptions().anchor(0.5f, 0.5f).position(latLng2))
.setIcon(BitmapDescriptorFactory.fromBitmap(vehiclePin));
使用下面的方法旋转
Bitmap rotateIconBitmap(double angle)
Bitmap source = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_vehicle_say_car);
Matrix matrix = new Matrix();
matrix.postRotate((float) angle);
return Bitmap.createBitmap(source, 0, 0,
source.getWidth(), source.getHeight(), matrix, true);
轻松实现超级喜欢旋转图标的方法
注意:- 如果标记图标未指向零度,您可能必须添加 90 度的偏移量
android 的sphericalutil 是开源的,如果你使用的是java,请参考它,你可以使用它。
https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/SphericalUtil.java
【讨论】:
角度可能是弧度,进入班级检查。【参考方案7】:如果点之间的距离更小,则示例 javascript 代码 -
brng = Math.atan2(newLat - oldLat, newLong - oldLong);
brng = brng * (180 / Math.PI);
brng = (brng + 360) % 360;
brng = 360 - brng;
【讨论】:
【参考方案8】:要提供航向,您必须计算方位。
要了解方位,请阅读this article。
根据这个article (section bearing),公式是:
θ = atan2( sin Δλ ⋅ cos φ2 , cos φ1 ⋅ sin φ2 − sin φ1 ⋅ cos φ2 ⋅ cos Δλ )
where φ1, λ1 is the start point,
φ2, λ2 the end point,
Δλ is the difference in longitude`
以下示例介绍了如何计算以纬度/经度表示的两点之间的角度(以度为单位)。 (用 C# 完成)
假设Point
是一个简单的类,具有两个double
属性X(表示经度)和Y(表示纬度)。
public double ComputeBearing(Point start,Point end)
var φ1 = start.Y; //latitude 1
var λ1 = start.X; //longitude 1
var φ2 = end.Y; //latitude 2
var λ2 = end.X; //longitude 2
var y = Math.Sin(this.degreeToRadian(λ2 - λ1)) * Math.Cos(this.degreeToRadian(φ2));
var x = Math.Cos(this.degreeToRadian(φ1)) * Math.Sin(this.degreeToRadian(φ2)) - Math.Sin(this.degreeToRadian(φ1)) * Math.Cos(this.degreeToRadian(φ2)) * Math.Cos(this.degreeToRadian(λ2 - λ1));
var θ = Math.Atan2(y, x);
θ = this.radianToDegree(θ);
return θ;
使用以下方法:
public double degreeToRadian(double angle)
return Math.PI * angle / 180.0;
public double radianToDegree(double angle)
return angle * (180.0 / Math.PI);
通过使用ComputeBearing
,您将轻松获得一个以度数表示的角度,可轻松用作航向
【讨论】:
【参考方案9】:我想你想要Great Circle bearing 的计算。
【讨论】:
嗨,HPM,不,我不需要大圆轴承。只需要角度:) @praethorian -- 两个纬度/经度点定义了一个大圆(弧),您必须解释它们之间的角度是什么意思。 @praethorian:我想你想要大圆轴承的计算。【参考方案10】:function calculateAngle(lat, lng)
var checkLengthInterval = 2;
// Calculate Angle
//If ObjFeed == [] add first object.
if (ObjFeed.length == 0)
ObjFeed.push( 'lat': lat, 'lng': lng );
else
// Get last object from list to calculate angle betwn last and latest.
var tempNode = ObjFeed[ObjFeed.length - 1];
// If last lat and lng is same as current it will always return 0 angle.so only push lat lng in obj which is diff than last one.
if (!(tempNode.lat == lat && tempNode.lng == lng))
ObjFeed.push( 'lat': lat, 'lng': lng );
else
console.log('exact match for lat lng');
// this is for to keep only few objects in the list and remove other
if (ObjFeed.length >= checkLengthInterval)
// calculating angle only if previous data point is available
ObjFeed = ObjFeed.slice(-1 * checkLengthInterval); // remove all items in array except last two
var point1 = ObjFeed[ObjFeed.length - checkLengthInterval];
var point2 = ObjFeed[ObjFeed.length - 1];
console.log('previous point1', point1);
console.log('next point2', point2);
var dLng = (point2.lng - point1.lng);
var dLat = (point2.lat - point1.lat);
dLng = dLng * 10000;
dLat = dLat * 10000;
var dlat_by_dlan = 0;
try
dlat_by_dlan = dLng / dLat;
catch (err)
dlat_by_dlan = NaN;
console.log('Exception: dLat == 0');
var angleDegreeBearing = 0, angleBearingRad = 0;
angleBearingRad = Math.atan(dlat_by_dlan);
angleDegreeBearing = angleBearingRad * 180 / Math.PI;
if (dLat < 0 && dLng < 0)
angleDegreeBearing = angleDegreeBearing + 180;
else if (dLat < 0 && dLng > 0)
angleDegreeBearing = angleDegreeBearing + 180;
else if (dLat == 0 && dLng == 0)
angleDegreeBearing = prevVechicleAngle;
else if (dlat_by_dlan == NaN)
angleDegreeBearing = prevVechicleAngle;
console.log('angleDegreeBearing', angleDegreeBearing);
else
// setting up default angle to 0 if previous data point is not available to calculate actual anglle
console.log('feedArray default angle 0');
angleDegreeBearing = 0;
prevVechicleAngle = angleDegreeBearing;
return angleDegreeBearing;
【讨论】:
【参考方案11】:如果您需要在旋转椭球体(即 WGS 84)上使用精确方法,algorithms 会变得非常沉重。您可能会受益于GeographicLib,它已在 C/C++、Java、JavaScript、Python、Matlab/Octave 等中实现。
对于这个问题,第一点和第二点之间有一个geodesic 或rhumb line。
测地线
测地线是曲面上两点之间的最短路径。这是对“用户前进方向”(来自问题)的最常见解释,因为它是最短和最直接的。可以使用GeodSolve 求解逆测地线计算。您也可以使用online interface。这个工具有输入/输出:
lat1 lon1 lat2 lon2 → azi1 azi2 s12
其中lat1 lon1 是第一个点的坐标对,lat2 lon2 是第二个点的坐标对。所有单位都是度数(不是弧度)。结果 azi1 或 α1 是从起点开始的方位角(也称为方位角),以从北顺时针方向的度数给出。第二个方位角在第二个点,因为沿测地线的两点之间的角度不是恒定的。而s12是两点之间的距离,以米为单位,精度为15nm。
恒向线
恒向线连接具有恒定方位角(或方位角)的两个坐标点。 可以使用RhumbSolve 解决反向恒向线计算。您也可以使用online interface。这个工具有输入/输出:
lat1 lon1 lat2 lon2 → azi12 s12
这些参数与 GeodSolve 相同,只是 azi12 是点之间的恒定角度。
【讨论】:
【参考方案12】:也许这就是你想要的:
cos(say) = (cosd(90-lat(1))) * (cos(90-lat(2)))
+ (sin(90-lat(1))) * (sind(90-lat(2)) * (cosd(abs(Landa(2)-landa(1)))));
【讨论】:
【参考方案13】:对于使用 C/C++ 的人,下面是测试代码:
static const auto PI = 3.14159265358979323846, diameterOfEarthMeters = 6371.0 * 2 * 1000;
double degreeToRadian (double degree) return (degree * PI / 180); ;
double radianToDegree (double radian) return (radian * 180 / PI); ;
double CoordinatesToAngle (const double latitude1,
const double longitude1,
const double latitude2,
const double longitude2)
const auto longitudeDifferenceRadians = degreeToRadian(longitude2 - longitude1);
auto latitude1Radian = degreeToRadian(latitude1),
latitude2Radian = degreeToRadian(latitude2);
const auto x = std::cos(latitude1Radian) * std::sin(latitude2Radian) -
std::sin(latitude1Radian) * std::cos(latitude2Radian) *
std::cos(longitudeDifferenceRadians);
const auto y = std::sin(longitudeDifferenceRadians) * std::cos(latitude2Radian);
return radianToDegree(std::atan2(y, x));
double CoordinatesToMeters (const double latitude1,
const double longitude1,
const double latitude2,
const double longitude2)
auto latitude1Radian = degreeToRadian(latitude1),
longitude1Radian = degreeToRadian(longitude1),
latitude2Radian = degreeToRadian(latitude2),
longitude2Radian = degreeToRadian(longitude2);
auto x = std::sin((latitude2Radian - latitude1Radian) / 2),
y = std::sin((longitude2Radian - longitude1Radian) / 2);
return diameterOfEarthMeters *
std::asin(std::sqrt((x * x) +
(std::cos(latitude1Radian) * std::cos(latitude2Radian) * y * y)));
【讨论】:
【参考方案14】:如果有人需要php
代码来实现此功能:
/**
* Calculate angle between 2 given latLng
* @param float $lat1
* @param float $lat2
* @param float $lng1
* @param float $lng2
* @return integer
*/
function angle($lat1, $lat2, $lng1, $lng2)
$dLon = $lng2 - $lng1;
$y = sin($dLon) * cos($lat2);
$x = cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($dLon);
return 360 - ((rad2deg(atan2($y, $x)) + 360) % 360);
【讨论】:
【参考方案15】:考虑到 Nayanesh Gupte 的答案及其 cmets。我修改了部分代码,写在PHP
。
函数如下:
function angleFromCoordinate($lat1, $long1, $lat2, $long2)
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($lat2);
$long1 = deg2rad($long1);
$long2 = deg2rad($long2);
$dLon = $long2 - $long1;
$y = sin($dLon) * cos($lat2);
$x = cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($dLon);
$brng = atan2($y, $x);
$brng = $brng * 180 / pi();
$brng = fmod($brng + 360, 360);
return $brng;
【讨论】:
【参考方案16】:确保它是一个恒向线轴承而不是一个大圆轴承 初始方位根据距离变化
double angle= Math.min((pbearingf-tbearingf) < 0 ? pbearingf-tbearingf+360:pbearingf-tbearingf, (tbearingf-pbearingf)<0?tbearingf-pbearingf+360:tbearingf-pbearingf);
【讨论】:
【参考方案17】:使用 javascript,只需使用 Turf:
var point1 = turf.point([-75.343, 39.984]);
var point2 = turf.point([-75.534, 39.123]);
var bearing = turf.bearing(point1, point2);
【讨论】:
【参考方案18】:这是在 Java 中使用 Math 计算方位的方法。就我而言,我的度数是经纬度,所以我在使用它们之前将它们转换为弧度。如果你有 lat-long 的弧度,你可能想删除这四行。我还评论了我用来计算方位的公式。
public float calculateBearing(float currentLat, float currentLon, float destinationLat, float destinationLon)
//if lat,lon are in degrees convert them to radians (in my case they are in degrees)
currentLat = (float) Math.toRadians(currentLat);
currentLon = (float) Math.toRadians(currentLon);
destinationLat = (float) Math.toRadians(destinationLat);
destinationLon = (float) Math.toRadians(destinationLon);
// a -> current | b -> destination
// ‘L’ be the longitude in radians,
// ‘θ’ be latitude in radians,
// ‘β‘ be Bearing.
// ∆ be the difference (b - a)
// ∆L = L b - L a
// β = atan2(X,Y)
// X = cos θb * sin ∆L
// Y = cos θa * sin θb – sin θa * cos θb * cos ∆L
double X, Y;
X = Math.cos(destinationLat) * Math.sin(destinationLon - currentLon);
Y = (Math.cos(currentLat) * Math.sin(destinationLat)) -
(Math.sin(currentLat) * Math.cos(destinationLat) * Math.cos(destinationLon - currentLon));
float radianBearing = (float) Math.atan2(X, Y);
//converting bearing in radian to degrees
return (float) Math.toDegrees(radianBearing );
我研究并遵循了此处提供的公式https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/。您可以进一步阅读本文以获得更好的理解。
如需测试,您可以访问此https://www.igismap.com/map-tool/bearing-angle。它有一张地图,您可以在其中选择起始坐标 (A) 和结束坐标 (B) 来获取方位。
【讨论】:
您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。以上是关于计算两个纬度/经度点之间的角度的主要内容,如果未能解决你的问题,请参考以下文章