handle_colocations.hpp 16 KB

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