envelope_segment.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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/numeric/conversion/cast.hpp>
  14. #include <boost/geometry/algorithms/detail/envelope/transform_units.hpp>
  15. #include <boost/geometry/core/assert.hpp>
  16. #include <boost/geometry/core/coordinate_system.hpp>
  17. #include <boost/geometry/core/coordinate_type.hpp>
  18. #include <boost/geometry/core/cs.hpp>
  19. #include <boost/geometry/core/point_type.hpp>
  20. #include <boost/geometry/core/radian_access.hpp>
  21. #include <boost/geometry/core/tags.hpp>
  22. #include <boost/geometry/formulas/meridian_segment.hpp>
  23. #include <boost/geometry/formulas/vertex_latitude.hpp>
  24. #include <boost/geometry/geometries/helper_geometry.hpp>
  25. #include <boost/geometry/strategy/cartesian/envelope_segment.hpp>
  26. #include <boost/geometry/strategy/envelope.hpp>
  27. #include <boost/geometry/strategies/normalize.hpp>
  28. #include <boost/geometry/strategies/spherical/azimuth.hpp>
  29. #include <boost/geometry/strategy/spherical/expand_box.hpp>
  30. #include <boost/geometry/util/math.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. BOOST_GEOMETRY_ASSERT(! math::equals(a1, a2));
  106. static CalculationType const pi_half = math::half_pi<CalculationType>();
  107. return (a1 < a2)
  108. ? (a1 < pi_half && pi_half < a2)
  109. : (a1 > pi_half && pi_half > a2);
  110. }
  111. // radians or degrees
  112. template <typename Units, typename CoordinateType>
  113. static inline bool crosses_antimeridian(CoordinateType const& lon1,
  114. CoordinateType const& lon2)
  115. {
  116. typedef math::detail::constants_on_spheroid
  117. <
  118. CoordinateType, Units
  119. > constants;
  120. return math::abs(lon1 - lon2) > constants::half_period(); // > pi
  121. }
  122. // degrees or radians
  123. template <typename Units, typename CalculationType, typename Strategy>
  124. static inline void compute_box_corners(CalculationType& lon1,
  125. CalculationType& lat1,
  126. CalculationType& lon2,
  127. CalculationType& lat2,
  128. CalculationType a1,
  129. CalculationType a2,
  130. Strategy const& strategy)
  131. {
  132. // coordinates are assumed to be in radians
  133. BOOST_GEOMETRY_ASSERT(lon1 <= lon2);
  134. boost::ignore_unused(lon1, lon2);
  135. CalculationType lat1_rad = math::as_radian<Units>(lat1);
  136. CalculationType lat2_rad = math::as_radian<Units>(lat2);
  137. if (math::equals(a1, a2))
  138. {
  139. // the segment must lie on the equator or is very short or is meridian
  140. return;
  141. }
  142. if (lat1 > lat2)
  143. {
  144. std::swap(lat1, lat2);
  145. std::swap(lat1_rad, lat2_rad);
  146. std::swap(a1, a2);
  147. }
  148. if (contains_pi_half(a1, a2))
  149. {
  150. CalculationType p_max = envelope_segment_call_vertex_latitude
  151. <CalculationType, CS_Tag>::apply(lat1_rad, a1, strategy);
  152. CalculationType const mid_lat = lat1 + lat2;
  153. if (mid_lat < 0)
  154. {
  155. // update using min latitude
  156. CalculationType const lat_min_rad = -p_max;
  157. CalculationType const lat_min
  158. = math::from_radian<Units>(lat_min_rad);
  159. if (lat1 > lat_min)
  160. {
  161. lat1 = lat_min;
  162. }
  163. }
  164. else
  165. {
  166. // update using max latitude
  167. CalculationType const lat_max_rad = p_max;
  168. CalculationType const lat_max
  169. = math::from_radian<Units>(lat_max_rad);
  170. if (lat2 < lat_max)
  171. {
  172. lat2 = lat_max;
  173. }
  174. }
  175. }
  176. }
  177. template <typename Units, typename CalculationType>
  178. static inline void special_cases(CalculationType& lon1,
  179. CalculationType& lat1,
  180. CalculationType& lon2,
  181. CalculationType& lat2)
  182. {
  183. typedef math::detail::constants_on_spheroid
  184. <
  185. CalculationType, Units
  186. > constants;
  187. bool is_pole1 = math::equals(math::abs(lat1), constants::max_latitude());
  188. bool is_pole2 = math::equals(math::abs(lat2), constants::max_latitude());
  189. if (is_pole1 && is_pole2)
  190. {
  191. // both points are poles; nothing more to do:
  192. // longitudes are already normalized to 0
  193. // but just in case
  194. lon1 = 0;
  195. lon2 = 0;
  196. }
  197. else if (is_pole1 && !is_pole2)
  198. {
  199. // first point is a pole, second point is not:
  200. // make the longitude of the first point the same as that
  201. // of the second point
  202. lon1 = lon2;
  203. }
  204. else if (!is_pole1 && is_pole2)
  205. {
  206. // second point is a pole, first point is not:
  207. // make the longitude of the second point the same as that
  208. // of the first point
  209. lon2 = lon1;
  210. }
  211. if (lon1 == lon2)
  212. {
  213. // segment lies on a meridian
  214. if (lat1 > lat2)
  215. {
  216. std::swap(lat1, lat2);
  217. }
  218. return;
  219. }
  220. BOOST_GEOMETRY_ASSERT(!is_pole1 && !is_pole2);
  221. if (lon1 > lon2)
  222. {
  223. swap(lon1, lat1, lon2, lat2);
  224. }
  225. if (crosses_antimeridian<Units>(lon1, lon2))
  226. {
  227. lon1 += constants::period();
  228. swap(lon1, lat1, lon2, lat2);
  229. }
  230. }
  231. template
  232. <
  233. typename Units,
  234. typename CalculationType,
  235. typename Box
  236. >
  237. static inline void create_box(CalculationType lon1,
  238. CalculationType lat1,
  239. CalculationType lon2,
  240. CalculationType lat2,
  241. Box& mbr)
  242. {
  243. typedef typename coordinate_type<Box>::type box_coordinate_type;
  244. typedef typename helper_geometry
  245. <
  246. Box, box_coordinate_type, Units
  247. >::type helper_box_type;
  248. helper_box_type helper_mbr;
  249. geometry::set
  250. <
  251. min_corner, 0
  252. >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon1));
  253. geometry::set
  254. <
  255. min_corner, 1
  256. >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat1));
  257. geometry::set
  258. <
  259. max_corner, 0
  260. >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon2));
  261. geometry::set
  262. <
  263. max_corner, 1
  264. >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat2));
  265. geometry::detail::envelope::transform_units(helper_mbr, mbr);
  266. }
  267. template <typename Units, typename CalculationType, typename Strategy>
  268. static inline void apply(CalculationType& lon1,
  269. CalculationType& lat1,
  270. CalculationType& lon2,
  271. CalculationType& lat2,
  272. Strategy const& strategy)
  273. {
  274. special_cases<Units>(lon1, lat1, lon2, lat2);
  275. CalculationType lon1_rad = math::as_radian<Units>(lon1);
  276. CalculationType lat1_rad = math::as_radian<Units>(lat1);
  277. CalculationType lon2_rad = math::as_radian<Units>(lon2);
  278. CalculationType lat2_rad = math::as_radian<Units>(lat2);
  279. CalculationType alp1, alp2;
  280. strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, alp1, alp2);
  281. compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy);
  282. }
  283. public:
  284. template
  285. <
  286. typename Units,
  287. typename CalculationType,
  288. typename Box,
  289. typename Strategy
  290. >
  291. static inline void apply(CalculationType lon1,
  292. CalculationType lat1,
  293. CalculationType lon2,
  294. CalculationType lat2,
  295. Box& mbr,
  296. Strategy const& strategy)
  297. {
  298. typedef envelope_segment_convert_polar<Units, typename cs_tag<Box>::type> convert_polar;
  299. convert_polar::pre(lat1, lat2);
  300. apply<Units>(lon1, lat1, lon2, lat2, strategy);
  301. convert_polar::post(lat1, lat2);
  302. create_box<Units>(lon1, lat1, lon2, lat2, mbr);
  303. }
  304. };
  305. } // namespace detail
  306. #endif // DOXYGEN_NO_DETAIL
  307. template
  308. <
  309. typename CalculationType = void
  310. >
  311. class spherical_segment
  312. {
  313. public:
  314. template <typename Point, typename Box>
  315. static inline void apply(Point const& point1, Point const& point2,
  316. Box& box)
  317. {
  318. Point p1_normalized, p2_normalized;
  319. strategy::normalize::spherical_point::apply(point1, p1_normalized);
  320. strategy::normalize::spherical_point::apply(point2, p2_normalized);
  321. geometry::strategy::azimuth::spherical<CalculationType> azimuth_spherical;
  322. typedef typename geometry::detail::cs_angular_units<Point>::type units_type;
  323. // first compute the envelope range for the first two coordinates
  324. strategy::envelope::detail::envelope_segment_impl
  325. <
  326. spherical_equatorial_tag
  327. >::template apply<units_type>(geometry::get<0>(p1_normalized),
  328. geometry::get<1>(p1_normalized),
  329. geometry::get<0>(p2_normalized),
  330. geometry::get<1>(p2_normalized),
  331. box,
  332. azimuth_spherical);
  333. // now compute the envelope range for coordinates of
  334. // dimension 2 and higher
  335. strategy::envelope::detail::envelope_one_segment
  336. <
  337. 2, dimension<Point>::value
  338. >::apply(point1, point2, box);
  339. }
  340. };
  341. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  342. namespace services
  343. {
  344. template <typename CalculationType>
  345. struct default_strategy<segment_tag, spherical_equatorial_tag, CalculationType>
  346. {
  347. typedef strategy::envelope::spherical_segment<CalculationType> type;
  348. };
  349. template <typename CalculationType>
  350. struct default_strategy<segment_tag, spherical_polar_tag, CalculationType>
  351. {
  352. typedef strategy::envelope::spherical_segment<CalculationType> type;
  353. };
  354. }
  355. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  356. }} // namespace strategy::envelope
  357. }} //namepsace boost::geometry
  358. #endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP