【问题标题】: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;
}

哪个是正确的?

【问题讨论】:

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

标签: android iphone geolocation augmented-reality


【解决方案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

      【讨论】:

      【解决方案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);
      }
      

      【讨论】:

        猜你喜欢
        • 2019-07-19
        • 2023-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-10
        • 1970-01-01
        • 2013-09-01
        • 2011-04-20
        相关资源
        最近更新 更多