1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2015-2016 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
11 
12 #include <cstddef>
13 
14 #include <boost/range.hpp>
15 
16 #include <boost/geometry/algorithms/detail/ring_identifier.hpp>
17 #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
18 #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
19 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
20 #include <boost/geometry/core/access.hpp>
21 #include <boost/geometry/core/assert.hpp>
22 
23 namespace boost { namespace geometry
24 {
25 
26 #ifndef DOXYGEN_NO_DETAIL
27 namespace detail { namespace overlay
28 {
29 
30 // Generic function (is this used somewhere else too?)
ring_id_by_seg_id(segment_identifier const & seg_id)31 inline ring_identifier ring_id_by_seg_id(segment_identifier const& seg_id)
32 {
33     return ring_identifier(seg_id.source_index, seg_id.multi_index, seg_id.ring_index);
34 }
35 
36 template
37 <
38     bool Reverse1,
39     bool Reverse2,
40     overlay_type OverlayType,
41     typename Geometry1,
42     typename Geometry2,
43     typename Turns,
44     typename Clusters,
45     typename RobustPolicy,
46     typename Visitor
47 >
48 struct traversal_switch_detector
49 {
50     typedef typename boost::range_value<Turns>::type turn_type;
51     typedef typename turn_type::turn_operation_type turn_operation_type;
52 
53     // For convenience
54     typedef std::set<signed_size_type>::const_iterator set_iterator;
55 
traversal_switch_detectorboost::geometry::detail::overlay::traversal_switch_detector56     inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2,
57             Turns& turns, Clusters& clusters,
58             RobustPolicy const& robust_policy, Visitor& visitor)
59         : m_geometry1(geometry1)
60         , m_geometry2(geometry2)
61         , m_turns(turns)
62         , m_clusters(clusters)
63         , m_robust_policy(robust_policy)
64         , m_visitor(visitor)
65         , m_region_id(0)
66     {
67 
68     }
69 
connects_same_zoneboost::geometry::detail::overlay::traversal_switch_detector70     static inline bool connects_same_zone(turn_type const& turn)
71     {
72         if (turn.cluster_id == -1)
73         {
74             // If it is a uu/ii-turn (non clustered), it is never same zone
75             return ! (turn.both(operation_union) || turn.both(operation_intersection));
76         }
77 
78         // It is a cluster, check zones of both operations
79         return turn.operations[0].enriched.zone
80                 == turn.operations[1].enriched.zone;
81     }
82 
get_region_idboost::geometry::detail::overlay::traversal_switch_detector83     inline int get_region_id(turn_operation_type const& op) const
84     {
85         std::map<ring_identifier, int>::const_iterator it
86                     = m_regions.find(ring_id_by_seg_id(op.seg_id));
87         return it == m_regions.end() ? -1 : it->second;
88     }
89 
create_regionboost::geometry::detail::overlay::traversal_switch_detector90     void create_region(ring_identifier const& ring_id, std::set<signed_size_type> const& ring_turn_indices, int region_id = -1)
91     {
92         std::map<ring_identifier, int>::const_iterator it = m_regions.find(ring_id);
93         if (it != m_regions.end())
94         {
95             // The ring is already gathered in a region, quit
96             return;
97         }
98         if (region_id == -1)
99         {
100             region_id = m_region_id++;
101         }
102 
103         // Assign this ring to specified region
104         m_regions[ring_id] = region_id;
105 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
106         std::cout << " ADD " << ring_id << "  TO REGION " << region_id << std::endl;
107 #endif
108 
109         // Find connecting rings, recursively
110         for (set_iterator sit = ring_turn_indices.begin();
111              sit != ring_turn_indices.end(); ++sit)
112         {
113             signed_size_type const turn_index = *sit;
114             turn_type const& turn = m_turns[turn_index];
115             if (! connects_same_zone(turn))
116             {
117                 // This is a non clustered uu/ii-turn, or a cluster connecting different 'zones'
118                 continue;
119             }
120 
121             // This turn connects two rings (interior connected), create the
122             // same region
123             for (int op_index = 0; op_index < 2; op_index++)
124             {
125                 turn_operation_type const& op = turn.operations[op_index];
126                 ring_identifier connected_ring_id = ring_id_by_seg_id(op.seg_id);
127                 if (connected_ring_id != ring_id)
128                 {
129                     propagate_region(connected_ring_id, region_id);
130                 }
131             }
132         }
133     }
134 
check_turns_per_ringboost::geometry::detail::overlay::traversal_switch_detector135     void check_turns_per_ring(ring_identifier const& ring_id,
136             std::set<signed_size_type> const& ring_turn_indices)
137     {
138         bool only_turn_on_ring = true;
139         if (ring_turn_indices.size() > 1)
140         {
141             // More turns on this ring. Only leave only_turn_on_ring true
142             // if they are all of the same cluster
143             int cluster_id = -1;
144             for (set_iterator sit = ring_turn_indices.begin();
145                  sit != ring_turn_indices.end(); ++sit)
146             {
147                 turn_type const& turn = m_turns[*sit];
148                 if (turn.cluster_id == -1)
149                 {
150                     // Unclustered turn - and there are 2 or more turns
151                     // so the ring has different turns
152                     only_turn_on_ring = false;
153                     break;
154                 }
155 
156                 // Clustered turn, check if it is the first or same as previous
157                 if (cluster_id == -1)
158                 {
159                     cluster_id = turn.cluster_id;
160                 }
161                 else if (turn.cluster_id != cluster_id)
162                 {
163                     only_turn_on_ring = false;
164                     break;
165                 }
166             }
167         }
168 
169         // Assign result to matching operation (a turn is always on two rings)
170         for (set_iterator sit = ring_turn_indices.begin();
171              sit != ring_turn_indices.end(); ++sit)
172         {
173             turn_type& turn = m_turns[*sit];
174             for (int i = 0; i < 2; i++)
175             {
176                 turn_operation_type& op = turn.operations[i];
177                 if (ring_id_by_seg_id(op.seg_id) == ring_id)
178                 {
179                     op.enriched.only_turn_on_ring = only_turn_on_ring;
180                 }
181             }
182         }
183     }
184 
propagate_regionboost::geometry::detail::overlay::traversal_switch_detector185     void propagate_region(ring_identifier const& ring_id, int region_id)
186     {
187         std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it = m_turns_per_ring.find(ring_id);
188         if (it != m_turns_per_ring.end())
189         {
190             create_region(ring_id, it->second, region_id);
191         }
192     }
193 
iterateboost::geometry::detail::overlay::traversal_switch_detector194     void iterate()
195     {
196 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
197         std::cout << "SWITCH BEGIN ITERATION" << std::endl;
198 #endif
199 
200         // Collect turns per ring
201         m_turns_per_ring.clear();
202         m_regions.clear();
203         m_region_id = 1;
204 
205         for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
206         {
207             turn_type const& turn = m_turns[turn_index];
208 
209             for (int op_index = 0; op_index < 2; op_index++)
210             {
211                 turn_operation_type const& op = turn.operations[op_index];
212                 m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].insert(turn_index);
213             }
214         }
215 
216         // All rings having turns are in the map. Now iterate them
217         for (std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it
218              = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
219         {
220             create_region(it->first, it->second);
221             check_turns_per_ring(it->first, it->second);
222         }
223 
224         // Now that all regions are filled, assign switch_source property
225         // Iterate through all clusters
226         for (typename Clusters::iterator it = m_clusters.begin(); it != m_clusters.end(); ++it)
227         {
228             cluster_info& cinfo = it->second;
229             if (cinfo.open_count <= 1)
230             {
231                 // Not a touching cluster
232                 continue;
233             }
234 
235             // A touching cluster, gather regions
236             std::set<int> regions;
237 
238             std::set<signed_size_type> const& ids = cinfo.turn_indices;
239 
240 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
241                 std::cout << "SWITCH EXAMINE CLUSTER " << it->first << std::endl;
242 #endif
243 
244             for (set_iterator sit = ids.begin(); sit != ids.end(); ++sit)
245             {
246                 signed_size_type turn_index = *sit;
247                 turn_type const& turn = m_turns[turn_index];
248                 for (int oi = 0; oi < 2; oi++)
249                 {
250                     int const region = get_region_id(turn.operations[oi]);
251                     regions.insert(region);
252                 }
253             }
254             // Switch source if this cluster connects the same region
255             cinfo.switch_source = regions.size() == 1;
256         }
257 
258         // Iterate through all uu/ii turns (non-clustered)
259         for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
260         {
261             turn_type& turn = m_turns[turn_index];
262 
263             if (turn.discarded
264                     || turn.blocked()
265                     || turn.cluster_id >= 0
266                     || ! (turn.both(operation_union) || turn.both(operation_intersection)))
267             {
268                 // Skip discarded, blocked, non-uu/ii and clustered turns
269                 continue;
270             }
271 
272             if (OverlayType == overlay_buffer)
273             {
274                 // For deflate, the region approach does not work because many
275                 // pieces are outside the real polygons
276                 // TODO: implement this in another way for buffer
277                 // (because now buffer might output invalid geometries)
278                 continue;
279             }
280 
281             int const region0 = get_region_id(turn.operations[0]);
282             int const region1 = get_region_id(turn.operations[1]);
283 
284             // Switch sources for same region
285             turn.switch_source = region0 == region1;
286         }
287 
288 
289 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
290         std::cout << "SWITCH END ITERATION" << std::endl;
291 
292         for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
293         {
294             turn_type const& turn = m_turns[turn_index];
295 
296             if ((turn.both(operation_union) || turn.both(operation_intersection))
297                  && turn.cluster_id < 0)
298             {
299                 std::cout << "UU/II SWITCH RESULT "
300                              << turn_index << " -> "
301                           << turn.switch_source << std::endl;
302             }
303         }
304 
305         for (typename Clusters::const_iterator it = m_clusters.begin(); it != m_clusters.end(); ++it)
306         {
307             cluster_info const& cinfo = it->second;
308             if (cinfo.open_count > 1)
309             {
310                 std::cout << "CL SWITCH RESULT " << it->first
311                              << " -> " << cinfo.switch_source << std::endl;
312             }
313             else
314             {
315                 std::cout << "CL SWITCH RESULT " << it->first
316                           << " is not registered as open" << std::endl;
317             }
318         }
319 #endif
320 
321     }
322 
323 private:
324 
325     Geometry1 const& m_geometry1;
326     Geometry2 const& m_geometry2;
327     Turns& m_turns;
328     Clusters& m_clusters;
329     RobustPolicy const& m_robust_policy;
330     Visitor& m_visitor;
331 
332     std::map<ring_identifier, int> m_regions;
333     std::map<ring_identifier, std::set<signed_size_type> > m_turns_per_ring;
334     int m_region_id;
335 
336 };
337 
338 }} // namespace detail::overlay
339 #endif // DOXYGEN_NO_DETAIL
340 
341 }} // namespace boost::geometry
342 
343 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
344