assign_parents.hpp 14 KB

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