vincenty.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  4. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
  5. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
  6. // This file was modified by Oracle on 2014, 2015, 2016, 2017.
  7. // Modifications copyright (c) 2014-2017 Oracle and/or its affiliates.
  8. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  9. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  10. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  11. // Use, modification and distribution is subject to the Boost Software License,
  12. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  13. // http://www.boost.org/LICENSE_1_0.txt)
  14. #include <geometry_test_common.hpp>
  15. #include <boost/concept_check.hpp>
  16. #include <boost/geometry/algorithms/assign.hpp>
  17. #include <boost/geometry/algorithms/distance.hpp>
  18. #include <boost/geometry/formulas/vincenty_inverse.hpp>
  19. #include <boost/geometry/formulas/vincenty_direct.hpp>
  20. #include <boost/geometry/geometries/point.hpp>
  21. #include <boost/geometry/srs/spheroid.hpp>
  22. #include <boost/geometry/strategies/concepts/distance_concept.hpp>
  23. #include <boost/geometry/strategies/geographic/distance_vincenty.hpp>
  24. #include <boost/geometry/strategies/geographic/side_vincenty.hpp>
  25. #include <test_common/test_point.hpp>
  26. #ifdef HAVE_TTMATH
  27. # include <boost/geometry/extensions/contrib/ttmath_stub.hpp>
  28. #endif
  29. template <typename T>
  30. void normalize_deg(T & deg)
  31. {
  32. while ( deg > T(180) )
  33. deg -= T(360);
  34. while ( deg <= T(-180) )
  35. deg += T(360);
  36. }
  37. template <typename T>
  38. T difference_deg(T const& a1, T const& a2)
  39. {
  40. T d = a1 - a2;
  41. normalize_deg(d);
  42. return d;
  43. }
  44. template <typename T>
  45. void check_deg(std::string const& name, T const& a1, T const& a2, T const& percent, T const& error)
  46. {
  47. T diff = bg::math::abs(difference_deg(a1, a2));
  48. if ( bg::math::equals(a1, T(0)) || bg::math::equals(a2, T(0)) )
  49. {
  50. if ( diff > error )
  51. {
  52. BOOST_ERROR(name << " - the difference {" << diff << "} between {" << a1 << "} and {" << a2 << "} exceeds {" << error << "}");
  53. }
  54. }
  55. else
  56. {
  57. T greater = (std::max)(bg::math::abs(a1), bg::math::abs(a2));
  58. if ( diff > greater * percent / T(100) )
  59. {
  60. BOOST_ERROR(name << " the difference {" << diff << "} between {" << a1 << "} and {" << a2 << "} exceeds {" << percent << "}%");
  61. }
  62. }
  63. }
  64. double azimuth(double deg, double min, double sec)
  65. {
  66. min = fabs(min);
  67. sec = fabs(sec);
  68. if ( deg < 0 )
  69. {
  70. min = -min;
  71. sec = -sec;
  72. }
  73. return deg + min/60.0 + sec/3600.0;
  74. }
  75. double azimuth(double deg, double min)
  76. {
  77. return azimuth(deg, min, 0.0);
  78. }
  79. template <typename P>
  80. bool non_precise_ct()
  81. {
  82. typedef typename bg::coordinate_type<P>::type ct;
  83. return boost::is_integral<ct>::value || boost::is_float<ct>::value;
  84. }
  85. template <typename P1, typename P2, typename Spheroid>
  86. void test_vincenty(double lon1, double lat1, double lon2, double lat2,
  87. double expected_distance,
  88. double expected_azimuth_12,
  89. double /*expected_azimuth_21*/,
  90. Spheroid const& spheroid)
  91. {
  92. typedef typename bg::promote_floating_point
  93. <
  94. typename bg::select_calculation_type<P1, P2, void>::type
  95. >::type calc_t;
  96. calc_t tolerance = non_precise_ct<P1>() || non_precise_ct<P2>() ?
  97. 5.0 : 0.001;
  98. calc_t error = non_precise_ct<P1>() || non_precise_ct<P2>() ?
  99. 1e-5 : 1e-12;
  100. // formula
  101. {
  102. double const d2r = bg::math::d2r<double>();
  103. double const r2d = bg::math::r2d<double>();
  104. typedef bg::formula::vincenty_inverse<calc_t, true, true> inverse_formula;
  105. typename inverse_formula::result_type
  106. result_i = inverse_formula::apply(lon1 * d2r,
  107. lat1 * d2r,
  108. lon2 * d2r,
  109. lat2 * d2r,
  110. spheroid);
  111. calc_t dist = result_i.distance;
  112. calc_t az12 = result_i.azimuth;
  113. //calc_t az21 = vi.azimuth21();
  114. calc_t az12_deg = az12 * r2d;
  115. //calc_t az21_deg = az21 * r2d;
  116. BOOST_CHECK_CLOSE(dist, calc_t(expected_distance), tolerance);
  117. check_deg("az12_deg", az12_deg, calc_t(expected_azimuth_12), tolerance, error);
  118. //check_deg("az21_deg", az21_deg, calc_t(expected_azimuth_21), tolerance, error);
  119. typedef bg::formula::vincenty_direct<calc_t> direct_formula;
  120. typename direct_formula::result_type
  121. result_d = direct_formula::apply(lon1 * d2r,
  122. lat1 * d2r,
  123. dist,
  124. az12,
  125. spheroid);
  126. calc_t direct_lon2 = result_d.lon2;
  127. calc_t direct_lat2 = result_d.lat2;
  128. //calc_t direct_az21 = vd.azimuth21();
  129. calc_t direct_lon2_deg = direct_lon2 * r2d;
  130. calc_t direct_lat2_deg = direct_lat2 * r2d;
  131. //calc_t direct_az21_deg = direct_az21 * r2d;
  132. check_deg("direct_lon2_deg", direct_lon2_deg, calc_t(lon2), tolerance, error);
  133. check_deg("direct_lat2_deg", direct_lat2_deg, calc_t(lat2), tolerance, error);
  134. //check_deg("direct_az21_deg", direct_az21_deg, az21_deg, tolerance, error);
  135. }
  136. // distance strategies
  137. {
  138. typedef bg::strategy::distance::vincenty<Spheroid> vincenty_type;
  139. typedef bg::strategy::distance::geographic<bg::strategy::vincenty, Spheroid> geographic_type;
  140. BOOST_CONCEPT_ASSERT(
  141. (
  142. bg::concepts::PointDistanceStrategy<vincenty_type, P1, P2>)
  143. );
  144. vincenty_type vincenty(spheroid);
  145. geographic_type geographic(spheroid);
  146. typedef typename bg::strategy::distance::services::return_type<vincenty_type, P1, P2>::type return_type;
  147. P1 p1;
  148. P2 p2;
  149. bg::assign_values(p1, lon1, lat1);
  150. bg::assign_values(p2, lon2, lat2);
  151. BOOST_CHECK_CLOSE(vincenty.apply(p1, p2), return_type(expected_distance), tolerance);
  152. BOOST_CHECK_CLOSE(geographic.apply(p1, p2), return_type(expected_distance), tolerance);
  153. BOOST_CHECK_CLOSE(bg::distance(p1, p2, vincenty), return_type(expected_distance), tolerance);
  154. }
  155. }
  156. template <typename P1, typename P2>
  157. void test_vincenty(double lon1, double lat1, double lon2, double lat2,
  158. double expected_distance,
  159. double expected_azimuth_12,
  160. double expected_azimuth_21)
  161. {
  162. test_vincenty<P1, P2>(lon1, lat1, lon2, lat2,
  163. expected_distance, expected_azimuth_12, expected_azimuth_21,
  164. bg::srs::spheroid<double>());
  165. }
  166. template <typename PS, typename P>
  167. void test_side(double lon1, double lat1,
  168. double lon2, double lat2,
  169. double lon, double lat,
  170. int expected_side)
  171. {
  172. // Set radius type, but for integer coordinates we want to have floating point radius type
  173. typedef typename bg::promote_floating_point
  174. <
  175. typename bg::coordinate_type<PS>::type
  176. >::type rtype;
  177. typedef bg::srs::spheroid<rtype> stype;
  178. typedef bg::strategy::side::vincenty<stype> strategy_type;
  179. typedef bg::strategy::side::geographic<bg::strategy::vincenty, stype> strategy2_type;
  180. strategy_type strategy;
  181. strategy2_type strategy2;
  182. PS p1, p2;
  183. P p;
  184. bg::assign_values(p1, lon1, lat1);
  185. bg::assign_values(p2, lon2, lat2);
  186. bg::assign_values(p, lon, lat);
  187. int side = strategy.apply(p1, p2, p);
  188. int side2 = strategy2.apply(p1, p2, p);
  189. BOOST_CHECK_EQUAL(side, expected_side);
  190. BOOST_CHECK_EQUAL(side2, expected_side);
  191. }
  192. template <typename P1, typename P2>
  193. void test_all()
  194. {
  195. // See:
  196. // - http://www.ga.gov.au/geodesy/datums/vincenty_inverse.jsp
  197. // - http://www.ga.gov.au/geodesy/datums/vincenty_direct.jsp
  198. // Values in the comments below was calculated using the above pages
  199. // in some cases distances may be different, previously used values was left
  200. // use km
  201. double gda_a = 6378.1370;
  202. double gda_f = 1.0 / 298.25722210;
  203. double gda_b = gda_a * ( 1.0 - gda_f );
  204. bg::srs::spheroid<double> gda_spheroid(gda_a, gda_b);
  205. // Test fractional coordinates only for non-integral types
  206. if ( BOOST_GEOMETRY_CONDITION(
  207. ! boost::is_integral<typename bg::coordinate_type<P1>::type>::value
  208. && ! boost::is_integral<typename bg::coordinate_type<P2>::type>::value ) )
  209. {
  210. // Flinders Peak -> Buninyong
  211. test_vincenty<P1, P2>(azimuth(144,25,29.52440), azimuth(-37,57,3.72030),
  212. azimuth(143,55,35.38390), azimuth(-37,39,10.15610),
  213. 54.972271, azimuth(306,52,5.37), azimuth(127,10,25.07),
  214. gda_spheroid);
  215. }
  216. // Lodz -> Trondheim
  217. test_vincenty<P1, P2>(azimuth(19,28), azimuth(51,47),
  218. azimuth(10,21), azimuth(63,23),
  219. 1399.032724, azimuth(340,54,25.14), azimuth(153,10,0.19),
  220. gda_spheroid);
  221. // London -> New York
  222. test_vincenty<P1, P2>(azimuth(0,7,39), azimuth(51,30,26),
  223. azimuth(-74,0,21), azimuth(40,42,46),
  224. 5602.044851, azimuth(288,31,36.82), azimuth(51,10,33.43),
  225. gda_spheroid);
  226. // Shanghai -> San Francisco
  227. test_vincenty<P1, P2>(azimuth(121,30), azimuth(31,12),
  228. azimuth(-122,25), azimuth(37,47),
  229. 9899.698550, azimuth(45,12,44.76), azimuth(309,50,20.88),
  230. gda_spheroid);
  231. test_vincenty<P1, P2>(0, 0, 0, 50, 5540.847042, 0, 180, gda_spheroid); // N
  232. test_vincenty<P1, P2>(0, 0, 0, -50, 5540.847042, 180, 0, gda_spheroid); // S
  233. test_vincenty<P1, P2>(0, 0, 50, 0, 5565.974540, 90, -90, gda_spheroid); // E
  234. test_vincenty<P1, P2>(0, 0, -50, 0, 5565.974540, -90, 90, gda_spheroid); // W
  235. test_vincenty<P1, P2>(0, 0, 50, 50, 7284.879297, azimuth(32,51,55.87), azimuth(237,24,50.12), gda_spheroid); // NE
  236. // The original distance values, azimuths calculated using the web form mentioned above
  237. // Using default spheroid units (meters)
  238. test_vincenty<P1, P2>(0, 89, 1, 80, 1005153.5769, azimuth(178,53,23.85), azimuth(359,53,18.35)); // sub-polar
  239. test_vincenty<P1, P2>(4, 52, 4, 52, 0.0, 0, 0); // no point difference
  240. test_vincenty<P1, P2>(4, 52, 3, 40, 1336039.890, azimuth(183,41,29.08), azimuth(2,58,5.13)); // normal case
  241. test_side<P1, P2>(0, 0, 0, 1, 0, 2, 0);
  242. test_side<P1, P2>(0, 0, 0, 1, 0, -2, 0);
  243. test_side<P1, P2>(10, 0, 10, 1, 10, 2, 0);
  244. test_side<P1, P2>(10, 0, 10, -1, 10, 2, 0);
  245. test_side<P1, P2>(10, 0, 10, 1, 0, 2, 1); // left
  246. test_side<P1, P2>(10, 0, 10, -1, 0, 2, -1); // right
  247. test_side<P1, P2>(-10, -10, 10, 10, 10, 0, -1); // right
  248. test_side<P1, P2>(-10, -10, 10, 10, -10, 0, 1); // left
  249. test_side<P1, P2>(170, -10, -170, 10, -170, 0, -1); // right
  250. test_side<P1, P2>(170, -10, -170, 10, 170, 0, 1); // left
  251. }
  252. template <typename P>
  253. void test_all()
  254. {
  255. test_all<P, P>();
  256. }
  257. int test_main(int, char* [])
  258. {
  259. //test_all<float[2]>();
  260. //test_all<double[2]>();
  261. test_all<bg::model::point<double, 2, bg::cs::geographic<bg::degree> > >();
  262. test_all<bg::model::point<float, 2, bg::cs::geographic<bg::degree> > >();
  263. test_all<bg::model::point<int, 2, bg::cs::geographic<bg::degree> > >();
  264. #if defined(HAVE_TTMATH)
  265. test_all<bg::model::point<ttmath::Big<1,4>, 2, bg::cs::geographic<bg::degree> > >();
  266. test_all<bg::model::point<ttmath_big, 2, bg::cs::geographic<bg::degree> > >();
  267. #endif
  268. return 0;
  269. }