File indexing completed on 2025-12-16 09:51:42
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #ifndef BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
0016 #define BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
0017
0018 #include <boost/geometry/core/assert.hpp>
0019 #include <boost/geometry/core/cs.hpp>
0020 #include <boost/geometry/util/math.hpp>
0021
0022
0023 namespace boost { namespace geometry
0024 {
0025
0026 namespace math
0027 {
0028
0029 #ifndef DOXYGEN_NO_DETAIL
0030 namespace detail
0031 {
0032
0033
0034 template <typename CoordinateType, typename Units, bool IsEquatorial = true>
0035 struct constants_on_spheroid
0036 {
0037 static inline CoordinateType period()
0038 {
0039 return math::two_pi<CoordinateType>();
0040 }
0041
0042 static inline CoordinateType half_period()
0043 {
0044 return math::pi<CoordinateType>();
0045 }
0046
0047 static inline CoordinateType quarter_period()
0048 {
0049 static CoordinateType const
0050 pi_half = math::pi<CoordinateType>() / CoordinateType(2);
0051 return pi_half;
0052 }
0053
0054 static inline CoordinateType min_longitude()
0055 {
0056 static CoordinateType const minus_pi = -math::pi<CoordinateType>();
0057 return minus_pi;
0058 }
0059
0060 static inline CoordinateType max_longitude()
0061 {
0062 return math::pi<CoordinateType>();
0063 }
0064
0065 static inline CoordinateType min_latitude()
0066 {
0067 static CoordinateType const minus_half_pi
0068 = -math::half_pi<CoordinateType>();
0069 return minus_half_pi;
0070 }
0071
0072 static inline CoordinateType max_latitude()
0073 {
0074 return math::half_pi<CoordinateType>();
0075 }
0076 };
0077
0078 template <typename CoordinateType>
0079 struct constants_on_spheroid<CoordinateType, radian, false>
0080 : constants_on_spheroid<CoordinateType, radian, true>
0081 {
0082 static inline CoordinateType min_latitude()
0083 {
0084 return CoordinateType(0);
0085 }
0086
0087 static inline CoordinateType max_latitude()
0088 {
0089 return math::pi<CoordinateType>();
0090 }
0091 };
0092
0093 template <typename CoordinateType>
0094 struct constants_on_spheroid<CoordinateType, degree, true>
0095 {
0096 static inline CoordinateType period()
0097 {
0098 return CoordinateType(360.0);
0099 }
0100
0101 static inline CoordinateType half_period()
0102 {
0103 return CoordinateType(180.0);
0104 }
0105
0106 static inline CoordinateType quarter_period()
0107 {
0108 return CoordinateType(90.0);
0109 }
0110
0111 static inline CoordinateType min_longitude()
0112 {
0113 return CoordinateType(-180.0);
0114 }
0115
0116 static inline CoordinateType max_longitude()
0117 {
0118 return CoordinateType(180.0);
0119 }
0120
0121 static inline CoordinateType min_latitude()
0122 {
0123 return CoordinateType(-90.0);
0124 }
0125
0126 static inline CoordinateType max_latitude()
0127 {
0128 return CoordinateType(90.0);
0129 }
0130 };
0131
0132 template <typename CoordinateType>
0133 struct constants_on_spheroid<CoordinateType, degree, false>
0134 : constants_on_spheroid<CoordinateType, degree, true>
0135 {
0136 static inline CoordinateType min_latitude()
0137 {
0138 return CoordinateType(0);
0139 }
0140
0141 static inline CoordinateType max_latitude()
0142 {
0143 return CoordinateType(180.0);
0144 }
0145 };
0146
0147
0148 }
0149 #endif
0150
0151
0152 template <typename Units, typename CoordinateType>
0153 inline CoordinateType latitude_convert_ep(CoordinateType const& lat)
0154 {
0155 using constants = math::detail::constants_on_spheroid<CoordinateType, Units>;
0156
0157 return constants::quarter_period() - lat;
0158 }
0159
0160
0161 template <typename Units, bool IsEquatorial, typename T>
0162 static bool is_latitude_pole(T const& lat)
0163 {
0164 using constants = math::detail::constants_on_spheroid<T, Units>;
0165
0166 return math::equals(math::abs(IsEquatorial
0167 ? lat
0168 : math::latitude_convert_ep<Units>(lat)),
0169 constants::quarter_period());
0170 }
0171
0172
0173 template <typename Units, typename T>
0174 static bool is_longitude_antimeridian(T const& lon)
0175 {
0176 using constants = math::detail::constants_on_spheroid<T, Units>;
0177
0178 return math::equals(math::abs(lon), constants::half_period());
0179 }
0180
0181
0182 #ifndef DOXYGEN_NO_DETAIL
0183 namespace detail
0184 {
0185
0186
0187 template <typename Units, bool IsEquatorial>
0188 struct latitude_convert_if_polar
0189 {
0190 template <typename T>
0191 static inline void apply(T & ) {}
0192 };
0193
0194 template <typename Units>
0195 struct latitude_convert_if_polar<Units, false>
0196 {
0197 template <typename T>
0198 static inline void apply(T & lat)
0199 {
0200 lat = latitude_convert_ep<Units>(lat);
0201 }
0202 };
0203
0204
0205 template <typename Units, typename CoordinateType, bool IsEquatorial = true>
0206 class normalize_spheroidal_coordinates
0207 {
0208 using constants = constants_on_spheroid<CoordinateType, Units>;
0209
0210 protected:
0211 static inline CoordinateType normalize_up(CoordinateType const& value)
0212 {
0213 return
0214 math::mod(value + constants::half_period(), constants::period())
0215 - constants::half_period();
0216 }
0217
0218 static inline CoordinateType normalize_down(CoordinateType const& value)
0219 {
0220 return
0221 math::mod(value - constants::half_period(), constants::period())
0222 + constants::half_period();
0223 }
0224
0225 public:
0226 static inline void apply(CoordinateType& longitude, bool exact = true)
0227 {
0228
0229 CoordinateType const epsilon = std::numeric_limits<float>::epsilon();
0230 static constexpr bool is_integer = std::numeric_limits<CoordinateType>::is_integer;
0231
0232 if (exact || is_integer ? math::equals(math::abs(longitude), constants::half_period())
0233 : math::abs(math::abs(longitude) - constants::half_period()) <= epsilon)
0234 {
0235 longitude = constants::half_period();
0236 }
0237 else if (longitude > constants::half_period())
0238 {
0239 longitude = normalize_up(longitude);
0240 if (exact || is_integer ? math::equals(longitude, -constants::half_period())
0241 : math::abs(longitude + constants::half_period()) <= epsilon)
0242 {
0243 longitude = constants::half_period();
0244 }
0245 }
0246 else if (longitude < -constants::half_period())
0247 {
0248 longitude = normalize_down(longitude);
0249 }
0250 }
0251
0252 static inline void apply(CoordinateType& longitude,
0253 CoordinateType& latitude,
0254 bool normalize_poles = true,
0255 bool exact = true)
0256 {
0257 latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
0258
0259 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
0260
0261 if (math::larger(latitude, constants::half_period()))
0262 {
0263 latitude = normalize_up(latitude);
0264 }
0265 else if (math::smaller(latitude, -constants::half_period()))
0266 {
0267 latitude = normalize_down(latitude);
0268 }
0269
0270
0271 if (latitude < constants::min_latitude())
0272 {
0273 latitude = -constants::half_period() - latitude;
0274 longitude -= constants::half_period();
0275 }
0276 else if (latitude > constants::max_latitude())
0277 {
0278 latitude = constants::half_period() - latitude;
0279 longitude -= constants::half_period();
0280 }
0281 #endif
0282
0283
0284 apply(longitude, exact);
0285
0286
0287 if (normalize_poles)
0288 {
0289 if (math::equals(math::abs(latitude), constants::max_latitude()))
0290 {
0291
0292
0293 longitude = CoordinateType(0);
0294 }
0295 }
0296
0297 latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
0298
0299 #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
0300 BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude));
0301 BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude()));
0302 #endif
0303
0304 BOOST_GEOMETRY_ASSERT(! math::larger_or_equals(constants::min_longitude(), longitude));
0305 BOOST_GEOMETRY_ASSERT(! math::larger(longitude, constants::max_longitude()));
0306 }
0307 };
0308
0309
0310 template <typename Units, typename CoordinateType>
0311 inline void normalize_angle_loop(CoordinateType& angle)
0312 {
0313 using constants = constants_on_spheroid<CoordinateType, Units>;
0314 CoordinateType const pi = constants::half_period();
0315 CoordinateType const two_pi = constants::period();
0316 while (angle > pi)
0317 angle -= two_pi;
0318 while (angle <= -pi)
0319 angle += two_pi;
0320 }
0321
0322 template <typename Units, typename CoordinateType>
0323 inline void normalize_angle_cond(CoordinateType& angle)
0324 {
0325 using constants = constants_on_spheroid<CoordinateType, Units>;
0326 CoordinateType const pi = constants::half_period();
0327 CoordinateType const two_pi = constants::period();
0328 if (angle > pi)
0329 angle -= two_pi;
0330 else if (angle <= -pi)
0331 angle += two_pi;
0332 }
0333
0334
0335 }
0336 #endif
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347 template <typename Units, typename CoordinateType>
0348 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
0349 CoordinateType& latitude,
0350 bool exact = true)
0351 {
0352 detail::normalize_spheroidal_coordinates
0353 <
0354 Units, CoordinateType
0355 >::apply(longitude, latitude, true, exact);
0356 }
0357
0358 template <typename Units, bool IsEquatorial, typename CoordinateType>
0359 inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
0360 CoordinateType& latitude,
0361 bool exact = true)
0362 {
0363 detail::normalize_spheroidal_coordinates
0364 <
0365 Units, CoordinateType, IsEquatorial
0366 >::apply(longitude, latitude, true, exact);
0367 }
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378 template <typename Units, typename CoordinateType>
0379 inline void normalize_longitude(CoordinateType& longitude, bool exact = true)
0380 {
0381 detail::normalize_spheroidal_coordinates
0382 <
0383 Units, CoordinateType
0384 >::apply(longitude, exact);
0385 }
0386
0387
0388
0389
0390
0391
0392
0393
0394
0395 template <typename Units, typename CoordinateType>
0396 inline void normalize_azimuth(CoordinateType& angle)
0397 {
0398 math::normalize_longitude<Units, CoordinateType>(angle, true);
0399 }
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409 template<typename ValueType>
0410 inline void normalize_unit_vector(ValueType& x, ValueType& y)
0411 {
0412 ValueType h = boost::math::hypot(x, y);
0413
0414 BOOST_GEOMETRY_ASSERT(h > 0);
0415
0416 x /= h; y /= h;
0417 }
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428 template <typename Units, typename CoordinateType>
0429 inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
0430 CoordinateType const& longitude2)
0431 {
0432 CoordinateType diff = longitude2 - longitude1;
0433 math::normalize_longitude<Units, CoordinateType>(diff, true);
0434 return diff;
0435 }
0436
0437
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447 template <typename Units, typename CoordinateType>
0448 inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
0449 CoordinateType const& longitude2)
0450 {
0451 using constants = math::detail::constants_on_spheroid<CoordinateType, Units>;
0452
0453 CoordinateType const c0 = 0;
0454 CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
0455 if (diff < c0)
0456 {
0457 diff += constants::period();
0458 }
0459 return diff;
0460 }
0461
0462
0463
0464
0465
0466
0467
0468
0469
0470 template <typename Units, typename CoordinateType>
0471 inline CoordinateType longitude_difference(CoordinateType const& longitude1,
0472 CoordinateType const& longitude2)
0473 {
0474 return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
0475 }
0476
0477 template <typename Units, typename CoordinateType>
0478 inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
0479 CoordinateType const& longitude_a2,
0480 CoordinateType const& longitude_b)
0481 {
0482 CoordinateType const c0 = 0;
0483 CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
0484 CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
0485 if (dist_a12 < c0)
0486 {
0487 dist_a12 = -dist_a12;
0488 dist_a1b = -dist_a1b;
0489 }
0490
0491 return dist_a1b < c0 ? dist_a1b
0492 : dist_a1b > dist_a12 ? dist_a1b - dist_a12
0493 : c0;
0494 }
0495
0496
0497 }
0498
0499
0500 }}
0501
0502 #endif