area_formulas.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. // Boost.Geometry
  2. // Copyright (c) 2023-2024 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2015-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_FORMULAS_AREA_FORMULAS_HPP
  10. #define BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP
  11. #include <boost/geometry/core/radian_access.hpp>
  12. #include <boost/geometry/formulas/flattening.hpp>
  13. #include <boost/geometry/formulas/mean_radius.hpp>
  14. #include <boost/geometry/formulas/karney_inverse.hpp>
  15. #include <boost/geometry/util/constexpr.hpp>
  16. #include <boost/geometry/util/math.hpp>
  17. #include <boost/math/special_functions/hypot.hpp>
  18. namespace boost { namespace geometry { namespace formula
  19. {
  20. /*!
  21. \brief Formulas for computing spherical and ellipsoidal polygon area.
  22. The current class computes the area of the trapezoid defined by a segment
  23. the two meridians passing by the endpoints and the equator.
  24. \author See
  25. - Danielsen JS, The area under the geodesic. Surv Rev 30(232):
  26. 61–66, 1989
  27. - Charles F.F Karney, Algorithms for geodesics, 2011
  28. https://arxiv.org/pdf/1109.4448.pdf
  29. */
  30. template
  31. <
  32. typename CT,
  33. std::size_t SeriesOrder = 2,
  34. bool ExpandEpsN = true
  35. >
  36. class area_formulas
  37. {
  38. public:
  39. //TODO: move the following to a more general space to be used by other
  40. // classes as well
  41. /*
  42. Evaluate the polynomial in x using Horner's method.
  43. */
  44. template <typename NT, typename IteratorType>
  45. static inline NT horner_evaluate(NT const& x,
  46. IteratorType begin,
  47. IteratorType end)
  48. {
  49. NT result(0);
  50. IteratorType it = end;
  51. do
  52. {
  53. result = result * x + *--it;
  54. }
  55. while (it != begin);
  56. return result;
  57. }
  58. /*
  59. Clenshaw algorithm for summing trigonometric series
  60. https://en.wikipedia.org/wiki/Clenshaw_algorithm
  61. */
  62. template <typename NT, typename IteratorType>
  63. static inline NT clenshaw_sum(NT const& cosx,
  64. IteratorType begin,
  65. IteratorType end)
  66. {
  67. IteratorType it = end;
  68. bool odd = true;
  69. CT b_k, b_k1(0), b_k2(0);
  70. do
  71. {
  72. CT c_k = odd ? *--it : NT(0);
  73. b_k = c_k + NT(2) * cosx * b_k1 - b_k2;
  74. b_k2 = b_k1;
  75. b_k1 = b_k;
  76. odd = !odd;
  77. }
  78. while (it != begin);
  79. return *begin + b_k1 * cosx - b_k2;
  80. }
  81. template<typename T>
  82. static inline void normalize(T& x, T& y)
  83. {
  84. T h = boost::math::hypot(x, y);
  85. x /= h;
  86. y /= h;
  87. }
  88. /*
  89. Generate and evaluate the series expansion of the following integral
  90. I4 = -integrate( (t(ep2) - t(k2*sin(sigma1)^2)) / (ep2 - k2*sin(sigma1)^2)
  91. * sin(sigma1)/2, sigma1, pi/2, sigma )
  92. where
  93. t(x) = sqrt(1+1/x)*asinh(sqrt(x)) + x
  94. valid for ep2 and k2 small. We substitute k2 = 4 * eps / (1 - eps)^2
  95. and ep2 = 4 * n / (1 - n)^2 and expand in eps and n.
  96. The resulting sum of the series is of the form
  97. sum(C4[l] * cos((2*l+1)*sigma), l, 0, maxpow-1) )
  98. The above expansion is performed in Computer Algebra System Maxima.
  99. The C++ code (that yields the function evaluate_coeffs_n below) is generated
  100. by the following Maxima script and is based on script:
  101. http://geographiclib.sourceforge.net/html/geod.mac
  102. // Maxima script begin
  103. taylordepth:5$
  104. ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$
  105. jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],
  106. ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$
  107. compute(maxpow):=block([int,t,intexp,area, x,ep2,k2],
  108. maxpow:maxpow-1,
  109. t : sqrt(1+1/x) * asinh(sqrt(x)) + x,
  110. int:-(tf(ep2) - tf(k2*sin(sigma)^2)) / (ep2 - k2*sin(sigma)^2)
  111. * sin(sigma)/2,
  112. int:subst([tf(ep2)=subst([x=ep2],t),
  113. tf(k2*sin(sigma)^2)=subst([x=k2*sin(sigma)^2],t)],
  114. int),
  115. int:subst([abs(sin(sigma))=sin(sigma)],int),
  116. int:subst([k2=4*eps/(1-eps)^2,ep2=4*n/(1-n)^2],int),
  117. intexp:jtaylor(int,n,eps,maxpow),
  118. area:trigreduce(integrate(intexp,sigma)),
  119. area:expand(area-subst(sigma=%pi/2,area)),
  120. for i:0 thru maxpow do C4[i]:coeff(area,cos((2*i+1)*sigma)),
  121. if expand(area-sum(C4[i]*cos((2*i+1)*sigma),i,0,maxpow)) # 0
  122. then error("left over terms in I4"),
  123. 'done)$
  124. printcode(maxpow):=
  125. block([tab2:" ",tab3:" "],
  126. print(" switch (SeriesOrder) {"),
  127. for nn:1 thru maxpow do block([c],
  128. print(concat(tab2,"case ",string(nn-1),":")),
  129. c:0,
  130. for m:0 thru nn-1 do block(
  131. [q:jtaylor(subst([n=n],C4[m]),n,eps,nn-1),
  132. linel:1200],
  133. for j:m thru nn-1 do (
  134. print(concat(tab3,"coeffs_n[",c,"] = ",
  135. string(horner(coeff(q,eps,j))),";")),
  136. c:c+1)
  137. ),
  138. print(concat(tab3,"break;"))),
  139. print(" }"),
  140. 'done)$
  141. maxpow:6$
  142. compute(maxpow)$
  143. printcode(maxpow)$
  144. // Maxima script end
  145. In the resulting code we should replace each number x by CT(x)
  146. e.g. using the following scirpt:
  147. sed -e 's/[0-9]\+/CT(&)/g; s/\[CT(/\[/g; s/)\]/\]/g;
  148. s/case\sCT(/case /g; s/):/:/g'
  149. */
  150. static inline void evaluate_coeffs_n(CT const& n, CT coeffs_n[])
  151. {
  152. switch (SeriesOrder) {
  153. case 0:
  154. coeffs_n[0] = CT(2)/CT(3);
  155. break;
  156. case 1:
  157. coeffs_n[0] = (CT(10)-CT(4)*n)/CT(15);
  158. coeffs_n[1] = -CT(1)/CT(5);
  159. coeffs_n[2] = CT(1)/CT(45);
  160. break;
  161. case 2:
  162. coeffs_n[0] = (n*(CT(8)*n-CT(28))+CT(70))/CT(105);
  163. coeffs_n[1] = (CT(16)*n-CT(7))/CT(35);
  164. coeffs_n[2] = -CT(2)/CT(105);
  165. coeffs_n[3] = (CT(7)-CT(16)*n)/CT(315);
  166. coeffs_n[4] = -CT(2)/CT(105);
  167. coeffs_n[5] = CT(4)/CT(525);
  168. break;
  169. case 3:
  170. coeffs_n[0] = (n*(n*(CT(4)*n+CT(24))-CT(84))+CT(210))/CT(315);
  171. coeffs_n[1] = ((CT(48)-CT(32)*n)*n-CT(21))/CT(105);
  172. coeffs_n[2] = (-CT(32)*n-CT(6))/CT(315);
  173. coeffs_n[3] = CT(11)/CT(315);
  174. coeffs_n[4] = (n*(CT(32)*n-CT(48))+CT(21))/CT(945);
  175. coeffs_n[5] = (CT(64)*n-CT(18))/CT(945);
  176. coeffs_n[6] = -CT(1)/CT(105);
  177. coeffs_n[7] = (CT(12)-CT(32)*n)/CT(1575);
  178. coeffs_n[8] = -CT(8)/CT(1575);
  179. coeffs_n[9] = CT(8)/CT(2205);
  180. break;
  181. case 4:
  182. coeffs_n[0] = (n*(n*(n*(CT(16)*n+CT(44))+CT(264))-CT(924))+CT(2310))/CT(3465);
  183. coeffs_n[1] = (n*(n*(CT(48)*n-CT(352))+CT(528))-CT(231))/CT(1155);
  184. coeffs_n[2] = (n*(CT(1088)*n-CT(352))-CT(66))/CT(3465);
  185. coeffs_n[3] = (CT(121)-CT(368)*n)/CT(3465);
  186. coeffs_n[4] = CT(4)/CT(1155);
  187. coeffs_n[5] = (n*((CT(352)-CT(48)*n)*n-CT(528))+CT(231))/CT(10395);
  188. coeffs_n[6] = ((CT(704)-CT(896)*n)*n-CT(198))/CT(10395);
  189. coeffs_n[7] = (CT(80)*n-CT(99))/CT(10395);
  190. coeffs_n[8] = CT(4)/CT(1155);
  191. coeffs_n[9] = (n*(CT(320)*n-CT(352))+CT(132))/CT(17325);
  192. coeffs_n[10] = (CT(384)*n-CT(88))/CT(17325);
  193. coeffs_n[11] = -CT(8)/CT(1925);
  194. coeffs_n[12] = (CT(88)-CT(256)*n)/CT(24255);
  195. coeffs_n[13] = -CT(16)/CT(8085);
  196. coeffs_n[14] = CT(64)/CT(31185);
  197. break;
  198. case 5:
  199. coeffs_n[0] = (n*(n*(n*(n*(CT(100)*n+CT(208))+CT(572))+CT(3432))-CT(12012))+CT(30030))
  200. /CT(45045);
  201. coeffs_n[1] = (n*(n*(n*(CT(64)*n+CT(624))-CT(4576))+CT(6864))-CT(3003))/CT(15015);
  202. coeffs_n[2] = (n*((CT(14144)-CT(10656)*n)*n-CT(4576))-CT(858))/CT(45045);
  203. coeffs_n[3] = ((-CT(224)*n-CT(4784))*n+CT(1573))/CT(45045);
  204. coeffs_n[4] = (CT(1088)*n+CT(156))/CT(45045);
  205. coeffs_n[5] = CT(97)/CT(15015);
  206. coeffs_n[6] = (n*(n*((-CT(64)*n-CT(624))*n+CT(4576))-CT(6864))+CT(3003))/CT(135135);
  207. coeffs_n[7] = (n*(n*(CT(5952)*n-CT(11648))+CT(9152))-CT(2574))/CT(135135);
  208. coeffs_n[8] = (n*(CT(5792)*n+CT(1040))-CT(1287))/CT(135135);
  209. coeffs_n[9] = (CT(468)-CT(2944)*n)/CT(135135);
  210. coeffs_n[10] = CT(1)/CT(9009);
  211. coeffs_n[11] = (n*((CT(4160)-CT(1440)*n)*n-CT(4576))+CT(1716))/CT(225225);
  212. coeffs_n[12] = ((CT(4992)-CT(8448)*n)*n-CT(1144))/CT(225225);
  213. coeffs_n[13] = (CT(1856)*n-CT(936))/CT(225225);
  214. coeffs_n[14] = CT(8)/CT(10725);
  215. coeffs_n[15] = (n*(CT(3584)*n-CT(3328))+CT(1144))/CT(315315);
  216. coeffs_n[16] = (CT(1024)*n-CT(208))/CT(105105);
  217. coeffs_n[17] = -CT(136)/CT(63063);
  218. coeffs_n[18] = (CT(832)-CT(2560)*n)/CT(405405);
  219. coeffs_n[19] = -CT(128)/CT(135135);
  220. coeffs_n[20] = CT(128)/CT(99099);
  221. break;
  222. }
  223. }
  224. /*
  225. Expand in k2 and ep2.
  226. */
  227. static inline void evaluate_coeffs_ep(CT const& ep, CT coeffs_n[])
  228. {
  229. switch (SeriesOrder) {
  230. case 0:
  231. coeffs_n[0] = CT(2)/CT(3);
  232. break;
  233. case 1:
  234. coeffs_n[0] = (CT(10)-ep)/CT(15);
  235. coeffs_n[1] = -CT(1)/CT(20);
  236. coeffs_n[2] = CT(1)/CT(180);
  237. break;
  238. case 2:
  239. coeffs_n[0] = (ep*(CT(4)*ep-CT(7))+CT(70))/CT(105);
  240. coeffs_n[1] = (CT(4)*ep-CT(7))/CT(140);
  241. coeffs_n[2] = CT(1)/CT(42);
  242. coeffs_n[3] = (CT(7)-CT(4)*ep)/CT(1260);
  243. coeffs_n[4] = -CT(1)/CT(252);
  244. coeffs_n[5] = CT(1)/CT(2100);
  245. break;
  246. case 3:
  247. coeffs_n[0] = (ep*((CT(12)-CT(8)*ep)*ep-CT(21))+CT(210))/CT(315);
  248. coeffs_n[1] = ((CT(12)-CT(8)*ep)*ep-CT(21))/CT(420);
  249. coeffs_n[2] = (CT(3)-CT(2)*ep)/CT(126);
  250. coeffs_n[3] = -CT(1)/CT(72);
  251. coeffs_n[4] = (ep*(CT(8)*ep-CT(12))+CT(21))/CT(3780);
  252. coeffs_n[5] = (CT(2)*ep-CT(3))/CT(756);
  253. coeffs_n[6] = CT(1)/CT(360);
  254. coeffs_n[7] = (CT(3)-CT(2)*ep)/CT(6300);
  255. coeffs_n[8] = -CT(1)/CT(1800);
  256. coeffs_n[9] = CT(1)/CT(17640);
  257. break;
  258. case 4:
  259. coeffs_n[0] = (ep*(ep*(ep*(CT(64)*ep-CT(88))+CT(132))-CT(231))+CT(2310))/CT(3465);
  260. coeffs_n[1] = (ep*(ep*(CT(64)*ep-CT(88))+CT(132))-CT(231))/CT(4620);
  261. coeffs_n[2] = (ep*(CT(16)*ep-CT(22))+CT(33))/CT(1386);
  262. coeffs_n[3] = (CT(8)*ep-CT(11))/CT(792);
  263. coeffs_n[4] = CT(1)/CT(110);
  264. coeffs_n[5] = (ep*((CT(88)-CT(64)*ep)*ep-CT(132))+CT(231))/CT(41580);
  265. coeffs_n[6] = ((CT(22)-CT(16)*ep)*ep-CT(33))/CT(8316);
  266. coeffs_n[7] = (CT(11)-CT(8)*ep)/CT(3960);
  267. coeffs_n[8] = -CT(1)/CT(495);
  268. coeffs_n[9] = (ep*(CT(16)*ep-CT(22))+CT(33))/CT(69300);
  269. coeffs_n[10] = (CT(8)*ep-CT(11))/CT(19800);
  270. coeffs_n[11] = CT(1)/CT(1925);
  271. coeffs_n[12] = (CT(11)-CT(8)*ep)/CT(194040);
  272. coeffs_n[13] = -CT(1)/CT(10780);
  273. coeffs_n[14] = CT(1)/CT(124740);
  274. break;
  275. case 5:
  276. coeffs_n[0] = (ep*(ep*(ep*((CT(832)-CT(640)*ep)*ep-CT(1144))+CT(1716))-CT(3003))+CT(30030))/CT(45045);
  277. coeffs_n[1] = (ep*(ep*((CT(832)-CT(640)*ep)*ep-CT(1144))+CT(1716))-CT(3003))/CT(60060);
  278. coeffs_n[2] = (ep*((CT(208)-CT(160)*ep)*ep-CT(286))+CT(429))/CT(18018);
  279. coeffs_n[3] = ((CT(104)-CT(80)*ep)*ep-CT(143))/CT(10296);
  280. coeffs_n[4] = (CT(13)-CT(10)*ep)/CT(1430);
  281. coeffs_n[5] = -CT(1)/CT(156);
  282. coeffs_n[6] = (ep*(ep*(ep*(CT(640)*ep-CT(832))+CT(1144))-CT(1716))+CT(3003))/CT(540540);
  283. coeffs_n[7] = (ep*(ep*(CT(160)*ep-CT(208))+CT(286))-CT(429))/CT(108108);
  284. coeffs_n[8] = (ep*(CT(80)*ep-CT(104))+CT(143))/CT(51480);
  285. coeffs_n[9] = (CT(10)*ep-CT(13))/CT(6435);
  286. coeffs_n[10] = CT(5)/CT(3276);
  287. coeffs_n[11] = (ep*((CT(208)-CT(160)*ep)*ep-CT(286))+CT(429))/CT(900900);
  288. coeffs_n[12] = ((CT(104)-CT(80)*ep)*ep-CT(143))/CT(257400);
  289. coeffs_n[13] = (CT(13)-CT(10)*ep)/CT(25025);
  290. coeffs_n[14] = -CT(1)/CT(2184);
  291. coeffs_n[15] = (ep*(CT(80)*ep-CT(104))+CT(143))/CT(2522520);
  292. coeffs_n[16] = (CT(10)*ep-CT(13))/CT(140140);
  293. coeffs_n[17] = CT(5)/CT(45864);
  294. coeffs_n[18] = (CT(13)-CT(10)*ep)/CT(1621620);
  295. coeffs_n[19] = -CT(1)/CT(58968);
  296. coeffs_n[20] = CT(1)/CT(792792);
  297. break;
  298. }
  299. }
  300. /*
  301. Given the set of coefficients coeffs1[] evaluate on var2 and return
  302. the set of coefficients coeffs2[]
  303. */
  304. template <typename CoeffsType>
  305. static inline void evaluate_coeffs_var2(CT const& var2,
  306. CoeffsType const coeffs1[],
  307. CT coeffs2[])
  308. {
  309. std::size_t begin(0), end(0);
  310. for(std::size_t i = 0; i <= SeriesOrder; i++)
  311. {
  312. end = begin + SeriesOrder + 1 - i;
  313. coeffs2[i] = ((i==0) ? CT(1) : math::pow(var2, int(i)))
  314. * horner_evaluate(var2, coeffs1 + begin, coeffs1 + end);
  315. begin = end;
  316. }
  317. }
  318. static inline CT trapezoidal_formula(CT lat1r, CT lat2r, CT lon21r)
  319. {
  320. CT const c1 = CT(1);
  321. CT const c2 = CT(2);
  322. CT const tan_lat1 = tan(lat1r / c2);
  323. CT const tan_lat2 = tan(lat2r / c2);
  324. return c2 * atan(((tan_lat1 + tan_lat2) / (c1 + tan_lat1 * tan_lat2))* tan(lon21r / c2));
  325. }
  326. /*
  327. Compute the spherical excess of a geodesic (or shperical) segment
  328. */
  329. template
  330. <
  331. bool LongSegment,
  332. typename PointOfSegment
  333. >
  334. static inline CT spherical(PointOfSegment const& p1,
  335. PointOfSegment const& p2)
  336. {
  337. CT const pi = math::pi<CT>();
  338. CT excess;
  339. CT const lon1r = get_as_radian<0>(p1);
  340. CT const lat1r = get_as_radian<1>(p1);
  341. CT const lon2r = get_as_radian<0>(p2);
  342. CT const lat2r = get_as_radian<1>(p2);
  343. CT lon12r = lon2r - lon1r;
  344. math::normalize_longitude<radian, CT>(lon12r);
  345. if (lon12r == pi || lon12r == -pi)
  346. {
  347. return pi;
  348. }
  349. if BOOST_GEOMETRY_CONSTEXPR (LongSegment)
  350. {
  351. if (lat1r != lat2r) // not for segments parallel to equator
  352. {
  353. CT const cbet1 = cos(lat1r);
  354. CT const sbet1 = sin(lat1r);
  355. CT const cbet2 = cos(lat2r);
  356. CT const sbet2 = sin(lat2r);
  357. CT const omg12 = lon2r - lon1r;
  358. CT const comg12 = cos(omg12);
  359. CT const somg12 = sin(omg12);
  360. CT const cbet1_sbet2 = cbet1 * sbet2;
  361. CT const sbet1_cbet2 = sbet1 * cbet2;
  362. CT const alp1 = atan2(cbet1_sbet2 - sbet1_cbet2 * comg12, cbet2 * somg12);
  363. CT const alp2 = atan2(cbet1_sbet2 * comg12 - sbet1_cbet2, cbet1 * somg12);
  364. excess = alp2 - alp1;
  365. }
  366. else
  367. {
  368. excess = trapezoidal_formula(lat1r, lat2r, lon12r);
  369. }
  370. }
  371. else
  372. {
  373. excess = trapezoidal_formula(lat1r, lat2r, lon12r);
  374. }
  375. return excess;
  376. }
  377. struct return_type_ellipsoidal
  378. {
  379. return_type_ellipsoidal()
  380. : spherical_term(0),
  381. ellipsoidal_term(0)
  382. {}
  383. CT spherical_term;
  384. CT ellipsoidal_term;
  385. };
  386. /*
  387. Compute the ellipsoidal correction of a geodesic (or shperical) segment
  388. */
  389. template
  390. <
  391. template <typename, bool, bool, bool, bool, bool> class Inverse,
  392. typename PointOfSegment,
  393. typename SpheroidConst
  394. >
  395. static inline auto ellipsoidal(PointOfSegment const& p1,
  396. PointOfSegment const& p2,
  397. SpheroidConst const& spheroid_const)
  398. {
  399. return_type_ellipsoidal result;
  400. CT const lon1r = get_as_radian<0>(p1);
  401. CT const lat1r = get_as_radian<1>(p1);
  402. CT const lon2r = get_as_radian<0>(p2);
  403. CT const lat2r = get_as_radian<1>(p2);
  404. // Azimuth Approximation
  405. using inverse_type = Inverse<CT, true, true, true, false, false>;
  406. auto i_res = inverse_type::apply(lon1r, lat1r, lon2r, lat2r, spheroid_const.m_spheroid);
  407. CT const alp1 = i_res.azimuth;
  408. CT const alp2 = i_res.reverse_azimuth;
  409. // Constants
  410. CT const c0 = CT(0);
  411. CT const c1 = CT(1);
  412. CT const c2 = CT(2);
  413. CT const pi = math::pi<CT>();
  414. CT const half_pi = pi / c2;
  415. CT const ep = spheroid_const.m_ep;
  416. CT const one_minus_f = c1 - spheroid_const.m_f;
  417. // Basic trigonometric computations
  418. // the compiler could optimize here using sincos function
  419. // TODO: optimization: those quantities are already computed in inverse formula
  420. // at least in some inverse formulas, so do not compute them again here
  421. /*
  422. CT sin_bet1 = sin(lat1r);
  423. CT cos_bet1 = cos(lat1r);
  424. CT sin_bet2 = sin(lat2r);
  425. CT cos_bet2 = cos(lat2r);
  426. sin_bet1 *= one_minus_f;
  427. sin_bet2 *= one_minus_f;
  428. normalize(sin_bet1, cos_bet1);
  429. normalize(sin_bet2, cos_bet2);
  430. */
  431. CT const tan_bet1 = tan(lat1r) * one_minus_f;
  432. CT const tan_bet2 = tan(lat2r) * one_minus_f;
  433. CT const cos_bet1 = cos(atan(tan_bet1));
  434. CT const cos_bet2 = cos(atan(tan_bet2));
  435. CT const sin_bet1 = tan_bet1 * cos_bet1;
  436. CT const sin_bet2 = tan_bet2 * cos_bet2;
  437. CT const sin_alp1 = sin(alp1);
  438. CT const cos_alp1 = cos(alp1);
  439. CT const cos_alp2 = cos(alp2);
  440. CT const sin_alp0 = sin_alp1 * cos_bet1;
  441. // Spherical term computation
  442. CT excess;
  443. CT lon12r = lon2r - lon1r;
  444. math::normalize_longitude<radian, CT>(lon12r);
  445. // Comparing with "==" works with all test cases here, but could potential create numerical issues
  446. if (lon12r == pi || lon12r == -pi)
  447. {
  448. result.spherical_term = pi;
  449. }
  450. else
  451. {
  452. bool const meridian = lon12r == c0
  453. || lat1r == half_pi || lat1r == -half_pi
  454. || lat2r == half_pi || lat2r == -half_pi;
  455. if (!meridian && (i_res.distance)
  456. < mean_radius<CT>(spheroid_const.m_spheroid) / CT(638)) // short segment
  457. {
  458. excess = trapezoidal_formula(lat1r, lat2r, lon12r);
  459. }
  460. else
  461. {
  462. /* in some cases this formula gives more accurate results
  463. CT sin_omg12 = cos_omg1 * sin_omg2 - sin_omg1 * cos_omg2;
  464. normalize(sin_omg12, cos_omg12);
  465. CT cos_omg12p1 = CT(1) + cos_omg12;
  466. CT cos_bet1p1 = CT(1) + cos_bet1;
  467. CT cos_bet2p1 = CT(1) + cos_bet2;
  468. excess = CT(2) * atan2(sin_omg12 * (sin_bet1 * cos_bet2p1 + sin_bet2 * cos_bet1p1),
  469. cos_omg12p1 * (sin_bet1 * sin_bet2 + cos_bet1p1 * cos_bet2p1));
  470. */
  471. excess = alp2 - alp1;
  472. }
  473. result.spherical_term = excess;
  474. }
  475. // Ellipsoidal term computation (uses integral approximation)
  476. CT const cos_alp0 = math::sqrt(c1 - math::sqr(sin_alp0));
  477. //CT const cos_alp0 = hypot(cos_alp1, sin_alp1 * sin_bet1);
  478. CT cos_sig1 = cos_alp1 * cos_bet1;
  479. CT cos_sig2 = cos_alp2 * cos_bet2;
  480. CT sin_sig1 = sin_bet1;
  481. CT sin_sig2 = sin_bet2;
  482. normalize(sin_sig1, cos_sig1);
  483. normalize(sin_sig2, cos_sig2);
  484. CT coeffs[SeriesOrder + 1];
  485. if (ExpandEpsN) // expand by eps and n
  486. {
  487. CT const k2 = math::sqr(ep * cos_alp0);
  488. CT const sqrt_k2_plus_one = math::sqrt(c1 + k2);
  489. CT const eps = (sqrt_k2_plus_one - c1) / (sqrt_k2_plus_one + c1);
  490. // Generate and evaluate the polynomials on eps (i.e. var2 = eps)
  491. // to get the final series coefficients
  492. evaluate_coeffs_var2(eps, spheroid_const.m_coeffs_var, coeffs);
  493. }
  494. else
  495. { // expand by k2 and ep
  496. CT const k2 = math::sqr(ep * cos_alp0);
  497. CT const ep2 = math::sqr(ep);
  498. CT coeffs_var[((SeriesOrder+2)*(SeriesOrder+1))/2];
  499. // Generate and evaluate the polynomials on ep2
  500. evaluate_coeffs_ep(ep2, coeffs_var);
  501. // Generate and evaluate the polynomials on k2 (i.e. var2 = k2)
  502. evaluate_coeffs_var2(k2, coeffs_var, coeffs);
  503. }
  504. // Evaluate the trigonometric sum
  505. constexpr auto series_order_plus_one = SeriesOrder + 1;
  506. CT const I12 = clenshaw_sum(cos_sig2, coeffs, coeffs + series_order_plus_one)
  507. - clenshaw_sum(cos_sig1, coeffs, coeffs + series_order_plus_one);
  508. // The part of the ellipsodal correction that depends on
  509. // point coordinates
  510. result.ellipsoidal_term = cos_alp0 * sin_alp0 * I12;
  511. return result;
  512. }
  513. // Check whenever a segment crosses the prime meridian
  514. // First normalize to [0,360)
  515. template <typename PointOfSegment>
  516. static inline bool crosses_prime_meridian(PointOfSegment const& p1,
  517. PointOfSegment const& p2)
  518. {
  519. CT const pi = geometry::math::pi<CT>();
  520. CT const two_pi = geometry::math::two_pi<CT>();
  521. CT const lon1r = get_as_radian<0>(p1);
  522. CT const lon2r = get_as_radian<0>(p2);
  523. CT lon12 = lon2r - lon1r;
  524. math::normalize_longitude<radian, CT>(lon12);
  525. // Comparing with "==" works with all test cases here, but could potential create numerical issues
  526. if (lon12 == pi || lon12 == -pi)
  527. {
  528. return true;
  529. }
  530. CT const p1_lon = lon1r - ( std::floor( lon1r / two_pi ) * two_pi );
  531. CT const p2_lon = lon2r - ( std::floor( lon2r / two_pi ) * two_pi );
  532. CT const max_lon = (std::max)(p1_lon, p2_lon);
  533. CT const min_lon = (std::min)(p1_lon, p2_lon);
  534. return max_lon > pi && min_lon < pi && max_lon - min_lon > pi;
  535. }
  536. };
  537. }}} // namespace boost::geometry::formula
  538. #endif // BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP