File indexing completed on 2025-06-30 08:14:18
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #ifndef BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP
0014 #define BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP
0015
0016 #include <boost/geometry/core/radian_access.hpp>
0017 #include <boost/geometry/formulas/flattening.hpp>
0018 #include <boost/geometry/formulas/mean_radius.hpp>
0019 #include <boost/geometry/formulas/karney_inverse.hpp>
0020 #include <boost/geometry/util/constexpr.hpp>
0021 #include <boost/geometry/util/math.hpp>
0022 #include <boost/math/special_functions/hypot.hpp>
0023
0024 namespace boost { namespace geometry { namespace formula
0025 {
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 template
0039 <
0040 typename CT,
0041 std::size_t SeriesOrder = 2,
0042 bool ExpandEpsN = true
0043 >
0044 class area_formulas
0045 {
0046
0047 public:
0048
0049
0050
0051
0052
0053
0054 template <typename NT, typename IteratorType>
0055 static inline NT horner_evaluate(NT const& x,
0056 IteratorType begin,
0057 IteratorType end)
0058 {
0059 NT result(0);
0060 IteratorType it = end;
0061 do
0062 {
0063 result = result * x + *--it;
0064 }
0065 while (it != begin);
0066 return result;
0067 }
0068
0069
0070
0071
0072
0073 template <typename NT, typename IteratorType>
0074 static inline NT clenshaw_sum(NT const& cosx,
0075 IteratorType begin,
0076 IteratorType end)
0077 {
0078 IteratorType it = end;
0079 bool odd = true;
0080 CT b_k, b_k1(0), b_k2(0);
0081 do
0082 {
0083 CT c_k = odd ? *--it : NT(0);
0084 b_k = c_k + NT(2) * cosx * b_k1 - b_k2;
0085 b_k2 = b_k1;
0086 b_k1 = b_k;
0087 odd = !odd;
0088 }
0089 while (it != begin);
0090
0091 return *begin + b_k1 * cosx - b_k2;
0092 }
0093
0094 template<typename T>
0095 static inline void normalize(T& x, T& y)
0096 {
0097 T h = boost::math::hypot(x, y);
0098 x /= h;
0099 y /= h;
0100 }
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 static inline void evaluate_coeffs_n(CT const& n, CT coeffs_n[])
0177 {
0178 switch (SeriesOrder) {
0179 case 0:
0180 coeffs_n[0] = CT(2)/CT(3);
0181 break;
0182 case 1:
0183 coeffs_n[0] = (CT(10)-CT(4)*n)/CT(15);
0184 coeffs_n[1] = -CT(1)/CT(5);
0185 coeffs_n[2] = CT(1)/CT(45);
0186 break;
0187 case 2:
0188 coeffs_n[0] = (n*(CT(8)*n-CT(28))+CT(70))/CT(105);
0189 coeffs_n[1] = (CT(16)*n-CT(7))/CT(35);
0190 coeffs_n[2] = -CT(2)/CT(105);
0191 coeffs_n[3] = (CT(7)-CT(16)*n)/CT(315);
0192 coeffs_n[4] = -CT(2)/CT(105);
0193 coeffs_n[5] = CT(4)/CT(525);
0194 break;
0195 case 3:
0196 coeffs_n[0] = (n*(n*(CT(4)*n+CT(24))-CT(84))+CT(210))/CT(315);
0197 coeffs_n[1] = ((CT(48)-CT(32)*n)*n-CT(21))/CT(105);
0198 coeffs_n[2] = (-CT(32)*n-CT(6))/CT(315);
0199 coeffs_n[3] = CT(11)/CT(315);
0200 coeffs_n[4] = (n*(CT(32)*n-CT(48))+CT(21))/CT(945);
0201 coeffs_n[5] = (CT(64)*n-CT(18))/CT(945);
0202 coeffs_n[6] = -CT(1)/CT(105);
0203 coeffs_n[7] = (CT(12)-CT(32)*n)/CT(1575);
0204 coeffs_n[8] = -CT(8)/CT(1575);
0205 coeffs_n[9] = CT(8)/CT(2205);
0206 break;
0207 case 4:
0208 coeffs_n[0] = (n*(n*(n*(CT(16)*n+CT(44))+CT(264))-CT(924))+CT(2310))/CT(3465);
0209 coeffs_n[1] = (n*(n*(CT(48)*n-CT(352))+CT(528))-CT(231))/CT(1155);
0210 coeffs_n[2] = (n*(CT(1088)*n-CT(352))-CT(66))/CT(3465);
0211 coeffs_n[3] = (CT(121)-CT(368)*n)/CT(3465);
0212 coeffs_n[4] = CT(4)/CT(1155);
0213 coeffs_n[5] = (n*((CT(352)-CT(48)*n)*n-CT(528))+CT(231))/CT(10395);
0214 coeffs_n[6] = ((CT(704)-CT(896)*n)*n-CT(198))/CT(10395);
0215 coeffs_n[7] = (CT(80)*n-CT(99))/CT(10395);
0216 coeffs_n[8] = CT(4)/CT(1155);
0217 coeffs_n[9] = (n*(CT(320)*n-CT(352))+CT(132))/CT(17325);
0218 coeffs_n[10] = (CT(384)*n-CT(88))/CT(17325);
0219 coeffs_n[11] = -CT(8)/CT(1925);
0220 coeffs_n[12] = (CT(88)-CT(256)*n)/CT(24255);
0221 coeffs_n[13] = -CT(16)/CT(8085);
0222 coeffs_n[14] = CT(64)/CT(31185);
0223 break;
0224 case 5:
0225 coeffs_n[0] = (n*(n*(n*(n*(CT(100)*n+CT(208))+CT(572))+CT(3432))-CT(12012))+CT(30030))
0226 /CT(45045);
0227 coeffs_n[1] = (n*(n*(n*(CT(64)*n+CT(624))-CT(4576))+CT(6864))-CT(3003))/CT(15015);
0228 coeffs_n[2] = (n*((CT(14144)-CT(10656)*n)*n-CT(4576))-CT(858))/CT(45045);
0229 coeffs_n[3] = ((-CT(224)*n-CT(4784))*n+CT(1573))/CT(45045);
0230 coeffs_n[4] = (CT(1088)*n+CT(156))/CT(45045);
0231 coeffs_n[5] = CT(97)/CT(15015);
0232 coeffs_n[6] = (n*(n*((-CT(64)*n-CT(624))*n+CT(4576))-CT(6864))+CT(3003))/CT(135135);
0233 coeffs_n[7] = (n*(n*(CT(5952)*n-CT(11648))+CT(9152))-CT(2574))/CT(135135);
0234 coeffs_n[8] = (n*(CT(5792)*n+CT(1040))-CT(1287))/CT(135135);
0235 coeffs_n[9] = (CT(468)-CT(2944)*n)/CT(135135);
0236 coeffs_n[10] = CT(1)/CT(9009);
0237 coeffs_n[11] = (n*((CT(4160)-CT(1440)*n)*n-CT(4576))+CT(1716))/CT(225225);
0238 coeffs_n[12] = ((CT(4992)-CT(8448)*n)*n-CT(1144))/CT(225225);
0239 coeffs_n[13] = (CT(1856)*n-CT(936))/CT(225225);
0240 coeffs_n[14] = CT(8)/CT(10725);
0241 coeffs_n[15] = (n*(CT(3584)*n-CT(3328))+CT(1144))/CT(315315);
0242 coeffs_n[16] = (CT(1024)*n-CT(208))/CT(105105);
0243 coeffs_n[17] = -CT(136)/CT(63063);
0244 coeffs_n[18] = (CT(832)-CT(2560)*n)/CT(405405);
0245 coeffs_n[19] = -CT(128)/CT(135135);
0246 coeffs_n[20] = CT(128)/CT(99099);
0247 break;
0248 }
0249 }
0250
0251
0252
0253
0254 static inline void evaluate_coeffs_ep(CT const& ep, CT coeffs_n[])
0255 {
0256 switch (SeriesOrder) {
0257 case 0:
0258 coeffs_n[0] = CT(2)/CT(3);
0259 break;
0260 case 1:
0261 coeffs_n[0] = (CT(10)-ep)/CT(15);
0262 coeffs_n[1] = -CT(1)/CT(20);
0263 coeffs_n[2] = CT(1)/CT(180);
0264 break;
0265 case 2:
0266 coeffs_n[0] = (ep*(CT(4)*ep-CT(7))+CT(70))/CT(105);
0267 coeffs_n[1] = (CT(4)*ep-CT(7))/CT(140);
0268 coeffs_n[2] = CT(1)/CT(42);
0269 coeffs_n[3] = (CT(7)-CT(4)*ep)/CT(1260);
0270 coeffs_n[4] = -CT(1)/CT(252);
0271 coeffs_n[5] = CT(1)/CT(2100);
0272 break;
0273 case 3:
0274 coeffs_n[0] = (ep*((CT(12)-CT(8)*ep)*ep-CT(21))+CT(210))/CT(315);
0275 coeffs_n[1] = ((CT(12)-CT(8)*ep)*ep-CT(21))/CT(420);
0276 coeffs_n[2] = (CT(3)-CT(2)*ep)/CT(126);
0277 coeffs_n[3] = -CT(1)/CT(72);
0278 coeffs_n[4] = (ep*(CT(8)*ep-CT(12))+CT(21))/CT(3780);
0279 coeffs_n[5] = (CT(2)*ep-CT(3))/CT(756);
0280 coeffs_n[6] = CT(1)/CT(360);
0281 coeffs_n[7] = (CT(3)-CT(2)*ep)/CT(6300);
0282 coeffs_n[8] = -CT(1)/CT(1800);
0283 coeffs_n[9] = CT(1)/CT(17640);
0284 break;
0285 case 4:
0286 coeffs_n[0] = (ep*(ep*(ep*(CT(64)*ep-CT(88))+CT(132))-CT(231))+CT(2310))/CT(3465);
0287 coeffs_n[1] = (ep*(ep*(CT(64)*ep-CT(88))+CT(132))-CT(231))/CT(4620);
0288 coeffs_n[2] = (ep*(CT(16)*ep-CT(22))+CT(33))/CT(1386);
0289 coeffs_n[3] = (CT(8)*ep-CT(11))/CT(792);
0290 coeffs_n[4] = CT(1)/CT(110);
0291 coeffs_n[5] = (ep*((CT(88)-CT(64)*ep)*ep-CT(132))+CT(231))/CT(41580);
0292 coeffs_n[6] = ((CT(22)-CT(16)*ep)*ep-CT(33))/CT(8316);
0293 coeffs_n[7] = (CT(11)-CT(8)*ep)/CT(3960);
0294 coeffs_n[8] = -CT(1)/CT(495);
0295 coeffs_n[9] = (ep*(CT(16)*ep-CT(22))+CT(33))/CT(69300);
0296 coeffs_n[10] = (CT(8)*ep-CT(11))/CT(19800);
0297 coeffs_n[11] = CT(1)/CT(1925);
0298 coeffs_n[12] = (CT(11)-CT(8)*ep)/CT(194040);
0299 coeffs_n[13] = -CT(1)/CT(10780);
0300 coeffs_n[14] = CT(1)/CT(124740);
0301 break;
0302 case 5:
0303 coeffs_n[0] = (ep*(ep*(ep*((CT(832)-CT(640)*ep)*ep-CT(1144))+CT(1716))-CT(3003))+CT(30030))/CT(45045);
0304 coeffs_n[1] = (ep*(ep*((CT(832)-CT(640)*ep)*ep-CT(1144))+CT(1716))-CT(3003))/CT(60060);
0305 coeffs_n[2] = (ep*((CT(208)-CT(160)*ep)*ep-CT(286))+CT(429))/CT(18018);
0306 coeffs_n[3] = ((CT(104)-CT(80)*ep)*ep-CT(143))/CT(10296);
0307 coeffs_n[4] = (CT(13)-CT(10)*ep)/CT(1430);
0308 coeffs_n[5] = -CT(1)/CT(156);
0309 coeffs_n[6] = (ep*(ep*(ep*(CT(640)*ep-CT(832))+CT(1144))-CT(1716))+CT(3003))/CT(540540);
0310 coeffs_n[7] = (ep*(ep*(CT(160)*ep-CT(208))+CT(286))-CT(429))/CT(108108);
0311 coeffs_n[8] = (ep*(CT(80)*ep-CT(104))+CT(143))/CT(51480);
0312 coeffs_n[9] = (CT(10)*ep-CT(13))/CT(6435);
0313 coeffs_n[10] = CT(5)/CT(3276);
0314 coeffs_n[11] = (ep*((CT(208)-CT(160)*ep)*ep-CT(286))+CT(429))/CT(900900);
0315 coeffs_n[12] = ((CT(104)-CT(80)*ep)*ep-CT(143))/CT(257400);
0316 coeffs_n[13] = (CT(13)-CT(10)*ep)/CT(25025);
0317 coeffs_n[14] = -CT(1)/CT(2184);
0318 coeffs_n[15] = (ep*(CT(80)*ep-CT(104))+CT(143))/CT(2522520);
0319 coeffs_n[16] = (CT(10)*ep-CT(13))/CT(140140);
0320 coeffs_n[17] = CT(5)/CT(45864);
0321 coeffs_n[18] = (CT(13)-CT(10)*ep)/CT(1621620);
0322 coeffs_n[19] = -CT(1)/CT(58968);
0323 coeffs_n[20] = CT(1)/CT(792792);
0324 break;
0325 }
0326 }
0327
0328
0329
0330
0331
0332 template <typename CoeffsType>
0333 static inline void evaluate_coeffs_var2(CT const& var2,
0334 CoeffsType const coeffs1[],
0335 CT coeffs2[])
0336 {
0337 std::size_t begin(0), end(0);
0338 for(std::size_t i = 0; i <= SeriesOrder; i++)
0339 {
0340 end = begin + SeriesOrder + 1 - i;
0341 coeffs2[i] = ((i==0) ? CT(1) : math::pow(var2, int(i)))
0342 * horner_evaluate(var2, coeffs1 + begin, coeffs1 + end);
0343 begin = end;
0344 }
0345 }
0346
0347 static inline CT trapezoidal_formula(CT lat1r, CT lat2r, CT lon21r)
0348 {
0349 CT const c1 = CT(1);
0350 CT const c2 = CT(2);
0351 CT const tan_lat1 = tan(lat1r / c2);
0352 CT const tan_lat2 = tan(lat2r / c2);
0353
0354 return c2 * atan(((tan_lat1 + tan_lat2) / (c1 + tan_lat1 * tan_lat2))* tan(lon21r / c2));
0355 }
0356
0357
0358
0359
0360 template
0361 <
0362 bool LongSegment,
0363 typename PointOfSegment
0364 >
0365 static inline CT spherical(PointOfSegment const& p1,
0366 PointOfSegment const& p2)
0367 {
0368 CT const pi = math::pi<CT>();
0369
0370 CT excess;
0371
0372 CT const lon1r = get_as_radian<0>(p1);
0373 CT const lat1r = get_as_radian<1>(p1);
0374 CT const lon2r = get_as_radian<0>(p2);
0375 CT const lat2r = get_as_radian<1>(p2);
0376
0377 CT lon12r = lon2r - lon1r;
0378 math::normalize_longitude<radian, CT>(lon12r);
0379
0380 if (lon12r == pi || lon12r == -pi)
0381 {
0382 return pi;
0383 }
0384
0385 if BOOST_GEOMETRY_CONSTEXPR (LongSegment)
0386 {
0387 if (lat1r != lat2r)
0388 {
0389 CT const cbet1 = cos(lat1r);
0390 CT const sbet1 = sin(lat1r);
0391 CT const cbet2 = cos(lat2r);
0392 CT const sbet2 = sin(lat2r);
0393
0394 CT const omg12 = lon2r - lon1r;
0395 CT const comg12 = cos(omg12);
0396 CT const somg12 = sin(omg12);
0397
0398 CT const cbet1_sbet2 = cbet1 * sbet2;
0399 CT const sbet1_cbet2 = sbet1 * cbet2;
0400 CT const alp1 = atan2(cbet1_sbet2 - sbet1_cbet2 * comg12, cbet2 * somg12);
0401 CT const alp2 = atan2(cbet1_sbet2 * comg12 - sbet1_cbet2, cbet1 * somg12);
0402
0403 excess = alp2 - alp1;
0404 }
0405 else
0406 {
0407 excess = trapezoidal_formula(lat1r, lat2r, lon12r);
0408 }
0409 }
0410 else
0411 {
0412 excess = trapezoidal_formula(lat1r, lat2r, lon12r);
0413 }
0414
0415 return excess;
0416 }
0417
0418 struct return_type_ellipsoidal
0419 {
0420 return_type_ellipsoidal()
0421 : spherical_term(0),
0422 ellipsoidal_term(0)
0423 {}
0424
0425 CT spherical_term;
0426 CT ellipsoidal_term;
0427 };
0428
0429
0430
0431
0432 template
0433 <
0434 template <typename, bool, bool, bool, bool, bool> class Inverse,
0435 typename PointOfSegment,
0436 typename SpheroidConst
0437 >
0438 static inline auto ellipsoidal(PointOfSegment const& p1,
0439 PointOfSegment const& p2,
0440 SpheroidConst const& spheroid_const)
0441 {
0442 return_type_ellipsoidal result;
0443
0444 CT const lon1r = get_as_radian<0>(p1);
0445 CT const lat1r = get_as_radian<1>(p1);
0446 CT const lon2r = get_as_radian<0>(p2);
0447 CT const lat2r = get_as_radian<1>(p2);
0448
0449
0450
0451 using inverse_type = Inverse<CT, true, true, true, false, false>;
0452 auto i_res = inverse_type::apply(lon1r, lat1r, lon2r, lat2r, spheroid_const.m_spheroid);
0453
0454 CT const alp1 = i_res.azimuth;
0455 CT const alp2 = i_res.reverse_azimuth;
0456
0457
0458
0459 CT const c0 = CT(0);
0460 CT const c1 = CT(1);
0461 CT const c2 = CT(2);
0462 CT const pi = math::pi<CT>();
0463 CT const half_pi = pi / c2;
0464 CT const ep = spheroid_const.m_ep;
0465 CT const one_minus_f = c1 - spheroid_const.m_f;
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482
0483 CT const tan_bet1 = tan(lat1r) * one_minus_f;
0484 CT const tan_bet2 = tan(lat2r) * one_minus_f;
0485 CT const cos_bet1 = cos(atan(tan_bet1));
0486 CT const cos_bet2 = cos(atan(tan_bet2));
0487 CT const sin_bet1 = tan_bet1 * cos_bet1;
0488 CT const sin_bet2 = tan_bet2 * cos_bet2;
0489
0490 CT const sin_alp1 = sin(alp1);
0491 CT const cos_alp1 = cos(alp1);
0492 CT const cos_alp2 = cos(alp2);
0493 CT const sin_alp0 = sin_alp1 * cos_bet1;
0494
0495
0496
0497 CT excess;
0498
0499 CT lon12r = lon2r - lon1r;
0500 math::normalize_longitude<radian, CT>(lon12r);
0501
0502
0503 if (lon12r == pi || lon12r == -pi)
0504 {
0505 result.spherical_term = pi;
0506 }
0507 else
0508 {
0509 bool const meridian = lon12r == c0
0510 || lat1r == half_pi || lat1r == -half_pi
0511 || lat2r == half_pi || lat2r == -half_pi;
0512
0513 if (!meridian && (i_res.distance)
0514 < mean_radius<CT>(spheroid_const.m_spheroid) / CT(638))
0515 {
0516 excess = trapezoidal_formula(lat1r, lat2r, lon12r);
0517 }
0518 else
0519 {
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531 excess = alp2 - alp1;
0532 }
0533
0534 result.spherical_term = excess;
0535 }
0536
0537
0538
0539 CT const cos_alp0 = math::sqrt(c1 - math::sqr(sin_alp0));
0540
0541 CT cos_sig1 = cos_alp1 * cos_bet1;
0542 CT cos_sig2 = cos_alp2 * cos_bet2;
0543 CT sin_sig1 = sin_bet1;
0544 CT sin_sig2 = sin_bet2;
0545
0546 normalize(sin_sig1, cos_sig1);
0547 normalize(sin_sig2, cos_sig2);
0548
0549 CT coeffs[SeriesOrder + 1];
0550
0551 if (ExpandEpsN)
0552 {
0553 CT const k2 = math::sqr(ep * cos_alp0);
0554 CT const sqrt_k2_plus_one = math::sqrt(c1 + k2);
0555 CT const eps = (sqrt_k2_plus_one - c1) / (sqrt_k2_plus_one + c1);
0556
0557
0558
0559 evaluate_coeffs_var2(eps, spheroid_const.m_coeffs_var, coeffs);
0560 }
0561 else
0562 {
0563
0564 CT const k2 = math::sqr(ep * cos_alp0);
0565 CT const ep2 = math::sqr(ep);
0566
0567 CT coeffs_var[((SeriesOrder+2)*(SeriesOrder+1))/2];
0568
0569
0570 evaluate_coeffs_ep(ep2, coeffs_var);
0571
0572
0573 evaluate_coeffs_var2(k2, coeffs_var, coeffs);
0574 }
0575
0576
0577 constexpr auto series_order_plus_one = SeriesOrder + 1;
0578 CT const I12 = clenshaw_sum(cos_sig2, coeffs, coeffs + series_order_plus_one)
0579 - clenshaw_sum(cos_sig1, coeffs, coeffs + series_order_plus_one);
0580
0581
0582
0583 result.ellipsoidal_term = cos_alp0 * sin_alp0 * I12;
0584
0585 return result;
0586 }
0587
0588
0589
0590 template <typename PointOfSegment>
0591 static inline bool crosses_prime_meridian(PointOfSegment const& p1,
0592 PointOfSegment const& p2)
0593 {
0594 CT const pi = geometry::math::pi<CT>();
0595 CT const two_pi = geometry::math::two_pi<CT>();
0596
0597 CT const lon1r = get_as_radian<0>(p1);
0598 CT const lon2r = get_as_radian<0>(p2);
0599
0600 CT lon12 = lon2r - lon1r;
0601 math::normalize_longitude<radian, CT>(lon12);
0602
0603
0604 if (lon12 == pi || lon12 == -pi)
0605 {
0606 return true;
0607 }
0608
0609 CT const p1_lon = lon1r - ( std::floor( lon1r / two_pi ) * two_pi );
0610 CT const p2_lon = lon2r - ( std::floor( lon2r / two_pi ) * two_pi );
0611
0612 CT const max_lon = (std::max)(p1_lon, p2_lon);
0613 CT const min_lon = (std::min)(p1_lon, p2_lon);
0614
0615 return max_lon > pi && min_lon < pi && max_lon - min_lon > pi;
0616 }
0617
0618 };
0619
0620 }}}
0621
0622
0623 #endif