计算两个纬度/经度点之间的角度

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 等中实现。

对于这个问题,第一点和第二点之间有一个geodesicrhumb line

测地线

测地线是曲面上两点之间的最短路径。这是对“用户前进方向”(来自问题)的最常见解释,因为它是最短和最直接的。可以使用GeodSolve 求解逆测地线计算。您也可以使用online interface。这个工具有输入/输出:

lat1 lon1 lat2 lon2azi1 azi2 s12

其中lat1 lon1 是第一个点的坐标对,lat2 lon2 是第二个点的坐标对。所有单位都是度数(不是弧度)。结果 azi1 或 α1 是从起点开始的方位角(也称为方位角),以从北顺时针方向的度数给出。第二个方位角在第二个点,因为沿测地线的两点之间的角度不是恒定的。而s12是两点之间的距离,以米为单位,精度为15nm。

恒向线

恒向线连接具有恒定方位角(或方位角)的两个坐标点。 可以使用RhumbSolve 解决反向恒向线计算。您也可以使用online interface。这个工具有输入/输出:

lat1 lon1 lat2 lon2azi12 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

latitudelongitude 已在函数内转换为 弧度

函数如下:

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。

以上是关于计算两个纬度/经度点之间的角度的主要内容,如果未能解决你的问题,请参考以下文章

两个纬度/经度点之间的谷歌地图距离计算?

怎么知道经纬度算距离,

如何两个WGS84坐标之间计算方位角(角向北)

如何计算每10公里2点之间的所有点(纬度和经度)?

如何计算两对坐标(纬度/经度)之间的距离

java计算两个经纬度之间的距离