计算两个位置(纬度、经度)之间的方位角

Posted

技术标签:

【中文标题】计算两个位置(纬度、经度)之间的方位角【英文标题】:Calculate bearing between two locations (lat, long) 【发布时间】:2011-12-28 17:05:58 【问题描述】:

我正在尝试开发自己的增强现实引擎。

在互联网上搜索,我发现这很有用 tutorial。阅读它,我发现重要的是用户位置、点位置和北方之间的方位。

以下图片来自该教程。

接着,我写了一个Objective-C方法来获取beta:

+ (float) calculateBetaFrom:(CLLocationCoordinate2D)user to:(CLLocationCoordinate2D)destination

    double beta = 0;
    double a, b = 0;

    a = destination.latitude - user.latitude;
    b = destination.longitude - user.longitude;

    beta = atan2(a, b) * 180.0 / M_PI;
    if (beta < 0.0)
        beta += 360.0;
    else if (beta > 360.0)
        beta -= 360;

    return beta;

但是,当我尝试它时,效果并不好。

所以,我检查了 iPhone AR Toolkit,看看它是如何工作的(我一直在使用这个工具包,但它对我来说太大了)。

而且,在ARGeoCoordinate.m 中还有另一种如何获得测试版的实现:

- (float)angleFromCoordinate:(CLLocationCoordinate2D)first toCoordinate:(CLLocationCoordinate2D)second 

    float longitudinalDifference    = second.longitude - first.longitude;
    float latitudinalDifference     = second.latitude  - first.latitude;
    float possibleAzimuth           = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);

    if (longitudinalDifference > 0) 
        return possibleAzimuth;
    else if (longitudinalDifference < 0) 
        return possibleAzimuth + M_PI;
    else if (latitudinalDifference < 0) 
        return M_PI;

    return 0.0f;

它使用这个公式:

float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);

为什么 (M_PI * .5f) 在这个公式中?没看懂。

继续搜索,我发现另一个page 谈论如何计算两个位置的距离和方位。在此页面中还有另一个实现:

/**
 * Returns the (initial) bearing from this point to the supplied point, in degrees
 *   see http://williams.best.vwh.net/avform.htm#Crs
 *
 * @param   LatLon point: Latitude/longitude of destination point
 * @returns Number Initial bearing in degrees from North
 */
LatLon.prototype.bearingTo = function(point) 
  var lat1 = this._lat.toRad(), lat2 = point._lat.toRad();
  var dLon = (point._lon-this._lon).toRad();

  var y = Math.sin(dLon) * Math.cos(lat2);
  var x = Math.cos(lat1)*Math.sin(lat2) -
          Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
  var brng = Math.atan2(y, x);

  return (brng.toDeg()+360) % 360;

哪个是正确的?

【问题讨论】:

您解决过这个问题吗?我对您使用的解决方案感兴趣 是的,我必须尽快添加自己的答案。 【参考方案1】:

计算方位

//Source
JSONObject source = step.getJSONObject("start_location");
double lat1 = Double.parseDouble(source.getString("lat"));
double lng1 = Double.parseDouble(source.getString("lng"));

// destination
JSONObject destination = step.getJSONObject("end_location");
double lat2 = Double.parseDouble(destination.getString("lat"));
double lng2 = Double.parseDouble(destination.getString("lng"));

double dLon = (lng2-lng1);
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.toDegrees((Math.atan2(y, x)));
brng = (360 - ((brng + 360) % 360));

将度数转换为弧度

Radians = Degrees * PI / 180

将弧度转换为度数

Degrees = Radians * 180 / PI

【讨论】:

您将值转换为度数,但纬度和经度不是以度为单位吗?您不应该先将它们转换为弧度吗? 我没有将纬度和经度转换为半径。我不认为这有什么区别 您是否尝试过在线轴承匹配进行测试?例如。通过此链接进行测试。 sunearthtools.com/tools/distance.php【参考方案2】:

我知道这个问题很老,但这里有一个更简单的解决方案:

浮动轴承 = loc1.bearingTo(loc2);

【讨论】:

【参考方案3】:

在公式中

float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);

术语(M_PI * .5f) 表示π/2,即90°。这意味着它与您最初所说的公式相同,因为对于上图它成立

β = arctan (a/b) = 90° - arctan(b/a)。

因此,如果a 指的是经度差,b 指的是纬度差,那么这两个公式是相似的。最后一个公式使用我的方程的第一部分再次计算出相同的结果。

【讨论】:

您将 a 称为 lat 的差异,将 b 称为 long 的差异。请参阅我的回复,了解a 实际上应该如何区分 long 和 b 应该如何区分 lat。编辑您的答案以与图表保持一致可能会很有用,这样我们就不会在必要时混淆未来的读者? 谢谢你,Dolbz,你是对的,a和b应该改。我编辑了我的主要答案,以获得正确的答案。 :)【参考方案4】:

试试这个以获得准确的结果:

private static double degreeToRadians(double latLong) 
    return (Math.PI * latLong / 180.0);


private static double radiansToDegree(double latLong) 
    return (latLong * 180.0 / Math.PI);


public static double getBearing() 

//Source
JSONObject source = step.getJSONObject("start_location");
double lat1 = Double.parseDouble(source.getString("lat"));
double lng1 = Double.parseDouble(source.getString("lng"));

// destination
JSONObject destination = step.getJSONObject("end_location");
double lat2 = Double.parseDouble(destination.getString("lat"));
double lng2 = Double.parseDouble(destination.getString("lng"));

    double fLat = degreeToRadians(lat1);
    double fLong = degreeToRadians(lng1);
    double tLat = degreeToRadians(lat2);
    double tLong = degreeToRadians(lng2);

    double dLon = (tLong - fLong);

    double degree = radiansToDegree(Math.atan2(sin(dLon) * cos(tLat),
            cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(dLon)));

    if (degree >= 0) 
        return degree;
     else 
        return 360 + degree;
    

您可以在http://www.sunearthtools.com/tools/distance.php 上测试轴承结果。

【讨论】:

我已经实现了这个数学部分(从双 dLon = 开始),当我计算倒数航向时,结果不是 +/- 180*,而是 +/- 180* + /- 0.25*。我是唯一一个看到这个问题的人吗?是数学如何(不精确)还是我做错了什么?【参考方案5】:

图中a 是经度差,b 是纬度差,因此在您编写的方法中,您把它们弄错了。

a = destination.latitude - user.latitude; // should be b
b = destination.longitude - user.longitude; // should be a

尝试切换它们,看看会发生什么。

请参阅 Palund 的回复以获取其他问题的答案。

【讨论】:

谢谢,你是对的:我在 a 和 b 上犯了一个错误。我已经切换了它们,我得到了 beta = 9 度。但是,如果我使用 LatLon.prototype.bearingTo,在相同的位置,我得到 beta = 7 度。我认为第二个更准确,但我不明白它没有在 iPhone AR Toolkit 中实现。再次感谢。 这解决了问题。只需使用 arctan(b, a) 代替 (a,b)。【参考方案6】:

/* Kirit vaghela 的答案已被修改.. Math.sin 给出弧度值,因此要获得度数值,我们需要在 Math.sin() 或 Math.cos() 中传递 Math.toRadians(value) */

    double lat1 = 39.099912;
    double lat2 = 38.627089;
    double lng1 = -94.581213;
    double lng2 = -90.200203;

    double dLon = (lng2-lng1);
    double x = Math.sin(Math.toRadians(dLon)) * Math.cos(Math.toRadians(lat2));
    double y = Math.cos(Math.toRadians(lat1))*Math.sin(Math.toRadians(lat2)) - Math.sin(Math.toRadians(lat1))*Math.cos(Math.toRadians(lat2))*Math.cos(Math.toRadians(dLon));
    double bearing = Math.toDegrees((Math.atan2(x, y)));
    System.out.println("BearingAngle : "+bearing);

【讨论】:

【参考方案7】:

如果您愿意,可以查看 mixare 增强现实引擎中使用的代码,它在 github 上,还有一个 iPhone 版本:github.com/mixare

【讨论】:

谢谢。我现在看到您已经使用了这个 ar 浏览器。你能帮我找出哪个类包含方位计算吗? 您好,我不是 iPhone 版本的开发人员,但根据以下位置提供的类文档:code.google.com/p/mixare/wiki/FileResponsibility 它应该是类 AugmentedViewController。来源为:github.com/mixare/mixare-iphone/blob/master/Classes/gui/…【参考方案8】:

输入以度为单位。

#define PI 3.14159265358979323846
#define RADIO_TERRESTRE 6372797.56085
#define GRADOS_RADIANES PI / 180
#define RADIANES_GRADOS 180 / PI

double calculateBearing(double lon1, double lat1, double lon2, double lat2)

    double longitude1 = lon1;
    double longitude2 = lon2;
    double latitude1 = lat1 * GRADOS_RADIANES;
    double latitude2 = lat2 * GRADOS_RADIANES;
    double longDiff= (longitude2-longitude1) * GRADOS_RADIANES;
    double y= sin(longDiff) * cos(latitude2);
    double x= cos(latitude1) * sin(latitude2) - sin(latitude1) * cos(latitude2) * cos(longDiff);
    
    // std::cout <<__FILE__ << "." << __FUNCTION__ << " line:" << __LINE__ << "  "
    
    return fmod(((RADIANES_GRADOS *(atan2(y, x)))+360),360);

【讨论】:

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

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

计算两个 GPS 坐标之间的罗盘方位问题

如何使用它们的经度和纬度值计算两个位置之间的距离

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

计算两个 CLLocationCoordinate2D 位置之间的偏移量

如何计算两个经纬度距离之间的时差