【问题标题】:Finding Earth Coordinates (latitude,longitude), Distance (meters) and Bearing (angle)查找地球坐标(纬度、经度)、距离(米)和方位角(角度)
【发布时间】:2015-11-12 20:09:05
【问题描述】:

我需要以各种方式处理地球坐标。 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

从第一个和movable type scripts website,我发现下面是公式:

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

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 的差异。 例如

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";
}

【问题讨论】:

  • 你认为它会编译为 C 吗?它是不是C。没有“有点怀孕”!
  • @Olaf,代码仅供参考,不是问题的中心点。它不会按原样在 C 中编译,但它很容易移植。我提到的另一个问题实际上来自 C。为了获得更广泛的受众,C 和 C++ 都被标记了。
  • double ... 浮点运算可能不精确。
  • 尝试将数字转换为定点表示以捕获完整的给定精度并进行计算。但即使在此之前,也要手动计算。
  • @EugeneSh.,我不明白你的意见。您指的是哪个特定方程?你能举一个将数字转换为定点表示的例子吗? double 可能存在不准确之处,但从这个意义上讲,0.0005 相当大。 double 和 long double 的结果相同。

标签: c++ c geometry coordinates earthdistance


【解决方案1】:

在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.

【讨论】:

    【解决方案2】:

    部分回答

    将角度92.97° 转换为弧度后,调用sin/cos/tan 将有效地将角度更改为2.97°。由于周期减少发生在在度到弧度转换和在 trig 函数调用中,因此仅这一步就会损失 6 位精度。

    可以提高以度为单位的大角度三角函数的精度。使用幸运的方面,恰好在一个圆圈中有 360.0 度。执行“模 45°”,可能使用remquo(angle, 45, &amp;octant),然后然后在调用带有弧度参数的三角函数之前转换为弧度。

    例如sind()


    您的 77.641308 和 77.653130 的答案相差大约 6600 分之一(大约 13 位精度)。这个答案可能无法完全解释这一点,但应该有所帮助。 (如果在某处使用了float,则应使用double。)

    【讨论】:

      猜你喜欢
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      • 2012-02-10
      • 2019-06-12
      • 2016-02-03
      • 2014-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多