distance_cross_track.hpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2014-2019.
  4. // Modifications copyright (c) 2014-2019, Oracle and/or its affiliates.
  5. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  6. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  7. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  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. #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
  12. #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
  13. #include <algorithm>
  14. #include <boost/config.hpp>
  15. #include <boost/concept_check.hpp>
  16. #include <boost/mpl/if.hpp>
  17. #include <boost/type_traits/is_void.hpp>
  18. #include <boost/geometry/core/cs.hpp>
  19. #include <boost/geometry/core/access.hpp>
  20. #include <boost/geometry/core/radian_access.hpp>
  21. #include <boost/geometry/core/tags.hpp>
  22. #include <boost/geometry/formulas/spherical.hpp>
  23. #include <boost/geometry/strategies/distance.hpp>
  24. #include <boost/geometry/strategies/concepts/distance_concept.hpp>
  25. #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
  26. #include <boost/geometry/strategies/spherical/point_in_point.hpp>
  27. #include <boost/geometry/strategies/spherical/intersection.hpp>
  28. #include <boost/geometry/util/math.hpp>
  29. #include <boost/geometry/util/promote_floating_point.hpp>
  30. #include <boost/geometry/util/select_calculation_type.hpp>
  31. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  32. # include <boost/geometry/io/dsv/write.hpp>
  33. #endif
  34. namespace boost { namespace geometry
  35. {
  36. namespace strategy { namespace distance
  37. {
  38. namespace comparable
  39. {
  40. /*
  41. Given a spherical segment AB and a point D, we are interested in
  42. computing the distance of D from AB. This is usually known as the
  43. cross track distance.
  44. If the projection (along great circles) of the point D lies inside
  45. the segment AB, then the distance (cross track error) XTD is given
  46. by the formula (see http://williams.best.vwh.net/avform.htm#XTE):
  47. XTD = asin( sin(dist_AD) * sin(crs_AD-crs_AB) )
  48. where dist_AD is the great circle distance between the points A and
  49. B, and crs_AD, crs_AB is the course (bearing) between the points A,
  50. D and A, B, respectively.
  51. If the point D does not project inside the arc AB, then the distance
  52. of D from AB is the minimum of the two distances dist_AD and dist_BD.
  53. Our reference implementation for this procedure is listed below
  54. (this was the old Boost.Geometry implementation of the cross track distance),
  55. where:
  56. * The member variable m_strategy is the underlying haversine strategy.
  57. * p stands for the point D.
  58. * sp1 stands for the segment endpoint A.
  59. * sp2 stands for the segment endpoint B.
  60. ================= reference implementation -- start =================
  61. return_type d1 = m_strategy.apply(sp1, p);
  62. return_type d3 = m_strategy.apply(sp1, sp2);
  63. if (geometry::math::equals(d3, 0.0))
  64. {
  65. // "Degenerate" segment, return either d1 or d2
  66. return d1;
  67. }
  68. return_type d2 = m_strategy.apply(sp2, p);
  69. return_type crs_AD = geometry::detail::course<return_type>(sp1, p);
  70. return_type crs_AB = geometry::detail::course<return_type>(sp1, sp2);
  71. return_type crs_BA = crs_AB - geometry::math::pi<return_type>();
  72. return_type crs_BD = geometry::detail::course<return_type>(sp2, p);
  73. return_type d_crs1 = crs_AD - crs_AB;
  74. return_type d_crs2 = crs_BD - crs_BA;
  75. // d1, d2, d3 are in principle not needed, only the sign matters
  76. return_type projection1 = cos( d_crs1 ) * d1 / d3;
  77. return_type projection2 = cos( d_crs2 ) * d2 / d3;
  78. if (projection1 > 0.0 && projection2 > 0.0)
  79. {
  80. return_type XTD
  81. = radius() * math::abs( asin( sin( d1 / radius() ) * sin( d_crs1 ) ));
  82. // Return shortest distance, projected point on segment sp1-sp2
  83. return return_type(XTD);
  84. }
  85. else
  86. {
  87. // Return shortest distance, project either on point sp1 or sp2
  88. return return_type( (std::min)( d1 , d2 ) );
  89. }
  90. ================= reference implementation -- end =================
  91. Motivation
  92. ----------
  93. In what follows we develop a comparable version of the cross track
  94. distance strategy, that meets the following goals:
  95. * It is more efficient than the original cross track strategy (less
  96. operations and less calls to mathematical functions).
  97. * Distances using the comparable cross track strategy can not only
  98. be compared with other distances using the same strategy, but also with
  99. distances computed with the comparable version of the haversine strategy.
  100. * It can serve as the basis for the computation of the cross track distance,
  101. as it is more efficient to compute its comparable version and
  102. transform that to the actual cross track distance, rather than
  103. follow/use the reference implementation listed above.
  104. Major idea
  105. ----------
  106. The idea here is to use the comparable haversine strategy to compute
  107. the distances d1, d2 and d3 in the above listing. Once we have done
  108. that we need also to make sure that instead of returning XTD (as
  109. computed above) that we return a distance CXTD that is compatible
  110. with the comparable haversine distance. To achieve this CXTD must satisfy
  111. the relation:
  112. XTD = 2 * R * asin( sqrt(XTD) )
  113. where R is the sphere's radius.
  114. Below we perform the mathematical analysis that show how to compute CXTD.
  115. Mathematical analysis
  116. ---------------------
  117. Below we use the following trigonometric identities:
  118. sin(2 * x) = 2 * sin(x) * cos(x)
  119. cos(asin(x)) = sqrt(1 - x^2)
  120. Observation:
  121. The distance d1 needed when the projection of the point D is within the
  122. segment must be the true distance. However, comparable::haversine<>
  123. returns a comparable distance instead of the one needed.
  124. To remedy this, we implicitly compute what is needed.
  125. More precisely, we need to compute sin(true_d1):
  126. sin(true_d1) = sin(2 * asin(sqrt(d1)))
  127. = 2 * sin(asin(sqrt(d1)) * cos(asin(sqrt(d1)))
  128. = 2 * sqrt(d1) * sqrt(1-(sqrt(d1))^2)
  129. = 2 * sqrt(d1 - d1 * d1)
  130. This relation is used below.
  131. As we mentioned above the goal is to find CXTD (named "a" below for
  132. brevity) such that ("b" below stands for "d1", and "c" for "d_crs1"):
  133. 2 * R * asin(sqrt(a)) == R * asin(2 * sqrt(b-b^2) * sin(c))
  134. Analysis:
  135. 2 * R * asin(sqrt(a)) == R * asin(2 * sqrt(b-b^2) * sin(c))
  136. <=> 2 * asin(sqrt(a)) == asin(sqrt(b-b^2) * sin(c))
  137. <=> sin(2 * asin(sqrt(a))) == 2 * sqrt(b-b^2) * sin(c)
  138. <=> 2 * sin(asin(sqrt(a))) * cos(asin(sqrt(a))) == 2 * sqrt(b-b^2) * sin(c)
  139. <=> 2 * sqrt(a) * sqrt(1-a) == 2 * sqrt(b-b^2) * sin(c)
  140. <=> sqrt(a) * sqrt(1-a) == sqrt(b-b^2) * sin(c)
  141. <=> sqrt(a-a^2) == sqrt(b-b^2) * sin(c)
  142. <=> a-a^2 == (b-b^2) * (sin(c))^2
  143. Consider the quadratic equation: x^2-x+p^2 == 0,
  144. where p = sqrt(b-b^2) * sin(c); its discriminant is:
  145. d = 1 - 4 * p^2 = 1 - 4 * (b-b^2) * (sin(c))^2
  146. The two solutions are:
  147. a_1 = (1 - sqrt(d)) / 2
  148. a_2 = (1 + sqrt(d)) / 2
  149. Which one to choose?
  150. "a" refers to the distance (on the unit sphere) of D from the
  151. supporting great circle Circ(A,B) of the segment AB.
  152. The two different values for "a" correspond to the lengths of the two
  153. arcs delimited D and the points of intersection of Circ(A,B) and the
  154. great circle perperdicular to Circ(A,B) passing through D.
  155. Clearly, the value we want is the smallest among these two distances,
  156. hence the root we must choose is the smallest root among the two.
  157. So the answer is:
  158. CXTD = ( 1 - sqrt(1 - 4 * (b-b^2) * (sin(c))^2) ) / 2
  159. Therefore, in order to implement the comparable version of the cross
  160. track strategy we need to:
  161. (1) Use the comparable version of the haversine strategy instead of
  162. the non-comparable one.
  163. (2) Instead of return XTD when D projects inside the segment AB, we
  164. need to return CXTD, given by the following formula:
  165. CXTD = ( 1 - sqrt(1 - 4 * (d1-d1^2) * (sin(d_crs1))^2) ) / 2;
  166. Complexity Analysis
  167. -------------------
  168. In the analysis that follows we refer to the actual implementation below.
  169. In particular, instead of computing CXTD as above, we use the more
  170. efficient (operation-wise) computation of CXTD shown here:
  171. return_type sin_d_crs1 = sin(d_crs1);
  172. return_type d1_x_sin = d1 * sin_d_crs1;
  173. return_type d = d1_x_sin * (sin_d_crs1 - d1_x_sin);
  174. return d / (0.5 + math::sqrt(0.25 - d));
  175. Notice that instead of computing:
  176. 0.5 - 0.5 * sqrt(1 - 4 * d) = 0.5 - sqrt(0.25 - d)
  177. we use the following formula instead:
  178. d / (0.5 + sqrt(0.25 - d)).
  179. This is done for numerical robustness. The expression 0.5 - sqrt(0.25 - x)
  180. has large numerical errors for values of x close to 0 (if using doubles
  181. the error start to become large even when d is as large as 0.001).
  182. To remedy that, we re-write 0.5 - sqrt(0.25 - x) as:
  183. 0.5 - sqrt(0.25 - d)
  184. = (0.5 - sqrt(0.25 - d) * (0.5 - sqrt(0.25 - d)) / (0.5 + sqrt(0.25 - d)).
  185. The numerator is the difference of two squares:
  186. (0.5 - sqrt(0.25 - d) * (0.5 - sqrt(0.25 - d))
  187. = 0.5^2 - (sqrt(0.25 - d))^ = 0.25 - (0.25 - d) = d,
  188. which gives the expression we use.
  189. For the complexity analysis, we distinguish between two cases:
  190. (A) The distance is realized between the point D and an
  191. endpoint of the segment AB
  192. Gains:
  193. Since we are using comparable::haversine<> which is called
  194. 3 times, we gain:
  195. -> 3 calls to sqrt
  196. -> 3 calls to asin
  197. -> 6 multiplications
  198. Loses: None
  199. So the net gain is:
  200. -> 6 function calls (sqrt/asin)
  201. -> 6 arithmetic operations
  202. If we use comparable::cross_track<> to compute
  203. cross_track<> we need to account for a call to sqrt, a call
  204. to asin and 2 multiplications. In this case the net gain is:
  205. -> 4 function calls (sqrt/asin)
  206. -> 4 arithmetic operations
  207. (B) The distance is realized between the point D and an
  208. interior point of the segment AB
  209. Gains:
  210. Since we are using comparable::haversine<> which is called
  211. 3 times, we gain:
  212. -> 3 calls to sqrt
  213. -> 3 calls to asin
  214. -> 6 multiplications
  215. Also we gain the operations used to compute XTD:
  216. -> 2 calls to sin
  217. -> 1 call to asin
  218. -> 1 call to abs
  219. -> 2 multiplications
  220. -> 1 division
  221. So the total gains are:
  222. -> 9 calls to sqrt/sin/asin
  223. -> 1 call to abs
  224. -> 8 multiplications
  225. -> 1 division
  226. Loses:
  227. To compute a distance compatible with comparable::haversine<>
  228. we need to perform a few more operations, namely:
  229. -> 1 call to sin
  230. -> 1 call to sqrt
  231. -> 2 multiplications
  232. -> 1 division
  233. -> 1 addition
  234. -> 2 subtractions
  235. So roughly speaking the net gain is:
  236. -> 8 fewer function calls and 3 fewer arithmetic operations
  237. If we were to implement cross_track directly from the
  238. comparable version (much like what haversine<> does using
  239. comparable::haversine<>) we need additionally
  240. -> 2 function calls (asin/sqrt)
  241. -> 2 multiplications
  242. So it pays off to re-implement cross_track<> to use
  243. comparable::cross_track<>; in this case the net gain would be:
  244. -> 6 function calls
  245. -> 1 arithmetic operation
  246. Summary/Conclusion
  247. ------------------
  248. Following the mathematical and complexity analysis above, the
  249. comparable cross track strategy (as implemented below) satisfies
  250. all the goal mentioned in the beginning:
  251. * It is more efficient than its non-comparable counter-part.
  252. * Comparable distances using this new strategy can also be compared
  253. with comparable distances computed with the comparable haversine
  254. strategy.
  255. * It turns out to be more efficient to compute the actual cross
  256. track distance XTD by first computing CXTD, and then computing
  257. XTD by means of the formula:
  258. XTD = 2 * R * asin( sqrt(CXTD) )
  259. */
  260. template
  261. <
  262. typename CalculationType = void,
  263. typename Strategy = comparable::haversine<double, CalculationType>
  264. >
  265. class cross_track
  266. {
  267. public :
  268. typedef within::spherical_point_point equals_point_point_strategy_type;
  269. typedef intersection::spherical_segments
  270. <
  271. CalculationType
  272. > relate_segment_segment_strategy_type;
  273. static inline relate_segment_segment_strategy_type get_relate_segment_segment_strategy()
  274. {
  275. return relate_segment_segment_strategy_type();
  276. }
  277. typedef within::spherical_winding
  278. <
  279. void, void, CalculationType
  280. > point_in_geometry_strategy_type;
  281. static inline point_in_geometry_strategy_type get_point_in_geometry_strategy()
  282. {
  283. return point_in_geometry_strategy_type();
  284. }
  285. template <typename Point, typename PointOfSegment>
  286. struct return_type
  287. : promote_floating_point
  288. <
  289. typename select_calculation_type
  290. <
  291. Point,
  292. PointOfSegment,
  293. CalculationType
  294. >::type
  295. >
  296. {};
  297. typedef typename Strategy::radius_type radius_type;
  298. inline cross_track()
  299. {}
  300. explicit inline cross_track(typename Strategy::radius_type const& r)
  301. : m_strategy(r)
  302. {}
  303. inline cross_track(Strategy const& s)
  304. : m_strategy(s)
  305. {}
  306. // It might be useful in the future
  307. // to overload constructor with strategy info.
  308. // crosstrack(...) {}
  309. template <typename Point, typename PointOfSegment>
  310. inline typename return_type<Point, PointOfSegment>::type
  311. apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const
  312. {
  313. #if !defined(BOOST_MSVC)
  314. BOOST_CONCEPT_ASSERT
  315. (
  316. (concepts::PointDistanceStrategy<Strategy, Point, PointOfSegment>)
  317. );
  318. #endif
  319. typedef typename return_type<Point, PointOfSegment>::type return_type;
  320. // http://williams.best.vwh.net/avform.htm#XTE
  321. return_type d1 = m_strategy.apply(sp1, p);
  322. return_type d3 = m_strategy.apply(sp1, sp2);
  323. if (geometry::math::equals(d3, 0.0))
  324. {
  325. // "Degenerate" segment, return either d1 or d2
  326. return d1;
  327. }
  328. return_type d2 = m_strategy.apply(sp2, p);
  329. return_type lon1 = geometry::get_as_radian<0>(sp1);
  330. return_type lat1 = geometry::get_as_radian<1>(sp1);
  331. return_type lon2 = geometry::get_as_radian<0>(sp2);
  332. return_type lat2 = geometry::get_as_radian<1>(sp2);
  333. return_type lon = geometry::get_as_radian<0>(p);
  334. return_type lat = geometry::get_as_radian<1>(p);
  335. return_type crs_AD = geometry::formula::spherical_azimuth<return_type, false>
  336. (lon1, lat1, lon, lat).azimuth;
  337. geometry::formula::result_spherical<return_type> result =
  338. geometry::formula::spherical_azimuth<return_type, true>
  339. (lon1, lat1, lon2, lat2);
  340. return_type crs_AB = result.azimuth;
  341. return_type crs_BA = result.reverse_azimuth - geometry::math::pi<return_type>();
  342. return_type crs_BD = geometry::formula::spherical_azimuth<return_type, false>
  343. (lon2, lat2, lon, lat).azimuth;
  344. return_type d_crs1 = crs_AD - crs_AB;
  345. return_type d_crs2 = crs_BD - crs_BA;
  346. // d1, d2, d3 are in principle not needed, only the sign matters
  347. return_type projection1 = cos( d_crs1 ) * d1 / d3;
  348. return_type projection2 = cos( d_crs2 ) * d2 / d3;
  349. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  350. std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " "
  351. << crs_AD * geometry::math::r2d<return_type>() << std::endl;
  352. std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " "
  353. << crs_AB * geometry::math::r2d<return_type>() << std::endl;
  354. std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " "
  355. << crs_BA * geometry::math::r2d<return_type>() << std::endl;
  356. std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " "
  357. << crs_BD * geometry::math::r2d<return_type>() << std::endl;
  358. std::cout << "Projection AD-AB " << projection1 << " : "
  359. << d_crs1 * geometry::math::r2d<return_type>() << std::endl;
  360. std::cout << "Projection BD-BA " << projection2 << " : "
  361. << d_crs2 * geometry::math::r2d<return_type>() << std::endl;
  362. std::cout << " d1: " << (d1 )
  363. << " d2: " << (d2 )
  364. << std::endl;
  365. #endif
  366. if (projection1 > 0.0 && projection2 > 0.0)
  367. {
  368. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  369. return_type XTD = radius() * geometry::math::abs( asin( sin( d1 ) * sin( d_crs1 ) ));
  370. std::cout << "Projection ON the segment" << std::endl;
  371. std::cout << "XTD: " << XTD
  372. << " d1: " << (d1 * radius())
  373. << " d2: " << (d2 * radius())
  374. << std::endl;
  375. #endif
  376. return_type const half(0.5);
  377. return_type const quarter(0.25);
  378. return_type sin_d_crs1 = sin(d_crs1);
  379. /*
  380. This is the straightforward obvious way to continue:
  381. return_type discriminant
  382. = 1.0 - 4.0 * (d1 - d1 * d1) * sin_d_crs1 * sin_d_crs1;
  383. return 0.5 - 0.5 * math::sqrt(discriminant);
  384. Below we optimize the number of arithmetic operations
  385. and account for numerical robustness:
  386. */
  387. return_type d1_x_sin = d1 * sin_d_crs1;
  388. return_type d = d1_x_sin * (sin_d_crs1 - d1_x_sin);
  389. return d / (half + math::sqrt(quarter - d));
  390. }
  391. else
  392. {
  393. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  394. std::cout << "Projection OUTSIDE the segment" << std::endl;
  395. #endif
  396. // Return shortest distance, project either on point sp1 or sp2
  397. return return_type( (std::min)( d1 , d2 ) );
  398. }
  399. }
  400. template <typename T1, typename T2>
  401. inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const
  402. {
  403. return m_strategy.radius() * (lat1 - lat2);
  404. }
  405. inline typename Strategy::radius_type radius() const
  406. { return m_strategy.radius(); }
  407. private :
  408. Strategy m_strategy;
  409. };
  410. } // namespace comparable
  411. /*!
  412. \brief Strategy functor for distance point to segment calculation
  413. \ingroup strategies
  414. \details Class which calculates the distance of a point to a segment, for points on a sphere or globe
  415. \see http://williams.best.vwh.net/avform.htm
  416. \tparam CalculationType \tparam_calculation
  417. \tparam Strategy underlying point-point distance strategy, defaults to haversine
  418. \qbk{
  419. [heading See also]
  420. [link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)]
  421. }
  422. */
  423. template
  424. <
  425. typename CalculationType = void,
  426. typename Strategy = haversine<double, CalculationType>
  427. >
  428. class cross_track
  429. {
  430. public :
  431. typedef within::spherical_point_point equals_point_point_strategy_type;
  432. typedef intersection::spherical_segments
  433. <
  434. CalculationType
  435. > relate_segment_segment_strategy_type;
  436. static inline relate_segment_segment_strategy_type get_relate_segment_segment_strategy()
  437. {
  438. return relate_segment_segment_strategy_type();
  439. }
  440. typedef within::spherical_winding
  441. <
  442. void, void, CalculationType
  443. > point_in_geometry_strategy_type;
  444. static inline point_in_geometry_strategy_type get_point_in_geometry_strategy()
  445. {
  446. return point_in_geometry_strategy_type();
  447. }
  448. template <typename Point, typename PointOfSegment>
  449. struct return_type
  450. : promote_floating_point
  451. <
  452. typename select_calculation_type
  453. <
  454. Point,
  455. PointOfSegment,
  456. CalculationType
  457. >::type
  458. >
  459. {};
  460. typedef typename Strategy::radius_type radius_type;
  461. inline cross_track()
  462. {}
  463. explicit inline cross_track(typename Strategy::radius_type const& r)
  464. : m_strategy(r)
  465. {}
  466. inline cross_track(Strategy const& s)
  467. : m_strategy(s)
  468. {}
  469. // It might be useful in the future
  470. // to overload constructor with strategy info.
  471. // crosstrack(...) {}
  472. template <typename Point, typename PointOfSegment>
  473. inline typename return_type<Point, PointOfSegment>::type
  474. apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const
  475. {
  476. #if !defined(BOOST_MSVC)
  477. BOOST_CONCEPT_ASSERT
  478. (
  479. (concepts::PointDistanceStrategy<Strategy, Point, PointOfSegment>)
  480. );
  481. #endif
  482. typedef typename return_type<Point, PointOfSegment>::type return_type;
  483. typedef cross_track<CalculationType, Strategy> this_type;
  484. typedef typename services::comparable_type
  485. <
  486. this_type
  487. >::type comparable_type;
  488. comparable_type cstrategy
  489. = services::get_comparable<this_type>::apply(m_strategy);
  490. return_type const a = cstrategy.apply(p, sp1, sp2);
  491. return_type const c = return_type(2.0) * asin(math::sqrt(a));
  492. return c * radius();
  493. }
  494. template <typename T1, typename T2>
  495. inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const
  496. {
  497. return m_strategy.radius() * (lat1 - lat2);
  498. }
  499. inline typename Strategy::radius_type radius() const
  500. { return m_strategy.radius(); }
  501. private :
  502. Strategy m_strategy;
  503. };
  504. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  505. namespace services
  506. {
  507. template <typename CalculationType, typename Strategy>
  508. struct tag<cross_track<CalculationType, Strategy> >
  509. {
  510. typedef strategy_tag_distance_point_segment type;
  511. };
  512. template <typename CalculationType, typename Strategy, typename P, typename PS>
  513. struct return_type<cross_track<CalculationType, Strategy>, P, PS>
  514. : cross_track<CalculationType, Strategy>::template return_type<P, PS>
  515. {};
  516. template <typename CalculationType, typename Strategy>
  517. struct comparable_type<cross_track<CalculationType, Strategy> >
  518. {
  519. typedef comparable::cross_track
  520. <
  521. CalculationType, typename comparable_type<Strategy>::type
  522. > type;
  523. };
  524. template
  525. <
  526. typename CalculationType,
  527. typename Strategy
  528. >
  529. struct get_comparable<cross_track<CalculationType, Strategy> >
  530. {
  531. typedef typename comparable_type
  532. <
  533. cross_track<CalculationType, Strategy>
  534. >::type comparable_type;
  535. public :
  536. static inline comparable_type
  537. apply(cross_track<CalculationType, Strategy> const& strategy)
  538. {
  539. return comparable_type(strategy.radius());
  540. }
  541. };
  542. template
  543. <
  544. typename CalculationType,
  545. typename Strategy,
  546. typename P,
  547. typename PS
  548. >
  549. struct result_from_distance<cross_track<CalculationType, Strategy>, P, PS>
  550. {
  551. private :
  552. typedef typename cross_track
  553. <
  554. CalculationType, Strategy
  555. >::template return_type<P, PS>::type return_type;
  556. public :
  557. template <typename T>
  558. static inline return_type
  559. apply(cross_track<CalculationType, Strategy> const& , T const& distance)
  560. {
  561. return distance;
  562. }
  563. };
  564. // Specializations for comparable::cross_track
  565. template <typename RadiusType, typename CalculationType>
  566. struct tag<comparable::cross_track<RadiusType, CalculationType> >
  567. {
  568. typedef strategy_tag_distance_point_segment type;
  569. };
  570. template
  571. <
  572. typename RadiusType,
  573. typename CalculationType,
  574. typename P,
  575. typename PS
  576. >
  577. struct return_type<comparable::cross_track<RadiusType, CalculationType>, P, PS>
  578. : comparable::cross_track
  579. <
  580. RadiusType, CalculationType
  581. >::template return_type<P, PS>
  582. {};
  583. template <typename RadiusType, typename CalculationType>
  584. struct comparable_type<comparable::cross_track<RadiusType, CalculationType> >
  585. {
  586. typedef comparable::cross_track<RadiusType, CalculationType> type;
  587. };
  588. template <typename RadiusType, typename CalculationType>
  589. struct get_comparable<comparable::cross_track<RadiusType, CalculationType> >
  590. {
  591. private :
  592. typedef comparable::cross_track<RadiusType, CalculationType> this_type;
  593. public :
  594. static inline this_type apply(this_type const& input)
  595. {
  596. return input;
  597. }
  598. };
  599. template
  600. <
  601. typename RadiusType,
  602. typename CalculationType,
  603. typename P,
  604. typename PS
  605. >
  606. struct result_from_distance
  607. <
  608. comparable::cross_track<RadiusType, CalculationType>, P, PS
  609. >
  610. {
  611. private :
  612. typedef comparable::cross_track<RadiusType, CalculationType> strategy_type;
  613. typedef typename return_type<strategy_type, P, PS>::type return_type;
  614. public :
  615. template <typename T>
  616. static inline return_type apply(strategy_type const& strategy,
  617. T const& distance)
  618. {
  619. return_type const s
  620. = sin( (distance / strategy.radius()) / return_type(2.0) );
  621. return s * s;
  622. }
  623. };
  624. /*
  625. TODO: spherical polar coordinate system requires "get_as_radian_equatorial<>"
  626. template <typename Point, typename PointOfSegment, typename Strategy>
  627. struct default_strategy
  628. <
  629. segment_tag, Point, PointOfSegment,
  630. spherical_polar_tag, spherical_polar_tag,
  631. Strategy
  632. >
  633. {
  634. typedef cross_track
  635. <
  636. void,
  637. typename boost::mpl::if_
  638. <
  639. boost::is_void<Strategy>,
  640. typename default_strategy
  641. <
  642. point_tag, Point, PointOfSegment,
  643. spherical_polar_tag, spherical_polar_tag
  644. >::type,
  645. Strategy
  646. >::type
  647. > type;
  648. };
  649. */
  650. template <typename Point, typename PointOfSegment, typename Strategy>
  651. struct default_strategy
  652. <
  653. point_tag, segment_tag, Point, PointOfSegment,
  654. spherical_equatorial_tag, spherical_equatorial_tag,
  655. Strategy
  656. >
  657. {
  658. typedef cross_track
  659. <
  660. void,
  661. typename boost::mpl::if_
  662. <
  663. boost::is_void<Strategy>,
  664. typename default_strategy
  665. <
  666. point_tag, point_tag, Point, PointOfSegment,
  667. spherical_equatorial_tag, spherical_equatorial_tag
  668. >::type,
  669. Strategy
  670. >::type
  671. > type;
  672. };
  673. template <typename PointOfSegment, typename Point, typename Strategy>
  674. struct default_strategy
  675. <
  676. segment_tag, point_tag, PointOfSegment, Point,
  677. spherical_equatorial_tag, spherical_equatorial_tag,
  678. Strategy
  679. >
  680. {
  681. typedef typename default_strategy
  682. <
  683. point_tag, segment_tag, Point, PointOfSegment,
  684. spherical_equatorial_tag, spherical_equatorial_tag,
  685. Strategy
  686. >::type type;
  687. };
  688. } // namespace services
  689. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  690. }} // namespace strategy::distance
  691. }} // namespace boost::geometry
  692. #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP