normalize_spheroidal_coordinates.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2015-2022, Oracle and/or its affiliates.
  4. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program
  7. // Licensed under the Boost Software License version 1.0.
  8. // http://www.boost.org/users/license.html
  9. #ifndef BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
  10. #define BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP
  11. #include <boost/geometry/core/assert.hpp>
  12. #include <boost/geometry/core/cs.hpp>
  13. #include <boost/geometry/util/math.hpp>
  14. namespace boost { namespace geometry
  15. {
  16. namespace math
  17. {
  18. #ifndef DOXYGEN_NO_DETAIL
  19. namespace detail
  20. {
  21. // CoordinateType, radian, true
  22. template <typename CoordinateType, typename Units, bool IsEquatorial = true>
  23. struct constants_on_spheroid
  24. {
  25. static inline CoordinateType period()
  26. {
  27. return math::two_pi<CoordinateType>();
  28. }
  29. static inline CoordinateType half_period()
  30. {
  31. return math::pi<CoordinateType>();
  32. }
  33. static inline CoordinateType quarter_period()
  34. {
  35. static CoordinateType const
  36. pi_half = math::pi<CoordinateType>() / CoordinateType(2);
  37. return pi_half;
  38. }
  39. static inline CoordinateType min_longitude()
  40. {
  41. static CoordinateType const minus_pi = -math::pi<CoordinateType>();
  42. return minus_pi;
  43. }
  44. static inline CoordinateType max_longitude()
  45. {
  46. return math::pi<CoordinateType>();
  47. }
  48. static inline CoordinateType min_latitude()
  49. {
  50. static CoordinateType const minus_half_pi
  51. = -math::half_pi<CoordinateType>();
  52. return minus_half_pi;
  53. }
  54. static inline CoordinateType max_latitude()
  55. {
  56. return math::half_pi<CoordinateType>();
  57. }
  58. };
  59. template <typename CoordinateType>
  60. struct constants_on_spheroid<CoordinateType, radian, false>
  61. : constants_on_spheroid<CoordinateType, radian, true>
  62. {
  63. static inline CoordinateType min_latitude()
  64. {
  65. return CoordinateType(0);
  66. }
  67. static inline CoordinateType max_latitude()
  68. {
  69. return math::pi<CoordinateType>();
  70. }
  71. };
  72. template <typename CoordinateType>
  73. struct constants_on_spheroid<CoordinateType, degree, true>
  74. {
  75. static inline CoordinateType period()
  76. {
  77. return CoordinateType(360.0);
  78. }
  79. static inline CoordinateType half_period()
  80. {
  81. return CoordinateType(180.0);
  82. }
  83. static inline CoordinateType quarter_period()
  84. {
  85. return CoordinateType(90.0);
  86. }
  87. static inline CoordinateType min_longitude()
  88. {
  89. return CoordinateType(-180.0);
  90. }
  91. static inline CoordinateType max_longitude()
  92. {
  93. return CoordinateType(180.0);
  94. }
  95. static inline CoordinateType min_latitude()
  96. {
  97. return CoordinateType(-90.0);
  98. }
  99. static inline CoordinateType max_latitude()
  100. {
  101. return CoordinateType(90.0);
  102. }
  103. };
  104. template <typename CoordinateType>
  105. struct constants_on_spheroid<CoordinateType, degree, false>
  106. : constants_on_spheroid<CoordinateType, degree, true>
  107. {
  108. static inline CoordinateType min_latitude()
  109. {
  110. return CoordinateType(0);
  111. }
  112. static inline CoordinateType max_latitude()
  113. {
  114. return CoordinateType(180.0);
  115. }
  116. };
  117. } // namespace detail
  118. #endif // DOXYGEN_NO_DETAIL
  119. template <typename Units, typename CoordinateType>
  120. inline CoordinateType latitude_convert_ep(CoordinateType const& lat)
  121. {
  122. typedef math::detail::constants_on_spheroid
  123. <
  124. CoordinateType,
  125. Units
  126. > constants;
  127. return constants::quarter_period() - lat;
  128. }
  129. template <typename Units, bool IsEquatorial, typename T>
  130. static bool is_latitude_pole(T const& lat)
  131. {
  132. typedef math::detail::constants_on_spheroid
  133. <
  134. T,
  135. Units
  136. > constants;
  137. return math::equals(math::abs(IsEquatorial
  138. ? lat
  139. : math::latitude_convert_ep<Units>(lat)),
  140. constants::quarter_period());
  141. }
  142. template <typename Units, typename T>
  143. static bool is_longitude_antimeridian(T const& lon)
  144. {
  145. typedef math::detail::constants_on_spheroid
  146. <
  147. T,
  148. Units
  149. > constants;
  150. return math::equals(math::abs(lon), constants::half_period());
  151. }
  152. #ifndef DOXYGEN_NO_DETAIL
  153. namespace detail
  154. {
  155. template <typename Units, bool IsEquatorial>
  156. struct latitude_convert_if_polar
  157. {
  158. template <typename T>
  159. static inline void apply(T & /*lat*/) {}
  160. };
  161. template <typename Units>
  162. struct latitude_convert_if_polar<Units, false>
  163. {
  164. template <typename T>
  165. static inline void apply(T & lat)
  166. {
  167. lat = latitude_convert_ep<Units>(lat);
  168. }
  169. };
  170. template <typename Units, typename CoordinateType, bool IsEquatorial = true>
  171. class normalize_spheroidal_coordinates
  172. {
  173. typedef constants_on_spheroid<CoordinateType, Units> constants;
  174. protected:
  175. static inline CoordinateType normalize_up(CoordinateType const& value)
  176. {
  177. return
  178. math::mod(value + constants::half_period(), constants::period())
  179. - constants::half_period();
  180. }
  181. static inline CoordinateType normalize_down(CoordinateType const& value)
  182. {
  183. return
  184. math::mod(value - constants::half_period(), constants::period())
  185. + constants::half_period();
  186. }
  187. public:
  188. static inline void apply(CoordinateType& longitude)
  189. {
  190. // normalize longitude
  191. if (math::equals(math::abs(longitude), constants::half_period()))
  192. {
  193. longitude = constants::half_period();
  194. }
  195. else if (longitude > constants::half_period())
  196. {
  197. longitude = normalize_up(longitude);
  198. if (math::equals(longitude, -constants::half_period()))
  199. {
  200. longitude = constants::half_period();
  201. }
  202. }
  203. else if (longitude < -constants::half_period())
  204. {
  205. longitude = normalize_down(longitude);
  206. }
  207. }
  208. static inline void apply(CoordinateType& longitude,
  209. CoordinateType& latitude,
  210. bool normalize_poles = true)
  211. {
  212. latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
  213. #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
  214. // normalize latitude
  215. if (math::larger(latitude, constants::half_period()))
  216. {
  217. latitude = normalize_up(latitude);
  218. }
  219. else if (math::smaller(latitude, -constants::half_period()))
  220. {
  221. latitude = normalize_down(latitude);
  222. }
  223. // fix latitude range
  224. if (latitude < constants::min_latitude())
  225. {
  226. latitude = -constants::half_period() - latitude;
  227. longitude -= constants::half_period();
  228. }
  229. else if (latitude > constants::max_latitude())
  230. {
  231. latitude = constants::half_period() - latitude;
  232. longitude -= constants::half_period();
  233. }
  234. #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
  235. // normalize longitude
  236. apply(longitude);
  237. // finally normalize poles
  238. if (normalize_poles)
  239. {
  240. if (math::equals(math::abs(latitude), constants::max_latitude()))
  241. {
  242. // for the north and south pole we set the longitude to 0
  243. // (works for both radians and degrees)
  244. longitude = CoordinateType(0);
  245. }
  246. }
  247. latitude_convert_if_polar<Units, IsEquatorial>::apply(latitude);
  248. #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE
  249. BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude));
  250. BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude()));
  251. #endif // BOOST_GEOMETRY_NORMALIZE_LATITUDE
  252. BOOST_GEOMETRY_ASSERT(! math::larger_or_equals(constants::min_longitude(), longitude));
  253. BOOST_GEOMETRY_ASSERT(! math::larger(longitude, constants::max_longitude()));
  254. }
  255. };
  256. template <typename Units, typename CoordinateType>
  257. inline void normalize_angle_loop(CoordinateType& angle)
  258. {
  259. typedef constants_on_spheroid<CoordinateType, Units> constants;
  260. CoordinateType const pi = constants::half_period();
  261. CoordinateType const two_pi = constants::period();
  262. while (angle > pi)
  263. angle -= two_pi;
  264. while (angle <= -pi)
  265. angle += two_pi;
  266. }
  267. template <typename Units, typename CoordinateType>
  268. inline void normalize_angle_cond(CoordinateType& angle)
  269. {
  270. typedef constants_on_spheroid<CoordinateType, Units> constants;
  271. CoordinateType const pi = constants::half_period();
  272. CoordinateType const two_pi = constants::period();
  273. if (angle > pi)
  274. angle -= two_pi;
  275. else if (angle <= -pi)
  276. angle += two_pi;
  277. }
  278. } // namespace detail
  279. #endif // DOXYGEN_NO_DETAIL
  280. /*!
  281. \brief Short utility to normalize the coordinates on a spheroid
  282. \tparam Units The units of the coordindate system in the spheroid
  283. \tparam CoordinateType The type of the coordinates
  284. \param longitude Longitude
  285. \param latitude Latitude
  286. \ingroup utility
  287. */
  288. template <typename Units, typename CoordinateType>
  289. inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
  290. CoordinateType& latitude)
  291. {
  292. detail::normalize_spheroidal_coordinates
  293. <
  294. Units, CoordinateType
  295. >::apply(longitude, latitude);
  296. }
  297. template <typename Units, bool IsEquatorial, typename CoordinateType>
  298. inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
  299. CoordinateType& latitude)
  300. {
  301. detail::normalize_spheroidal_coordinates
  302. <
  303. Units, CoordinateType, IsEquatorial
  304. >::apply(longitude, latitude);
  305. }
  306. /*!
  307. \brief Short utility to normalize the longitude on a spheroid.
  308. Note that in general both coordinates should be normalized at once.
  309. This utility is suitable e.g. for normalization of the difference of longitudes.
  310. \tparam Units The units of the coordindate system in the spheroid
  311. \tparam CoordinateType The type of the coordinates
  312. \param longitude Longitude
  313. \ingroup utility
  314. */
  315. template <typename Units, typename CoordinateType>
  316. inline void normalize_longitude(CoordinateType& longitude)
  317. {
  318. detail::normalize_spheroidal_coordinates
  319. <
  320. Units, CoordinateType
  321. >::apply(longitude);
  322. }
  323. /*!
  324. \brief Short utility to normalize the azimuth on a spheroid
  325. in the range (-180, 180].
  326. \tparam Units The units of the coordindate system in the spheroid
  327. \tparam CoordinateType The type of the coordinates
  328. \param angle Angle
  329. \ingroup utility
  330. */
  331. template <typename Units, typename CoordinateType>
  332. inline void normalize_azimuth(CoordinateType& angle)
  333. {
  334. normalize_longitude<Units, CoordinateType>(angle);
  335. }
  336. /*!
  337. \brief Normalize the given values.
  338. \tparam ValueType The type of the values
  339. \param x Value x
  340. \param y Value y
  341. TODO: adl1995 - Merge this function with
  342. formulas/vertex_longitude.hpp
  343. */
  344. template<typename ValueType>
  345. inline void normalize_unit_vector(ValueType& x, ValueType& y)
  346. {
  347. ValueType h = boost::math::hypot(x, y);
  348. BOOST_GEOMETRY_ASSERT(h > 0);
  349. x /= h; y /= h;
  350. }
  351. /*!
  352. \brief Short utility to calculate difference between two longitudes
  353. normalized in range (-180, 180].
  354. \tparam Units The units of the coordindate system in the spheroid
  355. \tparam CoordinateType The type of the coordinates
  356. \param longitude1 Longitude 1
  357. \param longitude2 Longitude 2
  358. \ingroup utility
  359. */
  360. template <typename Units, typename CoordinateType>
  361. inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
  362. CoordinateType const& longitude2)
  363. {
  364. CoordinateType diff = longitude2 - longitude1;
  365. math::normalize_longitude<Units, CoordinateType>(diff);
  366. return diff;
  367. }
  368. /*!
  369. \brief Short utility to calculate difference between two longitudes
  370. normalized in range [0, 360).
  371. \tparam Units The units of the coordindate system in the spheroid
  372. \tparam CoordinateType The type of the coordinates
  373. \param longitude1 Longitude 1
  374. \param longitude2 Longitude 2
  375. \ingroup utility
  376. */
  377. template <typename Units, typename CoordinateType>
  378. inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
  379. CoordinateType const& longitude2)
  380. {
  381. typedef math::detail::constants_on_spheroid
  382. <
  383. CoordinateType, Units
  384. > constants;
  385. CoordinateType const c0 = 0;
  386. CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
  387. if (diff < c0) // (-180, 180] -> [0, 360)
  388. {
  389. diff += constants::period();
  390. }
  391. return diff;
  392. }
  393. /*!
  394. \brief The abs difference between longitudes in range [0, 180].
  395. \tparam Units The units of the coordindate system in the spheroid
  396. \tparam CoordinateType The type of the coordinates
  397. \param longitude1 Longitude 1
  398. \param longitude2 Longitude 2
  399. \ingroup utility
  400. */
  401. template <typename Units, typename CoordinateType>
  402. inline CoordinateType longitude_difference(CoordinateType const& longitude1,
  403. CoordinateType const& longitude2)
  404. {
  405. return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
  406. }
  407. template <typename Units, typename CoordinateType>
  408. inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
  409. CoordinateType const& longitude_a2,
  410. CoordinateType const& longitude_b)
  411. {
  412. CoordinateType const c0 = 0;
  413. CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
  414. CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
  415. if (dist_a12 < c0)
  416. {
  417. dist_a12 = -dist_a12;
  418. dist_a1b = -dist_a1b;
  419. }
  420. return dist_a1b < c0 ? dist_a1b
  421. : dist_a1b > dist_a12 ? dist_a1b - dist_a12
  422. : c0;
  423. }
  424. } // namespace math
  425. }} // namespace boost::geometry
  426. #endif // BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP