envelope_range.hpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Boost.Geometry
  2. // Copyright (c) 2021-2022, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  4. // Licensed under the Boost Software License version 1.0.
  5. // http://www.boost.org/users/license.html
  6. #ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_RANGE_HPP
  7. #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_RANGE_HPP
  8. #include <boost/range/size.hpp>
  9. #include <boost/geometry/algorithms/assign.hpp>
  10. #include <boost/geometry/algorithms/detail/envelope/initialize.hpp>
  11. #include <boost/geometry/geometries/segment.hpp>
  12. #include <boost/geometry/strategy/spherical/envelope_point.hpp>
  13. #include <boost/geometry/strategy/spherical/envelope_segment.hpp>
  14. #include <boost/geometry/strategy/spherical/expand_segment.hpp>
  15. #include <boost/geometry/views/closeable_view.hpp>
  16. // Get rid of this dependency?
  17. #include <boost/geometry/strategies/spherical/point_in_poly_winding.hpp>
  18. namespace boost { namespace geometry
  19. {
  20. namespace strategy { namespace envelope
  21. {
  22. #ifndef DOXYGEN_NO_DETAIL
  23. namespace detail
  24. {
  25. template <typename Range, typename Box, typename EnvelopeStrategy, typename ExpandStrategy>
  26. inline void spheroidal_linestring(Range const& range, Box& mbr,
  27. EnvelopeStrategy const& envelope_strategy,
  28. ExpandStrategy const& expand_strategy)
  29. {
  30. auto it = boost::begin(range);
  31. auto const end = boost::end(range);
  32. if (it == end)
  33. {
  34. // initialize box (assign inverse)
  35. geometry::detail::envelope::initialize<Box>::apply(mbr);
  36. return;
  37. }
  38. auto prev = it;
  39. ++it;
  40. if (it == end)
  41. {
  42. // initialize box with the first point
  43. envelope::spherical_point::apply(*prev, mbr);
  44. return;
  45. }
  46. // initialize box with the first segment
  47. envelope_strategy.apply(*prev, *it, mbr);
  48. // consider now the remaining segments in the range (if any)
  49. prev = it;
  50. ++it;
  51. while (it != end)
  52. {
  53. using point_t = typename boost::range_value<Range>::type;
  54. geometry::model::referring_segment<point_t const> const seg(*prev, *it);
  55. expand_strategy.apply(mbr, seg);
  56. prev = it;
  57. ++it;
  58. }
  59. }
  60. // This strategy is intended to be used together with winding strategy to check
  61. // if ring/polygon has a pole in its interior or exterior. It is not intended
  62. // for checking if the pole is on the boundary.
  63. template <typename CalculationType = void>
  64. struct side_of_pole
  65. {
  66. typedef spherical_tag cs_tag;
  67. template <typename P>
  68. static inline int apply(P const& p1, P const& p2, P const& pole)
  69. {
  70. using calc_t = typename promote_floating_point
  71. <
  72. typename select_calculation_type_alt
  73. <
  74. CalculationType, P
  75. >::type
  76. >::type;
  77. using units_t = typename geometry::detail::cs_angular_units<P>::type;
  78. using constants = math::detail::constants_on_spheroid<calc_t, units_t>;
  79. calc_t const c0 = 0;
  80. calc_t const pi = constants::half_period();
  81. calc_t const lon1 = get<0>(p1);
  82. calc_t const lat1 = get<1>(p1);
  83. calc_t const lon2 = get<0>(p2);
  84. calc_t const lat2 = get<1>(p2);
  85. calc_t const lat_pole = get<1>(pole);
  86. calc_t const s_lon_diff = math::longitude_distance_signed<units_t>(lon1, lon2);
  87. bool const s_vertical = math::equals(s_lon_diff, c0)
  88. || math::equals(s_lon_diff, pi);
  89. // Side of vertical segment is 0 for both poles.
  90. if (s_vertical)
  91. {
  92. return 0;
  93. }
  94. // This strategy shouldn't be called in this case but just in case
  95. // check if segment starts at a pole
  96. if (math::equals(lat_pole, lat1) || math::equals(lat_pole, lat2))
  97. {
  98. return 0;
  99. }
  100. // -1 is rhs
  101. // 1 is lhs
  102. if (lat_pole >= c0) // north pole
  103. {
  104. return s_lon_diff < c0 ? -1 : 1;
  105. }
  106. else // south pole
  107. {
  108. return s_lon_diff > c0 ? -1 : 1;
  109. }
  110. }
  111. };
  112. template <typename Point, typename Range, typename Strategy>
  113. inline int point_in_range(Point const& point, Range const& range, Strategy const& strategy)
  114. {
  115. typename Strategy::state_type state;
  116. auto it = boost::begin(range);
  117. auto const end = boost::end(range);
  118. for (auto previous = it++ ; it != end ; ++previous, ++it )
  119. {
  120. if (! strategy.apply(point, *previous, *it, state))
  121. {
  122. break;
  123. }
  124. }
  125. return strategy.result(state);
  126. }
  127. template <typename T, typename Ring, typename PoleWithinStrategy>
  128. inline bool pole_within(T const& lat_pole, Ring const& ring,
  129. PoleWithinStrategy const& pole_within_strategy)
  130. {
  131. if (boost::size(ring) < core_detail::closure::minimum_ring_size
  132. <
  133. geometry::closure<Ring>::value
  134. >::value)
  135. {
  136. return false;
  137. }
  138. using point_t = typename geometry::point_type<Ring>::type;
  139. point_t point;
  140. geometry::assign_zero(point);
  141. geometry::set<1>(point, lat_pole);
  142. geometry::detail::closed_clockwise_view<Ring const> view(ring);
  143. return point_in_range(point, view, pole_within_strategy) > 0;
  144. }
  145. template
  146. <
  147. typename Range,
  148. typename Box,
  149. typename EnvelopeStrategy,
  150. typename ExpandStrategy,
  151. typename PoleWithinStrategy
  152. >
  153. inline void spheroidal_ring(Range const& range, Box& mbr,
  154. EnvelopeStrategy const& envelope_strategy,
  155. ExpandStrategy const& expand_strategy,
  156. PoleWithinStrategy const& pole_within_strategy)
  157. {
  158. geometry::detail::closed_view<Range const> closed_range(range);
  159. spheroidal_linestring(closed_range, mbr, envelope_strategy, expand_strategy);
  160. using coord_t = typename geometry::coordinate_type<Box>::type;
  161. using point_t = typename geometry::point_type<Box>::type;
  162. using units_t = typename geometry::detail::cs_angular_units<point_t>::type;
  163. using constants_t = math::detail::constants_on_spheroid<coord_t, units_t>;
  164. coord_t const two_pi = constants_t::period();
  165. coord_t const lon_min = geometry::get<0, 0>(mbr);
  166. coord_t const lon_max = geometry::get<1, 0>(mbr);
  167. // If box covers the whole longitude range it is possible that the ring contains
  168. // one of the poles.
  169. // Technically it is possible that a reversed ring may cover more than
  170. // half of the globe and mbr of it's linear ring may be small and not cover the
  171. // longitude range. We currently don't support such rings.
  172. if (lon_max - lon_min >= two_pi)
  173. {
  174. coord_t const lat_n_pole = constants_t::max_latitude();
  175. coord_t const lat_s_pole = constants_t::min_latitude();
  176. coord_t lat_min = geometry::get<0, 1>(mbr);
  177. coord_t lat_max = geometry::get<1, 1>(mbr);
  178. // Normalize box latitudes, just in case
  179. if (math::equals(lat_min, lat_s_pole))
  180. {
  181. lat_min = lat_s_pole;
  182. }
  183. if (math::equals(lat_max, lat_n_pole))
  184. {
  185. lat_max = lat_n_pole;
  186. }
  187. if (lat_max < lat_n_pole)
  188. {
  189. if (pole_within(lat_n_pole, range, pole_within_strategy))
  190. {
  191. lat_max = lat_n_pole;
  192. }
  193. }
  194. if (lat_min > lat_s_pole)
  195. {
  196. if (pole_within(lat_s_pole, range, pole_within_strategy))
  197. {
  198. lat_min = lat_s_pole;
  199. }
  200. }
  201. geometry::set<0, 1>(mbr, lat_min);
  202. geometry::set<1, 1>(mbr, lat_max);
  203. }
  204. }
  205. } // namespace detail
  206. #endif // DOXYGEN_NO_DETAIL
  207. template <typename CalculationType = void>
  208. class spherical_linestring
  209. {
  210. public:
  211. template <typename Range, typename Box>
  212. static inline void apply(Range const& range, Box& mbr)
  213. {
  214. detail::spheroidal_linestring(range, mbr,
  215. envelope::spherical_segment<CalculationType>(),
  216. expand::spherical_segment<CalculationType>());
  217. }
  218. };
  219. template <typename CalculationType = void>
  220. class spherical_ring
  221. {
  222. public:
  223. template <typename Range, typename Box>
  224. static inline void apply(Range const& range, Box& mbr)
  225. {
  226. detail::spheroidal_ring(range, mbr,
  227. envelope::spherical_segment<CalculationType>(),
  228. expand::spherical_segment<CalculationType>(),
  229. within::detail::spherical_winding_base
  230. <
  231. envelope::detail::side_of_pole<CalculationType>,
  232. CalculationType
  233. >());
  234. }
  235. };
  236. }} // namespace strategy::envelope
  237. }} //namepsace boost::geometry
  238. #endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_RANGE_HPP