karney_inverse.hpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. // Boost.Geometry
  2. // Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.
  3. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  4. // Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.
  5. // This file was modified by Oracle on 2019-2021.
  6. // Modifications copyright (c) 2019-2021 Oracle and/or its affiliates.
  7. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  8. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  9. // Use, modification and distribution is subject to the Boost Software License,
  10. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  11. // http://www.boost.org/LICENSE_1_0.txt)
  12. // This file is converted from GeographicLib, https://geographiclib.sourceforge.io
  13. // GeographicLib is originally written by Charles Karney.
  14. // Author: Charles Karney (2008-2017)
  15. // Last updated version of GeographicLib: 1.49
  16. // Original copyright notice:
  17. // Copyright (c) Charles Karney (2008-2017) <charles@karney.com> and licensed
  18. // under the MIT/X11 License. For more information, see
  19. // https://geographiclib.sourceforge.io
  20. #ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP
  21. #define BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP
  22. #include <boost/core/invoke_swap.hpp>
  23. #include <boost/math/constants/constants.hpp>
  24. #include <boost/math/special_functions/hypot.hpp>
  25. #include <boost/geometry/util/constexpr.hpp>
  26. #include <boost/geometry/util/math.hpp>
  27. #include <boost/geometry/util/precise_math.hpp>
  28. #include <boost/geometry/util/series_expansion.hpp>
  29. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  30. #include <boost/geometry/formulas/flattening.hpp>
  31. #include <boost/geometry/formulas/result_inverse.hpp>
  32. namespace boost { namespace geometry { namespace math {
  33. /*!
  34. \brief The exact difference of two angles reduced to (-180deg, 180deg].
  35. */
  36. template<typename T>
  37. inline T difference_angle(T const& x, T const& y, T& e)
  38. {
  39. auto res1 = boost::geometry::detail::precise_math::two_sum(
  40. std::remainder(-x, T(360)), std::remainder(y, T(360)));
  41. normalize_azimuth<degree, T>(res1[0]);
  42. // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
  43. // abs(t) <= eps (eps = 2^-45 for doubles). The only case where the
  44. // addition of t takes the result outside the range (-180,180] is d = 180
  45. // and t > 0. The case, d = -180 + eps, t = -eps, can't happen, since
  46. // sum_error would have returned the exact result in such a case (i.e., given t = 0).
  47. auto res2 = boost::geometry::detail::precise_math::two_sum(
  48. res1[0] == 180 && res1[1] > 0 ? -180 : res1[0], res1[1]);
  49. e = res2[1];
  50. return res2[0];
  51. }
  52. }}} // namespace boost::geometry::math
  53. namespace boost { namespace geometry { namespace formula
  54. {
  55. namespace se = series_expansion;
  56. namespace detail
  57. {
  58. template <
  59. typename CT,
  60. bool EnableDistance,
  61. bool EnableAzimuth,
  62. bool EnableReverseAzimuth = false,
  63. bool EnableReducedLength = false,
  64. bool EnableGeodesicScale = false,
  65. size_t SeriesOrder = 8
  66. >
  67. class karney_inverse
  68. {
  69. static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
  70. static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
  71. static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
  72. static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
  73. public:
  74. typedef result_inverse<CT> result_type;
  75. template <typename T1, typename T2, typename Spheroid>
  76. static inline result_type apply(T1 const& lo1,
  77. T1 const& la1,
  78. T2 const& lo2,
  79. T2 const& la2,
  80. Spheroid const& spheroid)
  81. {
  82. static CT const c0 = 0;
  83. static CT const c0_001 = 0.001;
  84. static CT const c0_1 = 0.1;
  85. static CT const c1 = 1;
  86. static CT const c2 = 2;
  87. static CT const c3 = 3;
  88. static CT const c8 = 8;
  89. static CT const c16 = 16;
  90. static CT const c90 = 90;
  91. static CT const c180 = 180;
  92. static CT const c200 = 200;
  93. static CT const pi = math::pi<CT>();
  94. static CT const d2r = math::d2r<CT>();
  95. static CT const r2d = math::r2d<CT>();
  96. result_type result;
  97. CT lat1 = la1 * r2d;
  98. CT lat2 = la2 * r2d;
  99. CT lon1 = lo1 * r2d;
  100. CT lon2 = lo2 * r2d;
  101. CT const a = CT(get_radius<0>(spheroid));
  102. CT const b = CT(get_radius<2>(spheroid));
  103. CT const f = formula::flattening<CT>(spheroid);
  104. CT const one_minus_f = c1 - f;
  105. CT const two_minus_f = c2 - f;
  106. CT const tol0 = std::numeric_limits<CT>::epsilon();
  107. CT const tol1 = c200 * tol0;
  108. CT const tol2 = sqrt(tol0);
  109. // Check on bisection interval.
  110. CT const tol_bisection = tol0 * tol2;
  111. CT const etol2 = c0_1 * tol2 /
  112. sqrt((std::max)(c0_001, std::abs(f)) * (std::min)(c1, c1 - f / c2) / c2);
  113. CT tiny = std::sqrt((std::numeric_limits<CT>::min)());
  114. CT const n = f / two_minus_f;
  115. CT const e2 = f * two_minus_f;
  116. CT const ep2 = e2 / math::sqr(one_minus_f);
  117. // Compute the longitudinal difference.
  118. CT lon12_error;
  119. CT lon12 = math::difference_angle(lon1, lon2, lon12_error);
  120. int lon12_sign = lon12 >= 0 ? 1 : -1;
  121. // Make points close to the meridian to lie on it.
  122. lon12 = lon12_sign * lon12;
  123. lon12_error = (c180 - lon12) - lon12_sign * lon12_error;
  124. // Convert to radians.
  125. CT lam12 = lon12 * d2r;
  126. CT sin_lam12;
  127. CT cos_lam12;
  128. if (lon12 > c90)
  129. {
  130. math::sin_cos_degrees(lon12_error, sin_lam12, cos_lam12);
  131. cos_lam12 *= -c1;
  132. }
  133. else
  134. {
  135. math::sin_cos_degrees(lon12, sin_lam12, cos_lam12);
  136. }
  137. // Make points close to the equator to lie on it.
  138. lat1 = math::round_angle(std::abs(lat1) > c90 ? c90 : lat1);
  139. lat2 = math::round_angle(std::abs(lat2) > c90 ? c90 : lat2);
  140. // Arrange points in a canonical form, as explained in
  141. // paper, Algorithms for geodesics, Eq. (44):
  142. //
  143. // 0 <= lon12 <= 180
  144. // -90 <= lat1 <= 0
  145. // lat1 <= lat2 <= -lat1
  146. int swap_point = std::abs(lat1) < std::abs(lat2) ? -1 : 1;
  147. if (swap_point < 0)
  148. {
  149. lon12_sign *= -1;
  150. boost::core::invoke_swap(lat1, lat2);
  151. }
  152. // Enforce lat1 to be <= 0.
  153. int lat_sign = lat1 < 0 ? 1 : -1;
  154. lat1 *= lat_sign;
  155. lat2 *= lat_sign;
  156. CT sin_beta1, cos_beta1;
  157. math::sin_cos_degrees(lat1, sin_beta1, cos_beta1);
  158. sin_beta1 *= one_minus_f;
  159. math::normalize_unit_vector<CT>(sin_beta1, cos_beta1);
  160. cos_beta1 = (std::max)(tiny, cos_beta1);
  161. CT sin_beta2, cos_beta2;
  162. math::sin_cos_degrees(lat2, sin_beta2, cos_beta2);
  163. sin_beta2 *= one_minus_f;
  164. math::normalize_unit_vector<CT>(sin_beta2, cos_beta2);
  165. cos_beta2 = (std::max)(tiny, cos_beta2);
  166. // If cos_beta1 < -sin_beta1, then cos_beta2 - cos_beta1 is a
  167. // sensitive measure of the |beta1| - |beta2|. Alternatively,
  168. // (cos_beta1 >= -sin_beta1), abs(sin_beta2) + sin_beta1 is
  169. // a better measure.
  170. // Sometimes these quantities vanish and in that case we
  171. // force beta2 = +/- bet1a exactly.
  172. if (cos_beta1 < -sin_beta1)
  173. {
  174. if (cos_beta1 == cos_beta2)
  175. {
  176. sin_beta2 = sin_beta2 < 0 ? sin_beta1 : -sin_beta1;
  177. }
  178. }
  179. else
  180. {
  181. if (std::abs(sin_beta2) == -sin_beta1)
  182. {
  183. cos_beta2 = cos_beta1;
  184. }
  185. }
  186. CT const dn1 = sqrt(c1 + ep2 * math::sqr(sin_beta1));
  187. CT const dn2 = sqrt(c1 + ep2 * math::sqr(sin_beta2));
  188. CT sigma12;
  189. CT m12x = c0;
  190. CT s12x;
  191. CT M21;
  192. // Index zero element of coeffs_C1 is unused.
  193. se::coeffs_C1<SeriesOrder, CT> const coeffs_C1(n);
  194. bool meridian = lat1 == -90 || sin_lam12 == 0;
  195. CT cos_alpha1, sin_alpha1;
  196. CT cos_alpha2, sin_alpha2;
  197. if (meridian)
  198. {
  199. // Endpoints lie on a single full meridian.
  200. // Point to the target latitude.
  201. cos_alpha1 = cos_lam12;
  202. sin_alpha1 = sin_lam12;
  203. // Heading north at the target.
  204. cos_alpha2 = c1;
  205. sin_alpha2 = c0;
  206. CT sin_sigma1 = sin_beta1;
  207. CT cos_sigma1 = cos_alpha1 * cos_beta1;
  208. CT sin_sigma2 = sin_beta2;
  209. CT cos_sigma2 = cos_alpha2 * cos_beta2;
  210. sigma12 = std::atan2((std::max)(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
  211. cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);
  212. CT dummy;
  213. meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
  214. sin_sigma2, cos_sigma2, dn2,
  215. cos_beta1, cos_beta2, s12x,
  216. m12x, dummy, result.geodesic_scale,
  217. M21, coeffs_C1);
  218. if (sigma12 < c1 || m12x >= c0)
  219. {
  220. if (sigma12 < c3 * tiny)
  221. {
  222. sigma12 = m12x = s12x = c0;
  223. }
  224. m12x *= b;
  225. s12x *= b;
  226. }
  227. else
  228. {
  229. // m12 < 0, i.e., prolate and too close to anti-podal.
  230. meridian = false;
  231. }
  232. }
  233. CT omega12;
  234. if (!meridian && sin_beta1 == c0 &&
  235. (f <= c0 || lon12_error >= f * c180))
  236. {
  237. // Points lie on the equator.
  238. cos_alpha1 = cos_alpha2 = c0;
  239. sin_alpha1 = sin_alpha2 = c1;
  240. s12x = a * lam12;
  241. sigma12 = omega12 = lam12 / one_minus_f;
  242. m12x = b * sin(sigma12);
  243. if BOOST_GEOMETRY_CONSTEXPR (EnableGeodesicScale)
  244. {
  245. result.geodesic_scale = cos(sigma12);
  246. }
  247. }
  248. else if (!meridian)
  249. {
  250. // If point1 and point2 belong within a hemisphere bounded by a
  251. // meridian and geodesic is neither meridional nor equatorial.
  252. // Find the starting point for Newton's method.
  253. CT dnm = c1;
  254. sigma12 = newton_start(sin_beta1, cos_beta1, dn1,
  255. sin_beta2, cos_beta2, dn2,
  256. lam12, sin_lam12, cos_lam12,
  257. sin_alpha1, cos_alpha1,
  258. sin_alpha2, cos_alpha2,
  259. dnm, coeffs_C1, ep2,
  260. tol1, tol2, etol2,
  261. n, f);
  262. if (sigma12 >= c0)
  263. {
  264. // Short lines case (newton_start sets sin_alpha2, cos_alpha2, dnm).
  265. s12x = sigma12 * b * dnm;
  266. m12x = math::sqr(dnm) * b * sin(sigma12 / dnm);
  267. if BOOST_GEOMETRY_CONSTEXPR (EnableGeodesicScale)
  268. {
  269. result.geodesic_scale = cos(sigma12 / dnm);
  270. }
  271. // Convert to radians.
  272. omega12 = lam12 / (one_minus_f * dnm);
  273. }
  274. else
  275. {
  276. // Apply the Newton's method.
  277. CT sin_sigma1 = c0, cos_sigma1 = c0;
  278. CT sin_sigma2 = c0, cos_sigma2 = c0;
  279. CT eps = c0, diff_omega12 = c0;
  280. // Bracketing range.
  281. CT sin_alpha1a = tiny, cos_alpha1a = c1;
  282. CT sin_alpha1b = tiny, cos_alpha1b = -c1;
  283. size_t iteration = 0;
  284. size_t max_iterations = 20 + std::numeric_limits<size_t>::digits + 10;
  285. for (bool tripn = false, tripb = false;
  286. iteration < max_iterations;
  287. ++iteration)
  288. {
  289. CT dv = c0;
  290. CT v = lambda12(sin_beta1, cos_beta1, dn1,
  291. sin_beta2, cos_beta2, dn2,
  292. sin_alpha1, cos_alpha1,
  293. sin_lam12, cos_lam12,
  294. sin_alpha2, cos_alpha2,
  295. sigma12,
  296. sin_sigma1, cos_sigma1,
  297. sin_sigma2, cos_sigma2,
  298. eps, diff_omega12,
  299. iteration < max_iterations,
  300. dv, f, n, ep2, tiny, coeffs_C1);
  301. // Reversed test to allow escape with NaNs.
  302. if (tripb || !(std::abs(v) >= (tripn ? c8 : c1) * tol0))
  303. break;
  304. // Update bracketing values.
  305. if (v > c0 && (iteration > max_iterations ||
  306. cos_alpha1 / sin_alpha1 > cos_alpha1b / sin_alpha1b))
  307. {
  308. sin_alpha1b = sin_alpha1;
  309. cos_alpha1b = cos_alpha1;
  310. }
  311. else if (v < c0 && (iteration > max_iterations ||
  312. cos_alpha1 / sin_alpha1 < cos_alpha1a / sin_alpha1a))
  313. {
  314. sin_alpha1a = sin_alpha1;
  315. cos_alpha1a = cos_alpha1;
  316. }
  317. if (iteration < max_iterations && dv > c0)
  318. {
  319. CT diff_alpha1 = -v / dv;
  320. CT sin_diff_alpha1 = sin(diff_alpha1);
  321. CT cos_diff_alpha1 = cos(diff_alpha1);
  322. CT nsin_alpha1 = sin_alpha1 * cos_diff_alpha1 +
  323. cos_alpha1 * sin_diff_alpha1;
  324. if (nsin_alpha1 > c0 && std::abs(diff_alpha1) < pi)
  325. {
  326. cos_alpha1 = cos_alpha1 * cos_diff_alpha1 - sin_alpha1 * sin_diff_alpha1;
  327. sin_alpha1 = nsin_alpha1;
  328. math::normalize_unit_vector<CT>(sin_alpha1, cos_alpha1);
  329. // In some regimes we don't get quadratic convergence because
  330. // slope -> 0. So use convergence conditions based on epsilon
  331. // instead of sqrt(epsilon).
  332. tripn = std::abs(v) <= c16 * tol0;
  333. continue;
  334. }
  335. }
  336. // Either dv was not positive or updated value was outside legal
  337. // range. Use the midpoint of the bracket as the next estimate.
  338. // This mechanism is not needed for the WGS84 ellipsoid, but it does
  339. // catch problems with more eeccentric ellipsoids. Its efficacy is
  340. // such for the WGS84 test set with the starting guess set to alp1 =
  341. // 90deg:
  342. // the WGS84 test set: mean = 5.21, sd = 3.93, max = 24
  343. // WGS84 and random input: mean = 4.74, sd = 0.99
  344. sin_alpha1 = (sin_alpha1a + sin_alpha1b) / c2;
  345. cos_alpha1 = (cos_alpha1a + cos_alpha1b) / c2;
  346. math::normalize_unit_vector<CT>(sin_alpha1, cos_alpha1);
  347. tripn = false;
  348. tripb = (std::abs(sin_alpha1a - sin_alpha1) + (cos_alpha1a - cos_alpha1) < tol_bisection ||
  349. std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) < tol_bisection);
  350. }
  351. CT dummy;
  352. se::coeffs_C1<SeriesOrder, CT> const coeffs_C1_eps(eps);
  353. // Ensure that the reduced length and geodesic scale are computed in
  354. // a "canonical" way, with the I2 integral.
  355. meridian_length(eps, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
  356. sin_sigma2, cos_sigma2, dn2,
  357. cos_beta1, cos_beta2, s12x,
  358. m12x, dummy, result.geodesic_scale,
  359. M21, coeffs_C1_eps);
  360. m12x *= b;
  361. s12x *= b;
  362. }
  363. }
  364. if (swap_point < 0)
  365. {
  366. boost::core::invoke_swap(sin_alpha1, sin_alpha2);
  367. boost::core::invoke_swap(cos_alpha1, cos_alpha2);
  368. boost::core::invoke_swap(result.geodesic_scale, M21);
  369. }
  370. sin_alpha1 *= swap_point * lon12_sign;
  371. cos_alpha1 *= swap_point * lat_sign;
  372. sin_alpha2 *= swap_point * lon12_sign;
  373. cos_alpha2 *= swap_point * lat_sign;
  374. if BOOST_GEOMETRY_CONSTEXPR (EnableReducedLength)
  375. {
  376. result.reduced_length = m12x;
  377. }
  378. if BOOST_GEOMETRY_CONSTEXPR (CalcAzimuths)
  379. {
  380. if BOOST_GEOMETRY_CONSTEXPR (CalcFwdAzimuth)
  381. {
  382. result.azimuth = atan2(sin_alpha1, cos_alpha1);
  383. }
  384. if BOOST_GEOMETRY_CONSTEXPR (CalcRevAzimuth)
  385. {
  386. result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);
  387. }
  388. }
  389. if BOOST_GEOMETRY_CONSTEXPR (EnableDistance)
  390. {
  391. result.distance = s12x;
  392. }
  393. return result;
  394. }
  395. template <typename CoeffsC1>
  396. static inline void meridian_length(CT const& epsilon, CT const& ep2, CT const& sigma12,
  397. CT const& sin_sigma1, CT const& cos_sigma1, CT const& dn1,
  398. CT const& sin_sigma2, CT const& cos_sigma2, CT const& dn2,
  399. CT const& cos_beta1, CT const& cos_beta2,
  400. CT& s12x, CT& m12x, CT& m0,
  401. CT& M12, CT& M21,
  402. CoeffsC1 const& coeffs_C1)
  403. {
  404. static CT const c1 = 1;
  405. CT A12x = 0, J12 = 0;
  406. CT expansion_A1, expansion_A2;
  407. // Evaluate the coefficients for C2.
  408. se::coeffs_C2<SeriesOrder, CT> coeffs_C2(epsilon);
  409. if BOOST_GEOMETRY_CONSTEXPR (EnableDistance || EnableReducedLength || EnableGeodesicScale)
  410. {
  411. // Find the coefficients for A1 by computing the
  412. // series expansion using Horner scehme.
  413. expansion_A1 = se::evaluate_A1<SeriesOrder>(epsilon);
  414. if BOOST_GEOMETRY_CONSTEXPR (EnableReducedLength || EnableGeodesicScale)
  415. {
  416. // Find the coefficients for A2 by computing the
  417. // series expansion using Horner scehme.
  418. expansion_A2 = se::evaluate_A2<SeriesOrder>(epsilon);
  419. A12x = expansion_A1 - expansion_A2;
  420. expansion_A2 += c1;
  421. }
  422. expansion_A1 += c1;
  423. }
  424. if BOOST_GEOMETRY_CONSTEXPR (EnableDistance)
  425. {
  426. CT B1 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C1)
  427. - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);
  428. s12x = expansion_A1 * (sigma12 + B1);
  429. if BOOST_GEOMETRY_CONSTEXPR (EnableReducedLength || EnableGeodesicScale)
  430. {
  431. CT B2 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)
  432. - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);
  433. J12 = A12x * sigma12 + (expansion_A1 * B1 - expansion_A2 * B2);
  434. }
  435. }
  436. else if BOOST_GEOMETRY_CONSTEXPR (EnableReducedLength || EnableGeodesicScale)
  437. {
  438. for (size_t i = 1; i <= SeriesOrder; ++i)
  439. {
  440. coeffs_C2[i] = expansion_A1 * coeffs_C1[i] -
  441. expansion_A2 * coeffs_C2[i];
  442. }
  443. J12 = A12x * sigma12 +
  444. (se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)
  445. - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2));
  446. }
  447. if BOOST_GEOMETRY_CONSTEXPR (EnableReducedLength)
  448. {
  449. m0 = A12x;
  450. m12x = dn2 * (cos_sigma1 * sin_sigma2) -
  451. dn1 * (sin_sigma1 * cos_sigma2) -
  452. cos_sigma1 * cos_sigma2 * J12;
  453. }
  454. if BOOST_GEOMETRY_CONSTEXPR (EnableGeodesicScale)
  455. {
  456. CT cos_sigma12 = cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2;
  457. CT t = ep2 * (cos_beta1 - cos_beta2) *
  458. (cos_beta1 + cos_beta2) / (dn1 + dn2);
  459. M12 = cos_sigma12 + (t * sin_sigma2 - cos_sigma2 * J12) * sin_sigma1 / dn1;
  460. M21 = cos_sigma12 - (t * sin_sigma1 - cos_sigma1 * J12) * sin_sigma2 / dn2;
  461. }
  462. }
  463. /*
  464. Return a starting point for Newton's method in sin_alpha1 and
  465. cos_alpha1 (function value is -1). If Newton's method
  466. doesn't need to be used, return also sin_alpha2 and
  467. cos_alpha2 and function value is sig12.
  468. */
  469. template <typename CoeffsC1>
  470. static inline CT newton_start(CT const& sin_beta1, CT const& cos_beta1, CT const& dn1,
  471. CT const& sin_beta2, CT const& cos_beta2, CT dn2,
  472. CT const& lam12, CT const& sin_lam12, CT const& cos_lam12,
  473. CT& sin_alpha1, CT& cos_alpha1,
  474. CT& sin_alpha2, CT& cos_alpha2,
  475. CT& dnm, CoeffsC1 const& coeffs_C1, CT const& ep2,
  476. CT const& tol1, CT const& tol2, CT const& etol2, CT const& n,
  477. CT const& f)
  478. {
  479. static CT const c0 = 0;
  480. static CT const c0_01 = 0.01;
  481. static CT const c0_1 = 0.1;
  482. static CT const c0_5 = 0.5;
  483. static CT const c1 = 1;
  484. static CT const c2 = 2;
  485. static CT const c6 = 6;
  486. static CT const c1000 = 1000;
  487. static CT const pi = math::pi<CT>();
  488. CT const one_minus_f = c1 - f;
  489. CT const x_thresh = c1000 * tol2;
  490. // Return a starting point for Newton's method in sin_alpha1
  491. // and cos_alpha1 (function value is -1). If Newton's method
  492. // doesn't need to be used, return also sin_alpha2 and
  493. // cos_alpha2 and function value is sig12.
  494. CT sig12 = -c1;
  495. // bet12 = bet2 - bet1 in [0, pi); beta12a = bet2 + bet1 in (-pi, 0]
  496. CT sin_beta12 = sin_beta2 * cos_beta1 - cos_beta2 * sin_beta1;
  497. CT cos_beta12 = cos_beta2 * cos_beta1 + sin_beta2 * sin_beta1;
  498. CT sin_beta12a = sin_beta2 * cos_beta1 + cos_beta2 * sin_beta1;
  499. bool shortline = cos_beta12 >= c0 && sin_beta12 < c0_5 &&
  500. cos_beta2 * lam12 < c0_5;
  501. CT sin_omega12, cos_omega12;
  502. if (shortline)
  503. {
  504. CT sin_beta_m2 = math::sqr(sin_beta1 + sin_beta2);
  505. sin_beta_m2 /= sin_beta_m2 + math::sqr(cos_beta1 + cos_beta2);
  506. dnm = math::sqrt(c1 + ep2 * sin_beta_m2);
  507. CT omega12 = lam12 / (one_minus_f * dnm);
  508. sin_omega12 = sin(omega12);
  509. cos_omega12 = cos(omega12);
  510. }
  511. else
  512. {
  513. sin_omega12 = sin_lam12;
  514. cos_omega12 = cos_lam12;
  515. }
  516. sin_alpha1 = cos_beta2 * sin_omega12;
  517. cos_alpha1 = cos_omega12 >= c0 ?
  518. sin_beta12 + cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 + cos_omega12) :
  519. sin_beta12a - cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 - cos_omega12);
  520. CT sin_sigma12 = boost::math::hypot(sin_alpha1, cos_alpha1);
  521. CT cos_sigma12 = sin_beta1 * sin_beta2 + cos_beta1 * cos_beta2 * cos_omega12;
  522. if (shortline && sin_sigma12 < etol2)
  523. {
  524. sin_alpha2 = cos_beta1 * sin_omega12;
  525. cos_alpha2 = sin_beta12 - cos_beta1 * sin_beta2 *
  526. (cos_omega12 >= c0 ? math::sqr(sin_omega12) /
  527. (c1 + cos_omega12) : c1 - cos_omega12);
  528. math::normalize_unit_vector<CT>(sin_alpha2, cos_alpha2);
  529. // Set return value.
  530. sig12 = atan2(sin_sigma12, cos_sigma12);
  531. }
  532. // Skip astroid calculation if too eccentric.
  533. else if (std::abs(n) > c0_1 ||
  534. cos_sigma12 >= c0 ||
  535. sin_sigma12 >= c6 * std::abs(n) * pi *
  536. math::sqr(cos_beta1))
  537. {
  538. // Nothing to do, zeroth order spherical approximation will do.
  539. }
  540. else
  541. {
  542. // Scale lam12 and bet2 to x, y coordinate system where antipodal
  543. // point is at origin and singular point is at y = 0, x = -1.
  544. CT lambda_scale, beta_scale;
  545. CT y;
  546. volatile CT x;
  547. CT lam12x = atan2(-sin_lam12, -cos_lam12);
  548. if (f >= c0)
  549. {
  550. CT k2 = math::sqr(sin_beta1) * ep2;
  551. CT eps = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);
  552. se::coeffs_A3<SeriesOrder, CT> const coeffs_A3(n);
  553. CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());
  554. lambda_scale = f * cos_beta1 * A3 * pi;
  555. beta_scale = lambda_scale * cos_beta1;
  556. x = lam12x / lambda_scale;
  557. y = sin_beta12a / beta_scale;
  558. }
  559. else
  560. {
  561. CT cos_beta12a = cos_beta2 * cos_beta1 - sin_beta2 * sin_beta1;
  562. CT beta12a = atan2(sin_beta12a, cos_beta12a);
  563. CT m12b = c0;
  564. CT m0 = c1;
  565. CT dummy;
  566. meridian_length(n, ep2, pi + beta12a,
  567. sin_beta1, -cos_beta1, dn1,
  568. sin_beta2, cos_beta2, dn2,
  569. cos_beta1, cos_beta2, dummy,
  570. m12b, m0, dummy, dummy, coeffs_C1);
  571. x = -c1 + m12b / (cos_beta1 * cos_beta2 * m0 * pi);
  572. beta_scale = x < -c0_01
  573. ? sin_beta12a / x
  574. : -f * math::sqr(cos_beta1) * pi;
  575. lambda_scale = beta_scale / cos_beta1;
  576. y = lam12x / lambda_scale;
  577. }
  578. if (y > -tol1 && x > -c1 - x_thresh)
  579. {
  580. // Strip near cut.
  581. if (f >= c0)
  582. {
  583. sin_alpha1 = (std::min)(c1, -CT(x));
  584. cos_alpha1 = - math::sqrt(c1 - math::sqr(sin_alpha1));
  585. }
  586. else
  587. {
  588. cos_alpha1 = (std::max)(CT(x > -tol1 ? c0 : -c1), CT(x));
  589. sin_alpha1 = math::sqrt(c1 - math::sqr(cos_alpha1));
  590. }
  591. }
  592. else
  593. {
  594. // Solve the astroid problem.
  595. CT k = astroid(CT(x), y);
  596. CT omega12a = lambda_scale * (f >= c0 ? -x * k /
  597. (c1 + k) : -y * (c1 + k) / k);
  598. sin_omega12 = sin(omega12a);
  599. cos_omega12 = -cos(omega12a);
  600. // Update spherical estimate of alpha1 using omgega12 instead of lam12.
  601. sin_alpha1 = cos_beta2 * sin_omega12;
  602. cos_alpha1 = sin_beta12a - cos_beta2 * sin_beta1 *
  603. math::sqr(sin_omega12) / (c1 - cos_omega12);
  604. }
  605. }
  606. // Sanity check on starting guess. Backwards check allows NaN through.
  607. if (!(sin_alpha1 <= c0))
  608. {
  609. math::normalize_unit_vector<CT>(sin_alpha1, cos_alpha1);
  610. }
  611. else
  612. {
  613. sin_alpha1 = c1;
  614. cos_alpha1 = c0;
  615. }
  616. return sig12;
  617. }
  618. /*
  619. Solve the astroid problem using the equation:
  620. κ4 + 2κ3 + (1 − x2 − y 2 )κ2 − 2y 2 κ − y 2 = 0.
  621. For details, please refer to Eq. (65) in,
  622. Geodesics on an ellipsoid of revolution, Charles F.F Karney,
  623. https://arxiv.org/abs/1102.1215
  624. */
  625. static inline CT astroid(CT const& x, CT const& y)
  626. {
  627. static CT const c0 = 0;
  628. static CT const c1 = 1;
  629. static CT const c2 = 2;
  630. static CT const c3 = 3;
  631. static CT const c4 = 4;
  632. static CT const c6 = 6;
  633. CT k;
  634. CT p = math::sqr(x);
  635. CT q = math::sqr(y);
  636. CT r = (p + q - c1) / c6;
  637. if (!(q == c0 && r <= c0))
  638. {
  639. // Avoid possible division by zero when r = 0 by multiplying
  640. // equations for s and t by r^3 and r, respectively.
  641. CT S = p * q / c4;
  642. CT r2 = math::sqr(r);
  643. CT r3 = r * r2;
  644. // The discriminant of the quadratic equation for T3. This is
  645. // zero on the evolute curve p^(1/3)+q^(1/3) = 1.
  646. CT discriminant = S * (S + c2 * r3);
  647. CT u = r;
  648. if (discriminant >= c0)
  649. {
  650. CT T3 = S + r3;
  651. // Pick the sign on the sqrt to maximize abs(T3). This minimizes
  652. // loss of precision due to cancellation. The result is unchanged
  653. // because of the way the T is used in definition of u.
  654. T3 += T3 < c0 ? -std::sqrt(discriminant) : std::sqrt(discriminant);
  655. CT T = std::cbrt(T3);
  656. // T can be zero; but then r2 / T -> 0.
  657. u += T + (T != c0 ? r2 / T : c0);
  658. }
  659. else
  660. {
  661. CT ang = std::atan2(std::sqrt(-discriminant), -(S + r3));
  662. // There are three possible cube roots. We choose the root which avoids
  663. // cancellation. Note that discriminant < 0 implies that r < 0.
  664. u += c2 * r * cos(ang / c3);
  665. }
  666. CT v = std::sqrt(math::sqr(u) + q);
  667. // Avoid loss of accuracy when u < 0.
  668. CT uv = u < c0 ? q / (v - u) : u + v;
  669. CT w = (uv - q) / (c2 * v);
  670. // Rearrange expression for k to avoid loss of accuracy due to
  671. // subtraction. Division by 0 not possible because uv > 0, w >= 0.
  672. k = uv / (std::sqrt(uv + math::sqr(w)) + w);
  673. }
  674. else // q == 0 && r <= 0
  675. {
  676. // y = 0 with |x| <= 1. Handle this case directly.
  677. // For y small, positive root is k = abs(y)/sqrt(1-x^2).
  678. k = c0;
  679. }
  680. return k;
  681. }
  682. template <typename CoeffsC1>
  683. static inline CT lambda12(CT const& sin_beta1, CT const& cos_beta1, CT const& dn1,
  684. CT const& sin_beta2, CT const& cos_beta2, CT const& dn2,
  685. CT const& sin_alpha1, CT cos_alpha1,
  686. CT const& sin_lam120, CT const& cos_lam120,
  687. CT& sin_alpha2, CT& cos_alpha2,
  688. CT& sigma12,
  689. CT& sin_sigma1, CT& cos_sigma1,
  690. CT& sin_sigma2, CT& cos_sigma2,
  691. CT& eps, CT& diff_omega12,
  692. bool diffp, CT& diff_lam12,
  693. CT const& f, CT const& n, CT const& ep2, CT const& tiny,
  694. CoeffsC1 const& coeffs_C1)
  695. {
  696. static CT const c0 = 0;
  697. static CT const c1 = 1;
  698. static CT const c2 = 2;
  699. CT const one_minus_f = c1 - f;
  700. if (sin_beta1 == c0 && cos_alpha1 == c0)
  701. {
  702. // Break degeneracy of equatorial line.
  703. cos_alpha1 = -tiny;
  704. }
  705. CT sin_alpha0 = sin_alpha1 * cos_beta1;
  706. CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);
  707. CT sin_omega1, cos_omega1;
  708. CT sin_omega2, cos_omega2;
  709. CT sin_omega12, cos_omega12;
  710. CT lam12;
  711. sin_sigma1 = sin_beta1;
  712. sin_omega1 = sin_alpha0 * sin_beta1;
  713. cos_sigma1 = cos_omega1 = cos_alpha1 * cos_beta1;
  714. math::normalize_unit_vector<CT>(sin_sigma1, cos_sigma1);
  715. // Enforce symmetries in the case abs(beta2) = -beta1.
  716. // Otherwise, this can yield singularities in the Newton iteration.
  717. // sin(alpha2) * cos(beta2) = sin(alpha0).
  718. sin_alpha2 = cos_beta2 != cos_beta1 ?
  719. sin_alpha0 / cos_beta2 : sin_alpha1;
  720. cos_alpha2 = cos_beta2 != cos_beta1 || std::abs(sin_beta2) != -sin_beta1 ?
  721. sqrt(math::sqr(cos_alpha1 * cos_beta1) +
  722. (cos_beta1 < -sin_beta1 ?
  723. (cos_beta2 - cos_beta1) * (cos_beta1 + cos_beta2) :
  724. (sin_beta1 - sin_beta2) * (sin_beta1 + sin_beta2))) / cos_beta2 :
  725. std::abs(cos_alpha1);
  726. sin_sigma2 = sin_beta2;
  727. sin_omega2 = sin_alpha0 * sin_beta2;
  728. cos_sigma2 = cos_omega2 =
  729. (cos_alpha2 * cos_beta2);
  730. // Break degeneracy of equatorial line.
  731. math::normalize_unit_vector<CT>(sin_sigma2, cos_sigma2);
  732. // sig12 = sig2 - sig1, limit to [0, pi].
  733. sigma12 = atan2((std::max)(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
  734. cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);
  735. // omg12 = omg2 - omg1, limit to [0, pi].
  736. sin_omega12 = (std::max)(c0, cos_omega1 * sin_omega2 - sin_omega1 * cos_omega2);
  737. cos_omega12 = cos_omega1 * cos_omega2 + sin_omega1 * sin_omega2;
  738. // eta = omg12 - lam120.
  739. CT eta = atan2(sin_omega12 * cos_lam120 - cos_omega12 * sin_lam120,
  740. cos_omega12 * cos_lam120 + sin_omega12 * sin_lam120);
  741. CT B312;
  742. CT k2 = math::sqr(cos_alpha0) * ep2;
  743. eps = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);
  744. se::coeffs_C3<SeriesOrder, CT> const coeffs_C3(n, eps);
  745. B312 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C3)
  746. - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);
  747. se::coeffs_A3<SeriesOrder, CT> const coeffs_A3(n);
  748. CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());
  749. diff_omega12 = -f * A3 * sin_alpha0 * (sigma12 + B312);
  750. lam12 = eta + diff_omega12;
  751. if (diffp)
  752. {
  753. if (cos_alpha2 == c0)
  754. {
  755. diff_lam12 = - c2 * one_minus_f * dn1 / sin_beta1;
  756. }
  757. else
  758. {
  759. CT dummy;
  760. meridian_length(eps, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
  761. sin_sigma2, cos_sigma2, dn2,
  762. cos_beta1, cos_beta2, dummy,
  763. diff_lam12, dummy, dummy,
  764. dummy, coeffs_C1);
  765. diff_lam12 *= one_minus_f / (cos_alpha2 * cos_beta2);
  766. }
  767. }
  768. return lam12;
  769. }
  770. };
  771. } // namespace detail
  772. /*!
  773. \brief The solution of the inverse problem of geodesics on latlong coordinates,
  774. after Karney (2011).
  775. \author See
  776. - Charles F.F Karney, Algorithms for geodesics, 2011
  777. https://arxiv.org/pdf/1109.4448.pdf
  778. */
  779. template <
  780. typename CT,
  781. bool EnableDistance,
  782. bool EnableAzimuth,
  783. bool EnableReverseAzimuth = false,
  784. bool EnableReducedLength = false,
  785. bool EnableGeodesicScale = false
  786. >
  787. struct karney_inverse
  788. : detail::karney_inverse
  789. <
  790. CT,
  791. EnableDistance,
  792. EnableAzimuth,
  793. EnableReverseAzimuth,
  794. EnableReducedLength,
  795. EnableGeodesicScale
  796. >
  797. {};
  798. }}} // namespace boost::geometry::formula
  799. #endif // BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP