normalize_spheroidal_coordinates.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2015-2019, 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::smaller(constants::min_longitude(), longitude));
  253. BOOST_GEOMETRY_ASSERT(! math::larger(longitude, constants::max_longitude()));
  254. }
  255. };
  256. } // namespace detail
  257. #endif // DOXYGEN_NO_DETAIL
  258. /*!
  259. \brief Short utility to normalize the coordinates on a spheroid
  260. \tparam Units The units of the coordindate system in the spheroid
  261. \tparam CoordinateType The type of the coordinates
  262. \param longitude Longitude
  263. \param latitude Latitude
  264. \ingroup utility
  265. */
  266. template <typename Units, typename CoordinateType>
  267. inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
  268. CoordinateType& latitude)
  269. {
  270. detail::normalize_spheroidal_coordinates
  271. <
  272. Units, CoordinateType
  273. >::apply(longitude, latitude);
  274. }
  275. template <typename Units, bool IsEquatorial, typename CoordinateType>
  276. inline void normalize_spheroidal_coordinates(CoordinateType& longitude,
  277. CoordinateType& latitude)
  278. {
  279. detail::normalize_spheroidal_coordinates
  280. <
  281. Units, CoordinateType, IsEquatorial
  282. >::apply(longitude, latitude);
  283. }
  284. /*!
  285. \brief Short utility to normalize the longitude on a spheroid.
  286. Note that in general both coordinates should be normalized at once.
  287. This utility is suitable e.g. for normalization of the difference of longitudes.
  288. \tparam Units The units of the coordindate system in the spheroid
  289. \tparam CoordinateType The type of the coordinates
  290. \param longitude Longitude
  291. \ingroup utility
  292. */
  293. template <typename Units, typename CoordinateType>
  294. inline void normalize_longitude(CoordinateType& longitude)
  295. {
  296. detail::normalize_spheroidal_coordinates
  297. <
  298. Units, CoordinateType
  299. >::apply(longitude);
  300. }
  301. /*!
  302. \brief Short utility to normalize the azimuth on a spheroid
  303. in the range (-180, 180].
  304. \tparam Units The units of the coordindate system in the spheroid
  305. \tparam CoordinateType The type of the coordinates
  306. \param angle Angle
  307. \ingroup utility
  308. */
  309. template <typename Units, typename CoordinateType>
  310. inline void normalize_azimuth(CoordinateType& angle)
  311. {
  312. normalize_longitude<Units, CoordinateType>(angle);
  313. }
  314. /*!
  315. \brief Normalize the given values.
  316. \tparam ValueType The type of the values
  317. \param x Value x
  318. \param y Value y
  319. TODO: adl1995 - Merge this function with
  320. formulas/vertex_longitude.hpp
  321. */
  322. template<typename ValueType>
  323. inline void normalize_unit_vector(ValueType& x, ValueType& y)
  324. {
  325. ValueType h = boost::math::hypot(x, y);
  326. BOOST_GEOMETRY_ASSERT(h > 0);
  327. x /= h; y /= h;
  328. }
  329. /*!
  330. \brief Short utility to calculate difference between two longitudes
  331. normalized in range (-180, 180].
  332. \tparam Units The units of the coordindate system in the spheroid
  333. \tparam CoordinateType The type of the coordinates
  334. \param longitude1 Longitude 1
  335. \param longitude2 Longitude 2
  336. \ingroup utility
  337. */
  338. template <typename Units, typename CoordinateType>
  339. inline CoordinateType longitude_distance_signed(CoordinateType const& longitude1,
  340. CoordinateType const& longitude2)
  341. {
  342. CoordinateType diff = longitude2 - longitude1;
  343. math::normalize_longitude<Units, CoordinateType>(diff);
  344. return diff;
  345. }
  346. /*!
  347. \brief Short utility to calculate difference between two longitudes
  348. normalized in range [0, 360).
  349. \tparam Units The units of the coordindate system in the spheroid
  350. \tparam CoordinateType The type of the coordinates
  351. \param longitude1 Longitude 1
  352. \param longitude2 Longitude 2
  353. \ingroup utility
  354. */
  355. template <typename Units, typename CoordinateType>
  356. inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitude1,
  357. CoordinateType const& longitude2)
  358. {
  359. typedef math::detail::constants_on_spheroid
  360. <
  361. CoordinateType, Units
  362. > constants;
  363. CoordinateType const c0 = 0;
  364. CoordinateType diff = longitude_distance_signed<Units>(longitude1, longitude2);
  365. if (diff < c0) // (-180, 180] -> [0, 360)
  366. {
  367. diff += constants::period();
  368. }
  369. return diff;
  370. }
  371. /*!
  372. \brief The abs difference between longitudes in range [0, 180].
  373. \tparam Units The units of the coordindate system in the spheroid
  374. \tparam CoordinateType The type of the coordinates
  375. \param longitude1 Longitude 1
  376. \param longitude2 Longitude 2
  377. \ingroup utility
  378. */
  379. template <typename Units, typename CoordinateType>
  380. inline CoordinateType longitude_difference(CoordinateType const& longitude1,
  381. CoordinateType const& longitude2)
  382. {
  383. return math::abs(math::longitude_distance_signed<Units>(longitude1, longitude2));
  384. }
  385. template <typename Units, typename CoordinateType>
  386. inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1,
  387. CoordinateType const& longitude_a2,
  388. CoordinateType const& longitude_b)
  389. {
  390. CoordinateType const c0 = 0;
  391. CoordinateType dist_a12 = longitude_distance_signed<Units>(longitude_a1, longitude_a2);
  392. CoordinateType dist_a1b = longitude_distance_signed<Units>(longitude_a1, longitude_b);
  393. if (dist_a12 < c0)
  394. {
  395. dist_a12 = -dist_a12;
  396. dist_a1b = -dist_a1b;
  397. }
  398. return dist_a1b < c0 ? dist_a1b
  399. : dist_a1b > dist_a12 ? dist_a1b - dist_a12
  400. : c0;
  401. }
  402. } // namespace math
  403. }} // namespace boost::geometry
  404. #endif // BOOST_GEOMETRY_UTIL_NORMALIZE_SPHEROIDAL_COORDINATES_HPP