distance_cross_track.hpp 33 KB

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