assign_parents.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2017-2023.
  5. // Modifications copyright (c) 2017-2023 Oracle and/or its affiliates.
  6. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  7. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  8. // Use, modification and distribution is subject to the Boost Software License,
  9. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  10. // http://www.boost.org/LICENSE_1_0.txt)
  11. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
  12. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
  13. #include <boost/range/begin.hpp>
  14. #include <boost/range/end.hpp>
  15. #include <boost/geometry/core/coordinate_type.hpp>
  16. #include <boost/geometry/algorithms/area_result.hpp>
  17. #include <boost/geometry/algorithms/envelope.hpp>
  18. #include <boost/geometry/algorithms/expand.hpp>
  19. #include <boost/geometry/algorithms/detail/covered_by/implementation.hpp>
  20. #include <boost/geometry/algorithms/detail/partition.hpp>
  21. #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
  22. #include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp>
  23. #include <boost/geometry/geometries/box.hpp>
  24. #include <boost/geometry/util/for_each_with_index.hpp>
  25. namespace boost { namespace geometry
  26. {
  27. #ifndef DOXYGEN_NO_DETAIL
  28. namespace detail { namespace overlay
  29. {
  30. template
  31. <
  32. typename Item,
  33. typename InnerGeometry,
  34. typename Geometry1, typename Geometry2,
  35. typename RingCollection,
  36. typename Strategy
  37. >
  38. static inline bool within_selected_input(Item const& item2,
  39. InnerGeometry const& inner_geometry,
  40. ring_identifier const& outer_id,
  41. Geometry1 const& geometry1, Geometry2 const& geometry2,
  42. RingCollection const& collection,
  43. Strategy const& strategy)
  44. {
  45. typedef typename geometry::tag<Geometry1>::type tag1;
  46. typedef typename geometry::tag<Geometry2>::type tag2;
  47. // NOTE: range_in_geometry first checks the item2.point and then
  48. // if this point is on boundary it checks points of inner_geometry
  49. // ring until a point inside/outside other geometry ring is found
  50. switch (outer_id.source_index)
  51. {
  52. // covered_by
  53. case 0 :
  54. return range_in_geometry(item2.point, inner_geometry,
  55. get_ring<tag1>::apply(outer_id, geometry1), strategy) >= 0;
  56. case 1 :
  57. return range_in_geometry(item2.point, inner_geometry,
  58. get_ring<tag2>::apply(outer_id, geometry2), strategy) >= 0;
  59. case 2 :
  60. return range_in_geometry(item2.point, inner_geometry,
  61. get_ring<void>::apply(outer_id, collection), strategy) >= 0;
  62. }
  63. return false;
  64. }
  65. template
  66. <
  67. typename Item,
  68. typename Geometry1, typename Geometry2,
  69. typename RingCollection,
  70. typename Strategy
  71. >
  72. static inline bool within_selected_input(Item const& item2,
  73. ring_identifier const& inner_id, ring_identifier const& outer_id,
  74. Geometry1 const& geometry1, Geometry2 const& geometry2,
  75. RingCollection const& collection,
  76. Strategy const& strategy)
  77. {
  78. typedef typename geometry::tag<Geometry1>::type tag1;
  79. typedef typename geometry::tag<Geometry2>::type tag2;
  80. switch (inner_id.source_index)
  81. {
  82. case 0 :
  83. return within_selected_input(item2,
  84. get_ring<tag1>::apply(inner_id, geometry1),
  85. outer_id, geometry1, geometry2, collection, strategy);
  86. case 1 :
  87. return within_selected_input(item2,
  88. get_ring<tag2>::apply(inner_id, geometry2),
  89. outer_id, geometry1, geometry2, collection, strategy);
  90. case 2 :
  91. return within_selected_input(item2,
  92. get_ring<void>::apply(inner_id, collection),
  93. outer_id, geometry1, geometry2, collection, strategy);
  94. }
  95. return false;
  96. }
  97. template <typename Point, typename AreaType>
  98. struct ring_info_helper
  99. {
  100. ring_identifier id;
  101. AreaType real_area;
  102. AreaType abs_area;
  103. model::box<Point> envelope;
  104. inline ring_info_helper()
  105. : real_area(0), abs_area(0)
  106. {}
  107. inline ring_info_helper(ring_identifier i, AreaType const& a)
  108. : id(i), real_area(a), abs_area(geometry::math::abs(a))
  109. {}
  110. };
  111. template <typename Strategy>
  112. struct ring_info_helper_get_box
  113. {
  114. ring_info_helper_get_box(Strategy const& strategy)
  115. : m_strategy(strategy)
  116. {}
  117. template <typename Box, typename InputItem>
  118. inline void apply(Box& total, InputItem const& item) const
  119. {
  120. assert_coordinate_type_equal(total, item.envelope);
  121. geometry::expand(total, item.envelope, m_strategy);
  122. }
  123. Strategy const& m_strategy;
  124. };
  125. template <typename Strategy>
  126. struct ring_info_helper_overlaps_box
  127. {
  128. ring_info_helper_overlaps_box(Strategy const& strategy)
  129. : m_strategy(strategy)
  130. {}
  131. template <typename Box, typename InputItem>
  132. inline bool apply(Box const& box, InputItem const& item) const
  133. {
  134. assert_coordinate_type_equal(box, item.envelope);
  135. return ! geometry::detail::disjoint::disjoint_box_box(
  136. box, item.envelope, m_strategy);
  137. }
  138. Strategy const& m_strategy;
  139. };
  140. // Segments intersection Strategy
  141. template
  142. <
  143. typename Geometry1,
  144. typename Geometry2,
  145. typename Collection,
  146. typename RingMap,
  147. typename Strategy
  148. >
  149. struct assign_visitor
  150. {
  151. typedef typename RingMap::mapped_type ring_info_type;
  152. Geometry1 const& m_geometry1;
  153. Geometry2 const& m_geometry2;
  154. Collection const& m_collection;
  155. RingMap& m_ring_map;
  156. Strategy const& m_strategy;
  157. bool m_check_for_orientation;
  158. inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c,
  159. RingMap& map, Strategy const& strategy, bool check)
  160. : m_geometry1(g1)
  161. , m_geometry2(g2)
  162. , m_collection(c)
  163. , m_ring_map(map)
  164. , m_strategy(strategy)
  165. , m_check_for_orientation(check)
  166. {}
  167. template <typename Item>
  168. inline bool apply(Item const& outer, Item const& inner, bool first = true)
  169. {
  170. if (first && outer.abs_area < inner.abs_area)
  171. {
  172. // Apply with reversed arguments
  173. apply(inner, outer, false);
  174. return true;
  175. }
  176. if (m_check_for_orientation
  177. || (math::larger(outer.real_area, 0)
  178. && math::smaller(inner.real_area, 0)))
  179. {
  180. ring_info_type& inner_in_map = m_ring_map[inner.id];
  181. if (geometry::covered_by(inner_in_map.point, outer.envelope, m_strategy)
  182. && within_selected_input(inner_in_map, inner.id, outer.id,
  183. m_geometry1, m_geometry2, m_collection,
  184. m_strategy)
  185. )
  186. {
  187. // Assign a parent if there was no earlier parent, or the newly
  188. // found parent is smaller than the previous one
  189. if (inner_in_map.parent.source_index == -1
  190. || outer.abs_area < inner_in_map.parent_area)
  191. {
  192. inner_in_map.parent = outer.id;
  193. inner_in_map.parent_area = outer.abs_area;
  194. }
  195. }
  196. }
  197. return true;
  198. }
  199. };
  200. template
  201. <
  202. overlay_type OverlayType,
  203. typename Geometry1, typename Geometry2,
  204. typename RingCollection,
  205. typename RingMap,
  206. typename Strategy
  207. >
  208. inline void assign_parents(Geometry1 const& geometry1,
  209. Geometry2 const& geometry2,
  210. RingCollection const& collection,
  211. RingMap& ring_map,
  212. Strategy const& strategy)
  213. {
  214. static bool const is_difference = OverlayType == overlay_difference;
  215. static bool const is_buffer = OverlayType == overlay_buffer;
  216. static bool const is_dissolve = OverlayType == overlay_dissolve;
  217. static bool const check_for_orientation = is_buffer || is_dissolve;
  218. typedef typename geometry::tag<Geometry1>::type tag1;
  219. typedef typename geometry::tag<Geometry2>::type tag2;
  220. typedef typename RingMap::mapped_type ring_info_type;
  221. typedef typename ring_info_type::point_type point_type;
  222. typedef model::box<point_type> box_type;
  223. typedef typename geometry::area_result
  224. <
  225. point_type, Strategy // TODO: point_type is technically incorrect
  226. >::type area_result_type;
  227. {
  228. std::size_t count_total = ring_map.size();
  229. std::size_t count_positive = 0;
  230. std::size_t index_positive = 0; // only used if count_positive>0
  231. // Copy to vector (this might be obsolete, using the map directly)
  232. using helper = ring_info_helper<point_type, area_result_type>;
  233. std::vector<helper> vector(count_total);
  234. for_each_with_index(ring_map, [&](std::size_t index, auto const& pair)
  235. {
  236. vector[index] = helper(pair.first, pair.second.get_area());
  237. helper& item = vector[index];
  238. switch(pair.first.source_index)
  239. {
  240. case 0 :
  241. geometry::envelope(get_ring<tag1>::apply(pair.first, geometry1),
  242. item.envelope, strategy);
  243. break;
  244. case 1 :
  245. geometry::envelope(get_ring<tag2>::apply(pair.first, geometry2),
  246. item.envelope, strategy);
  247. break;
  248. case 2 :
  249. geometry::envelope(get_ring<void>::apply(pair.first, collection),
  250. item.envelope, strategy);
  251. break;
  252. }
  253. // Expand envelope slightly
  254. expand_by_epsilon(item.envelope);
  255. if (item.real_area > 0)
  256. {
  257. count_positive++;
  258. index_positive = index;
  259. }
  260. });
  261. if (! check_for_orientation)
  262. {
  263. if (count_positive == count_total)
  264. {
  265. // Optimization for only positive rings
  266. // -> no assignment of parents or reversal necessary, ready here.
  267. return;
  268. }
  269. if (count_positive == 1 && ! is_difference && ! is_dissolve)
  270. {
  271. // Optimization for one outer ring
  272. // -> assign this as parent to all others (all interior rings)
  273. // In unions, this is probably the most occuring case and gives
  274. // a dramatic improvement (factor 5 for star_comb testcase)
  275. // In difference or other cases where interior rings might be
  276. // located outside the outer ring, this cannot be done
  277. ring_identifier id_of_positive = vector[index_positive].id;
  278. ring_info_type& outer = ring_map[id_of_positive];
  279. for_each_with_index(vector, [&](std::size_t index, auto const& item)
  280. {
  281. if (index != index_positive)
  282. {
  283. ring_info_type& inner = ring_map[item.id];
  284. inner.parent = id_of_positive;
  285. outer.children.push_back(item.id);
  286. }
  287. });
  288. return;
  289. }
  290. }
  291. assign_visitor
  292. <
  293. Geometry1, Geometry2,
  294. RingCollection, RingMap,
  295. Strategy
  296. > visitor(geometry1, geometry2, collection, ring_map, strategy, check_for_orientation);
  297. geometry::partition
  298. <
  299. box_type
  300. >::apply(vector, visitor,
  301. ring_info_helper_get_box<Strategy>(strategy),
  302. ring_info_helper_overlaps_box<Strategy>(strategy));
  303. }
  304. if (check_for_orientation)
  305. {
  306. for (auto& pair : ring_map)
  307. {
  308. ring_info_type& info = pair.second;
  309. if (geometry::math::equals(info.get_area(), 0))
  310. {
  311. info.discarded = true;
  312. }
  313. else if (info.parent.source_index >= 0)
  314. {
  315. ring_info_type const& parent = ring_map[info.parent];
  316. bool const pos = math::larger(info.get_area(), 0);
  317. bool const parent_pos = math::larger(parent.area, 0);
  318. bool const double_neg = is_dissolve && ! pos && ! parent_pos;
  319. if ((pos && parent_pos) || double_neg)
  320. {
  321. // Discard positive inner ring with positive parent
  322. // Also, for some cases (dissolve), negative inner ring
  323. // with negative parent should be discarded
  324. info.discarded = true;
  325. }
  326. if (pos || info.discarded)
  327. {
  328. // Remove parent ID from any positive or discarded inner rings
  329. info.parent.source_index = -1;
  330. }
  331. }
  332. else if (info.parent.source_index < 0
  333. && math::smaller(info.get_area(), 0))
  334. {
  335. // Reverse negative ring without parent
  336. info.reversed = true;
  337. }
  338. }
  339. }
  340. // Assign childlist
  341. for (auto& pair : ring_map)
  342. {
  343. if (pair.second.parent.source_index >= 0)
  344. {
  345. ring_map[pair.second.parent].children.push_back(pair.first);
  346. }
  347. }
  348. }
  349. // Version for one geometry (called by buffer/dissolve)
  350. template
  351. <
  352. overlay_type OverlayType,
  353. typename Geometry,
  354. typename RingCollection,
  355. typename RingMap,
  356. typename Strategy
  357. >
  358. inline void assign_parents(Geometry const& geometry,
  359. RingCollection const& collection,
  360. RingMap& ring_map,
  361. Strategy const& strategy)
  362. {
  363. // Call it with an empty geometry as second geometry (source_id == 1)
  364. // (ring_map should be empty for source_id==1)
  365. Geometry empty;
  366. assign_parents<OverlayType>(geometry, empty,
  367. collection, ring_map, strategy);
  368. }
  369. }} // namespace detail::overlay
  370. #endif // DOXYGEN_NO_DETAIL
  371. }} // namespace geometry
  372. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP