sjoberg_intersection.hpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. // Boost.Geometry
  2. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2016-2019 Oracle and/or its affiliates.
  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_FORMULAS_SJOBERG_INTERSECTION_HPP
  9. #define BOOST_GEOMETRY_FORMULAS_SJOBERG_INTERSECTION_HPP
  10. #include <boost/math/constants/constants.hpp>
  11. #include <boost/geometry/core/radius.hpp>
  12. #include <boost/geometry/util/condition.hpp>
  13. #include <boost/geometry/util/math.hpp>
  14. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  15. #include <boost/geometry/formulas/flattening.hpp>
  16. #include <boost/geometry/formulas/spherical.hpp>
  17. namespace boost { namespace geometry { namespace formula
  18. {
  19. /*!
  20. \brief The intersection of two great circles as proposed by Sjoberg.
  21. \see See
  22. - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002
  23. http://link.springer.com/article/10.1007/s00190-001-0230-9
  24. */
  25. template <typename CT>
  26. struct sjoberg_intersection_spherical_02
  27. {
  28. // TODO: if it will be used as standalone formula
  29. // support segments on equator and endpoints on poles
  30. static inline bool apply(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2,
  31. CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2,
  32. CT & lon, CT & lat)
  33. {
  34. CT tan_lat = 0;
  35. bool res = apply_alt(lon1, lat1, lon_a2, lat_a2,
  36. lon2, lat2, lon_b2, lat_b2,
  37. lon, tan_lat);
  38. if (res)
  39. {
  40. lat = atan(tan_lat);
  41. }
  42. return res;
  43. }
  44. static inline bool apply_alt(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2,
  45. CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2,
  46. CT & lon, CT & tan_lat)
  47. {
  48. CT const cos_lon1 = cos(lon1);
  49. CT const sin_lon1 = sin(lon1);
  50. CT const cos_lon2 = cos(lon2);
  51. CT const sin_lon2 = sin(lon2);
  52. CT const sin_lat1 = sin(lat1);
  53. CT const sin_lat2 = sin(lat2);
  54. CT const cos_lat1 = cos(lat1);
  55. CT const cos_lat2 = cos(lat2);
  56. CT const tan_lat_a2 = tan(lat_a2);
  57. CT const tan_lat_b2 = tan(lat_b2);
  58. return apply(lon1, lon_a2, lon2, lon_b2,
  59. sin_lon1, cos_lon1, sin_lat1, cos_lat1,
  60. sin_lon2, cos_lon2, sin_lat2, cos_lat2,
  61. tan_lat_a2, tan_lat_b2,
  62. lon, tan_lat);
  63. }
  64. private:
  65. static inline bool apply(CT const& lon1, CT const& lon_a2, CT const& lon2, CT const& lon_b2,
  66. CT const& sin_lon1, CT const& cos_lon1, CT const& sin_lat1, CT const& cos_lat1,
  67. CT const& sin_lon2, CT const& cos_lon2, CT const& sin_lat2, CT const& cos_lat2,
  68. CT const& tan_lat_a2, CT const& tan_lat_b2,
  69. CT & lon, CT & tan_lat)
  70. {
  71. // NOTE:
  72. // cos_lat_ = 0 <=> segment on equator
  73. // tan_alpha_ = 0 <=> segment vertical
  74. CT const tan_lat1 = sin_lat1 / cos_lat1; //tan(lat1);
  75. CT const tan_lat2 = sin_lat2 / cos_lat2; //tan(lat2);
  76. CT const dlon1 = lon_a2 - lon1;
  77. CT const sin_dlon1 = sin(dlon1);
  78. CT const dlon2 = lon_b2 - lon2;
  79. CT const sin_dlon2 = sin(dlon2);
  80. CT const cos_dlon1 = cos(dlon1);
  81. CT const cos_dlon2 = cos(dlon2);
  82. CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1;
  83. CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2;
  84. CT const c0 = 0;
  85. bool const is_vertical1 = math::equals(sin_dlon1, c0) || math::equals(tan_alpha1_x, c0);
  86. bool const is_vertical2 = math::equals(sin_dlon2, c0) || math::equals(tan_alpha2_x, c0);
  87. CT tan_alpha1 = 0;
  88. CT tan_alpha2 = 0;
  89. if (is_vertical1 && is_vertical2)
  90. {
  91. // circles intersect at one of the poles or are collinear
  92. return false;
  93. }
  94. else if (is_vertical1)
  95. {
  96. tan_alpha2 = sin_dlon2 / tan_alpha2_x;
  97. lon = lon1;
  98. }
  99. else if (is_vertical2)
  100. {
  101. tan_alpha1 = sin_dlon1 / tan_alpha1_x;
  102. lon = lon2;
  103. }
  104. else
  105. {
  106. tan_alpha1 = sin_dlon1 / tan_alpha1_x;
  107. tan_alpha2 = sin_dlon2 / tan_alpha2_x;
  108. CT const T1 = tan_alpha1 * cos_lat1;
  109. CT const T2 = tan_alpha2 * cos_lat2;
  110. CT const T1T2 = T1*T2;
  111. CT const tan_lon_y = T1 * sin_lon2 - T2 * sin_lon1 + T1T2 * (tan_lat1 * cos_lon1 - tan_lat2 * cos_lon2);
  112. CT const tan_lon_x = T1 * cos_lon2 - T2 * cos_lon1 - T1T2 * (tan_lat1 * sin_lon1 - tan_lat2 * sin_lon2);
  113. lon = atan2(tan_lon_y, tan_lon_x);
  114. }
  115. // choose closer result
  116. CT const pi = math::pi<CT>();
  117. CT const lon_2 = lon > c0 ? lon - pi : lon + pi;
  118. CT const lon_dist1 = (std::max)((std::min)(math::longitude_difference<radian>(lon1, lon),
  119. math::longitude_difference<radian>(lon_a2, lon)),
  120. (std::min)(math::longitude_difference<radian>(lon2, lon),
  121. math::longitude_difference<radian>(lon_b2, lon)));
  122. CT const lon_dist2 = (std::max)((std::min)(math::longitude_difference<radian>(lon1, lon_2),
  123. math::longitude_difference<radian>(lon_a2, lon_2)),
  124. (std::min)(math::longitude_difference<radian>(lon2, lon_2),
  125. math::longitude_difference<radian>(lon_b2, lon_2)));
  126. if (lon_dist2 < lon_dist1)
  127. {
  128. lon = lon_2;
  129. }
  130. CT const sin_lon = sin(lon);
  131. CT const cos_lon = cos(lon);
  132. if (math::abs(tan_alpha1) >= math::abs(tan_alpha2)) // pick less vertical segment
  133. {
  134. CT const sin_dlon_1 = sin_lon * cos_lon1 - cos_lon * sin_lon1;
  135. CT const cos_dlon_1 = cos_lon * cos_lon1 + sin_lon * sin_lon1;
  136. CT const lat_y_1 = sin_dlon_1 + tan_alpha1 * sin_lat1 * cos_dlon_1;
  137. CT const lat_x_1 = tan_alpha1 * cos_lat1;
  138. tan_lat = lat_y_1 / lat_x_1;
  139. }
  140. else
  141. {
  142. CT const sin_dlon_2 = sin_lon * cos_lon2 - cos_lon * sin_lon2;
  143. CT const cos_dlon_2 = cos_lon * cos_lon2 + sin_lon * sin_lon2;
  144. CT const lat_y_2 = sin_dlon_2 + tan_alpha2 * sin_lat2 * cos_dlon_2;
  145. CT const lat_x_2 = tan_alpha2 * cos_lat2;
  146. tan_lat = lat_y_2 / lat_x_2;
  147. }
  148. return true;
  149. }
  150. };
  151. /*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2
  152. Maxima script:
  153. dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB);
  154. dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B);
  155. S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3);
  156. assume(c_j < 1);
  157. assume(c_j > 0);
  158. L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x));
  159. L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x));
  160. L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x));
  161. L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x));
  162. \see See
  163. - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007
  164. http://link.springer.com/article/10.1007/s00190-007-0204-7
  165. */
  166. template <unsigned int Order, typename CT>
  167. inline CT sjoberg_d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta,
  168. CT const& Cj, CT const& sqrt_1_Cj_sqr,
  169. CT const& e_sqr)
  170. {
  171. using math::detail::bounded;
  172. if (BOOST_GEOMETRY_CONDITION(Order == 0))
  173. {
  174. return 0;
  175. }
  176. CT const c1 = 1;
  177. CT const c2 = 2;
  178. CT const asin_B = asin(bounded(sin_beta / sqrt_1_Cj_sqr, -c1, c1));
  179. CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr);
  180. CT const L0 = (asin_B - asin_Bj) / c2;
  181. if (BOOST_GEOMETRY_CONDITION(Order == 1))
  182. {
  183. return -Cj * e_sqr * L0;
  184. }
  185. CT const c0 = 0;
  186. CT const c16 = 16;
  187. CT const X = sin_beta;
  188. CT const Xj = sin_betaj;
  189. CT const X_sqr = math::sqr(X);
  190. CT const Xj_sqr = math::sqr(Xj);
  191. CT const Cj_sqr = math::sqr(Cj);
  192. CT const Cj_sqr_plus_one = Cj_sqr + c1;
  193. CT const one_minus_Cj_sqr = c1 - Cj_sqr;
  194. CT const sqrt_Y = math::sqrt(bounded(-X_sqr + one_minus_Cj_sqr, c0));
  195. CT const sqrt_Yj = math::sqrt(-Xj_sqr + one_minus_Cj_sqr);
  196. CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16;
  197. if (BOOST_GEOMETRY_CONDITION(Order == 2))
  198. {
  199. return -Cj * e_sqr * (L0 + e_sqr * L1);
  200. }
  201. CT const c3 = 3;
  202. CT const c5 = 5;
  203. CT const c128 = 128;
  204. CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3;
  205. CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5);
  206. CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5);
  207. CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128;
  208. if (BOOST_GEOMETRY_CONDITION(Order == 3))
  209. {
  210. return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2));
  211. }
  212. CT const c8 = 8;
  213. CT const c9 = 9;
  214. CT const c10 = 10;
  215. CT const c15 = 15;
  216. CT const c24 = 24;
  217. CT const c26 = 26;
  218. CT const c33 = 33;
  219. CT const c6144 = 6144;
  220. CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15;
  221. CT const H = -c10 * Cj_sqr - c26;
  222. CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33;
  223. CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I;
  224. CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I;
  225. CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144;
  226. // Order 4 and higher
  227. return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3)));
  228. }
  229. /*!
  230. \brief The representation of geodesic as proposed by Sjoberg.
  231. \see See
  232. - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007
  233. http://link.springer.com/article/10.1007/s00190-007-0204-7
  234. - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant
  235. and the inverse geodetic problem by numerical integration, 2012
  236. https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml
  237. */
  238. template <typename CT, unsigned int Order>
  239. class sjoberg_geodesic
  240. {
  241. sjoberg_geodesic() {}
  242. static int sign_C(CT const& alphaj)
  243. {
  244. CT const c0 = 0;
  245. CT const c2 = 2;
  246. CT const pi = math::pi<CT>();
  247. CT const pi_half = pi / c2;
  248. return (pi_half < alphaj && alphaj < pi) || (-pi_half < alphaj && alphaj < c0) ? -1 : 1;
  249. }
  250. public:
  251. sjoberg_geodesic(CT const& lon, CT const& lat, CT const& alpha, CT const& f)
  252. : lonj(lon)
  253. , latj(lat)
  254. , alphaj(alpha)
  255. {
  256. CT const c0 = 0;
  257. CT const c1 = 1;
  258. CT const c2 = 2;
  259. //CT const pi = math::pi<CT>();
  260. //CT const pi_half = pi / c2;
  261. one_minus_f = c1 - f;
  262. e_sqr = f * (c2 - f);
  263. tan_latj = tan(lat);
  264. tan_betaj = one_minus_f * tan_latj;
  265. betaj = atan(tan_betaj);
  266. sin_betaj = sin(betaj);
  267. cos_betaj = cos(betaj);
  268. sin_alphaj = sin(alphaj);
  269. // Clairaut constant (lower-case in the paper)
  270. Cj = sign_C(alphaj) * cos_betaj * sin_alphaj;
  271. Cj_sqr = math::sqr(Cj);
  272. sqrt_1_Cj_sqr = math::sqrt(c1 - Cj_sqr);
  273. sign_lon_diff = alphaj >= 0 ? 1 : -1; // || alphaj == -pi ?
  274. //sign_lon_diff = 1;
  275. is_on_equator = math::equals(sqrt_1_Cj_sqr, c0);
  276. is_Cj_zero = math::equals(Cj, c0);
  277. t0j = c0;
  278. asin_tj_t0j = c0;
  279. if (! is_Cj_zero)
  280. {
  281. t0j = sqrt_1_Cj_sqr / Cj;
  282. }
  283. if (! is_on_equator)
  284. {
  285. //asin_tj_t0j = asin(tan_betaj / t0j);
  286. asin_tj_t0j = asin(tan_betaj * Cj / sqrt_1_Cj_sqr);
  287. }
  288. }
  289. struct vertex_data
  290. {
  291. //CT beta0j;
  292. CT sin_beta0j;
  293. CT dL0j;
  294. CT lon0j;
  295. };
  296. vertex_data get_vertex_data() const
  297. {
  298. CT const c2 = 2;
  299. CT const pi = math::pi<CT>();
  300. CT const pi_half = pi / c2;
  301. vertex_data res;
  302. if (! is_Cj_zero)
  303. {
  304. //res.beta0j = atan(t0j);
  305. //res.sin_beta0j = sin(res.beta0j);
  306. res.sin_beta0j = math::sign(t0j) * sqrt_1_Cj_sqr;
  307. res.dL0j = d_lambda(res.sin_beta0j);
  308. res.lon0j = lonj + sign_lon_diff * (pi_half - asin_tj_t0j + res.dL0j);
  309. }
  310. else
  311. {
  312. //res.beta0j = pi_half;
  313. //res.sin_beta0j = betaj >= 0 ? 1 : -1;
  314. res.sin_beta0j = 1;
  315. res.dL0j = 0;
  316. res.lon0j = lonj;
  317. }
  318. return res;
  319. }
  320. bool is_sin_beta_ok(CT const& sin_beta) const
  321. {
  322. CT const c1 = 1;
  323. return math::abs(sin_beta / sqrt_1_Cj_sqr) <= c1;
  324. }
  325. bool k_diff(CT const& sin_beta,
  326. CT & delta_k) const
  327. {
  328. if (is_Cj_zero)
  329. {
  330. delta_k = 0;
  331. return true;
  332. }
  333. // beta out of bounds and not close
  334. if (! (is_sin_beta_ok(sin_beta)
  335. || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) )
  336. {
  337. return false;
  338. }
  339. // NOTE: beta may be slightly out of bounds here but d_lambda handles that
  340. CT const dLj = d_lambda(sin_beta);
  341. delta_k = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj);
  342. return true;
  343. }
  344. bool lon_diff(CT const& sin_beta, CT const& t,
  345. CT & delta_lon) const
  346. {
  347. using math::detail::bounded;
  348. CT const c1 = 1;
  349. if (is_Cj_zero)
  350. {
  351. delta_lon = 0;
  352. return true;
  353. }
  354. CT delta_k = 0;
  355. if (! k_diff(sin_beta, delta_k))
  356. {
  357. return false;
  358. }
  359. CT const t_t0j = t / t0j;
  360. // NOTE: t may be slightly out of bounds here
  361. CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1));
  362. delta_lon = sign_lon_diff * asin_t_t0j + delta_k;
  363. return true;
  364. }
  365. bool k_diffs(CT const& sin_beta, vertex_data const& vd,
  366. CT & delta_k_before, CT & delta_k_behind,
  367. bool check_sin_beta = true) const
  368. {
  369. CT const pi = math::pi<CT>();
  370. if (is_Cj_zero)
  371. {
  372. delta_k_before = 0;
  373. delta_k_behind = sign_lon_diff * pi;
  374. return true;
  375. }
  376. // beta out of bounds and not close
  377. if (check_sin_beta
  378. && ! (is_sin_beta_ok(sin_beta)
  379. || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) )
  380. {
  381. return false;
  382. }
  383. // NOTE: beta may be slightly out of bounds here but d_lambda handles that
  384. CT const dLj = d_lambda(sin_beta);
  385. delta_k_before = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj);
  386. // This version require no additional dLj calculation
  387. delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + vd.dL0j + (vd.dL0j - dLj));
  388. // [Sjoberg12]
  389. //CT const dL101 = d_lambda(sin_betaj, vd.sin_beta0j);
  390. // WARNING: the following call might not work if beta was OoB because only the second argument is bounded
  391. //CT const dL_01 = d_lambda(sin_beta, vd.sin_beta0j);
  392. //delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + dL101 + dL_01);
  393. return true;
  394. }
  395. bool lon_diffs(CT const& sin_beta, CT const& t, vertex_data const& vd,
  396. CT & delta_lon_before, CT & delta_lon_behind) const
  397. {
  398. using math::detail::bounded;
  399. CT const c1 = 1;
  400. CT const pi = math::pi<CT>();
  401. if (is_Cj_zero)
  402. {
  403. delta_lon_before = 0;
  404. delta_lon_behind = sign_lon_diff * pi;
  405. return true;
  406. }
  407. CT delta_k_before = 0, delta_k_behind = 0;
  408. if (! k_diffs(sin_beta, vd, delta_k_before, delta_k_behind))
  409. {
  410. return false;
  411. }
  412. CT const t_t0j = t / t0j;
  413. // NOTE: t may be slightly out of bounds here
  414. CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1));
  415. CT const sign_asin_t_t0j = sign_lon_diff * asin_t_t0j;
  416. delta_lon_before = sign_asin_t_t0j + delta_k_before;
  417. delta_lon_behind = -sign_asin_t_t0j + delta_k_behind;
  418. return true;
  419. }
  420. bool lon(CT const& sin_beta, CT const& t, vertex_data const& vd,
  421. CT & lon_before, CT & lon_behind) const
  422. {
  423. using math::detail::bounded;
  424. CT const c1 = 1;
  425. CT const pi = math::pi<CT>();
  426. if (is_Cj_zero)
  427. {
  428. lon_before = lonj;
  429. lon_behind = lonj + sign_lon_diff * pi;
  430. return true;
  431. }
  432. if (! (is_sin_beta_ok(sin_beta)
  433. || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) )
  434. {
  435. return false;
  436. }
  437. CT const t_t0j = t / t0j;
  438. CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1));
  439. CT const dLj = d_lambda(sin_beta);
  440. lon_before = lonj + sign_lon_diff * (asin_t_t0j - asin_tj_t0j + dLj);
  441. lon_behind = vd.lon0j + (vd.lon0j - lon_before);
  442. return true;
  443. }
  444. CT lon(CT const& delta_lon) const
  445. {
  446. return lonj + delta_lon;
  447. }
  448. CT lat(CT const& t) const
  449. {
  450. // t = tan(beta) = (1-f)tan(lat)
  451. return atan(t / one_minus_f);
  452. }
  453. void vertex(CT & lon, CT & lat) const
  454. {
  455. lon = get_vertex_data().lon0j;
  456. if (! is_Cj_zero)
  457. {
  458. lat = sjoberg_geodesic::lat(t0j);
  459. }
  460. else
  461. {
  462. CT const c2 = 2;
  463. lat = math::pi<CT>() / c2;
  464. }
  465. }
  466. CT lon_of_equator_intersection() const
  467. {
  468. CT const c0 = 0;
  469. CT const dLj = d_lambda(c0);
  470. CT const asin_tj_t0j = asin(Cj * tan_betaj / sqrt_1_Cj_sqr);
  471. return lonj - asin_tj_t0j + dLj;
  472. }
  473. CT d_lambda(CT const& sin_beta) const
  474. {
  475. return sjoberg_d_lambda_e_sqr<Order>(sin_betaj, sin_beta, Cj, sqrt_1_Cj_sqr, e_sqr);
  476. }
  477. // [Sjoberg12]
  478. /*CT d_lambda(CT const& sin_beta1, CT const& sin_beta2) const
  479. {
  480. return sjoberg_d_lambda_e_sqr<Order>(sin_beta1, sin_beta2, Cj, sqrt_1_Cj_sqr, e_sqr);
  481. }*/
  482. CT lonj;
  483. CT latj;
  484. CT alphaj;
  485. CT one_minus_f;
  486. CT e_sqr;
  487. CT tan_latj;
  488. CT tan_betaj;
  489. CT betaj;
  490. CT sin_betaj;
  491. CT cos_betaj;
  492. CT sin_alphaj;
  493. CT Cj;
  494. CT Cj_sqr;
  495. CT sqrt_1_Cj_sqr;
  496. int sign_lon_diff;
  497. bool is_on_equator;
  498. bool is_Cj_zero;
  499. CT t0j;
  500. CT asin_tj_t0j;
  501. };
  502. /*!
  503. \brief The intersection of two geodesics as proposed by Sjoberg.
  504. \see See
  505. - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002
  506. http://link.springer.com/article/10.1007/s00190-001-0230-9
  507. - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007
  508. http://link.springer.com/article/10.1007/s00190-007-0204-7
  509. - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant
  510. and the inverse geodetic problem by numerical integration, 2012
  511. https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml
  512. */
  513. template
  514. <
  515. typename CT,
  516. template <typename, bool, bool, bool, bool, bool> class Inverse,
  517. unsigned int Order = 4
  518. >
  519. class sjoberg_intersection
  520. {
  521. typedef sjoberg_geodesic<CT, Order> geodesic_type;
  522. typedef Inverse<CT, false, true, false, false, false> inverse_type;
  523. typedef typename inverse_type::result_type inverse_result;
  524. static bool const enable_02 = true;
  525. static int const max_iterations_02 = 10;
  526. static int const max_iterations_07 = 20;
  527. public:
  528. template <typename T1, typename T2, typename Spheroid>
  529. static inline bool apply(T1 const& lona1, T1 const& lata1,
  530. T1 const& lona2, T1 const& lata2,
  531. T2 const& lonb1, T2 const& latb1,
  532. T2 const& lonb2, T2 const& latb2,
  533. CT & lon, CT & lat,
  534. Spheroid const& spheroid)
  535. {
  536. CT const lon_a1 = lona1;
  537. CT const lat_a1 = lata1;
  538. CT const lon_a2 = lona2;
  539. CT const lat_a2 = lata2;
  540. CT const lon_b1 = lonb1;
  541. CT const lat_b1 = latb1;
  542. CT const lon_b2 = lonb2;
  543. CT const lat_b2 = latb2;
  544. inverse_result const res1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid);
  545. inverse_result const res2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid);
  546. return apply(lon_a1, lat_a1, lon_a2, lat_a2, res1.azimuth,
  547. lon_b1, lat_b1, lon_b2, lat_b2, res2.azimuth,
  548. lon, lat, spheroid);
  549. }
  550. // TODO: Currently may not work correctly if one of the endpoints is the pole
  551. template <typename Spheroid>
  552. static inline bool apply(CT const& lon_a1, CT const& lat_a1, CT const& lon_a2, CT const& lat_a2, CT const& alpha_a1,
  553. CT const& lon_b1, CT const& lat_b1, CT const& lon_b2, CT const& lat_b2, CT const& alpha_b1,
  554. CT & lon, CT & lat,
  555. Spheroid const& spheroid)
  556. {
  557. // coordinates in radians
  558. CT const c0 = 0;
  559. CT const c1 = 1;
  560. CT const f = formula::flattening<CT>(spheroid);
  561. CT const one_minus_f = c1 - f;
  562. geodesic_type geod1(lon_a1, lat_a1, alpha_a1, f);
  563. geodesic_type geod2(lon_b1, lat_b1, alpha_b1, f);
  564. // Cj = 1 if on equator <=> sqrt_1_Cj_sqr = 0
  565. // Cj = 0 if vertical <=> sqrt_1_Cj_sqr = 1
  566. if (geod1.is_on_equator && geod2.is_on_equator)
  567. {
  568. return false;
  569. }
  570. else if (geod1.is_on_equator)
  571. {
  572. lon = geod2.lon_of_equator_intersection();
  573. lat = c0;
  574. return true;
  575. }
  576. else if (geod2.is_on_equator)
  577. {
  578. lon = geod1.lon_of_equator_intersection();
  579. lat = c0;
  580. return true;
  581. }
  582. // (lon1 - lon2) normalized to (-180, 180]
  583. CT const lon1_minus_lon2 = math::longitude_distance_signed<radian>(geod2.lonj, geod1.lonj);
  584. // vertical segments
  585. if (geod1.is_Cj_zero && geod2.is_Cj_zero)
  586. {
  587. CT const pi = math::pi<CT>();
  588. // the geodesics are parallel, the intersection point cannot be calculated
  589. if ( math::equals(lon1_minus_lon2, c0)
  590. || math::equals(lon1_minus_lon2 + (lon1_minus_lon2 < c0 ? pi : -pi), c0) )
  591. {
  592. return false;
  593. }
  594. lon = c0;
  595. // the geodesics intersect at one of the poles
  596. CT const pi_half = pi / CT(2);
  597. CT const abs_lat_a1 = math::abs(lat_a1);
  598. CT const abs_lat_a2 = math::abs(lat_a2);
  599. if (math::equals(abs_lat_a1, abs_lat_a2))
  600. {
  601. lat = pi_half;
  602. }
  603. else
  604. {
  605. // pick the pole closest to one of the points of the first segment
  606. CT const& closer_lat = abs_lat_a1 > abs_lat_a2 ? lat_a1 : lat_a2;
  607. lat = closer_lat >= 0 ? pi_half : -pi_half;
  608. }
  609. return true;
  610. }
  611. CT lon_sph = 0;
  612. // Starting tan(beta)
  613. CT t = 0;
  614. /*if (geod1.is_Cj_zero)
  615. {
  616. CT const k_base = lon1_minus_lon2 + geod2.sign_lon_diff * geod2.asin_tj_t0j;
  617. t = sin(k_base) * geod2.t0j;
  618. lon_sph = vertical_intersection_longitude(geod1.lonj, lon_b1, lon_b2);
  619. }
  620. else if (geod2.is_Cj_zero)
  621. {
  622. CT const k_base = lon1_minus_lon2 - geod1.sign_lon_diff * geod1.asin_tj_t0j;
  623. t = sin(-k_base) * geod1.t0j;
  624. lon_sph = vertical_intersection_longitude(geod2.lonj, lon_a1, lon_a2);
  625. }
  626. else*/
  627. {
  628. // TODO: Consider using betas instead of latitudes.
  629. // Some function calls might be saved this way.
  630. CT tan_lat_sph = 0;
  631. sjoberg_intersection_spherical_02<CT>::apply_alt(lon_a1, lat_a1, lon_a2, lat_a2,
  632. lon_b1, lat_b1, lon_b2, lat_b2,
  633. lon_sph, tan_lat_sph);
  634. // Return for sphere
  635. if (math::equals(f, c0))
  636. {
  637. lon = lon_sph;
  638. lat = atan(tan_lat_sph);
  639. return true;
  640. }
  641. t = one_minus_f * tan_lat_sph; // tan(beta)
  642. }
  643. // TODO: no need to calculate atan here if reduced latitudes were used
  644. // instead of latitudes above, in sjoberg_intersection_spherical_02
  645. CT const beta = atan(t);
  646. if (enable_02 && newton_method(geod1, geod2, beta, t, lon1_minus_lon2, lon_sph, lon, lat))
  647. {
  648. // TODO: Newton's method may return wrong result in some specific cases
  649. // Detected for sphere and nearly sphere, e.g. A=6371228, B=6371227
  650. // and segments s1=(-121 -19,37 8) and s2=(-19 -15,-104 -58)
  651. // It's unclear if this is a bug or a characteristic of this method
  652. // so until this is investigated check if the resulting longitude is
  653. // between endpoints of the segments. It should be since before calling
  654. // this formula sides of endpoints WRT other segments are checked.
  655. if ( is_result_longitude_ok(geod1, lon_a1, lon_a2, lon)
  656. && is_result_longitude_ok(geod2, lon_b1, lon_b2, lon) )
  657. {
  658. return true;
  659. }
  660. }
  661. return converge_07(geod1, geod2, beta, t, lon1_minus_lon2, lon_sph, lon, lat);
  662. }
  663. private:
  664. static inline bool newton_method(geodesic_type const& geod1, geodesic_type const& geod2, // in
  665. CT beta, CT t, CT const& lon1_minus_lon2, CT const& lon_sph, // in
  666. CT & lon, CT & lat) // out
  667. {
  668. CT const c0 = 0;
  669. CT const c1 = 1;
  670. CT const e_sqr = geod1.e_sqr;
  671. CT lon1_diff = 0;
  672. CT lon2_diff = 0;
  673. // The segment is vertical and intersection point is behind the vertex
  674. // this method is unable to calculate correct result
  675. if (geod1.is_Cj_zero && math::abs(geod1.lonj - lon_sph) > math::half_pi<CT>())
  676. return false;
  677. if (geod2.is_Cj_zero && math::abs(geod2.lonj - lon_sph) > math::half_pi<CT>())
  678. return false;
  679. CT abs_dbeta_last = 0;
  680. // [Sjoberg02] converges faster than solution in [Sjoberg07]
  681. // Newton-Raphson method
  682. for (int i = 0; i < max_iterations_02; ++i)
  683. {
  684. CT const cos_beta = cos(beta);
  685. if (math::equals(cos_beta, c0))
  686. {
  687. return false;
  688. }
  689. CT const sin_beta = sin(beta);
  690. CT const cos_beta_sqr = math::sqr(cos_beta);
  691. CT const G = c1 - e_sqr * cos_beta_sqr;
  692. CT f1 = 0;
  693. CT f2 = 0;
  694. if (!geod1.is_Cj_zero)
  695. {
  696. bool is_beta_ok = geod1.lon_diff(sin_beta, t, lon1_diff);
  697. if (is_beta_ok)
  698. {
  699. CT const H = cos_beta_sqr - geod1.Cj_sqr;
  700. if (math::equals(H, c0))
  701. {
  702. return false;
  703. }
  704. f1 = geod1.Cj / cos_beta * math::sqrt(G / H);
  705. }
  706. else
  707. {
  708. return false;
  709. }
  710. }
  711. if (!geod2.is_Cj_zero)
  712. {
  713. bool is_beta_ok = geod2.lon_diff(sin_beta, t, lon2_diff);
  714. if (is_beta_ok)
  715. {
  716. CT const H = cos_beta_sqr - geod2.Cj_sqr;
  717. if (math::equals(H, c0))
  718. {
  719. // NOTE: This may happen for segment nearly
  720. // at the equator. Detected for (radian):
  721. // (-0.0872665 -0.0872665, -0.0872665 0.0872665)
  722. // x
  723. // (0 1.57e-07, -0.392699 1.57e-07)
  724. return false;
  725. }
  726. f2 = geod2.Cj / cos_beta * math::sqrt(G / H);
  727. }
  728. else
  729. {
  730. return false;
  731. }
  732. }
  733. // NOTE: Things may go wrong if the IP is near the vertex
  734. // 1. May converge into the wrong direction (from the other way around).
  735. // This happens when the starting point is on the other side than the vertex
  736. // 2. During converging may "jump" into the other side of the vertex.
  737. // In this case sin_beta/sqrt_1_Cj_sqr and t/t0j is not in [-1, 1]
  738. // 3. f1-f2 may be 0 which means that the intermediate point is on the vertex
  739. // In this case it's not possible to check if this is the correct result
  740. // 4. f1-f2 may also be 0 in other cases, e.g.
  741. // geodesics are symmetrical wrt equator and longitude directions are different
  742. CT const dbeta_denom = f1 - f2;
  743. //CT const dbeta_denom = math::abs(f1) + math::abs(f2);
  744. if (math::equals(dbeta_denom, c0))
  745. {
  746. return false;
  747. }
  748. // The sign of dbeta is changed WRT [Sjoberg02]
  749. CT const dbeta = (lon1_minus_lon2 + lon1_diff - lon2_diff) / dbeta_denom;
  750. CT const abs_dbeta = math::abs(dbeta);
  751. if (i > 0 && abs_dbeta > abs_dbeta_last)
  752. {
  753. // The algorithm is not converging
  754. // The intersection may be on the other side of the vertex
  755. return false;
  756. }
  757. abs_dbeta_last = abs_dbeta;
  758. if (math::equals(dbeta, c0))
  759. {
  760. // Result found
  761. break;
  762. }
  763. // Because the sign of dbeta is changed WRT [Sjoberg02] dbeta is subtracted here
  764. beta = beta - dbeta;
  765. t = tan(beta);
  766. }
  767. lat = geod1.lat(t);
  768. // NOTE: if Cj is 0 then the result is lonj or lonj+180
  769. lon = ! geod1.is_Cj_zero
  770. ? geod1.lon(lon1_diff)
  771. : geod2.lon(lon2_diff);
  772. return true;
  773. }
  774. static inline bool is_result_longitude_ok(geodesic_type const& geod,
  775. CT const& lon1, CT const& lon2, CT const& lon)
  776. {
  777. CT const c0 = 0;
  778. if (geod.is_Cj_zero)
  779. return true; // don't check vertical segment
  780. CT dist1p = math::longitude_distance_signed<radian>(lon1, lon);
  781. CT dist12 = math::longitude_distance_signed<radian>(lon1, lon2);
  782. if (dist12 < c0)
  783. {
  784. dist1p = -dist1p;
  785. dist12 = -dist12;
  786. }
  787. return (c0 <= dist1p && dist1p <= dist12)
  788. || math::equals(dist1p, c0)
  789. || math::equals(dist1p, dist12);
  790. }
  791. struct geodesics_type
  792. {
  793. geodesics_type(geodesic_type const& g1, geodesic_type const& g2)
  794. : geod1(g1)
  795. , geod2(g2)
  796. , vertex1(geod1.get_vertex_data())
  797. , vertex2(geod2.get_vertex_data())
  798. {}
  799. geodesic_type const& geod1;
  800. geodesic_type const& geod2;
  801. typename geodesic_type::vertex_data vertex1;
  802. typename geodesic_type::vertex_data vertex2;
  803. };
  804. struct converge_07_result
  805. {
  806. converge_07_result()
  807. : lon1(0), lon2(0), k1_diff(0), k2_diff(0), t1(0), t2(0)
  808. {}
  809. CT lon1, lon2;
  810. CT k1_diff, k2_diff;
  811. CT t1, t2;
  812. };
  813. static inline bool converge_07(geodesic_type const& geod1, geodesic_type const& geod2,
  814. CT beta, CT t,
  815. CT const& lon1_minus_lon2, CT const& lon_sph,
  816. CT & lon, CT & lat)
  817. {
  818. //CT const c0 = 0;
  819. //CT const c1 = 1;
  820. //CT const c2 = 2;
  821. //CT const pi = math::pi<CT>();
  822. geodesics_type geodesics(geod1, geod2);
  823. converge_07_result result;
  824. // calculate first pair of longitudes
  825. if (!converge_07_step_one(CT(sin(beta)), t, lon1_minus_lon2, geodesics, lon_sph, result, false))
  826. {
  827. return false;
  828. }
  829. int t_direction = 0;
  830. CT lon_diff_prev = math::longitude_difference<radian>(result.lon1, result.lon2);
  831. // [Sjoberg07]
  832. for (int i = 2; i < max_iterations_07; ++i)
  833. {
  834. // pick t candidates from previous result based on dir
  835. CT t_cand1 = result.t1;
  836. CT t_cand2 = result.t2;
  837. // if direction is 0 the closer one is the first
  838. if (t_direction < 0)
  839. {
  840. t_cand1 = (std::min)(result.t1, result.t2);
  841. t_cand2 = (std::max)(result.t1, result.t2);
  842. }
  843. else if (t_direction > 0)
  844. {
  845. t_cand1 = (std::max)(result.t1, result.t2);
  846. t_cand2 = (std::min)(result.t1, result.t2);
  847. }
  848. else
  849. {
  850. t_direction = t_cand1 < t_cand2 ? -1 : 1;
  851. }
  852. CT t1 = t;
  853. CT beta1 = beta;
  854. // check if the further calculation is needed
  855. if (converge_07_update(t1, beta1, t_cand1))
  856. {
  857. break;
  858. }
  859. bool try_t2 = false;
  860. converge_07_result result_curr;
  861. if (converge_07_step_one(CT(sin(beta1)), t1, lon1_minus_lon2, geodesics, lon_sph, result_curr))
  862. {
  863. CT const lon_diff1 = math::longitude_difference<radian>(result_curr.lon1, result_curr.lon2);
  864. if (lon_diff_prev > lon_diff1)
  865. {
  866. t = t1;
  867. beta = beta1;
  868. lon_diff_prev = lon_diff1;
  869. result = result_curr;
  870. }
  871. else if (t_cand1 != t_cand2)
  872. {
  873. try_t2 = true;
  874. }
  875. else
  876. {
  877. // the result is not fully correct but it won't be more accurate
  878. break;
  879. }
  880. }
  881. // ! converge_07_step_one
  882. else
  883. {
  884. if (t_cand1 != t_cand2)
  885. {
  886. try_t2 = true;
  887. }
  888. else
  889. {
  890. return false;
  891. }
  892. }
  893. if (try_t2)
  894. {
  895. CT t2 = t;
  896. CT beta2 = beta;
  897. // check if the further calculation is needed
  898. if (converge_07_update(t2, beta2, t_cand2))
  899. {
  900. break;
  901. }
  902. if (! converge_07_step_one(CT(sin(beta2)), t2, lon1_minus_lon2, geodesics, lon_sph, result_curr))
  903. {
  904. return false;
  905. }
  906. CT const lon_diff2 = math::longitude_difference<radian>(result_curr.lon1, result_curr.lon2);
  907. if (lon_diff_prev > lon_diff2)
  908. {
  909. t_direction *= -1;
  910. t = t2;
  911. beta = beta2;
  912. lon_diff_prev = lon_diff2;
  913. result = result_curr;
  914. }
  915. else
  916. {
  917. // the result is not fully correct but it won't be more accurate
  918. break;
  919. }
  920. }
  921. }
  922. lat = geod1.lat(t);
  923. lon = ! geod1.is_Cj_zero ? result.lon1 : result.lon2;
  924. math::normalize_longitude<radian>(lon);
  925. return true;
  926. }
  927. static inline bool converge_07_update(CT & t, CT & beta, CT const& t_new)
  928. {
  929. CT const c0 = 0;
  930. CT const beta_new = atan(t_new);
  931. CT const dbeta = beta_new - beta;
  932. beta = beta_new;
  933. t = t_new;
  934. return math::equals(dbeta, c0);
  935. }
  936. static inline CT const& pick_t(CT const& t1, CT const& t2, int direction)
  937. {
  938. return direction < 0 ? (std::min)(t1, t2) : (std::max)(t1, t2);
  939. }
  940. static inline bool converge_07_step_one(CT const& sin_beta,
  941. CT const& t,
  942. CT const& lon1_minus_lon2,
  943. geodesics_type const& geodesics,
  944. CT const& lon_sph,
  945. converge_07_result & result,
  946. bool check_sin_beta = true)
  947. {
  948. bool ok = converge_07_one_geod(sin_beta, t, geodesics.geod1, geodesics.vertex1, lon_sph,
  949. result.lon1, result.k1_diff, check_sin_beta)
  950. && converge_07_one_geod(sin_beta, t, geodesics.geod2, geodesics.vertex2, lon_sph,
  951. result.lon2, result.k2_diff, check_sin_beta);
  952. if (!ok)
  953. {
  954. return false;
  955. }
  956. CT const k = lon1_minus_lon2 + result.k1_diff - result.k2_diff;
  957. // get 2 possible ts one lesser and one greater than t
  958. // t1 is the closer one
  959. calc_ts(t, k, geodesics.geod1, geodesics.geod2, result.t1, result.t2);
  960. return true;
  961. }
  962. static inline bool converge_07_one_geod(CT const& sin_beta, CT const& t,
  963. geodesic_type const& geod,
  964. typename geodesic_type::vertex_data const& vertex,
  965. CT const& lon_sph,
  966. CT & lon, CT & k_diff,
  967. bool check_sin_beta)
  968. {
  969. using math::detail::bounded;
  970. CT const c1 = 1;
  971. CT k_diff_before = 0;
  972. CT k_diff_behind = 0;
  973. bool is_beta_ok = geod.k_diffs(sin_beta, vertex, k_diff_before, k_diff_behind, check_sin_beta);
  974. if (! is_beta_ok)
  975. {
  976. return false;
  977. }
  978. CT const asin_t_t0j = ! geod.is_Cj_zero ? asin(bounded(t / geod.t0j, -c1, c1)) : 0;
  979. CT const sign_asin_t_t0j = geod.sign_lon_diff * asin_t_t0j;
  980. CT const lon_before = geod.lonj + sign_asin_t_t0j + k_diff_before;
  981. CT const lon_behind = geod.lonj - sign_asin_t_t0j + k_diff_behind;
  982. CT const lon_dist_before = math::longitude_distance_signed<radian>(lon_before, lon_sph);
  983. CT const lon_dist_behind = math::longitude_distance_signed<radian>(lon_behind, lon_sph);
  984. if (math::abs(lon_dist_before) <= math::abs(lon_dist_behind))
  985. {
  986. k_diff = k_diff_before;
  987. lon = lon_before;
  988. }
  989. else
  990. {
  991. k_diff = k_diff_behind;
  992. lon = lon_behind;
  993. }
  994. return true;
  995. }
  996. static inline void calc_ts(CT const& t, CT const& k,
  997. geodesic_type const& geod1, geodesic_type const& geod2,
  998. CT & t1, CT& t2)
  999. {
  1000. CT const c0 = 0;
  1001. CT const c1 = 1;
  1002. CT const c2 = 2;
  1003. CT const K = sin(k);
  1004. BOOST_GEOMETRY_ASSERT(!geod1.is_Cj_zero || !geod2.is_Cj_zero);
  1005. if (geod1.is_Cj_zero)
  1006. {
  1007. t1 = K * geod2.t0j;
  1008. t2 = -t1;
  1009. }
  1010. else if (geod2.is_Cj_zero)
  1011. {
  1012. t1 = -K * geod1.t0j;
  1013. t2 = -t1;
  1014. }
  1015. else
  1016. {
  1017. CT const A = math::sqr(geod1.t0j) + math::sqr(geod2.t0j);
  1018. CT const B = c2 * geod1.t0j * geod2.t0j * math::sqrt(c1 - math::sqr(K));
  1019. CT const K_t01_t02 = K * geod1.t0j * geod2.t0j;
  1020. CT const D1 = math::sqrt(A + B);
  1021. CT const D2 = math::sqrt(A - B);
  1022. CT const t_new1 = math::equals(D1, c0) ? c0 : K_t01_t02 / D1;
  1023. CT const t_new2 = math::equals(D2, c0) ? c0 : K_t01_t02 / D2;
  1024. CT const t_new3 = -t_new1;
  1025. CT const t_new4 = -t_new2;
  1026. // Pick 2 nearest t_new, one greater and one lesser than current t
  1027. CT const abs_t_new1 = math::abs(t_new1);
  1028. CT const abs_t_new2 = math::abs(t_new2);
  1029. CT const abs_t_max = (std::max)(abs_t_new1, abs_t_new2);
  1030. t1 = -abs_t_max; // lesser
  1031. t2 = abs_t_max; // greater
  1032. if (t1 < t)
  1033. {
  1034. if (t_new1 < t && t_new1 > t1)
  1035. t1 = t_new1;
  1036. if (t_new2 < t && t_new2 > t1)
  1037. t1 = t_new2;
  1038. if (t_new3 < t && t_new3 > t1)
  1039. t1 = t_new3;
  1040. if (t_new4 < t && t_new4 > t1)
  1041. t1 = t_new4;
  1042. }
  1043. if (t2 > t)
  1044. {
  1045. if (t_new1 > t && t_new1 < t2)
  1046. t2 = t_new1;
  1047. if (t_new2 > t && t_new2 < t2)
  1048. t2 = t_new2;
  1049. if (t_new3 > t && t_new3 < t2)
  1050. t2 = t_new3;
  1051. if (t_new4 > t && t_new4 < t2)
  1052. t2 = t_new4;
  1053. }
  1054. }
  1055. // the first one is the closer one
  1056. if (math::abs(t - t2) < math::abs(t - t1))
  1057. {
  1058. std::swap(t2, t1);
  1059. }
  1060. }
  1061. static inline CT fj(CT const& cos_beta, CT const& cos2_beta, CT const& Cj, CT const& e_sqr)
  1062. {
  1063. CT const c1 = 1;
  1064. CT const Cj_sqr = math::sqr(Cj);
  1065. return Cj / cos_beta * math::sqrt((c1 - e_sqr * cos2_beta) / (cos2_beta - Cj_sqr));
  1066. }
  1067. /*static inline CT vertical_intersection_longitude(CT const& ip_lon, CT const& seg_lon1, CT const& seg_lon2)
  1068. {
  1069. CT const c0 = 0;
  1070. CT const lon_2 = ip_lon > c0 ? ip_lon - pi : ip_lon + pi;
  1071. return (std::min)(math::longitude_difference<radian>(ip_lon, seg_lon1),
  1072. math::longitude_difference<radian>(ip_lon, seg_lon2))
  1073. <=
  1074. (std::min)(math::longitude_difference<radian>(lon_2, seg_lon1),
  1075. math::longitude_difference<radian>(lon_2, seg_lon2))
  1076. ? ip_lon : lon_2;
  1077. }*/
  1078. };
  1079. }}} // namespace boost::geometry::formula
  1080. #endif // BOOST_GEOMETRY_FORMULAS_SJOBERG_INTERSECTION_HPP