distance_cross_track.hpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2016-2019, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Use, modification and distribution is subject to the Boost Software License,
  6. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. #ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP
  9. #define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP
  10. #include <algorithm>
  11. #include <boost/tuple/tuple.hpp>
  12. #include <boost/algorithm/minmax.hpp>
  13. #include <boost/config.hpp>
  14. #include <boost/concept_check.hpp>
  15. #include <boost/mpl/if.hpp>
  16. #include <boost/type_traits/is_void.hpp>
  17. #include <boost/geometry/core/cs.hpp>
  18. #include <boost/geometry/core/access.hpp>
  19. #include <boost/geometry/core/radian_access.hpp>
  20. #include <boost/geometry/core/tags.hpp>
  21. #include <boost/geometry/strategies/distance.hpp>
  22. #include <boost/geometry/strategies/concepts/distance_concept.hpp>
  23. #include <boost/geometry/strategies/spherical/distance_cross_track.hpp>
  24. #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
  25. #include <boost/geometry/strategies/spherical/point_in_point.hpp>
  26. #include <boost/geometry/strategies/geographic/azimuth.hpp>
  27. #include <boost/geometry/strategies/geographic/distance.hpp>
  28. #include <boost/geometry/strategies/geographic/parameters.hpp>
  29. #include <boost/geometry/strategies/geographic/intersection.hpp>
  30. #include <boost/geometry/formulas/vincenty_direct.hpp>
  31. #include <boost/geometry/util/math.hpp>
  32. #include <boost/geometry/util/promote_floating_point.hpp>
  33. #include <boost/geometry/util/select_calculation_type.hpp>
  34. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  35. #include <boost/geometry/formulas/result_direct.hpp>
  36. #include <boost/geometry/formulas/mean_radius.hpp>
  37. #include <boost/geometry/formulas/spherical.hpp>
  38. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  39. #include <boost/geometry/io/dsv/write.hpp>
  40. #endif
  41. #ifndef BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS
  42. #define BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS 100
  43. #endif
  44. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  45. #include <iostream>
  46. #endif
  47. namespace boost { namespace geometry
  48. {
  49. namespace strategy { namespace distance
  50. {
  51. namespace detail
  52. {
  53. /*!
  54. \brief Strategy functor for distance point to segment calculation on ellipsoid
  55. Algorithm uses direct and inverse geodesic problems as subroutines.
  56. The algorithm approximates the distance by an iterative Newton method.
  57. \ingroup strategies
  58. \details Class which calculates the distance of a point to a segment, for points
  59. on the ellipsoid
  60. \see C.F.F.Karney - Geodesics on an ellipsoid of revolution,
  61. https://arxiv.org/abs/1102.1215
  62. \tparam FormulaPolicy underlying point-point distance strategy
  63. \tparam Spheroid is the spheroidal model used
  64. \tparam CalculationType \tparam_calculation
  65. \tparam EnableClosestPoint computes the closest point on segment if true
  66. */
  67. template
  68. <
  69. typename FormulaPolicy = strategy::andoyer,
  70. typename Spheroid = srs::spheroid<double>,
  71. typename CalculationType = void,
  72. bool Bisection = false,
  73. bool EnableClosestPoint = false
  74. >
  75. class geographic_cross_track
  76. {
  77. public :
  78. typedef within::spherical_point_point equals_point_point_strategy_type;
  79. typedef intersection::geographic_segments
  80. <
  81. FormulaPolicy,
  82. strategy::default_order<FormulaPolicy>::value,
  83. Spheroid,
  84. CalculationType
  85. > relate_segment_segment_strategy_type;
  86. inline relate_segment_segment_strategy_type get_relate_segment_segment_strategy() const
  87. {
  88. return relate_segment_segment_strategy_type(m_spheroid);
  89. }
  90. typedef within::geographic_winding
  91. <
  92. void, void, FormulaPolicy, Spheroid, CalculationType
  93. > point_in_geometry_strategy_type;
  94. inline point_in_geometry_strategy_type get_point_in_geometry_strategy() const
  95. {
  96. return point_in_geometry_strategy_type(m_spheroid);
  97. }
  98. template <typename Point, typename PointOfSegment>
  99. struct return_type
  100. : promote_floating_point
  101. <
  102. typename select_calculation_type
  103. <
  104. Point,
  105. PointOfSegment,
  106. CalculationType
  107. >::type
  108. >
  109. {};
  110. explicit geographic_cross_track(Spheroid const& spheroid = Spheroid())
  111. : m_spheroid(spheroid)
  112. {}
  113. template <typename Point, typename PointOfSegment>
  114. inline typename return_type<Point, PointOfSegment>::type
  115. apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const
  116. {
  117. typedef typename geometry::detail::cs_angular_units<Point>::type units_type;
  118. return (apply<units_type>(get_as_radian<0>(sp1), get_as_radian<1>(sp1),
  119. get_as_radian<0>(sp2), get_as_radian<1>(sp2),
  120. get_as_radian<0>(p), get_as_radian<1>(p),
  121. m_spheroid)).distance;
  122. }
  123. // points on a meridian not crossing poles
  124. template <typename CT>
  125. inline CT vertical_or_meridian(CT const& lat1, CT const& lat2) const
  126. {
  127. typedef typename formula::meridian_inverse
  128. <
  129. CT,
  130. strategy::default_order<FormulaPolicy>::value
  131. > meridian_inverse;
  132. return meridian_inverse::meridian_not_crossing_pole_dist(lat1, lat2,
  133. m_spheroid);
  134. }
  135. Spheroid const& model() const
  136. {
  137. return m_spheroid;
  138. }
  139. private :
  140. template <typename CT>
  141. struct result_distance_point_segment
  142. {
  143. result_distance_point_segment()
  144. : distance(0)
  145. , closest_point_lon(0)
  146. , closest_point_lat(0)
  147. {}
  148. CT distance;
  149. CT closest_point_lon;
  150. CT closest_point_lat;
  151. };
  152. template <typename CT>
  153. result_distance_point_segment<CT>
  154. static inline non_iterative_case(CT const& lon, CT const& lat, CT const& distance)
  155. {
  156. result_distance_point_segment<CT> result;
  157. result.distance = distance;
  158. if (EnableClosestPoint)
  159. {
  160. result.closest_point_lon = lon;
  161. result.closest_point_lat = lat;
  162. }
  163. return result;
  164. }
  165. template <typename CT>
  166. result_distance_point_segment<CT>
  167. static inline non_iterative_case(CT const& lon1, CT const& lat1, //p1
  168. CT const& lon2, CT const& lat2, //p2
  169. Spheroid const& spheroid)
  170. {
  171. CT distance = geometry::strategy::distance::geographic<FormulaPolicy, Spheroid, CT>
  172. ::apply(lon1, lat1, lon2, lat2, spheroid);
  173. return non_iterative_case(lon1, lat1, distance);
  174. }
  175. template <typename CT>
  176. CT static inline normalize(CT const& g4, CT& der)
  177. {
  178. CT const pi = math::pi<CT>();
  179. if (g4 < -1.25*pi)//close to -270
  180. {
  181. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  182. std::cout << "g4=" << g4 * math::r2d<CT>() << ", close to -270" << std::endl;
  183. #endif
  184. return g4 + 1.5 * pi;
  185. }
  186. else if (g4 > 1.25*pi)//close to 270
  187. {
  188. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  189. std::cout << "g4=" << g4 * math::r2d<CT>() << ", close to 270" << std::endl;
  190. #endif
  191. der = -der;
  192. return - g4 + 1.5 * pi;
  193. }
  194. else if (g4 < 0 && g4 > -0.75*pi)//close to -90
  195. {
  196. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  197. std::cout << "g4=" << g4 * math::r2d<CT>() << ", close to -90" << std::endl;
  198. #endif
  199. der = -der;
  200. return -g4 - pi/2;
  201. }
  202. return g4 - pi/2;
  203. }
  204. template <typename CT>
  205. static void bisection(CT const& lon1, CT const& lat1, //p1
  206. CT const& lon2, CT const& lat2, //p2
  207. CT const& lon3, CT const& lat3, //query point p3
  208. Spheroid const& spheroid,
  209. CT const& s14_start, CT const& a12,
  210. result_distance_point_segment<CT>& result)
  211. {
  212. typedef typename FormulaPolicy::template direct<CT, true, false, false, false>
  213. direct_distance_type;
  214. typedef typename FormulaPolicy::template inverse<CT, true, false, false, false, false>
  215. inverse_distance_type;
  216. geometry::formula::result_direct<CT> res14;
  217. int counter = 0; // robustness
  218. bool dist_improve = true;
  219. CT pl_lon = lon1;
  220. CT pl_lat = lat1;
  221. CT pr_lon = lon2;
  222. CT pr_lat = lat2;
  223. CT s14 = s14_start;
  224. do{
  225. // Solve the direct problem to find p4 (GEO)
  226. res14 = direct_distance_type::apply(lon1, lat1, s14, a12, spheroid);
  227. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  228. std::cout << "dist(pl,p3)="
  229. << inverse_distance_type::apply(lon3, lat3,
  230. pr_lon, pr_lat,
  231. spheroid).distance
  232. << std::endl;
  233. std::cout << "dist(pr,p3)="
  234. << inverse_distance_type::apply(lon3, lat3,
  235. pr_lon, pr_lat,
  236. spheroid).distance
  237. << std::endl;
  238. #endif
  239. if (inverse_distance_type::apply(lon3, lat3, pl_lon, pl_lat, spheroid).distance
  240. < inverse_distance_type::apply(lon3, lat3, pr_lon, pr_lat, spheroid).distance)
  241. {
  242. s14 -= inverse_distance_type::apply(res14.lon2, res14.lat2, pl_lon, pl_lat,
  243. spheroid).distance/2;
  244. pr_lon = res14.lon2;
  245. pr_lat = res14.lat2;
  246. }
  247. else
  248. {
  249. s14 += inverse_distance_type::apply(res14.lon2, res14.lat2, pr_lon, pr_lat,
  250. spheroid).distance/2;
  251. pl_lon = res14.lon2;
  252. pl_lat = res14.lat2;
  253. }
  254. CT new_distance = inverse_distance_type::apply(lon3, lat3,
  255. res14.lon2, res14.lat2,
  256. spheroid).distance;
  257. dist_improve = new_distance != result.distance;
  258. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  259. std::cout << "p4=" << res14.lon2 * math::r2d<CT>() <<
  260. "," << res14.lat2 * math::r2d<CT>() << std::endl;
  261. std::cout << "pl=" << pl_lon * math::r2d<CT>() << "," << pl_lat * math::r2d<CT>()<< std::endl;
  262. std::cout << "pr=" << pr_lon * math::r2d<CT>() << "," << pr_lat * math::r2d<CT>() << std::endl;
  263. std::cout << "new_s14=" << s14 << std::endl;
  264. std::cout << std::setprecision(16) << "result.distance =" << result.distance << std::endl;
  265. std::cout << std::setprecision(16) << "new_distance =" << new_distance << std::endl;
  266. std::cout << "---------end of step " << counter << std::endl<< std::endl;
  267. if (!dist_improve)
  268. {
  269. std::cout << "Stop msg: res34.distance >= prev_distance" << std::endl;
  270. }
  271. if (counter == BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS)
  272. {
  273. std::cout << "Stop msg: counter" << std::endl;
  274. }
  275. #endif
  276. result.distance = new_distance;
  277. } while (dist_improve
  278. && counter++ < BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS);
  279. }
  280. template <typename CT>
  281. static void newton(CT const& lon1, CT const& lat1, //p1
  282. CT const& lon2, CT const& lat2, //p2
  283. CT const& lon3, CT const& lat3, //query point p3
  284. Spheroid const& spheroid,
  285. CT const& s14_start, CT const& a12,
  286. result_distance_point_segment<CT>& result)
  287. {
  288. typedef typename FormulaPolicy::template inverse<CT, true, true, false, true, true>
  289. inverse_distance_azimuth_quantities_type;
  290. typedef typename FormulaPolicy::template inverse<CT, false, true, false, false, false>
  291. inverse_dist_azimuth_type;
  292. typedef typename FormulaPolicy::template direct<CT, true, false, false, false>
  293. direct_distance_type;
  294. CT const half_pi = math::pi<CT>() / CT(2);
  295. CT prev_distance;
  296. geometry::formula::result_direct<CT> res14;
  297. geometry::formula::result_inverse<CT> res34;
  298. res34.distance = -1;
  299. int counter = 0; // robustness
  300. CT g4;
  301. CT delta_g4 = 0;
  302. bool dist_improve = true;
  303. CT s14 = s14_start;
  304. do{
  305. prev_distance = res34.distance;
  306. // Solve the direct problem to find p4 (GEO)
  307. res14 = direct_distance_type::apply(lon1, lat1, s14, a12, spheroid);
  308. // Solve an inverse problem to find g4
  309. // g4 is the angle between segment (p1,p2) and segment (p3,p4) that meet on p4 (GEO)
  310. CT a4 = inverse_dist_azimuth_type::apply(res14.lon2, res14.lat2,
  311. lon2, lat2, spheroid).azimuth;
  312. res34 = inverse_distance_azimuth_quantities_type::apply(res14.lon2, res14.lat2,
  313. lon3, lat3, spheroid);
  314. g4 = res34.azimuth - a4;
  315. CT M43 = res34.geodesic_scale; // cos(s14/earth_radius) is the spherical limit
  316. CT m34 = res34.reduced_length;
  317. if (m34 != 0)
  318. {
  319. CT der = (M43 / m34) * sin(g4);
  320. delta_g4 = normalize(g4, der);
  321. s14 -= der != 0 ? delta_g4 / der : 0;
  322. }
  323. result.distance = res34.distance;
  324. dist_improve = prev_distance > res34.distance || prev_distance == -1;
  325. if (!dist_improve)
  326. {
  327. result.distance = prev_distance;
  328. }
  329. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  330. std::cout << "p4=" << res14.lon2 * math::r2d<CT>() <<
  331. "," << res14.lat2 * math::r2d<CT>() << std::endl;
  332. std::cout << "a34=" << res34.azimuth * math::r2d<CT>() << std::endl;
  333. std::cout << "a4=" << a4 * math::r2d<CT>() << std::endl;
  334. std::cout << "g4(normalized)=" << g4 * math::r2d<CT>() << std::endl;
  335. std::cout << "delta_g4=" << delta_g4 * math::r2d<CT>() << std::endl;
  336. std::cout << "der=" << der << std::endl;
  337. std::cout << "M43=" << M43 << std::endl;
  338. std::cout << "m34=" << m34 << std::endl;
  339. std::cout << "new_s14=" << s14 << std::endl;
  340. std::cout << std::setprecision(16) << "dist =" << res34.distance << std::endl;
  341. std::cout << "---------end of step " << counter << std::endl<< std::endl;
  342. if (g4 == half_pi)
  343. {
  344. std::cout << "Stop msg: g4 == half_pi" << std::endl;
  345. }
  346. if (!dist_improve)
  347. {
  348. std::cout << "Stop msg: res34.distance >= prev_distance" << std::endl;
  349. }
  350. if (delta_g4 == 0)
  351. {
  352. std::cout << "Stop msg: delta_g4 == 0" << std::endl;
  353. }
  354. if (counter == BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS)
  355. {
  356. std::cout << "Stop msg: counter" << std::endl;
  357. }
  358. #endif
  359. } while (g4 != half_pi
  360. && dist_improve
  361. && delta_g4 != 0
  362. && counter++ < BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS);
  363. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  364. std::cout << "distance=" << res34.distance << std::endl;
  365. std::cout << "s34(geo) ="
  366. << inverse_distance_azimuth_quantities_type::apply(res14.lon2, res14.lat2, lon3, lat3, spheroid).distance
  367. << ", p4=(" << res14.lon2 * math::r2d<double>() << ","
  368. << res14.lat2 * math::r2d<double>() << ")"
  369. << std::endl;
  370. CT s31 = inverse_distance_azimuth_quantities_type::apply(lon3, lat3, lon1, lat1, spheroid).distance;
  371. CT s32 = inverse_distance_azimuth_quantities_type::apply(lon3, lat3, lon2, lat2, spheroid).distance;
  372. CT a4 = inverse_dist_azimuth_type::apply(res14.lon2, res14.lat2, lon2, lat2, spheroid).azimuth;
  373. geometry::formula::result_direct<CT> res4 = direct_distance_type::apply(res14.lon2, res14.lat2, .04, a4, spheroid);
  374. CT p4_plus = inverse_distance_azimuth_quantities_type::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance;
  375. geometry::formula::result_direct<CT> res1 = direct_distance_type::apply(lon1, lat1, s14-.04, a12, spheroid);
  376. CT p4_minus = inverse_distance_azimuth_quantities_type::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance;
  377. std::cout << "s31=" << s31 << "\ns32=" << s32
  378. << "\np4_plus=" << p4_plus << ", p4=(" << res4.lon2 * math::r2d<double>() << "," << res4.lat2 * math::r2d<double>() << ")"
  379. << "\np4_minus=" << p4_minus << ", p4=(" << res1.lon2 * math::r2d<double>() << "," << res1.lat2 * math::r2d<double>() << ")"
  380. << std::endl;
  381. if (res34.distance <= p4_plus && res34.distance <= p4_minus)
  382. {
  383. std::cout << "Closest point computed" << std::endl;
  384. }
  385. else
  386. {
  387. std::cout << "There is a closer point nearby" << std::endl;
  388. }
  389. #endif
  390. }
  391. template <typename Units, typename CT>
  392. result_distance_point_segment<CT>
  393. static inline apply(CT const& lo1, CT const& la1, //p1
  394. CT const& lo2, CT const& la2, //p2
  395. CT const& lo3, CT const& la3, //query point p3
  396. Spheroid const& spheroid)
  397. {
  398. typedef typename FormulaPolicy::template inverse<CT, true, true, false, false, false>
  399. inverse_dist_azimuth_type;
  400. typedef typename FormulaPolicy::template inverse<CT, true, true, true, false, false>
  401. inverse_dist_azimuth_reverse_type;
  402. CT const earth_radius = geometry::formula::mean_radius<CT>(spheroid);
  403. result_distance_point_segment<CT> result;
  404. // Constants
  405. //CT const f = geometry::formula::flattening<CT>(spheroid);
  406. CT const pi = math::pi<CT>();
  407. CT const half_pi = pi / CT(2);
  408. CT const c0 = CT(0);
  409. CT lon1 = lo1;
  410. CT lat1 = la1;
  411. CT lon2 = lo2;
  412. CT lat2 = la2;
  413. CT lon3 = lo3;
  414. CT lat3 = la3;
  415. if (lon1 > lon2)
  416. {
  417. std::swap(lon1, lon2);
  418. std::swap(lat1, lat2);
  419. }
  420. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  421. std::cout << ">>\nSegment=(" << lon1 * math::r2d<CT>();
  422. std::cout << "," << lat1 * math::r2d<CT>();
  423. std::cout << "),(" << lon2 * math::r2d<CT>();
  424. std::cout << "," << lat2 * math::r2d<CT>();
  425. std::cout << ")\np=(" << lon3 * math::r2d<CT>();
  426. std::cout << "," << lat3 * math::r2d<CT>();
  427. std::cout << ")" << std::endl;
  428. #endif
  429. //segment on equator
  430. //Note: antipodal points on equator does not define segment on equator
  431. //but pass by the pole
  432. CT diff = geometry::math::longitude_distance_signed<geometry::radian>(lon1, lon2);
  433. typedef typename formula::meridian_inverse<CT>
  434. meridian_inverse;
  435. bool meridian_not_crossing_pole =
  436. meridian_inverse::meridian_not_crossing_pole
  437. (lat1, lat2, diff);
  438. bool meridian_crossing_pole =
  439. meridian_inverse::meridian_crossing_pole(diff);
  440. if (math::equals(lat1, c0) && math::equals(lat2, c0) && !meridian_crossing_pole)
  441. {
  442. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  443. std::cout << "Equatorial segment" << std::endl;
  444. std::cout << "segment=(" << lon1 * math::r2d<CT>();
  445. std::cout << "," << lat1 * math::r2d<CT>();
  446. std::cout << "),(" << lon2 * math::r2d<CT>();
  447. std::cout << "," << lat2 * math::r2d<CT>();
  448. std::cout << ")\np=(" << lon3 * math::r2d<CT>();
  449. std::cout << "," << lat3 * math::r2d<CT>() << ")\n";
  450. #endif
  451. if (lon3 <= lon1)
  452. {
  453. return non_iterative_case(lon1, lat1, lon3, lat3, spheroid);
  454. }
  455. if (lon3 >= lon2)
  456. {
  457. return non_iterative_case(lon2, lat2, lon3, lat3, spheroid);
  458. }
  459. return non_iterative_case(lon3, lat1, lon3, lat3, spheroid);
  460. }
  461. if ( (meridian_not_crossing_pole || meridian_crossing_pole ) && std::abs(lat1) > std::abs(lat2))
  462. {
  463. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  464. std::cout << "Meridian segment not crossing pole" << std::endl;
  465. #endif
  466. std::swap(lat1,lat2);
  467. }
  468. if (meridian_crossing_pole)
  469. {
  470. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  471. std::cout << "Meridian segment crossing pole" << std::endl;
  472. #endif
  473. CT sign_non_zero = lat3 >= c0 ? 1 : -1;
  474. result_distance_point_segment<CT> res13 =
  475. apply<geometry::radian>(lon1, lat1,
  476. lon1, half_pi * sign_non_zero,
  477. lon3, lat3, spheroid);
  478. result_distance_point_segment<CT> res23 =
  479. apply<geometry::radian>(lon2, lat2,
  480. lon2, half_pi * sign_non_zero,
  481. lon3, lat3, spheroid);
  482. if (res13.distance < res23.distance)
  483. {
  484. return res13;
  485. }
  486. else
  487. {
  488. return res23;
  489. }
  490. }
  491. geometry::formula::result_inverse<CT> res12 =
  492. inverse_dist_azimuth_reverse_type::apply(lon1, lat1, lon2, lat2, spheroid);
  493. geometry::formula::result_inverse<CT> res13 =
  494. inverse_dist_azimuth_type::apply(lon1, lat1, lon3, lat3, spheroid);
  495. if (geometry::math::equals(res12.distance, c0))
  496. {
  497. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  498. std::cout << "Degenerate segment" << std::endl;
  499. std::cout << "distance between points=" << res13.distance << std::endl;
  500. #endif
  501. typename meridian_inverse::result res =
  502. meridian_inverse::apply(lon1, lat1, lon3, lat3, spheroid);
  503. return non_iterative_case(lon1, lat2,
  504. res.meridian ? res.distance : res13.distance);
  505. }
  506. // Compute a12 (GEO)
  507. CT a312 = res13.azimuth - res12.azimuth;
  508. // TODO: meridian case optimization
  509. if (geometry::math::equals(a312, c0) && meridian_not_crossing_pole)
  510. {
  511. boost::tuple<CT,CT> minmax_elem = boost::minmax(lat1, lat2);
  512. if (lat3 >= minmax_elem.template get<0>() &&
  513. lat3 <= minmax_elem.template get<1>())
  514. {
  515. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  516. std::cout << "Point on meridian segment" << std::endl;
  517. #endif
  518. return non_iterative_case(lon3, lat3, c0);
  519. }
  520. }
  521. CT projection1 = cos( a312 ) * res13.distance / res12.distance;
  522. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  523. std::cout << "a1=" << res12.azimuth * math::r2d<CT>() << std::endl;
  524. std::cout << "a13=" << res13.azimuth * math::r2d<CT>() << std::endl;
  525. std::cout << "a312=" << a312 * math::r2d<CT>() << std::endl;
  526. std::cout << "cos(a312)=" << cos(a312) << std::endl;
  527. std::cout << "projection 1=" << projection1 << std::endl;
  528. #endif
  529. if (projection1 < c0)
  530. {
  531. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  532. std::cout << "projection closer to p1" << std::endl;
  533. #endif
  534. // projection of p3 on geodesic spanned by segment (p1,p2) fall
  535. // outside of segment on the side of p1
  536. return non_iterative_case(lon1, lat1, lon3, lat3, spheroid);
  537. }
  538. geometry::formula::result_inverse<CT> res23 =
  539. inverse_dist_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid);
  540. CT a321 = res23.azimuth - res12.reverse_azimuth + pi;
  541. CT projection2 = cos( a321 ) * res23.distance / res12.distance;
  542. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  543. std::cout << "a21=" << res12.reverse_azimuth * math::r2d<CT>() << std::endl;
  544. std::cout << "a23=" << res23.azimuth * math::r2d<CT>() << std::endl;
  545. std::cout << "a321=" << a321 * math::r2d<CT>() << std::endl;
  546. std::cout << "cos(a321)=" << cos(a321) << std::endl;
  547. std::cout << "projection 2=" << projection2 << std::endl;
  548. #endif
  549. if (projection2 < c0)
  550. {
  551. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  552. std::cout << "projection closer to p2" << std::endl;
  553. #endif
  554. // projection of p3 on geodesic spanned by segment (p1,p2) fall
  555. // outside of segment on the side of p2
  556. return non_iterative_case(lon2, lat2, lon3, lat3, spheroid);
  557. }
  558. // Guess s14 (SPHERICAL) aka along-track distance
  559. typedef geometry::model::point
  560. <
  561. CT, 2,
  562. geometry::cs::spherical_equatorial<geometry::radian>
  563. > point;
  564. point p1 = point(lon1, lat1);
  565. point p2 = point(lon2, lat2);
  566. point p3 = point(lon3, lat3);
  567. geometry::strategy::distance::cross_track<CT> cross_track(earth_radius);
  568. CT s34_sph = cross_track.apply(p3, p1, p2);
  569. geometry::strategy::distance::haversine<CT> str(earth_radius);
  570. CT s13_sph = str.apply(p1, p3);
  571. //CT s14 = acos( cos(s13/earth_radius) / cos(s34/earth_radius) ) * earth_radius;
  572. CT cos_frac = cos(s13_sph / earth_radius) / cos(s34_sph / earth_radius);
  573. CT s14_sph = cos_frac >= 1 ? CT(0)
  574. : cos_frac <= -1 ? pi * earth_radius
  575. : acos(cos_frac) * earth_radius;
  576. CT a12_sph = geometry::formula::spherical_azimuth<>(lon1, lat1, lon2, lat2);
  577. geometry::formula::result_direct<CT> res
  578. = geometry::formula::spherical_direct<true, false>
  579. (lon1, lat1, s14_sph, a12_sph, srs::sphere<CT>(earth_radius));
  580. // this is what postgis (version 2.5) returns
  581. // geometry::strategy::distance::geographic<FormulaPolicy, Spheroid, CT>
  582. // ::apply(lon3, lat3, res.lon2, res.lat2, spheroid);
  583. #ifdef BOOST_GEOMETRY_DEBUG_GEOGRAPHIC_CROSS_TRACK
  584. std::cout << "s34=" << s34_sph << std::endl;
  585. std::cout << "s13=" << res13.distance << std::endl;
  586. std::cout << "s14=" << s14_sph << std::endl;
  587. std::cout << "===============" << std::endl;
  588. #endif
  589. // Update s14 (using Newton method)
  590. if (Bisection)
  591. {
  592. bisection<CT>(lon1, lat1, lon2, lat2, lon3, lat3,
  593. spheroid, res12.distance/2, res12.azimuth, result);
  594. }
  595. else
  596. {
  597. CT s14_start = geometry::strategy::distance::geographic<FormulaPolicy, Spheroid, CT>
  598. ::apply(lon1, lat1, res.lon2, res.lat2, spheroid);
  599. newton<CT>(lon1, lat1, lon2, lat2, lon3, lat3,
  600. spheroid, s14_start, res12.azimuth, result);
  601. }
  602. return result;
  603. }
  604. Spheroid m_spheroid;
  605. };
  606. } // namespace detail
  607. template
  608. <
  609. typename FormulaPolicy = strategy::andoyer,
  610. typename Spheroid = srs::spheroid<double>,
  611. typename CalculationType = void
  612. >
  613. class geographic_cross_track
  614. : public detail::geographic_cross_track
  615. <
  616. FormulaPolicy,
  617. Spheroid,
  618. CalculationType,
  619. false,
  620. false
  621. >
  622. {
  623. public :
  624. explicit geographic_cross_track(Spheroid const& spheroid = Spheroid())
  625. :
  626. detail::geographic_cross_track<
  627. FormulaPolicy,
  628. Spheroid,
  629. CalculationType,
  630. false,
  631. false
  632. >(spheroid)
  633. {}
  634. };
  635. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  636. namespace services
  637. {
  638. //tags
  639. template <typename FormulaPolicy>
  640. struct tag<geographic_cross_track<FormulaPolicy> >
  641. {
  642. typedef strategy_tag_distance_point_segment type;
  643. };
  644. template
  645. <
  646. typename FormulaPolicy,
  647. typename Spheroid
  648. >
  649. struct tag<geographic_cross_track<FormulaPolicy, Spheroid> >
  650. {
  651. typedef strategy_tag_distance_point_segment type;
  652. };
  653. template
  654. <
  655. typename FormulaPolicy,
  656. typename Spheroid,
  657. typename CalculationType
  658. >
  659. struct tag<geographic_cross_track<FormulaPolicy, Spheroid, CalculationType> >
  660. {
  661. typedef strategy_tag_distance_point_segment type;
  662. };
  663. template
  664. <
  665. typename FormulaPolicy,
  666. typename Spheroid,
  667. typename CalculationType,
  668. bool Bisection
  669. >
  670. struct tag<detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection> >
  671. {
  672. typedef strategy_tag_distance_point_segment type;
  673. };
  674. //return types
  675. template <typename FormulaPolicy, typename P, typename PS>
  676. struct return_type<geographic_cross_track<FormulaPolicy>, P, PS>
  677. : geographic_cross_track<FormulaPolicy>::template return_type<P, PS>
  678. {};
  679. template
  680. <
  681. typename FormulaPolicy,
  682. typename Spheroid,
  683. typename P,
  684. typename PS
  685. >
  686. struct return_type<geographic_cross_track<FormulaPolicy, Spheroid>, P, PS>
  687. : geographic_cross_track<FormulaPolicy, Spheroid>::template return_type<P, PS>
  688. {};
  689. template
  690. <
  691. typename FormulaPolicy,
  692. typename Spheroid,
  693. typename CalculationType,
  694. typename P,
  695. typename PS
  696. >
  697. struct return_type<geographic_cross_track<FormulaPolicy, Spheroid, CalculationType>, P, PS>
  698. : geographic_cross_track<FormulaPolicy, Spheroid, CalculationType>::template return_type<P, PS>
  699. {};
  700. template
  701. <
  702. typename FormulaPolicy,
  703. typename Spheroid,
  704. typename CalculationType,
  705. bool Bisection,
  706. typename P,
  707. typename PS
  708. >
  709. struct return_type<detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection>, P, PS>
  710. : detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection>::template return_type<P, PS>
  711. {};
  712. //comparable types
  713. template
  714. <
  715. typename FormulaPolicy,
  716. typename Spheroid,
  717. typename CalculationType
  718. >
  719. struct comparable_type<geographic_cross_track<FormulaPolicy, Spheroid, CalculationType> >
  720. {
  721. typedef geographic_cross_track
  722. <
  723. FormulaPolicy, Spheroid, CalculationType
  724. > type;
  725. };
  726. template
  727. <
  728. typename FormulaPolicy,
  729. typename Spheroid,
  730. typename CalculationType,
  731. bool Bisection
  732. >
  733. struct comparable_type<detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection> >
  734. {
  735. typedef detail::geographic_cross_track
  736. <
  737. FormulaPolicy, Spheroid, CalculationType, Bisection
  738. > type;
  739. };
  740. template
  741. <
  742. typename FormulaPolicy,
  743. typename Spheroid,
  744. typename CalculationType
  745. >
  746. struct get_comparable<geographic_cross_track<FormulaPolicy, Spheroid, CalculationType> >
  747. {
  748. public :
  749. static inline geographic_cross_track<FormulaPolicy, Spheroid, CalculationType>
  750. apply(geographic_cross_track<FormulaPolicy, Spheroid, CalculationType> const& strategy)
  751. {
  752. return strategy;
  753. }
  754. };
  755. template
  756. <
  757. typename FormulaPolicy,
  758. typename Spheroid,
  759. typename CalculationType,
  760. bool Bisection
  761. >
  762. struct get_comparable<detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection> >
  763. {
  764. public :
  765. static inline detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection>
  766. apply(detail::geographic_cross_track<FormulaPolicy, Spheroid, CalculationType, Bisection> const& strategy)
  767. {
  768. return strategy;
  769. }
  770. };
  771. template
  772. <
  773. typename FormulaPolicy,
  774. typename P,
  775. typename PS
  776. >
  777. struct result_from_distance<geographic_cross_track<FormulaPolicy>, P, PS>
  778. {
  779. private :
  780. typedef typename geographic_cross_track
  781. <
  782. FormulaPolicy
  783. >::template return_type<P, PS>::type return_type;
  784. public :
  785. template <typename T>
  786. static inline return_type
  787. apply(geographic_cross_track<FormulaPolicy> const& , T const& distance)
  788. {
  789. return distance;
  790. }
  791. };
  792. template
  793. <
  794. typename FormulaPolicy,
  795. typename Spheroid,
  796. typename CalculationType,
  797. typename P,
  798. typename PS
  799. >
  800. struct result_from_distance<geographic_cross_track<FormulaPolicy, Spheroid, CalculationType>, P, PS>
  801. {
  802. private :
  803. typedef typename geographic_cross_track
  804. <
  805. FormulaPolicy, Spheroid, CalculationType
  806. >::template return_type<P, PS>::type return_type;
  807. public :
  808. template <typename T>
  809. static inline return_type
  810. apply(geographic_cross_track<FormulaPolicy, Spheroid, CalculationType> const& , T const& distance)
  811. {
  812. return distance;
  813. }
  814. };
  815. template <typename Point, typename PointOfSegment>
  816. struct default_strategy
  817. <
  818. point_tag, segment_tag, Point, PointOfSegment,
  819. geographic_tag, geographic_tag
  820. >
  821. {
  822. typedef geographic_cross_track<> type;
  823. };
  824. template <typename PointOfSegment, typename Point>
  825. struct default_strategy
  826. <
  827. segment_tag, point_tag, PointOfSegment, Point,
  828. geographic_tag, geographic_tag
  829. >
  830. {
  831. typedef typename default_strategy
  832. <
  833. point_tag, segment_tag, Point, PointOfSegment,
  834. geographic_tag, geographic_tag
  835. >::type type;
  836. };
  837. } // namespace services
  838. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  839. }} // namespace strategy::distance
  840. }} // namespace boost::geometry
  841. #endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP