寻找地球坐标(纬度,经度),距离(米)和方位角(角度)

时间:2015-08-19 13:30:42

标签: c++ c geometry coordinates earthdistance

我需要以各种方式处理地球坐标。 C / C ++中没有直接执行此操作的功能 提到以下问题:

  1. Python: Get lat/long given current point, distance and bearing
  2. C: Given point of (latitude,longitude), distance and bearing, How to get the new latitude and longitude
  3. 从第一个和movable type scripts website开始,我发现以下是公式:

    查找2个坐标之间的方位(角度)

    x = cos(lat1Rad)*sin(lat2Rad) - sin(lat1Rad)*cos(lat2Rad)*cos(lon2Rad-lon1Rad);
    y = sin(lon2Rad-lon1Rad) * cos(lat2Rad);
    bearing = atan2(y, x);  // In radians; 
    // Convert to degrees and for -ve add 360
    

    查找2个坐标之间的距离(米)

    PI = 3.14159265358979323846, earthDiameterMeters = 2*6371*1000;
    x = sin((lat2Rad-lat1Rad) / 2);
    y = sin((lon2Rad-lon1Rad) / 2);
    meters = earthDiameterMeters * asin(sqrt(x*x + y*y*cos(lat1Rad)*cos(lat2Rad)));
    

    从坐标+距离+角度

    查找坐标
    meters *= 2 / earthDiameterMeters;
    lat2Rad = asin(sin(lat1Rad)*cos(meters) + cos(lat1Rad)*sin(meters)*cos(bearing));
    lon2Rad = lon1Rad + atan2(sin(bearing)*sin(meters)*cos(lat1Rad), 
                              cos(meters) - sin(lat1Rad)*sin(lat2Rad));
    

    伪代码下面应该相互验证上述3个方程:

    struct Coordinate { double lat, lon; } c1, c2;  
    auto degree = FindBearing(c1, c2);
    auto meters = FindDistance(c1, c2);
    auto cX = FindCoordiante(c1, degree, meters);
    

    现在实际上答案是几乎附近但不正确。即cX不等于c2!
    经度值总是存在0.0005差异。 e.g。

    c1 = (12.968460,77.641308)  
    c2 = (12.967862,77.653130)  
    angle = 92.97         ^^^
    distance = 1282.74  
    cX = (12.967862,77.653613)
                          ^^^
    

    我对数学'Havesine Forumla知之甚少。但我所知道的是,从the website fcc.gov开始,答案总是正确的。

    我做错了什么?

    Code仅供参考

    虽然语法是用C ++编写的,但所有数学函数都来自C,并且在C中也很容易移植(因此标记为两者)

    #include<iostream>
    #include<iomanip>
    #include<cmath>
    
    
    // Source: http://www.movable-type.co.uk/scripts/latlong.html
    
    static const double PI = 3.14159265358979323846, earthDiameterMeters = 6371.0 * 2 * 1000;
    
    double degreeToRadian (const double degree) { return (degree * PI / 180); };
    double radianToDegree (const double radian) { return (radian * 180 / PI); };
    
    double CoordinatesToAngle (double latitude1,
                               const double longitude1,
                               double latitude2,
                               const double longitude2)
    {
      const auto longitudeDifference = degreeToRadian(longitude2 - longitude1);
      latitude1 = degreeToRadian(latitude1);
      latitude2 = degreeToRadian(latitude2);
    
      using namespace std;
      const auto x = (cos(latitude1) * sin(latitude2)) -
                     (sin(latitude1) * cos(latitude2) * cos(longitudeDifference));
      const auto y = sin(longitudeDifference) * cos(latitude2);
    
      const auto degree = radianToDegree(atan2(y, x));
      return (degree >= 0)? degree : (degree + 360);
    }
    
    double CoordinatesToMeters (double latitude1,
                                double longitude1,
                                double latitude2,
                                double longitude2)
    {
      latitude1 = degreeToRadian(latitude1);
      longitude1 = degreeToRadian(longitude1);
      latitude2 = degreeToRadian(latitude2);
      longitude2 = degreeToRadian(longitude2);
    
      using namespace std;
      auto x = sin((latitude2 - latitude1) / 2), y = sin((longitude2 - longitude1) / 2);
    #if 1
      return earthDiameterMeters * asin(sqrt((x * x) + (cos(latitude1) * cos(latitude2) * y * y)));
    #else
      auto value = (x * x) + (cos(latitude1) * cos(latitude2) * y * y);
      return earthDiameterMeters * atan2(sqrt(value), sqrt(1 - value));
    #endif
    }
    
    std::pair<double,double> CoordinateToCoordinate (double latitude,
                                                     double longitude,
                                                     double angle,
                                                     double meters)
    {
      latitude = degreeToRadian(latitude);
      longitude = degreeToRadian(longitude);
      angle = degreeToRadian(angle);
      meters *= 2 / earthDiameterMeters;
    
      using namespace std;
      pair<double,double> coordinate;
    
      coordinate.first = radianToDegree(asin((sin(latitude) * cos(meters))
                                 + (cos(latitude) * sin(meters) * cos(angle))));
      coordinate.second = radianToDegree(longitude
                        + atan2((sin(angle) * sin(meters) * cos(latitude)),
                        cos(meters) - (sin(latitude) * sin(coordinate.first))));
    
      return coordinate;
    }
    
    int main ()
    {
      using namespace std;
      const auto latitude1 = 12.968460, longitude1 = 77.641308,
                 latitude2 = 12.967862, longitude2 = 77.653130;
    
      cout << std::setprecision(10);
      cout << "(" << latitude1 << "," << longitude1 << ") --- "
              "(" << latitude2 << "," << longitude2 << ")\n";
    
      auto angle = CoordinatesToAngle(latitude1, longitude1, latitude2, longitude2);
      cout << "Angle =  " << angle << endl;
    
      auto meters = CoordinatesToMeters(latitude1, longitude1, latitude2, longitude2);
      cout << "Meters = " << meters << endl;
    
      auto coordinate = CoordinateToCoordinate(latitude1, longitude1, angle, meters);
      cout << "Destination = (" << coordinate.first << "," << coordinate.second << ")\n";
    }
    

2 个答案:

答案 0 :(得分:2)

CoordinateToCoordinate中,您使用的sin(coordinate.first)已经是度数。使用sin(degreeToRadian(coordinate.first))

或者更清洁:

... CoordinateToCoordinate (...)
{
  ...
  coordinate.first = asin((sin(latitude) * cos(meters))
                        + (cos(latitude) * sin(meters) * cos(angle)));
  coordinate.second = longitude + atan2((sin(angle) * sin(meters) * cos(latitude)), 
         cos(meters) - (sin(latitude) * sin(coordinate.first)));

  coordinate.first = radianToDegree(coordinate.first);
  coordinate.second = radianToDegree(coordinate.second);

  return coordinate;
}

这解决了这个问题。 Live Demo

答案 1 :(得分:0)

部分答案

如果角度为92.97°,则转换为弧度,对sin/cos/tan的调用将有效地将角度更改为2.97°。由于周期缩短发生在度到弧度转换后以及在trig函数调用中发生周期减少,因此单独此步骤会丢失6位精度。

可以增强具有大角度度的三角函数的精度。使用幸运的方面,正好 360.0度的圆圈。在使用弧度参数调用trig函数之前,执行“模45°”,可能使用remquo(angle, 45, &octant)然后转换为弧度。

示例sind()

您对77.641308和77.653130的回答在6600(约13位精度)中大约相差1个部分。这个答案可能无法完全解释,但应该有所帮助。 (如果float的某些用法出现在某处,则应该double。)