1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2
3 // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
5
6 // This file was modified by Oracle on 2017.
7 // Modifications copyright (c) 2017 Oracle and/or its affiliates.
8
9 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
10
11 // Use, modification and distribution is subject to the Boost Software License,
12 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13 // http://www.boost.org/LICENSE_1_0.txt)
14
15 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
16 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
17
18 #include <cstddef>
19 #include <algorithm>
20 #include <map>
21 #include <vector>
22
23 #include <boost/core/ignore_unused.hpp>
24 #include <boost/range.hpp>
25 #include <boost/geometry/core/assert.hpp>
26 #include <boost/geometry/core/point_order.hpp>
27 #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
28 #include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
29 #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
30 #include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
31 #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
32 #include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
33 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
34 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
35 #include <boost/geometry/util/condition.hpp>
36
37 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
38 # include <iostream>
39 # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
40 # include <boost/geometry/io/wkt/wkt.hpp>
41 # define BOOST_GEOMETRY_DEBUG_IDENTIFIER
42 #endif
43
44 namespace boost { namespace geometry
45 {
46
47 #ifndef DOXYGEN_NO_DETAIL
48 namespace detail { namespace overlay
49 {
50
51 template <typename SegmentRatio>
52 struct segment_fraction
53 {
54 segment_identifier seg_id;
55 SegmentRatio fraction;
56
segment_fractionboost::geometry::detail::overlay::segment_fraction57 segment_fraction(segment_identifier const& id, SegmentRatio const& fr)
58 : seg_id(id)
59 , fraction(fr)
60 {}
61
segment_fractionboost::geometry::detail::overlay::segment_fraction62 segment_fraction()
63 {}
64
operator <boost::geometry::detail::overlay::segment_fraction65 bool operator<(segment_fraction<SegmentRatio> const& other) const
66 {
67 return seg_id == other.seg_id
68 ? fraction < other.fraction
69 : seg_id < other.seg_id;
70 }
71
72 };
73
74 struct turn_operation_index
75 {
turn_operation_indexboost::geometry::detail::overlay::turn_operation_index76 turn_operation_index(signed_size_type ti = -1,
77 signed_size_type oi = -1)
78 : turn_index(ti)
79 , op_index(oi)
80 {}
81
82 signed_size_type turn_index;
83 signed_size_type op_index; // only 0,1
84 };
85
86 template <typename Turns>
87 struct less_by_fraction_and_type
88 {
less_by_fraction_and_typeboost::geometry::detail::overlay::less_by_fraction_and_type89 inline less_by_fraction_and_type(Turns const& turns)
90 : m_turns(turns)
91 {
92 }
93
operator ()boost::geometry::detail::overlay::less_by_fraction_and_type94 inline bool operator()(turn_operation_index const& left,
95 turn_operation_index const& right) const
96 {
97 typedef typename boost::range_value<Turns>::type turn_type;
98 typedef typename turn_type::turn_operation_type turn_operation_type;
99
100 turn_type const& left_turn = m_turns[left.turn_index];
101 turn_type const& right_turn = m_turns[right.turn_index];
102 turn_operation_type const& left_op
103 = left_turn.operations[left.op_index];
104
105 turn_operation_type const& right_op
106 = right_turn.operations[right.op_index];
107
108 if (! (left_op.fraction == right_op.fraction))
109 {
110 return left_op.fraction < right_op.fraction;
111 }
112
113 // Order xx first - used to discard any following colocated turn
114 bool const left_both_xx = left_turn.both(operation_blocked);
115 bool const right_both_xx = right_turn.both(operation_blocked);
116 if (left_both_xx && ! right_both_xx)
117 {
118 return true;
119 }
120 if (! left_both_xx && right_both_xx)
121 {
122 return false;
123 }
124
125 bool const left_both_uu = left_turn.both(operation_union);
126 bool const right_both_uu = right_turn.both(operation_union);
127 if (left_both_uu && ! right_both_uu)
128 {
129 return true;
130 }
131 if (! left_both_uu && right_both_uu)
132 {
133 return false;
134 }
135
136 turn_operation_type const& left_other_op
137 = left_turn.operations[1 - left.op_index];
138
139 turn_operation_type const& right_other_op
140 = right_turn.operations[1 - right.op_index];
141
142 // Fraction is the same, now sort on ring id, first outer ring,
143 // then interior rings
144 return left_other_op.seg_id < right_other_op.seg_id;
145 }
146
147 private:
148 Turns const& m_turns;
149 };
150
151 template <typename Operation, typename ClusterPerSegment>
get_cluster_id(Operation const & op,ClusterPerSegment const & cluster_per_segment)152 inline signed_size_type get_cluster_id(Operation const& op, ClusterPerSegment const& cluster_per_segment)
153 {
154 typedef typename ClusterPerSegment::key_type segment_fraction_type;
155
156 segment_fraction_type seg_frac(op.seg_id, op.fraction);
157 typename ClusterPerSegment::const_iterator it
158 = cluster_per_segment.find(seg_frac);
159
160 if (it == cluster_per_segment.end())
161 {
162 return -1;
163 }
164 return it->second;
165 }
166
167 template <typename Operation, typename ClusterPerSegment>
add_cluster_id(Operation const & op,ClusterPerSegment & cluster_per_segment,signed_size_type id)168 inline void add_cluster_id(Operation const& op,
169 ClusterPerSegment& cluster_per_segment, signed_size_type id)
170 {
171 typedef typename ClusterPerSegment::key_type segment_fraction_type;
172
173 segment_fraction_type seg_frac(op.seg_id, op.fraction);
174
175 cluster_per_segment[seg_frac] = id;
176 }
177
178 template <typename Turn, typename ClusterPerSegment>
add_turn_to_cluster(Turn const & turn,ClusterPerSegment & cluster_per_segment,signed_size_type & cluster_id)179 inline signed_size_type add_turn_to_cluster(Turn const& turn,
180 ClusterPerSegment& cluster_per_segment, signed_size_type& cluster_id)
181 {
182 signed_size_type cid0 = get_cluster_id(turn.operations[0], cluster_per_segment);
183 signed_size_type cid1 = get_cluster_id(turn.operations[1], cluster_per_segment);
184
185 if (cid0 == -1 && cid1 == -1)
186 {
187 // Because of this, first cluster ID will be 1
188 ++cluster_id;
189 add_cluster_id(turn.operations[0], cluster_per_segment, cluster_id);
190 add_cluster_id(turn.operations[1], cluster_per_segment, cluster_id);
191 return cluster_id;
192 }
193 else if (cid0 == -1 && cid1 != -1)
194 {
195 add_cluster_id(turn.operations[0], cluster_per_segment, cid1);
196 return cid1;
197 }
198 else if (cid0 != -1 && cid1 == -1)
199 {
200 add_cluster_id(turn.operations[1], cluster_per_segment, cid0);
201 return cid0;
202 }
203 else if (cid0 == cid1)
204 {
205 // Both already added to same cluster, no action
206 return cid0;
207 }
208
209 // Both operations.seg_id/fraction were already part of any cluster, and
210 // these clusters are not the same. Merge of two clusters is necessary
211 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
212 std::cout << " TODO: merge " << cid0 << " and " << cid1 << std::endl;
213 #endif
214 return cid0;
215 }
216
217 template
218 <
219 typename Turns,
220 typename ClusterPerSegment,
221 typename Operations,
222 typename Geometry1,
223 typename Geometry2
224 >
handle_colocation_cluster(Turns & turns,signed_size_type & cluster_id,ClusterPerSegment & cluster_per_segment,Operations const & operations,Geometry1 const &,Geometry2 const &)225 inline void handle_colocation_cluster(Turns& turns,
226 signed_size_type& cluster_id,
227 ClusterPerSegment& cluster_per_segment,
228 Operations const& operations,
229 Geometry1 const& /*geometry1*/, Geometry2 const& /*geometry2*/)
230 {
231 typedef typename boost::range_value<Turns>::type turn_type;
232 typedef typename turn_type::turn_operation_type turn_operation_type;
233
234 std::vector<turn_operation_index>::const_iterator vit = operations.begin();
235
236 turn_operation_index ref_toi = *vit;
237 signed_size_type ref_id = -1;
238
239 for (++vit; vit != operations.end(); ++vit)
240 {
241 turn_type& ref_turn = turns[ref_toi.turn_index];
242 turn_operation_type const& ref_op
243 = ref_turn.operations[ref_toi.op_index];
244
245 turn_operation_index const& toi = *vit;
246 turn_type& turn = turns[toi.turn_index];
247 turn_operation_type const& op = turn.operations[toi.op_index];
248
249 BOOST_GEOMETRY_ASSERT(ref_op.seg_id == op.seg_id);
250
251 if (ref_op.fraction == op.fraction)
252 {
253 turn_operation_type const& other_op = turn.operations[1 - toi.op_index];
254
255 if (ref_id == -1)
256 {
257 ref_id = add_turn_to_cluster(ref_turn, cluster_per_segment, cluster_id);
258 }
259 BOOST_GEOMETRY_ASSERT(ref_id != -1);
260
261 // ref_turn (both operations) are already added to cluster,
262 // so also "op" is already added to cluster,
263 // We only need to add other_op
264 signed_size_type id = get_cluster_id(other_op, cluster_per_segment);
265 if (id != -1 && id != ref_id)
266 {
267 }
268 else if (id == -1)
269 {
270 // Add to same cluster
271 add_cluster_id(other_op, cluster_per_segment, ref_id);
272 id = ref_id;
273 }
274 }
275 else
276 {
277 // Not on same fraction on this segment
278 // assign for next
279 ref_toi = toi;
280 ref_id = -1;
281 }
282 }
283 }
284
285 template
286 <
287 typename Turns,
288 typename Clusters,
289 typename ClusterPerSegment
290 >
assign_cluster_to_turns(Turns & turns,Clusters & clusters,ClusterPerSegment const & cluster_per_segment)291 inline void assign_cluster_to_turns(Turns& turns,
292 Clusters& clusters,
293 ClusterPerSegment const& cluster_per_segment)
294 {
295 typedef typename boost::range_value<Turns>::type turn_type;
296 typedef typename turn_type::turn_operation_type turn_operation_type;
297 typedef typename ClusterPerSegment::key_type segment_fraction_type;
298
299 signed_size_type turn_index = 0;
300 for (typename boost::range_iterator<Turns>::type it = turns.begin();
301 it != turns.end(); ++it, ++turn_index)
302 {
303 turn_type& turn = *it;
304
305 if (turn.discarded)
306 {
307 // They were processed (to create proper map) but will not be added
308 // This might leave a cluster with only 1 turn, which will be fixed
309 // afterwards
310 continue;
311 }
312
313 for (int i = 0; i < 2; i++)
314 {
315 turn_operation_type const& op = turn.operations[i];
316 segment_fraction_type seg_frac(op.seg_id, op.fraction);
317 typename ClusterPerSegment::const_iterator cit = cluster_per_segment.find(seg_frac);
318 if (cit != cluster_per_segment.end())
319 {
320 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
321 if (turn.is_clustered()
322 && turn.cluster_id != cit->second)
323 {
324 std::cout << " CONFLICT " << std::endl;
325 }
326 #endif
327 turn.cluster_id = cit->second;
328 clusters[turn.cluster_id].turn_indices.insert(turn_index);
329 }
330 }
331 }
332 }
333
334 template <typename Turns, typename Clusters>
remove_clusters(Turns & turns,Clusters & clusters)335 inline void remove_clusters(Turns& turns, Clusters& clusters)
336 {
337 typename Clusters::iterator it = clusters.begin();
338 while (it != clusters.end())
339 {
340 // Hold iterator and increase. We can erase cit, this keeps the
341 // iterator valid (cf The standard associative-container erase idiom)
342 typename Clusters::iterator current_it = it;
343 ++it;
344
345 std::set<signed_size_type> const& turn_indices
346 = current_it->second.turn_indices;
347 if (turn_indices.size() == 1)
348 {
349 signed_size_type const turn_index = *turn_indices.begin();
350 turns[turn_index].cluster_id = -1;
351 clusters.erase(current_it);
352 }
353 }
354 }
355
356 template <typename Turns, typename Clusters>
cleanup_clusters(Turns & turns,Clusters & clusters)357 inline void cleanup_clusters(Turns& turns, Clusters& clusters)
358 {
359 // Removes discarded turns from clusters
360 for (typename Clusters::iterator mit = clusters.begin();
361 mit != clusters.end(); ++mit)
362 {
363 cluster_info& cinfo = mit->second;
364 std::set<signed_size_type>& ids = cinfo.turn_indices;
365 for (std::set<signed_size_type>::iterator sit = ids.begin();
366 sit != ids.end(); /* no increment */)
367 {
368 std::set<signed_size_type>::iterator current_it = sit;
369 ++sit;
370
371 signed_size_type const turn_index = *current_it;
372 if (turns[turn_index].discarded)
373 {
374 ids.erase(current_it);
375 }
376 }
377 }
378
379 remove_clusters(turns, clusters);
380 }
381
382 template <typename Turn, typename IdSet>
discard_ie_turn(Turn & turn,IdSet & ids,signed_size_type id)383 inline void discard_ie_turn(Turn& turn, IdSet& ids, signed_size_type id)
384 {
385 turn.discarded = true;
386 // Set cluster id to -1, but don't clear colocated flags
387 turn.cluster_id = -1;
388 // To remove it later from clusters
389 ids.insert(id);
390 }
391
392 template <bool Reverse>
is_interior(segment_identifier const & seg_id)393 inline bool is_interior(segment_identifier const& seg_id)
394 {
395 return Reverse ? seg_id.ring_index == -1 : seg_id.ring_index >= 0;
396 }
397
398 template <bool Reverse0, bool Reverse1>
is_ie_turn(segment_identifier const & ext_seg_0,segment_identifier const & ext_seg_1,segment_identifier const & int_seg_0,segment_identifier const & other_seg_1)399 inline bool is_ie_turn(segment_identifier const& ext_seg_0,
400 segment_identifier const& ext_seg_1,
401 segment_identifier const& int_seg_0,
402 segment_identifier const& other_seg_1)
403 {
404 if (ext_seg_0.source_index == ext_seg_1.source_index)
405 {
406 // External turn is a self-turn, dont discard internal turn for this
407 return false;
408 }
409
410
411 // Compares two segment identifiers from two turns (external / one internal)
412
413 // From first turn [0], both are from same polygon (multi_index),
414 // one is exterior (-1), the other is interior (>= 0),
415 // and the second turn [1] handles the same ring
416
417 // For difference, where the rings are processed in reversal, all interior
418 // rings become exterior and vice versa. But also the multi property changes:
419 // rings originally from the same multi should now be considered as from
420 // different multi polygons.
421 // But this is not always the case, and at this point hard to figure out
422 // (not yet implemented, TODO)
423
424 bool const same_multi0 = ! Reverse0
425 && ext_seg_0.multi_index == int_seg_0.multi_index;
426
427 bool const same_multi1 = ! Reverse1
428 && ext_seg_1.multi_index == other_seg_1.multi_index;
429
430 boost::ignore_unused(same_multi1);
431
432 return same_multi0
433 && same_multi1
434 && ! is_interior<Reverse0>(ext_seg_0)
435 && is_interior<Reverse0>(int_seg_0)
436 && ext_seg_1.ring_index == other_seg_1.ring_index;
437
438 // The other way round is tested in another call
439 }
440
441 template
442 <
443 bool Reverse0, bool Reverse1, // Reverse interpretation interior/exterior
444 typename Turns,
445 typename Clusters
446 >
discard_interior_exterior_turns(Turns & turns,Clusters & clusters)447 inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
448 {
449 typedef std::set<signed_size_type>::const_iterator set_iterator;
450 typedef typename boost::range_value<Turns>::type turn_type;
451
452 std::set<signed_size_type> ids_to_remove;
453
454 for (typename Clusters::iterator cit = clusters.begin();
455 cit != clusters.end(); ++cit)
456 {
457 cluster_info& cinfo = cit->second;
458 std::set<signed_size_type>& ids = cinfo.turn_indices;
459
460 ids_to_remove.clear();
461
462 for (set_iterator it = ids.begin(); it != ids.end(); ++it)
463 {
464 turn_type& turn = turns[*it];
465 segment_identifier const& seg_0 = turn.operations[0].seg_id;
466 segment_identifier const& seg_1 = turn.operations[1].seg_id;
467
468 if (! (turn.both(operation_union)
469 || turn.combination(operation_union, operation_blocked)))
470 {
471 // Not a uu/ux, so cannot be colocated with a iu turn
472 continue;
473 }
474
475 for (set_iterator int_it = ids.begin(); int_it != ids.end(); ++int_it)
476 {
477 if (*it == *int_it)
478 {
479 continue;
480 }
481
482 // Turn with, possibly, an interior ring involved
483 turn_type& int_turn = turns[*int_it];
484 segment_identifier const& int_seg_0 = int_turn.operations[0].seg_id;
485 segment_identifier const& int_seg_1 = int_turn.operations[1].seg_id;
486
487 if (is_ie_turn<Reverse0, Reverse1>(seg_0, seg_1, int_seg_0, int_seg_1))
488 {
489 discard_ie_turn(int_turn, ids_to_remove, *int_it);
490 }
491 if (is_ie_turn<Reverse1, Reverse0>(seg_1, seg_0, int_seg_1, int_seg_0))
492 {
493 discard_ie_turn(int_turn, ids_to_remove, *int_it);
494 }
495 }
496 }
497
498 // Erase from the ids (which cannot be done above)
499 for (set_iterator sit = ids_to_remove.begin();
500 sit != ids_to_remove.end(); ++sit)
501 {
502 ids.erase(*sit);
503 }
504 }
505 }
506
507 template <typename Geometry0, typename Geometry1>
get_preceding_segment_id(segment_identifier const & id,Geometry0 const & geometry0,Geometry1 const & geometry1)508 inline segment_identifier get_preceding_segment_id(segment_identifier const& id,
509 Geometry0 const& geometry0, Geometry1 const& geometry1)
510 {
511 segment_identifier result = id;
512
513 if (result.segment_index == 0)
514 {
515 // Assign to segment_count before decrement
516 result.segment_index
517 = id.source_index == 0
518 ? segment_count_on_ring(geometry0, id)
519 : segment_count_on_ring(geometry1, id);
520 }
521
522 result.segment_index--;
523
524 return result;
525 }
526
527 template
528 <
529 overlay_type OverlayType,
530 typename Turns,
531 typename Clusters
532 >
set_colocation(Turns & turns,Clusters const & clusters)533 inline void set_colocation(Turns& turns, Clusters const& clusters)
534 {
535 typedef std::set<signed_size_type>::const_iterator set_iterator;
536 typedef typename boost::range_value<Turns>::type turn_type;
537
538 for (typename Clusters::const_iterator cit = clusters.begin();
539 cit != clusters.end(); ++cit)
540 {
541 cluster_info const& cinfo = cit->second;
542 std::set<signed_size_type> const& ids = cinfo.turn_indices;
543
544 bool both_target = false;
545 for (set_iterator it = ids.begin(); it != ids.end(); ++it)
546 {
547 turn_type const& turn = turns[*it];
548 if (turn.both(operation_from_overlay<OverlayType>::value))
549 {
550 both_target = true;
551 break;
552 }
553 }
554
555 if (both_target)
556 {
557 for (set_iterator it = ids.begin(); it != ids.end(); ++it)
558 {
559 turn_type& turn = turns[*it];
560 turn.has_colocated_both = true;
561 }
562 }
563 }
564 }
565
566 template
567 <
568 typename Turns,
569 typename Clusters
570 >
check_colocation(bool & has_blocked,signed_size_type cluster_id,Turns const & turns,Clusters const & clusters)571 inline void check_colocation(bool& has_blocked,
572 signed_size_type cluster_id, Turns const& turns, Clusters const& clusters)
573 {
574 typedef typename boost::range_value<Turns>::type turn_type;
575
576 has_blocked = false;
577
578 typename Clusters::const_iterator mit = clusters.find(cluster_id);
579 if (mit == clusters.end())
580 {
581 return;
582 }
583
584 cluster_info const& cinfo = mit->second;
585
586 for (std::set<signed_size_type>::const_iterator it
587 = cinfo.turn_indices.begin();
588 it != cinfo.turn_indices.end(); ++it)
589 {
590 turn_type const& turn = turns[*it];
591 if (turn.any_blocked())
592 {
593 has_blocked = true;
594 }
595 }
596 }
597
598
599 // Checks colocated turns and flags combinations of uu/other, possibly a
600 // combination of a ring touching another geometry's interior ring which is
601 // tangential to the exterior ring
602
603 // This function can be extended to replace handle_tangencies: at each
604 // colocation incoming and outgoing vectors should be inspected
605
606 template
607 <
608 bool Reverse1, bool Reverse2,
609 overlay_type OverlayType,
610 typename Turns,
611 typename Clusters,
612 typename Geometry1,
613 typename Geometry2
614 >
handle_colocations(Turns & turns,Clusters & clusters,Geometry1 const & geometry1,Geometry2 const & geometry2)615 inline bool handle_colocations(Turns& turns, Clusters& clusters,
616 Geometry1 const& geometry1, Geometry2 const& geometry2)
617 {
618 static const detail::overlay::operation_type target_operation
619 = detail::overlay::operation_from_overlay<OverlayType>::value;
620 typedef std::map
621 <
622 segment_identifier,
623 std::vector<turn_operation_index>
624 > map_type;
625
626 // Create and fill map on segment-identifier Map is sorted on seg_id,
627 // meaning it is sorted on ring_identifier too. This means that exterior
628 // rings are handled first. If there is a colocation on the exterior ring,
629 // that information can be used for the interior ring too
630 map_type map;
631
632 signed_size_type index = 0;
633 for (typename boost::range_iterator<Turns>::type
634 it = boost::begin(turns);
635 it != boost::end(turns);
636 ++it, ++index)
637 {
638 map[it->operations[0].seg_id].push_back(turn_operation_index(index, 0));
639 map[it->operations[1].seg_id].push_back(turn_operation_index(index, 1));
640 }
641
642 // Check if there are multiple turns on one or more segments,
643 // if not then nothing is to be done
644 bool colocations = 0;
645 for (typename map_type::const_iterator it = map.begin();
646 it != map.end();
647 ++it)
648 {
649 if (it->second.size() > 1u)
650 {
651 colocations = true;
652 break;
653 }
654 }
655
656 if (! colocations)
657 {
658 return false;
659 }
660
661 // Sort all vectors, per same segment
662 less_by_fraction_and_type<Turns> less(turns);
663 for (typename map_type::iterator it = map.begin();
664 it != map.end(); ++it)
665 {
666 std::sort(it->second.begin(), it->second.end(), less);
667 }
668
669 typedef typename boost::range_value<Turns>::type turn_type;
670 typedef typename turn_type::segment_ratio_type segment_ratio_type;
671
672 typedef std::map
673 <
674 segment_fraction<segment_ratio_type>,
675 signed_size_type
676 > cluster_per_segment_type;
677
678 cluster_per_segment_type cluster_per_segment;
679
680 // Assign to zero, because of pre-increment later the cluster_id
681 // effectively starts with 1
682 // (and can later be negated to use uniquely with turn_index)
683 signed_size_type cluster_id = 0;
684
685 for (typename map_type::const_iterator it = map.begin();
686 it != map.end(); ++it)
687 {
688 if (it->second.size() > 1u)
689 {
690 handle_colocation_cluster(turns, cluster_id, cluster_per_segment,
691 it->second, geometry1, geometry2);
692 }
693 }
694
695 assign_cluster_to_turns(turns, clusters, cluster_per_segment);
696 // Get colocated information here and not later, to keep information
697 // on turns which are discarded afterwards
698 set_colocation<OverlayType>(turns, clusters);
699
700 if (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
701 {
702 discard_interior_exterior_turns
703 <
704 do_reverse<geometry::point_order<Geometry1>::value>::value != Reverse1,
705 do_reverse<geometry::point_order<Geometry2>::value>::value != Reverse2
706 >(turns, clusters);
707 }
708
709 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
710 std::cout << "*** Colocations " << map.size() << std::endl;
711 for (typename map_type::const_iterator it = map.begin();
712 it != map.end(); ++it)
713 {
714 std::cout << it->first << std::endl;
715 for (std::vector<turn_operation_index>::const_iterator vit
716 = it->second.begin(); vit != it->second.end(); ++vit)
717 {
718 turn_operation_index const& toi = *vit;
719 std::cout << geometry::wkt(turns[toi.turn_index].point)
720 << std::boolalpha
721 << " discarded=" << turns[toi.turn_index].discarded
722 << " colocated(uu)=" << turns[toi.turn_index].colocated_uu
723 << " colocated(ii)=" << turns[toi.turn_index].colocated_ii
724 << " " << operation_char(turns[toi.turn_index].operations[0].operation)
725 << " " << turns[toi.turn_index].operations[0].seg_id
726 << " " << turns[toi.turn_index].operations[0].fraction
727 << " // " << operation_char(turns[toi.turn_index].operations[1].operation)
728 << " " << turns[toi.turn_index].operations[1].seg_id
729 << " " << turns[toi.turn_index].operations[1].fraction
730 << std::endl;
731 }
732 }
733 #endif // DEBUG
734
735 return true;
736 }
737
738
739 struct is_turn_index
740 {
is_turn_indexboost::geometry::detail::overlay::is_turn_index741 is_turn_index(signed_size_type index)
742 : m_index(index)
743 {}
744
745 template <typename Indexed>
operator ()boost::geometry::detail::overlay::is_turn_index746 inline bool operator()(Indexed const& indexed) const
747 {
748 // Indexed is a indexed_turn_operation<Operation>
749 return indexed.turn_index == m_index;
750 }
751
752 signed_size_type m_index;
753 };
754
755 template
756 <
757 typename Sbs,
758 typename Point,
759 typename Turns,
760 typename Geometry1,
761 typename Geometry2
762 >
fill_sbs(Sbs & sbs,Point & turn_point,cluster_info const & cinfo,Turns const & turns,Geometry1 const & geometry1,Geometry2 const & geometry2)763 inline bool fill_sbs(Sbs& sbs, Point& turn_point,
764 cluster_info const& cinfo,
765 Turns const& turns,
766 Geometry1 const& geometry1, Geometry2 const& geometry2)
767 {
768 typedef typename boost::range_value<Turns>::type turn_type;
769
770 std::set<signed_size_type> const& ids = cinfo.turn_indices;
771
772 if (ids.empty())
773 {
774 return false;
775 }
776
777 bool first = true;
778 for (std::set<signed_size_type>::const_iterator sit = ids.begin();
779 sit != ids.end(); ++sit)
780 {
781 signed_size_type turn_index = *sit;
782 turn_type const& turn = turns[turn_index];
783 if (first )
784 {
785 turn_point = turn.point;
786 }
787 for (int i = 0; i < 2; i++)
788 {
789 sbs.add(turn.operations[i], turn_index, i, geometry1, geometry2, first);
790 first = false;
791 }
792 }
793 return true;
794 }
795
796 template
797 <
798 bool Reverse1, bool Reverse2,
799 overlay_type OverlayType,
800 typename Turns,
801 typename Clusters,
802 typename Geometry1,
803 typename Geometry2,
804 typename SideStrategy
805 >
gather_cluster_properties(Clusters & clusters,Turns & turns,operation_type for_operation,Geometry1 const & geometry1,Geometry2 const & geometry2,SideStrategy const & strategy)806 inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
807 operation_type for_operation,
808 Geometry1 const& geometry1, Geometry2 const& geometry2,
809 SideStrategy const& strategy)
810 {
811 typedef typename boost::range_value<Turns>::type turn_type;
812 typedef typename turn_type::point_type point_type;
813 typedef typename turn_type::turn_operation_type turn_operation_type;
814
815 // Define sorter, sorting counter-clockwise such that polygons are on the
816 // right side
817 typedef sort_by_side::side_sorter
818 <
819 Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less<int>
820 > sbs_type;
821
822 for (typename Clusters::iterator mit = clusters.begin();
823 mit != clusters.end(); ++mit)
824 {
825 cluster_info& cinfo = mit->second;
826
827 sbs_type sbs(strategy);
828 point_type turn_point; // should be all the same for all turns in cluster
829 if (! fill_sbs(sbs, turn_point, cinfo, turns, geometry1, geometry2))
830 {
831 continue;
832 }
833
834 sbs.apply(turn_point);
835
836 sbs.find_open();
837 sbs.assign_zones(for_operation);
838
839 cinfo.open_count = sbs.open_count(for_operation);
840
841 bool const set_startable = OverlayType != overlay_dissolve;
842
843 // Unset the startable flag for all 'closed' zones. This does not
844 // apply for self-turns, because those counts are not from both
845 // polygons
846 for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
847 {
848 typename sbs_type::rp const& ranked = sbs.m_ranked_points[i];
849 turn_type& turn = turns[ranked.turn_index];
850 turn_operation_type& op = turn.operations[ranked.operation_index];
851
852 if (set_startable
853 && for_operation == operation_union && cinfo.open_count == 0)
854 {
855 op.enriched.startable = false;
856 }
857
858 if (ranked.direction != sort_by_side::dir_to)
859 {
860 continue;
861 }
862
863 op.enriched.count_left = ranked.count_left;
864 op.enriched.count_right = ranked.count_right;
865 op.enriched.rank = ranked.rank;
866 op.enriched.zone = ranked.zone;
867
868 if (! set_startable)
869 {
870 continue;
871 }
872
873 if (BOOST_GEOMETRY_CONDITION(OverlayType != overlay_difference)
874 && is_self_turn<OverlayType>(turn))
875 {
876 // Difference needs the self-turns, TODO: investigate
877 continue;
878 }
879
880 if ((for_operation == operation_union
881 && ranked.count_left != 0)
882 || (for_operation == operation_intersection
883 && ranked.count_right != 2))
884 {
885 op.enriched.startable = false;
886 }
887 }
888
889 }
890 }
891
892
893 }} // namespace detail::overlay
894 #endif //DOXYGEN_NO_DETAIL
895
896
897 }} // namespace boost::geometry
898
899 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
900