123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- // Boost.Geometry (aka GGL, Generic Geometry Library)
- // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
- // Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
- // This file was modified by Oracle on 2017-2020.
- // Modifications copyright (c) 2017-2020 Oracle and/or its affiliates.
- // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
- // Use, modification and distribution is subject to the Boost Software License,
- // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
- #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
- #include <cstddef>
- #include <algorithm>
- #include <map>
- #include <vector>
- #include <boost/core/ignore_unused.hpp>
- #include <boost/range/begin.hpp>
- #include <boost/range/end.hpp>
- #include <boost/range/value_type.hpp>
- #include <boost/geometry/core/assert.hpp>
- #include <boost/geometry/core/point_order.hpp>
- #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
- #include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
- #include <boost/geometry/algorithms/detail/overlay/colocate_clusters.hpp>
- #include <boost/geometry/algorithms/detail/overlay/get_clusters.hpp>
- #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
- #include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
- #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
- #include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
- #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
- #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
- #include <boost/geometry/util/constexpr.hpp>
- #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
- # include <iostream>
- # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
- # include <boost/geometry/io/wkt/wkt.hpp>
- # define BOOST_GEOMETRY_DEBUG_IDENTIFIER
- #endif
- namespace boost { namespace geometry
- {
- #ifndef DOXYGEN_NO_DETAIL
- namespace detail { namespace overlay
- {
- // Removes clusters which have only one point left, or are empty.
- template <typename Turns, typename Clusters>
- inline void remove_clusters(Turns& turns, Clusters& clusters)
- {
- auto it = clusters.begin();
- while (it != clusters.end())
- {
- // Hold iterator and increase. We can erase cit, this keeps the
- // iterator valid (cf The standard associative-container erase idiom)
- auto current_it = it;
- ++it;
- auto const& turn_indices = current_it->second.turn_indices;
- if (turn_indices.size() == 1)
- {
- auto const turn_index = *turn_indices.begin();
- turns[turn_index].cluster_id = -1;
- clusters.erase(current_it);
- }
- }
- }
- template <typename Turns, typename Clusters>
- inline void cleanup_clusters(Turns& turns, Clusters& clusters)
- {
- // Removes discarded turns from clusters
- for (auto& pair : clusters)
- {
- auto& cinfo = pair.second;
- auto& indices = cinfo.turn_indices;
- for (auto sit = indices.begin(); sit != indices.end(); /* no increment */)
- {
- auto current_it = sit;
- ++sit;
- auto const turn_index = *current_it;
- if (turns[turn_index].discarded)
- {
- indices.erase(current_it);
- }
- }
- }
- remove_clusters(turns, clusters);
- }
- template <typename Turn, typename IndexSet>
- inline void discard_colocated_turn(Turn& turn, IndexSet& indices, signed_size_type index)
- {
- turn.discarded = true;
- // Set cluster id to -1, but don't clear colocated flags
- turn.cluster_id = -1;
- // To remove it later from clusters
- indices.insert(index);
- }
- template <bool Reverse>
- inline bool is_interior(segment_identifier const& seg_id)
- {
- return Reverse ? seg_id.ring_index == -1 : seg_id.ring_index >= 0;
- }
- template <bool Reverse0, bool Reverse1>
- inline bool 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)
- {
- if (ext_seg_0.source_index == ext_seg_1.source_index)
- {
- // External turn is a self-turn, dont discard internal turn for this
- return false;
- }
- // Compares two segment identifiers from two turns (external / one internal)
- // From first turn [0], both are from same polygon (multi_index),
- // one is exterior (-1), the other is interior (>= 0),
- // and the second turn [1] handles the same ring
- // For difference, where the rings are processed in reversal, all interior
- // rings become exterior and vice versa. But also the multi property changes:
- // rings originally from the same multi should now be considered as from
- // different multi polygons.
- // But this is not always the case, and at this point hard to figure out
- // (not yet implemented, TODO)
- bool const same_multi0 = ! Reverse0
- && ext_seg_0.multi_index == int_seg_0.multi_index;
- bool const same_multi1 = ! Reverse1
- && ext_seg_1.multi_index == other_seg_1.multi_index;
- boost::ignore_unused(same_multi1);
- return same_multi0
- && same_multi1
- && ! is_interior<Reverse0>(ext_seg_0)
- && is_interior<Reverse0>(int_seg_0)
- && ext_seg_1.ring_index == other_seg_1.ring_index;
- // The other way round is tested in another call
- }
- template
- <
- bool Reverse0, bool Reverse1, // Reverse interpretation interior/exterior
- typename Turns,
- typename Clusters
- >
- inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
- {
- std::set<signed_size_type> indices_to_remove;
- for (auto& pair : clusters)
- {
- cluster_info& cinfo = pair.second;
- indices_to_remove.clear();
- for (auto index : cinfo.turn_indices)
- {
- auto& turn = turns[index];
- segment_identifier const& seg_0 = turn.operations[0].seg_id;
- segment_identifier const& seg_1 = turn.operations[1].seg_id;
- if (! (turn.both(operation_union)
- || turn.combination(operation_union, operation_blocked)))
- {
- // Not a uu/ux, so cannot be colocated with a iu turn
- continue;
- }
- for (auto interior_index : cinfo.turn_indices)
- {
- if (index == interior_index)
- {
- continue;
- }
- // Turn with, possibly, an interior ring involved
- auto& interior_turn = turns[interior_index];
- segment_identifier const& int_seg_0 = interior_turn.operations[0].seg_id;
- segment_identifier const& int_seg_1 = interior_turn.operations[1].seg_id;
- if (is_ie_turn<Reverse0, Reverse1>(seg_0, seg_1, int_seg_0, int_seg_1))
- {
- discard_colocated_turn(interior_turn, indices_to_remove, interior_index);
- }
- if (is_ie_turn<Reverse1, Reverse0>(seg_1, seg_0, int_seg_1, int_seg_0))
- {
- discard_colocated_turn(interior_turn, indices_to_remove, interior_index);
- }
- }
- }
- // Erase from the indices (which cannot be done above)
- for (auto index : indices_to_remove)
- {
- cinfo.turn_indices.erase(index);
- }
- }
- }
- template
- <
- overlay_type OverlayType,
- typename Turns,
- typename Clusters
- >
- inline void set_colocation(Turns& turns, Clusters const& clusters)
- {
- for (auto const& pair : clusters)
- {
- cluster_info const& cinfo = pair.second;
- bool both_target = false;
- for (auto index : cinfo.turn_indices)
- {
- auto const& turn = turns[index];
- if (turn.both(operation_from_overlay<OverlayType>::value))
- {
- both_target = true;
- break;
- }
- }
- if (both_target)
- {
- for (auto index : cinfo.turn_indices)
- {
- auto& turn = turns[index];
- turn.has_colocated_both = true;
- }
- }
- }
- }
- template
- <
- typename Turns,
- typename Clusters
- >
- inline void check_colocation(bool& has_blocked,
- signed_size_type cluster_id, Turns const& turns, Clusters const& clusters)
- {
- typedef typename boost::range_value<Turns>::type turn_type;
- has_blocked = false;
- auto mit = clusters.find(cluster_id);
- if (mit == clusters.end())
- {
- return;
- }
- cluster_info const& cinfo = mit->second;
- for (auto index : cinfo.turn_indices)
- {
- turn_type const& turn = turns[index];
- if (turn.any_blocked())
- {
- has_blocked = true;
- }
- }
- }
- template
- <
- typename Turns,
- typename Clusters
- >
- inline void assign_cluster_ids(Turns& turns, Clusters const& clusters)
- {
- for (auto& turn : turns)
- {
- turn.cluster_id = -1;
- }
- for (auto const& kv : clusters)
- {
- for (auto const& index : kv.second.turn_indices)
- {
- turns[index].cluster_id = kv.first;
- }
- }
- }
- // Checks colocated turns and flags combinations of uu/other, possibly a
- // combination of a ring touching another geometry's interior ring which is
- // tangential to the exterior ring
- // This function can be extended to replace handle_tangencies: at each
- // colocation incoming and outgoing vectors should be inspected
- template
- <
- bool Reverse1, bool Reverse2,
- overlay_type OverlayType,
- typename Geometry0,
- typename Geometry1,
- typename Turns,
- typename Clusters,
- typename RobustPolicy
- >
- inline bool handle_colocations(Turns& turns, Clusters& clusters,
- RobustPolicy const& robust_policy)
- {
- static const detail::overlay::operation_type target_operation
- = detail::overlay::operation_from_overlay<OverlayType>::value;
- get_clusters(turns, clusters, robust_policy);
- if (clusters.empty())
- {
- return false;
- }
- assign_cluster_ids(turns, clusters);
- // Get colocated information here, and not later, to keep information
- // on turns which are discarded afterwards
- set_colocation<OverlayType>(turns, clusters);
- if BOOST_GEOMETRY_CONSTEXPR (target_operation == operation_intersection)
- {
- discard_interior_exterior_turns
- <
- do_reverse<geometry::point_order<Geometry0>::value>::value != Reverse1,
- do_reverse<geometry::point_order<Geometry1>::value>::value != Reverse2
- >(turns, clusters);
- }
- // There might be clusters having only one turn, if the rest is discarded
- // This is cleaned up later, after gathering the properties.
- #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
- std::cout << "*** Colocations " << map.size() << std::endl;
- for (auto const& kv : map)
- {
- std::cout << kv.first << std::endl;
- for (auto const& toi : kv.second)
- {
- detail::debug::debug_print_turn(turns[toi.turn_index]);
- std::cout << std::endl;
- }
- }
- #endif
- return true;
- }
- struct is_turn_index
- {
- is_turn_index(signed_size_type index)
- : m_index(index)
- {}
- template <typename Indexed>
- inline bool operator()(Indexed const& indexed) const
- {
- // Indexed is a indexed_turn_operation<Operation>
- return indexed.turn_index == m_index;
- }
- signed_size_type m_index;
- };
- template
- <
- typename Sbs,
- typename Point,
- typename Turns,
- typename Geometry1,
- typename Geometry2
- >
- inline bool fill_sbs(Sbs& sbs, Point& turn_point,
- cluster_info const& cinfo,
- Turns const& turns,
- Geometry1 const& geometry1, Geometry2 const& geometry2)
- {
- if (cinfo.turn_indices.empty())
- {
- return false;
- }
- bool first = true;
- for (auto turn_index : cinfo.turn_indices)
- {
- auto const& turn = turns[turn_index];
- if (first)
- {
- turn_point = turn.point;
- }
- for (int i = 0; i < 2; i++)
- {
- sbs.add(turn, turn.operations[i], turn_index, i, geometry1, geometry2, first);
- first = false;
- }
- }
- return true;
- }
- template
- <
- bool Reverse1, bool Reverse2,
- overlay_type OverlayType,
- typename Turns,
- typename Clusters,
- typename Geometry1,
- typename Geometry2,
- typename Strategy
- >
- inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
- operation_type for_operation,
- Geometry1 const& geometry1, Geometry2 const& geometry2,
- Strategy const& strategy)
- {
- typedef typename boost::range_value<Turns>::type turn_type;
- typedef typename turn_type::point_type point_type;
- typedef typename turn_type::turn_operation_type turn_operation_type;
- // Define sorter, sorting counter-clockwise such that polygons are on the
- // right side
- typedef sort_by_side::side_sorter
- <
- Reverse1, Reverse2, OverlayType, point_type, Strategy, std::less<int>
- > sbs_type;
- for (auto& pair : clusters)
- {
- cluster_info& cinfo = pair.second;
- sbs_type sbs(strategy);
- point_type turn_point; // should be all the same for all turns in cluster
- if (! fill_sbs(sbs, turn_point, cinfo, turns, geometry1, geometry2))
- {
- continue;
- }
- sbs.apply(turn_point);
- sbs.find_open();
- sbs.assign_zones(for_operation);
- cinfo.open_count = sbs.open_count(for_operation);
- // Determine spikes
- cinfo.spike_count = 0;
- for (std::size_t i = 0; i + 1 < sbs.m_ranked_points.size(); i++)
- {
- auto const& current = sbs.m_ranked_points[i];
- auto const& next = sbs.m_ranked_points[i + 1];
- if (current.rank == next.rank
- && current.direction == detail::overlay::sort_by_side::dir_from
- && next.direction == detail::overlay::sort_by_side::dir_to)
- {
- // It leaves, from cluster point, and immediately returns.
- cinfo.spike_count += 1;
- }
- }
- bool const set_startable = OverlayType != overlay_dissolve;
- // Unset the startable flag for all 'closed' zones. This does not
- // apply for self-turns, because those counts are not from both
- // polygons
- for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
- {
- typename sbs_type::rp const& ranked = sbs.m_ranked_points[i];
- turn_type& turn = turns[ranked.turn_index];
- turn_operation_type& op = turn.operations[ranked.operation_index];
- if (set_startable
- && for_operation == operation_union
- && cinfo.open_count == 0)
- {
- op.enriched.startable = false;
- }
- if (ranked.direction != sort_by_side::dir_to)
- {
- continue;
- }
- op.enriched.count_left = ranked.count_left;
- op.enriched.count_right = ranked.count_right;
- op.enriched.rank = ranked.rank;
- op.enriched.zone = ranked.zone;
- if (! set_startable)
- {
- continue;
- }
- if BOOST_GEOMETRY_CONSTEXPR (OverlayType == overlay_difference)
- {
- if (is_self_turn<OverlayType>(turn))
- {
- // TODO: investigate
- continue;
- }
- }
- if ((for_operation == operation_union
- && ranked.count_left != 0)
- || (for_operation == operation_intersection
- && ranked.count_right != 2))
- {
- op.enriched.startable = false;
- }
- }
- }
- }
- }} // namespace detail::overlay
- #endif //DOXYGEN_NO_DETAIL
- }} // namespace boost::geometry
- #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
|