envelope_multipoint.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2015-2018, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  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. // Distributed under the Boost Software License, Version 1.0.
  7. // (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_MULTIPOINT_HPP
  10. #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_MULTIPOINT_HPP
  11. #include <cstddef>
  12. #include <algorithm>
  13. #include <utility>
  14. #include <vector>
  15. #include <boost/algorithm/minmax_element.hpp>
  16. #include <boost/range.hpp>
  17. #include <boost/geometry/core/access.hpp>
  18. #include <boost/geometry/core/assert.hpp>
  19. #include <boost/geometry/core/coordinate_system.hpp>
  20. #include <boost/geometry/core/coordinate_type.hpp>
  21. #include <boost/geometry/core/tags.hpp>
  22. #include <boost/geometry/util/math.hpp>
  23. #include <boost/geometry/util/range.hpp>
  24. #include <boost/geometry/geometries/helper_geometry.hpp>
  25. #include <boost/geometry/algorithms/detail/envelope/box.hpp>
  26. #include <boost/geometry/algorithms/detail/envelope/initialize.hpp>
  27. #include <boost/geometry/algorithms/detail/envelope/range.hpp>
  28. #include <boost/geometry/algorithms/detail/expand/point.hpp>
  29. #include <boost/geometry/strategies/cartesian/envelope_point.hpp>
  30. #include <boost/geometry/strategies/normalize.hpp>
  31. #include <boost/geometry/strategies/spherical/envelope_box.hpp>
  32. #include <boost/geometry/strategies/spherical/envelope_point.hpp>
  33. namespace boost { namespace geometry
  34. {
  35. namespace strategy { namespace envelope
  36. {
  37. class spherical_multipoint
  38. {
  39. private:
  40. template <std::size_t Dim>
  41. struct coordinate_less
  42. {
  43. template <typename Point>
  44. inline bool operator()(Point const& point1, Point const& point2) const
  45. {
  46. return math::smaller(geometry::get<Dim>(point1),
  47. geometry::get<Dim>(point2));
  48. }
  49. };
  50. template <typename Constants, typename MultiPoint, typename OutputIterator>
  51. static inline void analyze_point_coordinates(MultiPoint const& multipoint,
  52. bool& has_south_pole,
  53. bool& has_north_pole,
  54. OutputIterator oit)
  55. {
  56. typedef typename boost::range_value<MultiPoint>::type point_type;
  57. typedef typename boost::range_iterator
  58. <
  59. MultiPoint const
  60. >::type iterator_type;
  61. // analyze point coordinates:
  62. // (1) normalize point coordinates
  63. // (2) check if any point is the north or the south pole
  64. // (3) put all non-pole points in a container
  65. //
  66. // notice that at this point in the algorithm, we have at
  67. // least two points on the spheroid
  68. has_south_pole = false;
  69. has_north_pole = false;
  70. for (iterator_type it = boost::begin(multipoint);
  71. it != boost::end(multipoint);
  72. ++it)
  73. {
  74. point_type point;
  75. normalize::spherical_point::apply(*it, point);
  76. if (math::equals(geometry::get<1>(point),
  77. Constants::min_latitude()))
  78. {
  79. has_south_pole = true;
  80. }
  81. else if (math::equals(geometry::get<1>(point),
  82. Constants::max_latitude()))
  83. {
  84. has_north_pole = true;
  85. }
  86. else
  87. {
  88. *oit++ = point;
  89. }
  90. }
  91. }
  92. template <typename SortedRange, typename Value>
  93. static inline Value maximum_gap(SortedRange const& sorted_range,
  94. Value& max_gap_left,
  95. Value& max_gap_right)
  96. {
  97. typedef typename boost::range_iterator
  98. <
  99. SortedRange const
  100. >::type iterator_type;
  101. iterator_type it1 = boost::begin(sorted_range), it2 = it1;
  102. ++it2;
  103. max_gap_left = geometry::get<0>(*it1);
  104. max_gap_right = geometry::get<0>(*it2);
  105. Value max_gap = max_gap_right - max_gap_left;
  106. for (++it1, ++it2; it2 != boost::end(sorted_range); ++it1, ++it2)
  107. {
  108. Value gap = geometry::get<0>(*it2) - geometry::get<0>(*it1);
  109. if (math::larger(gap, max_gap))
  110. {
  111. max_gap_left = geometry::get<0>(*it1);
  112. max_gap_right = geometry::get<0>(*it2);
  113. max_gap = gap;
  114. }
  115. }
  116. return max_gap;
  117. }
  118. template
  119. <
  120. typename Constants,
  121. typename PointRange,
  122. typename LongitudeLess,
  123. typename CoordinateType
  124. >
  125. static inline void get_min_max_longitudes(PointRange& range,
  126. LongitudeLess const& lon_less,
  127. CoordinateType& lon_min,
  128. CoordinateType& lon_max)
  129. {
  130. typedef typename boost::range_iterator
  131. <
  132. PointRange const
  133. >::type iterator_type;
  134. // compute min and max longitude values
  135. std::pair<iterator_type, iterator_type> min_max_longitudes
  136. = boost::minmax_element(boost::begin(range),
  137. boost::end(range),
  138. lon_less);
  139. lon_min = geometry::get<0>(*min_max_longitudes.first);
  140. lon_max = geometry::get<0>(*min_max_longitudes.second);
  141. // if the longitude span is "large" compute the true maximum gap
  142. if (math::larger(lon_max - lon_min, Constants::half_period()))
  143. {
  144. std::sort(boost::begin(range), boost::end(range), lon_less);
  145. CoordinateType max_gap_left = 0, max_gap_right = 0;
  146. CoordinateType max_gap
  147. = maximum_gap(range, max_gap_left, max_gap_right);
  148. CoordinateType complement_gap
  149. = Constants::period() + lon_min - lon_max;
  150. if (math::larger(max_gap, complement_gap))
  151. {
  152. lon_min = max_gap_right;
  153. lon_max = max_gap_left + Constants::period();
  154. }
  155. }
  156. }
  157. template
  158. <
  159. typename Constants,
  160. typename Iterator,
  161. typename LatitudeLess,
  162. typename CoordinateType
  163. >
  164. static inline void get_min_max_latitudes(Iterator const first,
  165. Iterator const last,
  166. LatitudeLess const& lat_less,
  167. bool has_south_pole,
  168. bool has_north_pole,
  169. CoordinateType& lat_min,
  170. CoordinateType& lat_max)
  171. {
  172. if (has_south_pole && has_north_pole)
  173. {
  174. lat_min = Constants::min_latitude();
  175. lat_max = Constants::max_latitude();
  176. }
  177. else if (has_south_pole)
  178. {
  179. lat_min = Constants::min_latitude();
  180. lat_max
  181. = geometry::get<1>(*std::max_element(first, last, lat_less));
  182. }
  183. else if (has_north_pole)
  184. {
  185. lat_min
  186. = geometry::get<1>(*std::min_element(first, last, lat_less));
  187. lat_max = Constants::max_latitude();
  188. }
  189. else
  190. {
  191. std::pair<Iterator, Iterator> min_max_latitudes
  192. = boost::minmax_element(first, last, lat_less);
  193. lat_min = geometry::get<1>(*min_max_latitudes.first);
  194. lat_max = geometry::get<1>(*min_max_latitudes.second);
  195. }
  196. }
  197. public:
  198. template <typename MultiPoint, typename Box>
  199. static inline void apply(MultiPoint const& multipoint, Box& mbr)
  200. {
  201. typedef typename point_type<MultiPoint>::type point_type;
  202. typedef typename coordinate_type<MultiPoint>::type coordinate_type;
  203. typedef typename boost::range_iterator
  204. <
  205. MultiPoint const
  206. >::type iterator_type;
  207. typedef math::detail::constants_on_spheroid
  208. <
  209. coordinate_type,
  210. typename geometry::detail::cs_angular_units<MultiPoint>::type
  211. > constants;
  212. if (boost::empty(multipoint))
  213. {
  214. geometry::detail::envelope::initialize<Box, 0, dimension<Box>::value>::apply(mbr);
  215. return;
  216. }
  217. geometry::detail::envelope::initialize<Box, 0, 2>::apply(mbr);
  218. if (boost::size(multipoint) == 1)
  219. {
  220. return dispatch::envelope
  221. <
  222. typename boost::range_value<MultiPoint>::type
  223. >::apply(range::front(multipoint), mbr, strategy::envelope::spherical_point());
  224. }
  225. // analyze the points and put the non-pole ones in the
  226. // points vector
  227. std::vector<point_type> points;
  228. bool has_north_pole = false, has_south_pole = false;
  229. analyze_point_coordinates<constants>(multipoint,
  230. has_south_pole, has_north_pole,
  231. std::back_inserter(points));
  232. coordinate_type lon_min, lat_min, lon_max, lat_max;
  233. if (points.size() == 1)
  234. {
  235. // we have one non-pole point and at least one pole point
  236. lon_min = geometry::get<0>(range::front(points));
  237. lon_max = geometry::get<0>(range::front(points));
  238. lat_min = has_south_pole
  239. ? constants::min_latitude()
  240. : constants::max_latitude();
  241. lat_max = has_north_pole
  242. ? constants::max_latitude()
  243. : constants::min_latitude();
  244. }
  245. else if (points.empty())
  246. {
  247. // all points are pole points
  248. BOOST_GEOMETRY_ASSERT(has_south_pole || has_north_pole);
  249. lon_min = coordinate_type(0);
  250. lon_max = coordinate_type(0);
  251. lat_min = has_south_pole
  252. ? constants::min_latitude()
  253. : constants::max_latitude();
  254. lat_max = (has_north_pole)
  255. ? constants::max_latitude()
  256. : constants::min_latitude();
  257. }
  258. else
  259. {
  260. get_min_max_longitudes<constants>(points,
  261. coordinate_less<0>(),
  262. lon_min,
  263. lon_max);
  264. get_min_max_latitudes<constants>(points.begin(),
  265. points.end(),
  266. coordinate_less<1>(),
  267. has_south_pole,
  268. has_north_pole,
  269. lat_min,
  270. lat_max);
  271. }
  272. typedef typename helper_geometry
  273. <
  274. Box,
  275. coordinate_type,
  276. typename geometry::detail::cs_angular_units<MultiPoint>::type
  277. >::type helper_box_type;
  278. helper_box_type helper_mbr;
  279. geometry::set<min_corner, 0>(helper_mbr, lon_min);
  280. geometry::set<min_corner, 1>(helper_mbr, lat_min);
  281. geometry::set<max_corner, 0>(helper_mbr, lon_max);
  282. geometry::set<max_corner, 1>(helper_mbr, lat_max);
  283. // now transform to output MBR (per index)
  284. geometry::detail::envelope::envelope_indexed_box_on_spheroid<min_corner, 2>::apply(helper_mbr, mbr);
  285. geometry::detail::envelope::envelope_indexed_box_on_spheroid<max_corner, 2>::apply(helper_mbr, mbr);
  286. // compute envelope for higher coordinates
  287. iterator_type it = boost::begin(multipoint);
  288. geometry::detail::envelope::envelope_one_point<2, dimension<Box>::value>::apply(*it, mbr);
  289. for (++it; it != boost::end(multipoint); ++it)
  290. {
  291. strategy::expand::detail::point_loop
  292. <
  293. 2, dimension<Box>::value
  294. >::apply(mbr, *it);
  295. }
  296. }
  297. };
  298. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  299. namespace services
  300. {
  301. template <typename CalculationType>
  302. struct default_strategy<multi_point_tag, spherical_equatorial_tag, CalculationType>
  303. {
  304. typedef strategy::envelope::spherical_multipoint type;
  305. };
  306. template <typename CalculationType>
  307. struct default_strategy<multi_point_tag, spherical_polar_tag, CalculationType>
  308. {
  309. typedef strategy::envelope::spherical_multipoint type;
  310. };
  311. template <typename CalculationType>
  312. struct default_strategy<multi_point_tag, geographic_tag, CalculationType>
  313. {
  314. typedef strategy::envelope::spherical_multipoint type;
  315. };
  316. } // namespace services
  317. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  318. }} // namespace strategy::envelope
  319. }} // namespace boost::geometry
  320. #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_MULTIPOINT_HPP