1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
6 // This file was modified by Oracle on 2017.
7 // Modifications copyright (c) 2017 Oracle and/or its affiliates.
9 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
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)
15 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
16 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
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/ring_identifier.hpp>
35 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
36 #include <boost/geometry/util/condition.hpp>
38 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
40 # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
41 # include <boost/geometry/io/wkt/wkt.hpp>
42 # define BOOST_GEOMETRY_DEBUG_IDENTIFIER
45 namespace boost { namespace geometry
48 #ifndef DOXYGEN_NO_DETAIL
49 namespace detail { namespace overlay
52 template <typename SegmentRatio>
53 struct segment_fraction
55 segment_identifier seg_id;
56 SegmentRatio fraction;
58 segment_fraction(segment_identifier const& id, SegmentRatio const& fr)
66 bool operator<(segment_fraction<SegmentRatio> const& other) const
68 return seg_id == other.seg_id
69 ? fraction < other.fraction
70 : seg_id < other.seg_id;
75 struct turn_operation_index
77 turn_operation_index(signed_size_type ti = -1,
78 signed_size_type oi = -1)
83 signed_size_type turn_index;
84 signed_size_type op_index; // only 0,1
87 template <typename Turns>
88 struct less_by_fraction_and_type
90 inline less_by_fraction_and_type(Turns const& turns)
95 inline bool operator()(turn_operation_index const& left,
96 turn_operation_index const& right) const
98 typedef typename boost::range_value<Turns>::type turn_type;
99 typedef typename turn_type::turn_operation_type turn_operation_type;
101 turn_type const& left_turn = m_turns[left.turn_index];
102 turn_type const& right_turn = m_turns[right.turn_index];
103 turn_operation_type const& left_op
104 = left_turn.operations[left.op_index];
106 turn_operation_type const& right_op
107 = right_turn.operations[right.op_index];
109 if (! (left_op.fraction == right_op.fraction))
111 return left_op.fraction < right_op.fraction;
114 // Order xx first - used to discard any following colocated turn
115 bool const left_both_xx = left_turn.both(operation_blocked);
116 bool const right_both_xx = right_turn.both(operation_blocked);
117 if (left_both_xx && ! right_both_xx)
121 if (! left_both_xx && right_both_xx)
126 bool const left_both_uu = left_turn.both(operation_union);
127 bool const right_both_uu = right_turn.both(operation_union);
128 if (left_both_uu && ! right_both_uu)
132 if (! left_both_uu && right_both_uu)
137 turn_operation_type const& left_other_op
138 = left_turn.operations[1 - left.op_index];
140 turn_operation_type const& right_other_op
141 = right_turn.operations[1 - right.op_index];
143 // Fraction is the same, now sort on ring id, first outer ring,
144 // then interior rings
145 return left_other_op.seg_id < right_other_op.seg_id;
149 Turns const& m_turns;
152 template <typename Operation, typename ClusterPerSegment>
153 inline signed_size_type get_cluster_id(Operation const& op, ClusterPerSegment const& cluster_per_segment)
155 typedef typename ClusterPerSegment::key_type segment_fraction_type;
157 segment_fraction_type seg_frac(op.seg_id, op.fraction);
158 typename ClusterPerSegment::const_iterator it
159 = cluster_per_segment.find(seg_frac);
161 if (it == cluster_per_segment.end())
168 template <typename Operation, typename ClusterPerSegment>
169 inline void add_cluster_id(Operation const& op,
170 ClusterPerSegment& cluster_per_segment, signed_size_type id)
172 typedef typename ClusterPerSegment::key_type segment_fraction_type;
174 segment_fraction_type seg_frac(op.seg_id, op.fraction);
176 cluster_per_segment[seg_frac] = id;
179 template <typename Turn, typename ClusterPerSegment>
180 inline signed_size_type add_turn_to_cluster(Turn const& turn,
181 ClusterPerSegment& cluster_per_segment, signed_size_type& cluster_id)
183 signed_size_type cid0 = get_cluster_id(turn.operations[0], cluster_per_segment);
184 signed_size_type cid1 = get_cluster_id(turn.operations[1], cluster_per_segment);
186 if (cid0 == -1 && cid1 == -1)
188 // Because of this, first cluster ID will be 1
190 add_cluster_id(turn.operations[0], cluster_per_segment, cluster_id);
191 add_cluster_id(turn.operations[1], cluster_per_segment, cluster_id);
194 else if (cid0 == -1 && cid1 != -1)
196 add_cluster_id(turn.operations[0], cluster_per_segment, cid1);
199 else if (cid0 != -1 && cid1 == -1)
201 add_cluster_id(turn.operations[1], cluster_per_segment, cid0);
204 else if (cid0 == cid1)
206 // Both already added to same cluster, no action
210 // Both operations.seg_id/fraction were already part of any cluster, and
211 // these clusters are not the same. Merge of two clusters is necessary
212 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
213 std::cout << " TODO: merge " << cid0 << " and " << cid1 << std::endl;
221 typename ClusterPerSegment,
226 inline void handle_colocation_cluster(Turns& turns,
227 signed_size_type& cluster_id,
228 ClusterPerSegment& cluster_per_segment,
229 Operations const& operations,
230 Geometry1 const& /*geometry1*/, Geometry2 const& /*geometry2*/)
232 typedef typename boost::range_value<Turns>::type turn_type;
233 typedef typename turn_type::turn_operation_type turn_operation_type;
235 std::vector<turn_operation_index>::const_iterator vit = operations.begin();
237 turn_operation_index ref_toi = *vit;
238 signed_size_type ref_id = -1;
240 for (++vit; vit != operations.end(); ++vit)
242 turn_type& ref_turn = turns[ref_toi.turn_index];
243 turn_operation_type const& ref_op
244 = ref_turn.operations[ref_toi.op_index];
246 turn_operation_index const& toi = *vit;
247 turn_type& turn = turns[toi.turn_index];
248 turn_operation_type const& op = turn.operations[toi.op_index];
250 BOOST_GEOMETRY_ASSERT(ref_op.seg_id == op.seg_id);
252 if (ref_op.fraction == op.fraction)
254 turn_operation_type const& other_op = turn.operations[1 - toi.op_index];
258 ref_id = add_turn_to_cluster(ref_turn, cluster_per_segment, cluster_id);
260 BOOST_GEOMETRY_ASSERT(ref_id != -1);
262 // ref_turn (both operations) are already added to cluster,
263 // so also "op" is already added to cluster,
264 // We only need to add other_op
265 signed_size_type id = get_cluster_id(other_op, cluster_per_segment);
266 if (id != -1 && id != ref_id)
271 // Add to same cluster
272 add_cluster_id(other_op, cluster_per_segment, ref_id);
278 // Not on same fraction on this segment
290 typename ClusterPerSegment
292 inline void assign_cluster_to_turns(Turns& turns,
294 ClusterPerSegment const& cluster_per_segment)
296 typedef typename boost::range_value<Turns>::type turn_type;
297 typedef typename turn_type::turn_operation_type turn_operation_type;
298 typedef typename ClusterPerSegment::key_type segment_fraction_type;
300 signed_size_type turn_index = 0;
301 for (typename boost::range_iterator<Turns>::type it = turns.begin();
302 it != turns.end(); ++it, ++turn_index)
304 turn_type& turn = *it;
308 // They were processed (to create proper map) but will not be added
309 // This might leave a cluster with only 1 turn, which will be fixed
314 for (int i = 0; i < 2; i++)
316 turn_operation_type const& op = turn.operations[i];
317 segment_fraction_type seg_frac(op.seg_id, op.fraction);
318 typename ClusterPerSegment::const_iterator cit = cluster_per_segment.find(seg_frac);
319 if (cit != cluster_per_segment.end())
321 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
322 if (turn.is_clustered()
323 && turn.cluster_id != cit->second)
325 std::cout << " CONFLICT " << std::endl;
328 turn.cluster_id = cit->second;
329 clusters[turn.cluster_id].turn_indices.insert(turn_index);
335 template <typename Turns, typename Clusters>
336 inline void remove_clusters(Turns& turns, Clusters& clusters)
338 typename Clusters::iterator it = clusters.begin();
339 while (it != clusters.end())
341 // Hold iterator and increase. We can erase cit, this keeps the
342 // iterator valid (cf The standard associative-container erase idiom)
343 typename Clusters::iterator current_it = it;
346 std::set<signed_size_type> const& turn_indices
347 = current_it->second.turn_indices;
348 if (turn_indices.size() == 1)
350 signed_size_type const turn_index = *turn_indices.begin();
351 turns[turn_index].cluster_id = -1;
352 clusters.erase(current_it);
357 template <typename Turns, typename Clusters>
358 inline void cleanup_clusters(Turns& turns, Clusters& clusters)
360 // Removes discarded turns from clusters
361 for (typename Clusters::iterator mit = clusters.begin();
362 mit != clusters.end(); ++mit)
364 cluster_info& cinfo = mit->second;
365 std::set<signed_size_type>& ids = cinfo.turn_indices;
366 for (std::set<signed_size_type>::iterator sit = ids.begin();
367 sit != ids.end(); /* no increment */)
369 std::set<signed_size_type>::iterator current_it = sit;
372 signed_size_type const turn_index = *current_it;
373 if (turns[turn_index].discarded)
375 ids.erase(current_it);
380 remove_clusters(turns, clusters);
383 template <typename Turn, typename IdSet>
384 inline void discard_ie_turn(Turn& turn, IdSet& ids, signed_size_type id)
386 turn.discarded = true;
387 // Set cluster id to -1, but don't clear colocated flags
388 turn.cluster_id = -1;
389 // To remove it later from clusters
393 template <bool Reverse>
394 inline bool is_interior(segment_identifier const& seg_id)
396 return Reverse ? seg_id.ring_index == -1 : seg_id.ring_index >= 0;
399 template <bool Reverse0, bool Reverse1>
400 inline bool is_ie_turn(segment_identifier const& ext_seg_0,
401 segment_identifier const& ext_seg_1,
402 segment_identifier const& int_seg_0,
403 segment_identifier const& other_seg_1)
405 if (ext_seg_0.source_index == ext_seg_1.source_index)
407 // External turn is a self-turn, dont discard internal turn for this
412 // Compares two segment identifiers from two turns (external / one internal)
414 // From first turn [0], both are from same polygon (multi_index),
415 // one is exterior (-1), the other is interior (>= 0),
416 // and the second turn [1] handles the same ring
418 // For difference, where the rings are processed in reversal, all interior
419 // rings become exterior and vice versa. But also the multi property changes:
420 // rings originally from the same multi should now be considered as from
421 // different multi polygons.
422 // But this is not always the case, and at this point hard to figure out
423 // (not yet implemented, TODO)
425 bool const same_multi0 = ! Reverse0
426 && ext_seg_0.multi_index == int_seg_0.multi_index;
428 bool const same_multi1 = ! Reverse1
429 && ext_seg_1.multi_index == other_seg_1.multi_index;
431 boost::ignore_unused(same_multi1);
435 && ! is_interior<Reverse0>(ext_seg_0)
436 && is_interior<Reverse0>(int_seg_0)
437 && ext_seg_1.ring_index == other_seg_1.ring_index;
439 // The other way round is tested in another call
444 bool Reverse0, bool Reverse1, // Reverse interpretation interior/exterior
448 inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
450 typedef std::set<signed_size_type>::const_iterator set_iterator;
451 typedef typename boost::range_value<Turns>::type turn_type;
453 std::set<signed_size_type> ids_to_remove;
455 for (typename Clusters::iterator cit = clusters.begin();
456 cit != clusters.end(); ++cit)
458 cluster_info& cinfo = cit->second;
459 std::set<signed_size_type>& ids = cinfo.turn_indices;
461 ids_to_remove.clear();
463 for (set_iterator it = ids.begin(); it != ids.end(); ++it)
465 turn_type& turn = turns[*it];
466 segment_identifier const& seg_0 = turn.operations[0].seg_id;
467 segment_identifier const& seg_1 = turn.operations[1].seg_id;
469 if (! (turn.both(operation_union)
470 || turn.combination(operation_union, operation_blocked)))
472 // Not a uu/ux, so cannot be colocated with a iu turn
476 for (set_iterator int_it = ids.begin(); int_it != ids.end(); ++int_it)
483 // Turn with, possibly, an interior ring involved
484 turn_type& int_turn = turns[*int_it];
485 segment_identifier const& int_seg_0 = int_turn.operations[0].seg_id;
486 segment_identifier const& int_seg_1 = int_turn.operations[1].seg_id;
488 if (is_ie_turn<Reverse0, Reverse1>(seg_0, seg_1, int_seg_0, int_seg_1))
490 discard_ie_turn(int_turn, ids_to_remove, *int_it);
492 if (is_ie_turn<Reverse1, Reverse0>(seg_1, seg_0, int_seg_1, int_seg_0))
494 discard_ie_turn(int_turn, ids_to_remove, *int_it);
499 // Erase from the ids (which cannot be done above)
500 for (set_iterator sit = ids_to_remove.begin();
501 sit != ids_to_remove.end(); ++sit)
508 template <typename Geometry0, typename Geometry1>
509 inline segment_identifier get_preceding_segment_id(segment_identifier const& id,
510 Geometry0 const& geometry0, Geometry1 const& geometry1)
512 segment_identifier result = id;
514 if (result.segment_index == 0)
516 // Assign to segment_count before decrement
518 = id.source_index == 0
519 ? segment_count_on_ring(geometry0, id)
520 : segment_count_on_ring(geometry1, id);
523 result.segment_index--;
530 overlay_type OverlayType,
534 inline void set_colocation(Turns& turns, Clusters const& clusters)
536 typedef std::set<signed_size_type>::const_iterator set_iterator;
537 typedef typename boost::range_value<Turns>::type turn_type;
539 for (typename Clusters::const_iterator cit = clusters.begin();
540 cit != clusters.end(); ++cit)
542 cluster_info const& cinfo = cit->second;
543 std::set<signed_size_type> const& ids = cinfo.turn_indices;
545 bool both_target = false;
546 for (set_iterator it = ids.begin(); it != ids.end(); ++it)
548 turn_type const& turn = turns[*it];
549 if (turn.both(operation_from_overlay<OverlayType>::value))
558 for (set_iterator it = ids.begin(); it != ids.end(); ++it)
560 turn_type& turn = turns[*it];
561 turn.has_colocated_both = true;
572 inline void check_colocation(bool& has_blocked,
573 signed_size_type cluster_id, Turns const& turns, Clusters const& clusters)
575 typedef typename boost::range_value<Turns>::type turn_type;
579 typename Clusters::const_iterator mit = clusters.find(cluster_id);
580 if (mit == clusters.end())
585 cluster_info const& cinfo = mit->second;
587 for (std::set<signed_size_type>::const_iterator it
588 = cinfo.turn_indices.begin();
589 it != cinfo.turn_indices.end(); ++it)
591 turn_type const& turn = turns[*it];
592 if (turn.any_blocked())
600 // Checks colocated turns and flags combinations of uu/other, possibly a
601 // combination of a ring touching another geometry's interior ring which is
602 // tangential to the exterior ring
604 // This function can be extended to replace handle_tangencies: at each
605 // colocation incoming and outgoing vectors should be inspected
609 bool Reverse1, bool Reverse2,
610 overlay_type OverlayType,
616 inline bool handle_colocations(Turns& turns, Clusters& clusters,
617 Geometry1 const& geometry1, Geometry2 const& geometry2)
619 static const detail::overlay::operation_type target_operation
620 = detail::overlay::operation_from_overlay<OverlayType>::value;
624 std::vector<turn_operation_index>
627 // Create and fill map on segment-identifier Map is sorted on seg_id,
628 // meaning it is sorted on ring_identifier too. This means that exterior
629 // rings are handled first. If there is a colocation on the exterior ring,
630 // that information can be used for the interior ring too
633 signed_size_type index = 0;
634 for (typename boost::range_iterator<Turns>::type
635 it = boost::begin(turns);
636 it != boost::end(turns);
639 map[it->operations[0].seg_id].push_back(turn_operation_index(index, 0));
640 map[it->operations[1].seg_id].push_back(turn_operation_index(index, 1));
643 // Check if there are multiple turns on one or more segments,
644 // if not then nothing is to be done
645 bool colocations = 0;
646 for (typename map_type::const_iterator it = map.begin();
650 if (it->second.size() > 1u)
662 // Sort all vectors, per same segment
663 less_by_fraction_and_type<Turns> less(turns);
664 for (typename map_type::iterator it = map.begin();
665 it != map.end(); ++it)
667 std::sort(it->second.begin(), it->second.end(), less);
670 typedef typename boost::range_value<Turns>::type turn_type;
671 typedef typename turn_type::segment_ratio_type segment_ratio_type;
675 segment_fraction<segment_ratio_type>,
677 > cluster_per_segment_type;
679 cluster_per_segment_type cluster_per_segment;
681 // Assign to zero, because of pre-increment later the cluster_id
682 // effectively starts with 1
683 // (and can later be negated to use uniquely with turn_index)
684 signed_size_type cluster_id = 0;
686 for (typename map_type::const_iterator it = map.begin();
687 it != map.end(); ++it)
689 if (it->second.size() > 1u)
691 handle_colocation_cluster(turns, cluster_id, cluster_per_segment,
692 it->second, geometry1, geometry2);
696 assign_cluster_to_turns(turns, clusters, cluster_per_segment);
697 // Get colocated information here and not later, to keep information
698 // on turns which are discarded afterwards
699 set_colocation<OverlayType>(turns, clusters);
701 if (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
703 discard_interior_exterior_turns
705 do_reverse<geometry::point_order<Geometry1>::value>::value != Reverse1,
706 do_reverse<geometry::point_order<Geometry2>::value>::value != Reverse2
710 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
711 std::cout << "*** Colocations " << map.size() << std::endl;
712 for (typename map_type::const_iterator it = map.begin();
713 it != map.end(); ++it)
715 std::cout << it->first << std::endl;
716 for (std::vector<turn_operation_index>::const_iterator vit
717 = it->second.begin(); vit != it->second.end(); ++vit)
719 turn_operation_index const& toi = *vit;
720 std::cout << geometry::wkt(turns[toi.turn_index].point)
722 << " discarded=" << turns[toi.turn_index].discarded
723 << " colocated(uu)=" << turns[toi.turn_index].colocated_uu
724 << " colocated(ii)=" << turns[toi.turn_index].colocated_ii
725 << " " << operation_char(turns[toi.turn_index].operations[0].operation)
726 << " " << turns[toi.turn_index].operations[0].seg_id
727 << " " << turns[toi.turn_index].operations[0].fraction
728 << " // " << operation_char(turns[toi.turn_index].operations[1].operation)
729 << " " << turns[toi.turn_index].operations[1].seg_id
730 << " " << turns[toi.turn_index].operations[1].fraction
742 is_turn_index(signed_size_type index)
746 template <typename Indexed>
747 inline bool operator()(Indexed const& indexed) const
749 // Indexed is a indexed_turn_operation<Operation>
750 return indexed.turn_index == m_index;
753 signed_size_type m_index;
759 bool Reverse1, bool Reverse2,
760 overlay_type OverlayType,
765 typename SideStrategy
767 inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
768 operation_type for_operation,
769 Geometry1 const& geometry1, Geometry2 const& geometry2,
770 SideStrategy const& strategy)
772 typedef typename boost::range_value<Turns>::type turn_type;
773 typedef typename turn_type::point_type point_type;
774 typedef typename turn_type::turn_operation_type turn_operation_type;
776 // Define sorter, sorting counter-clockwise such that polygons are on the
778 typedef sort_by_side::side_sorter
780 Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less<int>
783 for (typename Clusters::iterator mit = clusters.begin();
784 mit != clusters.end(); ++mit)
786 cluster_info& cinfo = mit->second;
787 std::set<signed_size_type> const& ids = cinfo.turn_indices;
793 sbs_type sbs(strategy);
794 point_type turn_point; // should be all the same for all turns in cluster
797 for (std::set<signed_size_type>::const_iterator sit = ids.begin();
798 sit != ids.end(); ++sit)
800 signed_size_type turn_index = *sit;
801 turn_type const& turn = turns[turn_index];
804 turn_point = turn.point;
806 for (int i = 0; i < 2; i++)
808 turn_operation_type const& op = turn.operations[i];
809 sbs.add(op, turn_index, i, geometry1, geometry2, first);
813 sbs.apply(turn_point);
816 sbs.assign_zones(for_operation);
818 cinfo.open_count = sbs.open_count(for_operation);
820 bool const set_startable = OverlayType != overlay_dissolve;
822 // Unset the startable flag for all 'closed' zones. This does not
823 // apply for self-turns, because those counts are not from both
825 for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
827 const typename sbs_type::rp& ranked = sbs.m_ranked_points[i];
828 turn_type& turn = turns[ranked.turn_index];
829 turn_operation_type& op = turn.operations[ranked.operation_index];
832 && for_operation == operation_union && cinfo.open_count == 0)
834 op.enriched.startable = false;
837 if (ranked.direction != sort_by_side::dir_to)
842 op.enriched.count_left = ranked.count_left;
843 op.enriched.count_right = ranked.count_right;
844 op.enriched.rank = ranked.rank;
845 op.enriched.zone = ranked.zone;
852 if (BOOST_GEOMETRY_CONDITION(OverlayType != overlay_difference)
853 && is_self_turn<OverlayType>(turn))
855 // Difference needs the self-turns, TODO: investigate
859 if ((for_operation == operation_union
860 && ranked.count_left != 0)
861 || (for_operation == operation_intersection
862 && ranked.count_right != 2))
864 op.enriched.startable = false;
872 }} // namespace detail::overlay
873 #endif //DOXYGEN_NO_DETAIL
876 }} // namespace boost::geometry
878 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP