envelope_segment.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2017-2020 Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Use, modification and distribution is subject to the Boost Software License,
  6. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. #ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
  9. #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
  10. #include <cstddef>
  11. #include <utility>
  12. #include <boost/core/ignore_unused.hpp>
  13. #include <boost/geometry/algorithms/detail/envelope/transform_units.hpp>
  14. #include <boost/geometry/core/assert.hpp>
  15. #include <boost/geometry/core/coordinate_system.hpp>
  16. #include <boost/geometry/core/coordinate_type.hpp>
  17. #include <boost/geometry/core/cs.hpp>
  18. #include <boost/geometry/core/point_type.hpp>
  19. #include <boost/geometry/core/radian_access.hpp>
  20. #include <boost/geometry/core/tags.hpp>
  21. #include <boost/geometry/formulas/meridian_segment.hpp>
  22. #include <boost/geometry/formulas/vertex_latitude.hpp>
  23. #include <boost/geometry/geometries/helper_geometry.hpp>
  24. #include <boost/geometry/strategy/cartesian/envelope_segment.hpp>
  25. #include <boost/geometry/strategy/envelope.hpp>
  26. #include <boost/geometry/strategies/normalize.hpp>
  27. #include <boost/geometry/strategies/spherical/azimuth.hpp>
  28. #include <boost/geometry/strategy/spherical/expand_box.hpp>
  29. #include <boost/geometry/util/math.hpp>
  30. #include <boost/geometry/util/numeric_cast.hpp>
  31. namespace boost { namespace geometry { namespace strategy { namespace envelope
  32. {
  33. #ifndef DOXYGEN_NO_DETAIL
  34. namespace detail
  35. {
  36. template <typename CalculationType, typename CS_Tag>
  37. struct envelope_segment_call_vertex_latitude
  38. {
  39. template <typename T1, typename T2, typename Strategy>
  40. static inline CalculationType apply(T1 const& lat1,
  41. T2 const& alp1,
  42. Strategy const& )
  43. {
  44. return geometry::formula::vertex_latitude<CalculationType, CS_Tag>
  45. ::apply(lat1, alp1);
  46. }
  47. };
  48. template <typename CalculationType>
  49. struct envelope_segment_call_vertex_latitude<CalculationType, geographic_tag>
  50. {
  51. template <typename T1, typename T2, typename Strategy>
  52. static inline CalculationType apply(T1 const& lat1,
  53. T2 const& alp1,
  54. Strategy const& strategy)
  55. {
  56. return geometry::formula::vertex_latitude<CalculationType, geographic_tag>
  57. ::apply(lat1, alp1, strategy.model());
  58. }
  59. };
  60. template <typename Units, typename CS_Tag>
  61. struct envelope_segment_convert_polar
  62. {
  63. template <typename T>
  64. static inline void pre(T & , T & ) {}
  65. template <typename T>
  66. static inline void post(T & , T & ) {}
  67. };
  68. template <typename Units>
  69. struct envelope_segment_convert_polar<Units, spherical_polar_tag>
  70. {
  71. template <typename T>
  72. static inline void pre(T & lat1, T & lat2)
  73. {
  74. lat1 = math::latitude_convert_ep<Units>(lat1);
  75. lat2 = math::latitude_convert_ep<Units>(lat2);
  76. }
  77. template <typename T>
  78. static inline void post(T & lat1, T & lat2)
  79. {
  80. lat1 = math::latitude_convert_ep<Units>(lat1);
  81. lat2 = math::latitude_convert_ep<Units>(lat2);
  82. std::swap(lat1, lat2);
  83. }
  84. };
  85. template <typename CS_Tag>
  86. class envelope_segment_impl
  87. {
  88. private:
  89. // degrees or radians
  90. template <typename CalculationType>
  91. static inline void swap(CalculationType& lon1,
  92. CalculationType& lat1,
  93. CalculationType& lon2,
  94. CalculationType& lat2)
  95. {
  96. std::swap(lon1, lon2);
  97. std::swap(lat1, lat2);
  98. }
  99. // radians
  100. template <typename CalculationType>
  101. static inline bool contains_pi_half(CalculationType const& a1,
  102. CalculationType const& a2)
  103. {
  104. // azimuths a1 and a2 are assumed to be in radians
  105. static CalculationType const pi_half = math::half_pi<CalculationType>();
  106. return (a1 < a2)
  107. ? (a1 < pi_half && pi_half < a2)
  108. : (a1 > pi_half && pi_half > a2);
  109. }
  110. // radians or degrees
  111. template <typename Units, typename CoordinateType>
  112. static inline bool crosses_antimeridian(CoordinateType const& lon1,
  113. CoordinateType const& lon2)
  114. {
  115. typedef math::detail::constants_on_spheroid
  116. <
  117. CoordinateType, Units
  118. > constants;
  119. return math::abs(lon1 - lon2) > constants::half_period(); // > pi
  120. }
  121. // degrees or radians
  122. template <typename Units, typename CalculationType, typename Strategy>
  123. static inline void compute_box_corners(CalculationType& lon1,
  124. CalculationType& lat1,
  125. CalculationType& lon2,
  126. CalculationType& lat2,
  127. CalculationType a1,
  128. CalculationType a2,
  129. Strategy const& strategy)
  130. {
  131. // coordinates are assumed to be in radians
  132. BOOST_GEOMETRY_ASSERT(lon1 <= lon2);
  133. boost::ignore_unused(lon1, lon2);
  134. CalculationType lat1_rad = math::as_radian<Units>(lat1);
  135. CalculationType lat2_rad = math::as_radian<Units>(lat2);
  136. if (lat1 > lat2)
  137. {
  138. std::swap(lat1, lat2);
  139. std::swap(lat1_rad, lat2_rad);
  140. std::swap(a1, a2);
  141. }
  142. if (contains_pi_half(a1, a2))
  143. {
  144. CalculationType p_max = envelope_segment_call_vertex_latitude
  145. <CalculationType, CS_Tag>::apply(lat1_rad, a1, strategy);
  146. CalculationType const mid_lat = lat1 + lat2;
  147. if (mid_lat < 0)
  148. {
  149. // update using min latitude
  150. CalculationType const lat_min_rad = -p_max;
  151. CalculationType const lat_min
  152. = math::from_radian<Units>(lat_min_rad);
  153. if (lat1 > lat_min)
  154. {
  155. lat1 = lat_min;
  156. }
  157. }
  158. else
  159. {
  160. // update using max latitude
  161. CalculationType const lat_max_rad = p_max;
  162. CalculationType const lat_max
  163. = math::from_radian<Units>(lat_max_rad);
  164. if (lat2 < lat_max)
  165. {
  166. lat2 = lat_max;
  167. }
  168. }
  169. }
  170. }
  171. template <typename Units, typename CalculationType>
  172. static inline void special_cases(CalculationType& lon1,
  173. CalculationType& lat1,
  174. CalculationType& lon2,
  175. CalculationType& lat2)
  176. {
  177. typedef math::detail::constants_on_spheroid
  178. <
  179. CalculationType, Units
  180. > constants;
  181. bool is_pole1 = math::equals(math::abs(lat1), constants::max_latitude());
  182. bool is_pole2 = math::equals(math::abs(lat2), constants::max_latitude());
  183. if (is_pole1 && is_pole2)
  184. {
  185. // both points are poles; nothing more to do:
  186. // longitudes are already normalized to 0
  187. // but just in case
  188. lon1 = 0;
  189. lon2 = 0;
  190. }
  191. else if (is_pole1 && !is_pole2)
  192. {
  193. // first point is a pole, second point is not:
  194. // make the longitude of the first point the same as that
  195. // of the second point
  196. lon1 = lon2;
  197. }
  198. else if (!is_pole1 && is_pole2)
  199. {
  200. // second point is a pole, first point is not:
  201. // make the longitude of the second point the same as that
  202. // of the first point
  203. lon2 = lon1;
  204. }
  205. if (lon1 == lon2)
  206. {
  207. // segment lies on a meridian
  208. if (lat1 > lat2)
  209. {
  210. std::swap(lat1, lat2);
  211. }
  212. return;
  213. }
  214. BOOST_GEOMETRY_ASSERT(!is_pole1 && !is_pole2);
  215. if (lon1 > lon2)
  216. {
  217. swap(lon1, lat1, lon2, lat2);
  218. }
  219. if (crosses_antimeridian<Units>(lon1, lon2))
  220. {
  221. lon1 += constants::period();
  222. swap(lon1, lat1, lon2, lat2);
  223. }
  224. }
  225. template
  226. <
  227. typename Units,
  228. typename CalculationType,
  229. typename Box
  230. >
  231. static inline void create_box(CalculationType lon1,
  232. CalculationType lat1,
  233. CalculationType lon2,
  234. CalculationType lat2,
  235. Box& mbr)
  236. {
  237. typedef typename coordinate_type<Box>::type box_coordinate_type;
  238. typedef typename helper_geometry
  239. <
  240. Box, box_coordinate_type, Units
  241. >::type helper_box_type;
  242. helper_box_type helper_mbr;
  243. geometry::set
  244. <
  245. min_corner, 0
  246. >(helper_mbr, util::numeric_cast<box_coordinate_type>(lon1));
  247. geometry::set
  248. <
  249. min_corner, 1
  250. >(helper_mbr, util::numeric_cast<box_coordinate_type>(lat1));
  251. geometry::set
  252. <
  253. max_corner, 0
  254. >(helper_mbr, util::numeric_cast<box_coordinate_type>(lon2));
  255. geometry::set
  256. <
  257. max_corner, 1
  258. >(helper_mbr, util::numeric_cast<box_coordinate_type>(lat2));
  259. geometry::detail::envelope::transform_units(helper_mbr, mbr);
  260. }
  261. template <typename Units, typename CalculationType, typename Strategy>
  262. static inline void apply(CalculationType& lon1,
  263. CalculationType& lat1,
  264. CalculationType& lon2,
  265. CalculationType& lat2,
  266. Strategy const& strategy)
  267. {
  268. special_cases<Units>(lon1, lat1, lon2, lat2);
  269. CalculationType lon1_rad = math::as_radian<Units>(lon1);
  270. CalculationType lat1_rad = math::as_radian<Units>(lat1);
  271. CalculationType lon2_rad = math::as_radian<Units>(lon2);
  272. CalculationType lat2_rad = math::as_radian<Units>(lat2);
  273. CalculationType alp1, alp2;
  274. strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, alp1, alp2);
  275. compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy);
  276. }
  277. public:
  278. template
  279. <
  280. typename Units,
  281. typename CalculationType,
  282. typename Box,
  283. typename Strategy
  284. >
  285. static inline void apply(CalculationType lon1,
  286. CalculationType lat1,
  287. CalculationType lon2,
  288. CalculationType lat2,
  289. Box& mbr,
  290. Strategy const& strategy)
  291. {
  292. typedef envelope_segment_convert_polar<Units, typename cs_tag<Box>::type> convert_polar;
  293. convert_polar::pre(lat1, lat2);
  294. apply<Units>(lon1, lat1, lon2, lat2, strategy);
  295. convert_polar::post(lat1, lat2);
  296. create_box<Units>(lon1, lat1, lon2, lat2, mbr);
  297. }
  298. };
  299. } // namespace detail
  300. #endif // DOXYGEN_NO_DETAIL
  301. template
  302. <
  303. typename CalculationType = void
  304. >
  305. class spherical_segment
  306. {
  307. public:
  308. template <typename Point, typename Box>
  309. static inline void apply(Point const& point1, Point const& point2,
  310. Box& box)
  311. {
  312. Point p1_normalized, p2_normalized;
  313. strategy::normalize::spherical_point::apply(point1, p1_normalized);
  314. strategy::normalize::spherical_point::apply(point2, p2_normalized);
  315. geometry::strategy::azimuth::spherical<CalculationType> azimuth_spherical;
  316. typedef typename geometry::detail::cs_angular_units<Point>::type units_type;
  317. // first compute the envelope range for the first two coordinates
  318. strategy::envelope::detail::envelope_segment_impl
  319. <
  320. spherical_equatorial_tag
  321. >::template apply<units_type>(geometry::get<0>(p1_normalized),
  322. geometry::get<1>(p1_normalized),
  323. geometry::get<0>(p2_normalized),
  324. geometry::get<1>(p2_normalized),
  325. box,
  326. azimuth_spherical);
  327. // now compute the envelope range for coordinates of
  328. // dimension 2 and higher
  329. strategy::envelope::detail::envelope_one_segment
  330. <
  331. 2, dimension<Point>::value
  332. >::apply(point1, point2, box);
  333. }
  334. };
  335. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  336. namespace services
  337. {
  338. template <typename CalculationType>
  339. struct default_strategy<segment_tag, spherical_equatorial_tag, CalculationType>
  340. {
  341. typedef strategy::envelope::spherical_segment<CalculationType> type;
  342. };
  343. template <typename CalculationType>
  344. struct default_strategy<segment_tag, spherical_polar_tag, CalculationType>
  345. {
  346. typedef strategy::envelope::spherical_segment<CalculationType> type;
  347. };
  348. }
  349. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  350. }} // namespace strategy::envelope
  351. }} //namepsace boost::geometry
  352. #endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP