我需要一個函數來高精度地計算一對WGS 84位置之間的距離,我打算使用boost geometry中的geographic
函數。爲什麼boost :: geometry geographic Vincenty距離在赤道附近不準確?
的boost geometry Design Rational狀態:
還有就是Andoyer方法,快速和精確的,並且在Vincenty方法,更慢和更精確..
然而,測試boost::geometry::distance
功能時與Andoyer
和Vincenty
戰略,我得到以下結果:
WGS 84 values (metres)
Semimajor axis: 6378137.000000
Flattening: 0.003353
Semiminor axis: 6356752.314245
Semimajor distance: 20037508.342789
Semiminor distance: 19970326.371123
Boost geometry near poles
Andoyer function:
Semimajor distance: 20037508.151445
Semiminor distance: 20003917.164970
Vincenty function:
Semimajor distance: **19970326.180419**
Semiminor distance: 20003931.266635
Boost geometry at poles
Andoyer function:
Semimajor distance: 0.000000
Semiminor distance: 0.000000
Vincenty function:
Semimajor distance: **19970326.371122**
Semiminor distance: 20003931.458623
沿着半長軸的距離(即,在赤道周圍)小於北極和南極之間的半微米軸周圍的距離。這不可能是正確的。
Semiminor和Andoyer
距離看起來合理。除了點位於地球的另一側時,當功能返回零時!
問題出在:Vincenty
算法,boost geometry
執行它,還是我的測試代碼?
測試代碼:
/// boost geometry WGS84 distance issue
// Note: M_PI is not part of the C or C++ standards, _USE_MATH_DEFINES enables it
#define _USE_MATH_DEFINES
#include <boost/geometry.hpp>
#include <cmath>
#include <iostream>
#include <ios>
// WGS 84 parameters from: Eurocontrol WGS 84 Implementation Manual
// Version 2.4 Chapter 3, page 14
/// The Semimajor axis measured in metres.
/// This is the radius at the equator.
constexpr double a = 6378137.0;
/// Flattening, a ratio.
/// This is the flattening of the ellipse at the poles
constexpr double f = 1.0/298.257223563;
/// The Semiminor axis measured in metres.
/// This is the radius at the poles.
/// Note: this is derived from the Semimajor axis and the flattening.
/// See WGS 84 Implementation Manual equation B-2, page 69.
constexpr double b = a * (1.0 - f);
int main(int /*argc*/, char ** /*argv*/)
{
std::cout.setf(std::ios::fixed);
std::cout << "WGS 84 values (metres)\n";
std::cout << "\tSemimajor axis:\t\t" << a << "\n";
std::cout << "\tFlattening:\t\t" << f << "\n";
std::cout << "\tSemiminor axis:\t\t" << b << "\n\n";
std::cout << "\tSemimajor distance:\t" << M_PI * a << "\n";
std::cout << "\tSemiminor distance:\t" << M_PI * b << "\n";
std::cout << std::endl;
// Min value for delta. 0.000000014 causes Andoyer to fail.
const double DELTA(0.000000015);
// For boost::geometry:
typedef boost::geometry::cs::geographic<boost::geometry::radian> Wgs84Coords;
typedef boost::geometry::model::point<double, 2, Wgs84Coords> GeographicPoint;
// Note boost points are Long & Lat NOT Lat & Long
GeographicPoint near_north_pole (0.0, M_PI_2 - DELTA);
GeographicPoint near_south_pole (0.0, -M_PI_2 + DELTA);
GeographicPoint near_equator_east (M_PI_2 - DELTA, 0.0);
GeographicPoint near_equator_west (-M_PI_2 + DELTA, 0.0);
// Note: the default boost geometry spheroid is WGS84
// #include <boost/geometry/core/srs.hpp>
typedef boost::geometry::srs::spheroid<double> SpheroidType;
SpheroidType spheriod;
//#include <boost/geometry/strategies/geographic/distance_andoyer.hpp>
typedef boost::geometry::strategy::distance::andoyer<SpheroidType>
AndoyerStrategy;
AndoyerStrategy andoyer(spheriod);
std::cout << "Boost geometry near poles\n";
std::cout << "Andoyer function:\n";
double andoyer_major(boost::geometry::distance(near_equator_east, near_equator_west, andoyer));
std::cout << "\tSemimajor distance:\t" << andoyer_major << "\n";
double andoyer_minor(boost::geometry::distance(near_north_pole, near_south_pole, andoyer));
std::cout << "\tSemiminor distance:\t" << andoyer_minor << "\n";
//#include <boost/geometry/strategies/geographic/distance_vincenty.hpp>
typedef boost::geometry::strategy::distance::vincenty<SpheroidType>
VincentyStrategy;
VincentyStrategy vincenty(spheriod);
std::cout << "Vincenty function:\n";
double vincenty_major(boost::geometry::distance(near_equator_east, near_equator_west, vincenty));
std::cout << "\tSemimajor distance:\t" << vincenty_major << "\n";
double vincenty_minor(boost::geometry::distance(near_north_pole, near_south_pole, vincenty));
std::cout << "\tSemiminor distance:\t" << vincenty_minor << "\n\n";
// Note boost points are Long & Lat NOT Lat & Long
GeographicPoint north_pole (0.0, M_PI_2);
GeographicPoint south_pole (0.0, -M_PI_2);
GeographicPoint equator_east (M_PI_2, 0.0);
GeographicPoint equator_west (-M_PI_2, 0.0);
std::cout << "Boost geometry at poles\n";
std::cout << "Andoyer function:\n";
andoyer_major = boost::geometry::distance(equator_east, equator_west, andoyer);
std::cout << "\tSemimajor distance:\t" << andoyer_major << "\n";
andoyer_minor = boost::geometry::distance(north_pole, south_pole, andoyer);
std::cout << "\tSemiminor distance:\t" << andoyer_minor << "\n";
std::cout << "Vincenty function:\n";
vincenty_major = boost::geometry::distance(equator_east, equator_west, vincenty);
std::cout << "\tSemimajor distance:\t" << vincenty_major << "\n";
vincenty_minor = boost::geometry::distance(north_pole, south_pole, vincenty);
std::cout << "\tSemiminor distance:\t" << vincenty_minor << "\n";
return 0;
}
謝謝@ jwd630,我正在閱讀'Charles FF Karney的[算法測地線](http://link.springer.com/article/10.1007%2Fs00190-012-0578-z)paper和geographiclib將是一個優秀的選擇。然而,'boost幾何體'是一個'boost'庫,即......幾乎是標準的,所以它**應該**正確工作。 – kenba
對不起@ jwd630但[geographiclib](http://geographiclib.sourceforge.net/)更糟!看到我的回答... – kenba
不,GeographicLib給出了正確答案! – cffk