envelope_segment.hpp 14 KB

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