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