• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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