traversal_switch_detector.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2015-2016 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2018.
  4. // Modifications copyright (c) 2018 Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
  10. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
  11. #include <cstddef>
  12. #include <map>
  13. #include <boost/range.hpp>
  14. #include <boost/geometry/algorithms/detail/ring_identifier.hpp>
  15. #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
  16. #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
  17. #include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
  18. #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
  19. #include <boost/geometry/core/access.hpp>
  20. #include <boost/geometry/core/assert.hpp>
  21. #include <boost/geometry/util/condition.hpp>
  22. namespace boost { namespace geometry
  23. {
  24. #ifndef DOXYGEN_NO_DETAIL
  25. namespace detail { namespace overlay
  26. {
  27. // Generic function (is this used somewhere else too?)
  28. inline ring_identifier ring_id_by_seg_id(segment_identifier const& seg_id)
  29. {
  30. return ring_identifier(seg_id.source_index, seg_id.multi_index, seg_id.ring_index);
  31. }
  32. template
  33. <
  34. bool Reverse1,
  35. bool Reverse2,
  36. overlay_type OverlayType,
  37. typename Geometry1,
  38. typename Geometry2,
  39. typename Turns,
  40. typename Clusters,
  41. typename RobustPolicy,
  42. typename Visitor
  43. >
  44. struct traversal_switch_detector
  45. {
  46. static const operation_type target_operation
  47. = operation_from_overlay<OverlayType>::value;
  48. static const operation_type opposite_operation
  49. = target_operation == operation_union
  50. ? operation_intersection
  51. : operation_union;
  52. enum isolation_type
  53. {
  54. isolation_unknown = -1,
  55. isolation_no = 0,
  56. isolation_yes = 1,
  57. isolation_multiple = 2
  58. };
  59. typedef typename boost::range_value<Turns>::type turn_type;
  60. typedef typename turn_type::turn_operation_type turn_operation_type;
  61. typedef std::set<signed_size_type> set_type;
  62. // Per ring, first turns are collected (in turn_indices), and later
  63. // a region_id is assigned
  64. struct merged_ring_properties
  65. {
  66. signed_size_type region_id;
  67. set_type turn_indices;
  68. merged_ring_properties()
  69. : region_id(-1)
  70. {}
  71. };
  72. struct connection_properties
  73. {
  74. std::size_t count;
  75. // Contains turn-index OR, if clustered, minus-cluster_id
  76. set_type unique_turn_ids;
  77. connection_properties()
  78. : count(0)
  79. {}
  80. };
  81. typedef std::map<signed_size_type, connection_properties> connection_map;
  82. // Per region, a set of properties is maintained, including its connections
  83. // to other regions
  84. struct region_properties
  85. {
  86. signed_size_type region_id;
  87. isolation_type isolated;
  88. set_type unique_turn_ids;
  89. // Maps from connected region_id to their properties
  90. connection_map connected_region_counts;
  91. region_properties()
  92. : region_id(-1)
  93. , isolated(isolation_unknown)
  94. {}
  95. };
  96. // Keeps turn indices per ring
  97. typedef std::map<ring_identifier, merged_ring_properties > merge_map;
  98. typedef std::map<signed_size_type, region_properties> region_connection_map;
  99. typedef set_type::const_iterator set_iterator;
  100. inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2,
  101. Turns& turns, Clusters& clusters,
  102. RobustPolicy const& robust_policy, Visitor& visitor)
  103. : m_geometry1(geometry1)
  104. , m_geometry2(geometry2)
  105. , m_turns(turns)
  106. , m_clusters(clusters)
  107. , m_robust_policy(robust_policy)
  108. , m_visitor(visitor)
  109. {
  110. }
  111. bool one_connection_to_another_region(region_properties const& region) const
  112. {
  113. // For example:
  114. // +----------------------+
  115. // | __ |
  116. // | / \|
  117. // | | x
  118. // | \__/|
  119. // | |
  120. // +----------------------+
  121. if (region.connected_region_counts.size() == 1)
  122. {
  123. connection_properties const& cprop = region.connected_region_counts.begin()->second;
  124. return cprop.count <= 1;
  125. }
  126. return region.connected_region_counts.empty();
  127. }
  128. // TODO: might be combined with previous
  129. bool multiple_connections_to_one_region(region_properties const& region) const
  130. {
  131. // For example:
  132. // +----------------------+
  133. // | __ |
  134. // | / \|
  135. // | | x
  136. // | \ /|
  137. // | / \|
  138. // | | x
  139. // | \__/|
  140. // | |
  141. // +----------------------+
  142. if (region.connected_region_counts.size() == 1)
  143. {
  144. connection_properties const& cprop = region.connected_region_counts.begin()->second;
  145. return cprop.count > 1;
  146. }
  147. return false;
  148. }
  149. bool one_connection_to_multiple_regions(region_properties const& region) const
  150. {
  151. // For example:
  152. // +----------------------+
  153. // | __ | __
  154. // | / \|/ |
  155. // | | x |
  156. // | \__/|\__|
  157. // | |
  158. // +----------------------+
  159. bool first = true;
  160. signed_size_type first_turn_id = 0;
  161. for (typename connection_map::const_iterator it = region.connected_region_counts.begin();
  162. it != region.connected_region_counts.end(); ++it)
  163. {
  164. connection_properties const& cprop = it->second;
  165. if (cprop.count != 1)
  166. {
  167. return false;
  168. }
  169. signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin();
  170. if (first)
  171. {
  172. first_turn_id = unique_turn_id;
  173. first = false;
  174. }
  175. else if (first_turn_id != unique_turn_id)
  176. {
  177. return false;
  178. }
  179. }
  180. return true;
  181. }
  182. bool ii_turn_connects_two_regions(region_properties const& region,
  183. region_properties const& connected_region,
  184. signed_size_type turn_index) const
  185. {
  186. turn_type const& turn = m_turns[turn_index];
  187. if (! turn.both(operation_intersection))
  188. {
  189. return false;
  190. }
  191. signed_size_type const id0 = turn.operations[0].enriched.region_id;
  192. signed_size_type const id1 = turn.operations[1].enriched.region_id;
  193. return (id0 == region.region_id && id1 == connected_region.region_id)
  194. || (id1 == region.region_id && id0 == connected_region.region_id);
  195. }
  196. bool isolated_multiple_connection(region_properties const& region,
  197. region_properties const& connected_region) const
  198. {
  199. if (connected_region.isolated != isolation_multiple)
  200. {
  201. return false;
  202. }
  203. // First step: compare turns of regions with turns of connected region
  204. set_type turn_ids = region.unique_turn_ids;
  205. for (set_iterator sit = connected_region.unique_turn_ids.begin();
  206. sit != connected_region.unique_turn_ids.end(); ++sit)
  207. {
  208. turn_ids.erase(*sit);
  209. }
  210. // There should be one connection (turn or cluster) left
  211. if (turn_ids.size() != 1)
  212. {
  213. return false;
  214. }
  215. for (set_iterator sit = connected_region.unique_turn_ids.begin();
  216. sit != connected_region.unique_turn_ids.end(); ++sit)
  217. {
  218. signed_size_type const id_or_index = *sit;
  219. if (id_or_index >= 0)
  220. {
  221. if (! ii_turn_connects_two_regions(region, connected_region, id_or_index))
  222. {
  223. return false;
  224. }
  225. }
  226. else
  227. {
  228. signed_size_type const cluster_id = -id_or_index;
  229. typename Clusters::const_iterator it = m_clusters.find(cluster_id);
  230. if (it != m_clusters.end())
  231. {
  232. cluster_info const& cinfo = it->second;
  233. for (set_iterator cit = cinfo.turn_indices.begin();
  234. cit != cinfo.turn_indices.end(); ++cit)
  235. {
  236. if (! ii_turn_connects_two_regions(region, connected_region, *cit))
  237. {
  238. return false;
  239. }
  240. }
  241. }
  242. }
  243. }
  244. return true;
  245. }
  246. bool has_only_isolated_children(region_properties const& region) const
  247. {
  248. bool first_with_turn = true;
  249. signed_size_type first_turn_id = 0;
  250. for (typename connection_map::const_iterator it = region.connected_region_counts.begin();
  251. it != region.connected_region_counts.end(); ++it)
  252. {
  253. signed_size_type const region_id = it->first;
  254. connection_properties const& cprop = it->second;
  255. typename region_connection_map::const_iterator mit = m_connected_regions.find(region_id);
  256. if (mit == m_connected_regions.end())
  257. {
  258. // Should not occur
  259. return false;
  260. }
  261. region_properties const& connected_region = mit->second;
  262. if (cprop.count != 1)
  263. {
  264. // If there are more connections, check their isolation
  265. if (! isolated_multiple_connection(region, connected_region))
  266. {
  267. return false;
  268. }
  269. }
  270. if (connected_region.isolated != isolation_yes
  271. && connected_region.isolated != isolation_multiple)
  272. {
  273. signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin();
  274. if (first_with_turn)
  275. {
  276. first_turn_id = unique_turn_id;
  277. first_with_turn = false;
  278. }
  279. else if (first_turn_id != unique_turn_id)
  280. {
  281. return false;
  282. }
  283. }
  284. }
  285. // If there is only one connection (with a 'parent'), and all other
  286. // connections are itself isolated, it is isolated
  287. return true;
  288. }
  289. void get_isolated_regions()
  290. {
  291. typedef typename region_connection_map::iterator it_type;
  292. // First time: check regions isolated (one connection only),
  293. // semi-isolated (multiple connections between same region),
  294. // and complex isolated (connection with multiple rings but all
  295. // at same point)
  296. for (it_type it = m_connected_regions.begin();
  297. it != m_connected_regions.end(); ++it)
  298. {
  299. region_properties& properties = it->second;
  300. if (one_connection_to_another_region(properties))
  301. {
  302. properties.isolated = isolation_yes;
  303. }
  304. else if (multiple_connections_to_one_region(properties))
  305. {
  306. properties.isolated = isolation_multiple;
  307. }
  308. else if (one_connection_to_multiple_regions(properties))
  309. {
  310. properties.isolated = isolation_yes;
  311. }
  312. }
  313. // Propagate isolation to next level
  314. // TODO: should be optimized
  315. std::size_t defensive_check = 0;
  316. bool changed = true;
  317. while (changed && defensive_check++ < m_connected_regions.size())
  318. {
  319. changed = false;
  320. for (it_type it = m_connected_regions.begin();
  321. it != m_connected_regions.end(); ++it)
  322. {
  323. region_properties& properties = it->second;
  324. if (properties.isolated == isolation_unknown
  325. && has_only_isolated_children(properties))
  326. {
  327. properties.isolated = isolation_yes;
  328. changed = true;
  329. }
  330. }
  331. }
  332. }
  333. void assign_isolation()
  334. {
  335. for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
  336. {
  337. turn_type& turn = m_turns[turn_index];
  338. for (int op_index = 0; op_index < 2; op_index++)
  339. {
  340. turn_operation_type& op = turn.operations[op_index];
  341. typename region_connection_map::const_iterator mit = m_connected_regions.find(op.enriched.region_id);
  342. if (mit != m_connected_regions.end())
  343. {
  344. region_properties const& prop = mit->second;
  345. op.enriched.isolated = prop.isolated == isolation_yes;
  346. }
  347. }
  348. }
  349. }
  350. void assign_region_ids()
  351. {
  352. for (typename merge_map::const_iterator it
  353. = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
  354. {
  355. ring_identifier const& ring_id = it->first;
  356. merged_ring_properties const& properties = it->second;
  357. for (set_iterator sit = properties.turn_indices.begin();
  358. sit != properties.turn_indices.end(); ++sit)
  359. {
  360. turn_type& turn = m_turns[*sit];
  361. if (! acceptable(turn))
  362. {
  363. // No assignment necessary
  364. continue;
  365. }
  366. for (int i = 0; i < 2; i++)
  367. {
  368. turn_operation_type& op = turn.operations[i];
  369. if (ring_id_by_seg_id(op.seg_id) == ring_id)
  370. {
  371. op.enriched.region_id = properties.region_id;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. void assign_connected_regions()
  378. {
  379. for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
  380. {
  381. turn_type const& turn = m_turns[turn_index];
  382. signed_size_type const unique_turn_id
  383. = turn.is_clustered() ? -turn.cluster_id : turn_index;
  384. turn_operation_type op0 = turn.operations[0];
  385. turn_operation_type op1 = turn.operations[1];
  386. signed_size_type const& id0 = op0.enriched.region_id;
  387. signed_size_type const& id1 = op1.enriched.region_id;
  388. // Add region (by assigning) and add involved turns
  389. if (id0 != -1)
  390. {
  391. m_connected_regions[id0].region_id = id0;
  392. m_connected_regions[id0].unique_turn_ids.insert(unique_turn_id);
  393. }
  394. if (id1 != -1 && id0 != id1)
  395. {
  396. m_connected_regions[id1].region_id = id1;
  397. m_connected_regions[id1].unique_turn_ids.insert(unique_turn_id);
  398. }
  399. if (id0 != id1 && id0 != -1 && id1 != -1)
  400. {
  401. // Assign connections
  402. connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1];
  403. connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0];
  404. // Reference this turn or cluster to later check uniqueness on ring
  405. if (prop0.unique_turn_ids.count(unique_turn_id) == 0)
  406. {
  407. prop0.count++;
  408. prop0.unique_turn_ids.insert(unique_turn_id);
  409. }
  410. if (prop1.unique_turn_ids.count(unique_turn_id) == 0)
  411. {
  412. prop1.count++;
  413. prop1.unique_turn_ids.insert(unique_turn_id);
  414. }
  415. }
  416. }
  417. }
  418. inline bool acceptable(turn_type const& turn) const
  419. {
  420. // Discarded turns don't connect rings to the same region
  421. // Also xx are not relevant
  422. // (otherwise discarded colocated uu turn could make a connection)
  423. return ! turn.discarded
  424. && ! turn.both(operation_blocked);
  425. }
  426. inline bool connects_same_region(turn_type const& turn) const
  427. {
  428. if (! acceptable(turn))
  429. {
  430. return false;
  431. }
  432. if (! turn.is_clustered())
  433. {
  434. // If it is a uu/ii-turn (non clustered), it is never same region
  435. return ! (turn.both(operation_union) || turn.both(operation_intersection));
  436. }
  437. if (BOOST_GEOMETRY_CONDITION(target_operation == operation_union))
  438. {
  439. // It is a cluster, check zones
  440. // (assigned by sort_by_side/handle colocations) of both operations
  441. return turn.operations[0].enriched.zone
  442. == turn.operations[1].enriched.zone;
  443. }
  444. // For an intersection, two regions connect if they are not ii
  445. // (ii-regions are isolated) or, in some cases, not iu (for example
  446. // when a multi-polygon is inside an interior ring and connecting it)
  447. return ! (turn.both(operation_intersection)
  448. || turn.combination(operation_intersection, operation_union));
  449. }
  450. inline signed_size_type get_region_id(turn_operation_type const& op) const
  451. {
  452. return op.enriched.region_id;
  453. }
  454. void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id,
  455. merged_ring_properties& properties, signed_size_type region_id = -1)
  456. {
  457. if (properties.region_id > 0)
  458. {
  459. // Already handled
  460. return;
  461. }
  462. // Assign new id if this is a new region
  463. if (region_id == -1)
  464. {
  465. region_id = new_region_id++;
  466. }
  467. // Assign this ring to specified region
  468. properties.region_id = region_id;
  469. #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
  470. std::cout << " ADD " << ring_id << " TO REGION " << region_id << std::endl;
  471. #endif
  472. // Find connecting rings, recursively
  473. for (set_iterator sit = properties.turn_indices.begin();
  474. sit != properties.turn_indices.end(); ++sit)
  475. {
  476. signed_size_type const turn_index = *sit;
  477. turn_type const& turn = m_turns[turn_index];
  478. if (! connects_same_region(turn))
  479. {
  480. // This is a non clustered uu/ii-turn, or a cluster connecting different 'zones'
  481. continue;
  482. }
  483. // Union: This turn connects two rings (interior connected), create the region
  484. // Intersection: This turn connects two rings, set same regions for these two rings
  485. for (int op_index = 0; op_index < 2; op_index++)
  486. {
  487. turn_operation_type const& op = turn.operations[op_index];
  488. ring_identifier connected_ring_id = ring_id_by_seg_id(op.seg_id);
  489. if (connected_ring_id != ring_id)
  490. {
  491. propagate_region(new_region_id, connected_ring_id, region_id);
  492. }
  493. }
  494. }
  495. }
  496. void propagate_region(signed_size_type& new_region_id,
  497. ring_identifier const& ring_id, signed_size_type region_id)
  498. {
  499. typename merge_map::iterator it = m_turns_per_ring.find(ring_id);
  500. if (it != m_turns_per_ring.end())
  501. {
  502. create_region(new_region_id, ring_id, it->second, region_id);
  503. }
  504. }
  505. void iterate()
  506. {
  507. #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
  508. std::cout << "BEGIN ITERATION GETTING REGION_IDS" << std::endl;
  509. #endif
  510. // Collect turns per ring
  511. m_turns_per_ring.clear();
  512. m_connected_regions.clear();
  513. for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
  514. {
  515. turn_type const& turn = m_turns[turn_index];
  516. if (turn.discarded
  517. && BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
  518. {
  519. // Discarded turn (union currently still needs it to determine regions)
  520. continue;
  521. }
  522. for (int op_index = 0; op_index < 2; op_index++)
  523. {
  524. turn_operation_type const& op = turn.operations[op_index];
  525. m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].turn_indices.insert(turn_index);
  526. }
  527. }
  528. // All rings having turns are in turns/ring map. Process them.
  529. {
  530. signed_size_type new_region_id = 1;
  531. for (typename merge_map::iterator it
  532. = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
  533. {
  534. create_region(new_region_id, it->first, it->second);
  535. }
  536. assign_region_ids();
  537. assign_connected_regions();
  538. get_isolated_regions();
  539. assign_isolation();
  540. }
  541. #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
  542. std::cout << "END ITERATION GETTIN REGION_IDS" << std::endl;
  543. for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
  544. {
  545. turn_type const& turn = m_turns[turn_index];
  546. if ((turn.both(operation_union) || turn.both(operation_intersection))
  547. && ! turn.is_clustered())
  548. {
  549. std::cout << "UU/II RESULT "
  550. << turn_index << " -> "
  551. << turn.operations[0].enriched.region_id
  552. << " " << turn.operations[1].enriched.region_id
  553. << std::endl;
  554. }
  555. }
  556. for (typename Clusters::const_iterator it = m_clusters.begin(); it != m_clusters.end(); ++it)
  557. {
  558. cluster_info const& cinfo = it->second;
  559. std::cout << "CL RESULT " << it->first
  560. << " -> " << cinfo.open_count << std::endl;
  561. }
  562. #endif
  563. }
  564. private:
  565. Geometry1 const& m_geometry1;
  566. Geometry2 const& m_geometry2;
  567. Turns& m_turns;
  568. Clusters& m_clusters;
  569. merge_map m_turns_per_ring;
  570. region_connection_map m_connected_regions;
  571. RobustPolicy const& m_robust_policy;
  572. Visitor& m_visitor;
  573. };
  574. }} // namespace detail::overlay
  575. #endif // DOXYGEN_NO_DETAIL
  576. }} // namespace boost::geometry
  577. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP