haversine.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  7. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  8. // Use, modification and distribution is subject to the Boost Software License,
  9. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  10. // http://www.boost.org/LICENSE_1_0.txt)
  11. #include <geometry_test_common.hpp>
  12. #include <boost/concept/requires.hpp>
  13. #include <boost/concept_check.hpp>
  14. #include <boost/core/ignore_unused.hpp>
  15. #include <boost/geometry/algorithms/assign.hpp>
  16. #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
  17. #include <boost/geometry/strategies/concepts/distance_concept.hpp>
  18. #include <boost/geometry/geometries/point.hpp>
  19. #ifdef HAVE_TTMATH
  20. # include <boost/geometry/extensions/contrib/ttmath_stub.hpp>
  21. #endif
  22. double const average_earth_radius = 6372795.0;
  23. template <typename Point, typename LatitudePolicy>
  24. struct test_distance
  25. {
  26. typedef bg::strategy::distance::haversine<double> haversine_type;
  27. typedef typename bg::strategy::distance::services::return_type<haversine_type, Point, Point>::type return_type;
  28. BOOST_CONCEPT_ASSERT
  29. (
  30. (bg::concepts::PointDistanceStrategy<haversine_type, Point, Point>)
  31. );
  32. static void test(double lon1, double lat1, double lon2, double lat2,
  33. double radius, double expected, double tolerance)
  34. {
  35. haversine_type strategy(radius);
  36. Point p1, p2;
  37. bg::assign_values(p1, lon1, LatitudePolicy::apply(lat1));
  38. bg::assign_values(p2, lon2, LatitudePolicy::apply(lat2));
  39. return_type d = strategy.apply(p1, p2);
  40. BOOST_CHECK_CLOSE(d, expected, tolerance);
  41. }
  42. };
  43. template <typename Point, typename LatitudePolicy>
  44. void test_all()
  45. {
  46. // earth to unit-sphere -> divide by earth circumference, then it is from 0-1,
  47. // then multiply with 2 PI, so effectively just divide by earth radius
  48. double e2u = 1.0 / average_earth_radius;
  49. // ~ Amsterdam/Paris, 467 kilometers
  50. double const a_p = 467.2704 * 1000.0;
  51. test_distance<Point, LatitudePolicy>::test(4, 52, 2, 48, average_earth_radius, a_p, 1.0);
  52. test_distance<Point, LatitudePolicy>::test(2, 48, 4, 52, average_earth_radius, a_p, 1.0);
  53. test_distance<Point, LatitudePolicy>::test(4, 52, 2, 48, 1.0, a_p * e2u, 0.001);
  54. // ~ Amsterdam/Barcelona
  55. double const a_b = 1232.9065 * 1000.0;
  56. test_distance<Point, LatitudePolicy>::test(4, 52, 2, 41, average_earth_radius, a_b, 1.0);
  57. test_distance<Point, LatitudePolicy>::test(2, 41, 4, 52, average_earth_radius, a_b, 1.0);
  58. test_distance<Point, LatitudePolicy>::test(4, 52, 2, 41, 1.0, a_b * e2u, 0.001);
  59. }
  60. template <typename P1, typename P2, typename CalculationType, typename LatitudePolicy>
  61. void test_services()
  62. {
  63. namespace bgsd = bg::strategy::distance;
  64. namespace services = bg::strategy::distance::services;
  65. {
  66. // Compile-check if there is a strategy for this type
  67. typedef typename services::default_strategy
  68. <
  69. bg::point_tag, bg::point_tag, P1, P2
  70. >::type haversine_strategy_type;
  71. boost::ignore_unused<haversine_strategy_type>();
  72. }
  73. P1 p1;
  74. bg::assign_values(p1, 4, 52);
  75. P2 p2;
  76. bg::assign_values(p2, 2, 48);
  77. // ~ Amsterdam/Paris, 467 kilometers
  78. double const km = 1000.0;
  79. double const a_p = 467.2704 * km;
  80. double const expected = a_p;
  81. double const expected_lower = 460.0 * km;
  82. double const expected_higher = 470.0 * km;
  83. // 1: normal, calculate distance:
  84. typedef bgsd::haversine<double, CalculationType> strategy_type;
  85. typedef typename bgsd::services::return_type<strategy_type, P1, P2>::type return_type;
  86. strategy_type strategy(average_earth_radius);
  87. return_type result = strategy.apply(p1, p2);
  88. BOOST_CHECK_CLOSE(result, return_type(expected), 0.001);
  89. // 2: the strategy should return the same result if we reverse parameters
  90. result = strategy.apply(p2, p1);
  91. BOOST_CHECK_CLOSE(result, return_type(expected), 0.001);
  92. // 3: "comparable" to construct a "comparable strategy" for P1/P2
  93. // a "comparable strategy" is a strategy which does not calculate the exact distance, but
  94. // which returns results which can be mutually compared (e.g. avoid sqrt)
  95. // 3a: "comparable_type"
  96. typedef typename services::comparable_type<strategy_type>::type comparable_type;
  97. // 3b: "get_comparable"
  98. comparable_type comparable = bgsd::services::get_comparable<strategy_type>::apply(strategy);
  99. // Check vice versa:
  100. // First the result of the comparable strategy
  101. return_type c_result = comparable.apply(p1, p2);
  102. // Second the comparable result of the expected distance
  103. return_type c_expected = services::result_from_distance<comparable_type, P1, P2>::apply(comparable, expected);
  104. // And that one should be equa.
  105. BOOST_CHECK_CLOSE(c_result, return_type(c_expected), 0.001);
  106. // 4: the comparable_type should have a distance_strategy_constructor as well,
  107. // knowing how to compare something with a fixed distance
  108. return_type c_dist_lower = services::result_from_distance<comparable_type, P1, P2>::apply(comparable, expected_lower);
  109. return_type c_dist_higher = services::result_from_distance<comparable_type, P1, P2>::apply(comparable, expected_higher);
  110. // If this is the case:
  111. BOOST_CHECK(c_dist_lower < c_result && c_result < c_dist_higher);
  112. // Calculate the Haversine by hand here:
  113. return_type c_check = return_type(2.0) * asin(sqrt(c_result)) * average_earth_radius;
  114. BOOST_CHECK_CLOSE(c_check, expected, 0.001);
  115. // This should also be the case
  116. return_type dist_lower = services::result_from_distance<strategy_type, P1, P2>::apply(strategy, expected_lower);
  117. return_type dist_higher = services::result_from_distance<strategy_type, P1, P2>::apply(strategy, expected_higher);
  118. BOOST_CHECK(dist_lower < result && result < dist_higher);
  119. }
  120. /****
  121. template <typename P, typename Strategy>
  122. void time_compare_s(int const n)
  123. {
  124. boost::timer t;
  125. P p1, p2;
  126. bg::assign_values(p1, 1, 1);
  127. bg::assign_values(p2, 2, 2);
  128. Strategy strategy;
  129. typename Strategy::return_type s = 0;
  130. for (int i = 0; i < n; i++)
  131. {
  132. for (int j = 0; j < n; j++)
  133. {
  134. s += strategy.apply(p1, p2);
  135. }
  136. }
  137. std::cout << "s: " << s << " t: " << t.elapsed() << std::endl;
  138. }
  139. template <typename P>
  140. void time_compare(int const n)
  141. {
  142. time_compare_s<P, bg::strategy::distance::haversine<double> >(n);
  143. time_compare_s<P, bg::strategy::distance::comparable::haversine<double> >(n);
  144. }
  145. #include <time.h>
  146. double time_sqrt(int n)
  147. {
  148. clock_t start = clock();
  149. double v = 2.0;
  150. double s = 0.0;
  151. for (int i = 0; i < n; i++)
  152. {
  153. for (int j = 0; j < n; j++)
  154. {
  155. s += sqrt(v);
  156. v += 1.0e-10;
  157. }
  158. }
  159. clock_t end = clock();
  160. double diff = double(end - start) / CLOCKS_PER_SEC;
  161. std::cout << "Check: " << s << " Time: " << diff << std::endl;
  162. return diff;
  163. }
  164. double time_normal(int n)
  165. {
  166. clock_t start = clock();
  167. double v = 2.0;
  168. double s = 0.0;
  169. for (int i = 0; i < n; i++)
  170. {
  171. for (int j = 0; j < n; j++)
  172. {
  173. s += v;
  174. v += 1.0e-10;
  175. }
  176. }
  177. clock_t end = clock();
  178. double diff = double(end - start) / CLOCKS_PER_SEC;
  179. std::cout << "Check: " << s << " Time: " << diff << std::endl;
  180. return diff;
  181. }
  182. ***/
  183. int test_main(int, char* [])
  184. {
  185. test_all<bg::model::point<int, 2, bg::cs::spherical_equatorial<bg::degree> >, geographic_policy>();
  186. test_all<bg::model::point<float, 2, bg::cs::spherical_equatorial<bg::degree> >, geographic_policy>();
  187. test_all<bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> >, geographic_policy>();
  188. // NYI: haversine for mathematical spherical coordinate systems
  189. // test_all<bg::model::point<double, 2, bg::cs::spherical<bg::degree> >, mathematical_policy>();
  190. //double t1 = time_sqrt(20000);
  191. //double t2 = time_normal(20000);
  192. //std::cout << "Factor: " << (t1 / t2) << std::endl;
  193. //time_compare<bg::model::point<double, 2, bg::cs::spherical<bg::radian> > >(10000);
  194. #if defined(HAVE_TTMATH)
  195. typedef ttmath::Big<1,4> tt;
  196. test_all<bg::model::point<tt, 2, bg::cs::spherical_equatorial<bg::degree> >, geographic_policy>();
  197. #endif
  198. test_services
  199. <
  200. bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> >,
  201. bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> >,
  202. double,
  203. geographic_policy
  204. >();
  205. return 0;
  206. }