handle_colocations.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2017-2020.
  5. // Modifications copyright (c) 2017-2020 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_HANDLE_COLOCATIONS_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
  12. #include <cstddef>
  13. #include <algorithm>
  14. #include <map>
  15. #include <vector>
  16. #include <boost/core/ignore_unused.hpp>
  17. #include <boost/range/begin.hpp>
  18. #include <boost/range/end.hpp>
  19. #include <boost/range/value_type.hpp>
  20. #include <boost/geometry/core/assert.hpp>
  21. #include <boost/geometry/core/point_order.hpp>
  22. #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
  23. #include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
  24. #include <boost/geometry/algorithms/detail/overlay/colocate_clusters.hpp>
  25. #include <boost/geometry/algorithms/detail/overlay/get_clusters.hpp>
  26. #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
  27. #include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
  28. #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
  29. #include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
  30. #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
  31. #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
  32. #include <boost/geometry/util/condition.hpp>
  33. #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
  34. # include <iostream>
  35. # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
  36. # include <boost/geometry/io/wkt/wkt.hpp>
  37. # define BOOST_GEOMETRY_DEBUG_IDENTIFIER
  38. #endif
  39. namespace boost { namespace geometry
  40. {
  41. #ifndef DOXYGEN_NO_DETAIL
  42. namespace detail { namespace overlay
  43. {
  44. // Removes clusters which have only one point left, or are empty.
  45. template <typename Turns, typename Clusters>
  46. inline void remove_clusters(Turns& turns, Clusters& clusters)
  47. {
  48. auto it = clusters.begin();
  49. while (it != clusters.end())
  50. {
  51. // Hold iterator and increase. We can erase cit, this keeps the
  52. // iterator valid (cf The standard associative-container erase idiom)
  53. auto current_it = it;
  54. ++it;
  55. auto const& turn_indices = current_it->second.turn_indices;
  56. if (turn_indices.size() == 1)
  57. {
  58. auto const turn_index = *turn_indices.begin();
  59. turns[turn_index].cluster_id = -1;
  60. clusters.erase(current_it);
  61. }
  62. }
  63. }
  64. template <typename Turns, typename Clusters>
  65. inline void cleanup_clusters(Turns& turns, Clusters& clusters)
  66. {
  67. // Removes discarded turns from clusters
  68. for (auto& pair : clusters)
  69. {
  70. auto& cinfo = pair.second;
  71. auto& indices = cinfo.turn_indices;
  72. for (auto sit = indices.begin(); sit != indices.end(); /* no increment */)
  73. {
  74. auto current_it = sit;
  75. ++sit;
  76. auto const turn_index = *current_it;
  77. if (turns[turn_index].discarded)
  78. {
  79. indices.erase(current_it);
  80. }
  81. }
  82. }
  83. remove_clusters(turns, clusters);
  84. colocate_clusters(clusters, turns);
  85. }
  86. template <typename Turn, typename IndexSet>
  87. inline void discard_colocated_turn(Turn& turn, IndexSet& indices, signed_size_type index)
  88. {
  89. turn.discarded = true;
  90. // Set cluster id to -1, but don't clear colocated flags
  91. turn.cluster_id = -1;
  92. // To remove it later from clusters
  93. indices.insert(index);
  94. }
  95. template <bool Reverse>
  96. inline bool is_interior(segment_identifier const& seg_id)
  97. {
  98. return Reverse ? seg_id.ring_index == -1 : seg_id.ring_index >= 0;
  99. }
  100. template <bool Reverse0, bool Reverse1>
  101. inline bool is_ie_turn(segment_identifier const& ext_seg_0,
  102. segment_identifier const& ext_seg_1,
  103. segment_identifier const& int_seg_0,
  104. segment_identifier const& other_seg_1)
  105. {
  106. if (ext_seg_0.source_index == ext_seg_1.source_index)
  107. {
  108. // External turn is a self-turn, dont discard internal turn for this
  109. return false;
  110. }
  111. // Compares two segment identifiers from two turns (external / one internal)
  112. // From first turn [0], both are from same polygon (multi_index),
  113. // one is exterior (-1), the other is interior (>= 0),
  114. // and the second turn [1] handles the same ring
  115. // For difference, where the rings are processed in reversal, all interior
  116. // rings become exterior and vice versa. But also the multi property changes:
  117. // rings originally from the same multi should now be considered as from
  118. // different multi polygons.
  119. // But this is not always the case, and at this point hard to figure out
  120. // (not yet implemented, TODO)
  121. bool const same_multi0 = ! Reverse0
  122. && ext_seg_0.multi_index == int_seg_0.multi_index;
  123. bool const same_multi1 = ! Reverse1
  124. && ext_seg_1.multi_index == other_seg_1.multi_index;
  125. boost::ignore_unused(same_multi1);
  126. return same_multi0
  127. && same_multi1
  128. && ! is_interior<Reverse0>(ext_seg_0)
  129. && is_interior<Reverse0>(int_seg_0)
  130. && ext_seg_1.ring_index == other_seg_1.ring_index;
  131. // The other way round is tested in another call
  132. }
  133. template
  134. <
  135. bool Reverse0, bool Reverse1, // Reverse interpretation interior/exterior
  136. typename Turns,
  137. typename Clusters
  138. >
  139. inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
  140. {
  141. std::set<signed_size_type> indices_to_remove;
  142. for (auto& pair : clusters)
  143. {
  144. cluster_info& cinfo = pair.second;
  145. indices_to_remove.clear();
  146. for (auto index : cinfo.turn_indices)
  147. {
  148. auto& turn = turns[index];
  149. segment_identifier const& seg_0 = turn.operations[0].seg_id;
  150. segment_identifier const& seg_1 = turn.operations[1].seg_id;
  151. if (! (turn.both(operation_union)
  152. || turn.combination(operation_union, operation_blocked)))
  153. {
  154. // Not a uu/ux, so cannot be colocated with a iu turn
  155. continue;
  156. }
  157. for (auto interior_index : cinfo.turn_indices)
  158. {
  159. if (index == interior_index)
  160. {
  161. continue;
  162. }
  163. // Turn with, possibly, an interior ring involved
  164. auto& interior_turn = turns[interior_index];
  165. segment_identifier const& int_seg_0 = interior_turn.operations[0].seg_id;
  166. segment_identifier const& int_seg_1 = interior_turn.operations[1].seg_id;
  167. if (is_ie_turn<Reverse0, Reverse1>(seg_0, seg_1, int_seg_0, int_seg_1))
  168. {
  169. discard_colocated_turn(interior_turn, indices_to_remove, interior_index);
  170. }
  171. if (is_ie_turn<Reverse1, Reverse0>(seg_1, seg_0, int_seg_1, int_seg_0))
  172. {
  173. discard_colocated_turn(interior_turn, indices_to_remove, interior_index);
  174. }
  175. }
  176. }
  177. // Erase from the indices (which cannot be done above)
  178. for (auto index : indices_to_remove)
  179. {
  180. cinfo.turn_indices.erase(index);
  181. }
  182. }
  183. }
  184. template
  185. <
  186. overlay_type OverlayType,
  187. typename Turns,
  188. typename Clusters
  189. >
  190. inline void set_colocation(Turns& turns, Clusters const& clusters)
  191. {
  192. for (auto const& pair : clusters)
  193. {
  194. cluster_info const& cinfo = pair.second;
  195. bool both_target = false;
  196. for (auto index : cinfo.turn_indices)
  197. {
  198. auto const& turn = turns[index];
  199. if (turn.both(operation_from_overlay<OverlayType>::value))
  200. {
  201. both_target = true;
  202. break;
  203. }
  204. }
  205. if (both_target)
  206. {
  207. for (auto index : cinfo.turn_indices)
  208. {
  209. auto& turn = turns[index];
  210. turn.has_colocated_both = true;
  211. }
  212. }
  213. }
  214. }
  215. template
  216. <
  217. typename Turns,
  218. typename Clusters
  219. >
  220. inline void check_colocation(bool& has_blocked,
  221. signed_size_type cluster_id, Turns const& turns, Clusters const& clusters)
  222. {
  223. typedef typename boost::range_value<Turns>::type turn_type;
  224. has_blocked = false;
  225. auto mit = clusters.find(cluster_id);
  226. if (mit == clusters.end())
  227. {
  228. return;
  229. }
  230. cluster_info const& cinfo = mit->second;
  231. for (auto index : cinfo.turn_indices)
  232. {
  233. turn_type const& turn = turns[index];
  234. if (turn.any_blocked())
  235. {
  236. has_blocked = true;
  237. }
  238. }
  239. }
  240. template
  241. <
  242. typename Turns,
  243. typename Clusters
  244. >
  245. inline void assign_cluster_ids(Turns& turns, Clusters const& clusters)
  246. {
  247. for (auto& turn : turns)
  248. {
  249. turn.cluster_id = -1;
  250. }
  251. for (auto const& kv : clusters)
  252. {
  253. for (auto const& index : kv.second.turn_indices)
  254. {
  255. turns[index].cluster_id = kv.first;
  256. }
  257. }
  258. }
  259. // Checks colocated turns and flags combinations of uu/other, possibly a
  260. // combination of a ring touching another geometry's interior ring which is
  261. // tangential to the exterior ring
  262. // This function can be extended to replace handle_tangencies: at each
  263. // colocation incoming and outgoing vectors should be inspected
  264. template
  265. <
  266. bool Reverse1, bool Reverse2,
  267. overlay_type OverlayType,
  268. typename Geometry0,
  269. typename Geometry1,
  270. typename Turns,
  271. typename Clusters,
  272. typename RobustPolicy
  273. >
  274. inline bool handle_colocations(Turns& turns, Clusters& clusters,
  275. RobustPolicy const& robust_policy)
  276. {
  277. static const detail::overlay::operation_type target_operation
  278. = detail::overlay::operation_from_overlay<OverlayType>::value;
  279. get_clusters(turns, clusters, robust_policy);
  280. if (clusters.empty())
  281. {
  282. return false;
  283. }
  284. assign_cluster_ids(turns, clusters);
  285. // Get colocated information here, and not later, to keep information
  286. // on turns which are discarded afterwards
  287. set_colocation<OverlayType>(turns, clusters);
  288. if (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
  289. {
  290. discard_interior_exterior_turns
  291. <
  292. do_reverse<geometry::point_order<Geometry0>::value>::value != Reverse1,
  293. do_reverse<geometry::point_order<Geometry1>::value>::value != Reverse2
  294. >(turns, clusters);
  295. }
  296. // There might be clusters having only one turn, if the rest is discarded
  297. // This is cleaned up later, after gathering the properties.
  298. #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
  299. std::cout << "*** Colocations " << map.size() << std::endl;
  300. for (auto const& kv : map)
  301. {
  302. std::cout << kv.first << std::endl;
  303. for (auto const& toi : kv.second)
  304. {
  305. detail::debug::debug_print_turn(turns[toi.turn_index]);
  306. std::cout << std::endl;
  307. }
  308. }
  309. #endif
  310. return true;
  311. }
  312. struct is_turn_index
  313. {
  314. is_turn_index(signed_size_type index)
  315. : m_index(index)
  316. {}
  317. template <typename Indexed>
  318. inline bool operator()(Indexed const& indexed) const
  319. {
  320. // Indexed is a indexed_turn_operation<Operation>
  321. return indexed.turn_index == m_index;
  322. }
  323. signed_size_type m_index;
  324. };
  325. template
  326. <
  327. typename Sbs,
  328. typename Point,
  329. typename Turns,
  330. typename Geometry1,
  331. typename Geometry2
  332. >
  333. inline bool fill_sbs(Sbs& sbs, Point& turn_point,
  334. cluster_info const& cinfo,
  335. Turns const& turns,
  336. Geometry1 const& geometry1, Geometry2 const& geometry2)
  337. {
  338. if (cinfo.turn_indices.empty())
  339. {
  340. return false;
  341. }
  342. bool first = true;
  343. for (auto turn_index : cinfo.turn_indices)
  344. {
  345. auto const& turn = turns[turn_index];
  346. if (first)
  347. {
  348. turn_point = turn.point;
  349. }
  350. for (int i = 0; i < 2; i++)
  351. {
  352. sbs.add(turn, turn.operations[i], turn_index, i, geometry1, geometry2, first);
  353. first = false;
  354. }
  355. }
  356. return true;
  357. }
  358. template
  359. <
  360. bool Reverse1, bool Reverse2,
  361. overlay_type OverlayType,
  362. typename Turns,
  363. typename Clusters,
  364. typename Geometry1,
  365. typename Geometry2,
  366. typename SideStrategy
  367. >
  368. inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
  369. operation_type for_operation,
  370. Geometry1 const& geometry1, Geometry2 const& geometry2,
  371. SideStrategy const& strategy)
  372. {
  373. typedef typename boost::range_value<Turns>::type turn_type;
  374. typedef typename turn_type::point_type point_type;
  375. typedef typename turn_type::turn_operation_type turn_operation_type;
  376. // Define sorter, sorting counter-clockwise such that polygons are on the
  377. // right side
  378. typedef sort_by_side::side_sorter
  379. <
  380. Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less<int>
  381. > sbs_type;
  382. for (auto& pair : clusters)
  383. {
  384. cluster_info& cinfo = pair.second;
  385. sbs_type sbs(strategy);
  386. point_type turn_point; // should be all the same for all turns in cluster
  387. if (! fill_sbs(sbs, turn_point, cinfo, turns, geometry1, geometry2))
  388. {
  389. continue;
  390. }
  391. sbs.apply(turn_point);
  392. sbs.find_open();
  393. sbs.assign_zones(for_operation);
  394. cinfo.open_count = sbs.open_count(for_operation);
  395. bool const set_startable = OverlayType != overlay_dissolve;
  396. // Unset the startable flag for all 'closed' zones. This does not
  397. // apply for self-turns, because those counts are not from both
  398. // polygons
  399. for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
  400. {
  401. typename sbs_type::rp const& ranked = sbs.m_ranked_points[i];
  402. turn_type& turn = turns[ranked.turn_index];
  403. turn_operation_type& op = turn.operations[ranked.operation_index];
  404. if (set_startable
  405. && for_operation == operation_union && cinfo.open_count == 0)
  406. {
  407. op.enriched.startable = false;
  408. }
  409. if (ranked.direction != sort_by_side::dir_to)
  410. {
  411. continue;
  412. }
  413. op.enriched.count_left = ranked.count_left;
  414. op.enriched.count_right = ranked.count_right;
  415. op.enriched.rank = ranked.rank;
  416. op.enriched.zone = ranked.zone;
  417. if (! set_startable)
  418. {
  419. continue;
  420. }
  421. if (BOOST_GEOMETRY_CONDITION(OverlayType == overlay_difference)
  422. && is_self_turn<OverlayType>(turn))
  423. {
  424. // TODO: investigate
  425. continue;
  426. }
  427. if ((for_operation == operation_union
  428. && ranked.count_left != 0)
  429. || (for_operation == operation_intersection
  430. && ranked.count_right != 2))
  431. {
  432. op.enriched.startable = false;
  433. }
  434. }
  435. }
  436. }
  437. }} // namespace detail::overlay
  438. #endif //DOXYGEN_NO_DETAIL
  439. }} // namespace boost::geometry
  440. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP