merge_elements.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // Boost.Geometry
  2. // Copyright (c) 2022-2023, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Licensed under the Boost Software License version 1.0.
  6. // http://www.boost.org/users/license.html
  7. #ifndef BOOST_GEOMETRY_ALGORITHMS_MERGE_ELEMENTS_HPP
  8. #define BOOST_GEOMETRY_ALGORITHMS_MERGE_ELEMENTS_HPP
  9. #include <vector>
  10. #include <boost/geometry/algorithms/detail/point_on_border.hpp>
  11. #include <boost/geometry/algorithms/detail/visit.hpp>
  12. #include <boost/geometry/algorithms/difference.hpp>
  13. #include <boost/geometry/algorithms/union.hpp>
  14. #include <boost/geometry/core/coordinate_system.hpp>
  15. #include <boost/geometry/core/coordinate_type.hpp>
  16. #include <boost/geometry/core/point_type.hpp>
  17. #include <boost/geometry/core/tag.hpp>
  18. #include <boost/geometry/core/tags.hpp>
  19. #include <boost/geometry/core/visit.hpp>
  20. #include <boost/geometry/geometries/point.hpp>
  21. #include <boost/geometry/policies/compare.hpp>
  22. #include <boost/geometry/util/range.hpp>
  23. #include <boost/geometry/strategies/relate/cartesian.hpp>
  24. #include <boost/geometry/strategies/relate/geographic.hpp>
  25. #include <boost/geometry/strategies/relate/spherical.hpp>
  26. namespace boost { namespace geometry
  27. {
  28. #ifndef DOXYGEN_NO_DETAIL
  29. namespace detail { namespace merge_elements
  30. {
  31. template <typename T>
  32. using is_pla = util::bool_constant<util::is_pointlike<T>::value || util::is_linear<T>::value || util::is_areal<T>::value>;
  33. template <typename T, typename ...Ts>
  34. struct are_areal : util::bool_constant<util::is_areal<T>::value && are_areal<Ts...>::value> {};
  35. template <typename T>
  36. struct are_areal<T> : util::is_areal<T> {};
  37. template <typename T, typename ...Ts>
  38. struct are_linear : util::bool_constant<util::is_linear<T>::value && are_linear<Ts...>::value> {};
  39. template <typename T>
  40. struct are_linear<T> : util::is_linear<T> {};
  41. template <typename T, typename ...Ts>
  42. struct are_pointlike : util::bool_constant<util::is_pointlike<T>::value && are_pointlike<Ts...>::value> {};
  43. template <typename T>
  44. struct are_pointlike<T> : util::is_pointlike<T> {};
  45. template <typename ...Ts>
  46. using are_same_kind = util::bool_constant<are_areal<Ts...>::value || are_linear<Ts...>::value || are_pointlike<Ts...>::value>;
  47. template
  48. <
  49. typename Geometry, typename It, typename PointLike, typename Linear, typename Areal,
  50. std::enable_if_t<util::is_areal<Geometry>::value, int> = 0
  51. >
  52. inline void distribute_element(Geometry const& geometry, It it, PointLike& , Linear&, Areal& areal)
  53. {
  54. typename geometry::point_type<Geometry>::type point;
  55. if (geometry::point_on_border(point, geometry))
  56. {
  57. using point_t = typename Areal::value_type::first_type;
  58. areal.emplace_back(point_t(geometry::get<0>(point), geometry::get<1>(point)), it);
  59. }
  60. }
  61. template
  62. <
  63. typename Geometry, typename It, typename PointLike, typename Linear, typename Areal,
  64. std::enable_if_t<util::is_linear<Geometry>::value, int> = 0
  65. >
  66. inline void distribute_element(Geometry const& geometry, It it, PointLike& , Linear& linear, Areal& )
  67. {
  68. typename geometry::point_type<Geometry>::type point;
  69. if (geometry::point_on_border(point, geometry))
  70. {
  71. using point_t = typename Linear::value_type::first_type;
  72. linear.emplace_back(point_t(geometry::get<0>(point), geometry::get<1>(point)), it);
  73. }
  74. }
  75. template
  76. <
  77. typename Geometry, typename It, typename PointLike, typename Linear, typename Areal,
  78. std::enable_if_t<util::is_pointlike<Geometry>::value, int> = 0
  79. >
  80. inline void distribute_element(Geometry const& geometry, It it, PointLike& pointlike, Linear& , Areal& )
  81. {
  82. typename geometry::point_type<Geometry>::type point;
  83. if (geometry::point_on_border(point, geometry))
  84. {
  85. using point_t = typename Linear::value_type::first_type;
  86. pointlike.emplace_back(point_t(geometry::get<0>(point), geometry::get<1>(point)), it);
  87. }
  88. }
  89. template
  90. <
  91. typename Geometry, typename It, typename PointLike, typename Linear, typename Areal,
  92. std::enable_if_t<! is_pla<Geometry>::value, int> = 0
  93. >
  94. inline void distribute_element(Geometry const& , It const&, PointLike const& , Linear const&, Areal const&)
  95. {}
  96. template
  97. <
  98. typename Geometry, typename MultiGeometry,
  99. std::enable_if_t<are_same_kind<Geometry, MultiGeometry>::value, int> = 0
  100. >
  101. inline void convert(Geometry const& geometry, MultiGeometry& result)
  102. {
  103. geometry::convert(geometry, result);
  104. }
  105. template
  106. <
  107. typename Geometry, typename MultiGeometry,
  108. std::enable_if_t<! are_same_kind<Geometry, MultiGeometry>::value, int> = 0
  109. >
  110. inline void convert(Geometry const& , MultiGeometry const& )
  111. {}
  112. template
  113. <
  114. typename Geometry1, typename Geometry2, typename MultiGeometry, typename Strategy,
  115. std::enable_if_t<are_same_kind<Geometry1, Geometry2, MultiGeometry>::value, int> = 0
  116. >
  117. inline void union_(Geometry1 const& geometry1, Geometry2 const& geometry2, MultiGeometry& result, Strategy const& strategy)
  118. {
  119. geometry::union_(geometry1, geometry2, result, strategy);
  120. }
  121. template
  122. <
  123. typename Geometry1, typename Geometry2, typename MultiGeometry, typename Strategy,
  124. std::enable_if_t<! are_same_kind<Geometry1, Geometry2, MultiGeometry>::value, int> = 0
  125. >
  126. inline void union_(Geometry1 const& , Geometry2 const& , MultiGeometry const& , Strategy const&)
  127. {}
  128. template <typename It>
  129. struct merge_data
  130. {
  131. merge_data(It first_, It last_)
  132. : first(first_), last(last_)
  133. {}
  134. It first, last;
  135. bool merge_results = false;
  136. };
  137. template <typename GeometryCollection, typename RandomIt, typename MultiGeometry, typename Strategy>
  138. inline void merge(RandomIt const first, RandomIt const last, MultiGeometry& out, Strategy const& strategy)
  139. {
  140. auto const size = last - first;
  141. if (size <= 0)
  142. {
  143. return;
  144. }
  145. auto const less = [](auto const& l, auto const& r)
  146. {
  147. return geometry::less<void, -1, Strategy>()(l.first, r.first);
  148. };
  149. std::vector<merge_data<RandomIt>> stack_in;
  150. std::vector<MultiGeometry> stack_out;
  151. stack_in.reserve(size / 2 + 1);
  152. stack_out.reserve(size / 2 + 1);
  153. stack_in.emplace_back(first, last);
  154. while (! stack_in.empty())
  155. {
  156. auto & b = stack_in.back();
  157. if (! b.merge_results)
  158. {
  159. auto const s = b.last - b.first;
  160. if (s > 2)
  161. {
  162. RandomIt const mid = b.first + s / 2;
  163. std::nth_element(b.first, mid, b.last, less);
  164. RandomIt const fir = b.first;
  165. RandomIt const las = b.last;
  166. b.merge_results = true;
  167. stack_in.emplace_back(fir, mid);
  168. stack_in.emplace_back(mid, las);
  169. }
  170. else if (s == 2)
  171. {
  172. MultiGeometry result;
  173. // VERSION 1
  174. // traits::iter_visit<GeometryCollection>::apply([&](auto const& g1)
  175. // {
  176. // traits::iter_visit<GeometryCollection>::apply([&](auto const& g2)
  177. // {
  178. // merge_elements::union_(g1, g2, result, strategy);
  179. // }, (b.first + 1)->second);
  180. // }, b.first->second);
  181. // VERSION 2
  182. // calling iter_visit non-recursively seems to decrease compilation time
  183. // greately with GCC
  184. MultiGeometry temp1, temp2;
  185. traits::iter_visit<GeometryCollection>::apply([&](auto const& g1)
  186. {
  187. merge_elements::convert(g1, temp1);
  188. }, b.first->second);
  189. traits::iter_visit<GeometryCollection>::apply([&](auto const& g2)
  190. {
  191. merge_elements::convert(g2, temp2);
  192. }, (b.first + 1)->second);
  193. geometry::union_(temp1, temp2, result, strategy);
  194. stack_out.push_back(std::move(result));
  195. stack_in.pop_back();
  196. }
  197. else if (s == 1)
  198. {
  199. MultiGeometry result;
  200. traits::iter_visit<GeometryCollection>::apply([&](auto const& g)
  201. {
  202. merge_elements::convert(g, result);
  203. }, b.first->second);
  204. stack_out.push_back(std::move(result));
  205. stack_in.pop_back();
  206. }
  207. }
  208. else if (b.merge_results)
  209. {
  210. MultiGeometry m2 = std::move(stack_out.back());
  211. stack_out.pop_back();
  212. MultiGeometry m1 = std::move(stack_out.back());
  213. stack_out.pop_back();
  214. MultiGeometry result;
  215. geometry::union_(m1, m2, result, strategy);
  216. stack_out.push_back(std::move(result));
  217. stack_in.pop_back();
  218. }
  219. }
  220. out = std::move(stack_out.back());
  221. }
  222. template <typename MultiGeometry, typename Geometry, typename Strategy>
  223. inline void subtract(MultiGeometry & multi, Geometry const& geometry, Strategy const& strategy)
  224. {
  225. MultiGeometry temp;
  226. geometry::difference(multi, geometry, temp, strategy);
  227. multi = std::move(temp);
  228. }
  229. struct merge_gc
  230. {
  231. template <typename GeometryCollection, typename Strategy>
  232. static void apply(GeometryCollection const& geometry_collection,
  233. GeometryCollection & out,
  234. Strategy const& strategy)
  235. {
  236. using original_point_t = typename geometry::point_type<GeometryCollection>::type;
  237. using iterator_t = typename boost::range_iterator<GeometryCollection const>::type;
  238. using coordinate_t = typename geometry::coordinate_type<original_point_t>::type;
  239. using cs_t = typename geometry::coordinate_system<original_point_t>::type;
  240. using point_t = model::point<coordinate_t, 2, cs_t>;
  241. using multi_point_t = typename util::sequence_find_if
  242. <
  243. typename traits::geometry_types<std::remove_const_t<GeometryCollection>>::type,
  244. util::is_multi_point
  245. >::type;
  246. using multi_linestring_t = typename util::sequence_find_if
  247. <
  248. typename traits::geometry_types<std::remove_const_t<GeometryCollection>>::type,
  249. util::is_multi_linestring
  250. >::type;
  251. using multi_polygon_t = typename util::sequence_find_if
  252. <
  253. typename traits::geometry_types<std::remove_const_t<GeometryCollection>>::type,
  254. util::is_multi_polygon
  255. >::type;
  256. // NOTE: Right now GC containing all of the above is required but technically
  257. // we could allow only some combinations and the algorithm below could
  258. // normalize GC accordingly.
  259. multi_point_t multi_point;
  260. multi_linestring_t multi_linestring;
  261. multi_polygon_t multi_polygon;
  262. std::vector<std::pair<point_t, iterator_t>> pointlike;
  263. std::vector<std::pair<point_t, iterator_t>> linear;
  264. std::vector<std::pair<point_t, iterator_t>> areal;
  265. detail::visit_breadth_first_impl<true>::apply([&](auto const& g, auto it)
  266. {
  267. merge_elements::distribute_element(g, it, pointlike, linear, areal);
  268. return true;
  269. }, geometry_collection);
  270. // TODO: make this optional?
  271. // TODO: merge linear at the end? (difference can break linear rings, their parts would be joined)
  272. merge<GeometryCollection>(pointlike.begin(), pointlike.end(), multi_point, strategy);
  273. merge<GeometryCollection>(linear.begin(), linear.end(), multi_linestring, strategy);
  274. merge<GeometryCollection>(areal.begin(), areal.end(), multi_polygon, strategy);
  275. // L \ A
  276. subtract(multi_linestring, multi_polygon, strategy);
  277. // P \ A
  278. subtract(multi_point, multi_polygon, strategy);
  279. // P \ L
  280. subtract(multi_point, multi_linestring, strategy);
  281. if (! geometry::is_empty(multi_point))
  282. {
  283. range::emplace_back(out, std::move(multi_point));
  284. }
  285. if (! geometry::is_empty(multi_linestring))
  286. {
  287. range::emplace_back(out, std::move(multi_linestring));
  288. }
  289. if (! geometry::is_empty(multi_polygon))
  290. {
  291. range::emplace_back(out, std::move(multi_polygon));
  292. }
  293. }
  294. };
  295. }} // namespace detail::merge_elements
  296. #endif // DOXYGEN_NO_DETAIL
  297. #ifndef DOXYGEN_NO_DISPATCH
  298. namespace dispatch
  299. {
  300. template <typename Geometry, typename Tag = typename tag<Geometry>::type>
  301. struct merge_elements
  302. : not_implemented<Geometry, Tag>
  303. {};
  304. template <typename GeometryCollection>
  305. struct merge_elements<GeometryCollection, geometry_collection_tag>
  306. : geometry::detail::merge_elements::merge_gc
  307. {};
  308. } // namespace dispatch
  309. #endif
  310. namespace resolve_strategy
  311. {
  312. template <typename Strategy>
  313. struct merge_elements
  314. {
  315. template <typename Geometry>
  316. static void apply(Geometry const& geometry, Geometry & out, Strategy const& strategy)
  317. {
  318. dispatch::merge_elements
  319. <
  320. Geometry
  321. >::apply(geometry, out, strategy);
  322. }
  323. };
  324. template <>
  325. struct merge_elements<default_strategy>
  326. {
  327. template <typename Geometry>
  328. static void apply(Geometry const& geometry, Geometry & out, default_strategy)
  329. {
  330. using strategy_type = typename strategies::relate::services::default_strategy
  331. <
  332. Geometry, Geometry
  333. >::type;
  334. dispatch::merge_elements
  335. <
  336. Geometry
  337. >::apply(geometry, out, strategy_type());
  338. }
  339. };
  340. } // namespace resolve_strategy
  341. template <typename Geometry, typename Strategy>
  342. inline void merge_elements(Geometry const& geometry, Geometry & out, Strategy const& strategy)
  343. {
  344. resolve_strategy::merge_elements
  345. <
  346. Strategy
  347. >::apply(geometry, out, strategy);
  348. }
  349. template <typename Geometry>
  350. inline void merge_elements(Geometry const& geometry, Geometry & out)
  351. {
  352. resolve_strategy::merge_elements
  353. <
  354. default_strategy
  355. >::apply(geometry, out, default_strategy());
  356. }
  357. }} // namespace boost::geometry
  358. #endif // BOOST_GEOMETRY_ALGORITHMS_MERGE_ELEMENTS_HPP