summaryrefslogtreecommitdiff
path: root/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp')
-rw-r--r--boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp408
1 files changed, 320 insertions, 88 deletions
diff --git a/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp
index 183131c74b..0b4f393ef4 100644
--- a/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp
+++ b/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp
@@ -16,6 +16,7 @@
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
+#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/assert.hpp>
@@ -47,10 +48,54 @@ template
>
struct traversal_switch_detector
{
+ enum isolation_type { isolation_unknown = -1, isolation_no = 0, isolation_yes = 1 };
+
typedef typename boost::range_value<Turns>::type turn_type;
typedef typename turn_type::turn_operation_type turn_operation_type;
- // For convenience
+ // Per ring, first turns are collected (in turn_indices), and later
+ // a region_id is assigned
+ struct merged_ring_properties
+ {
+ signed_size_type region_id;
+ std::set<signed_size_type> turn_indices;
+
+ merged_ring_properties()
+ : region_id(-1)
+ {}
+ };
+
+ struct connection_properties
+ {
+ std::size_t count;
+ std::set<signed_size_type> cluster_indices;
+ connection_properties()
+ : count(0)
+ {}
+ };
+
+ typedef std::map<signed_size_type, connection_properties> connection_map;
+
+ // Per region, a set of properties is maintained, including its connections
+ // to other regions
+ struct region_properties
+ {
+ signed_size_type region_id;
+ isolation_type isolated;
+
+ // Maps from connected region_id to their properties
+ connection_map connected_region_counts;
+
+ region_properties()
+ : region_id(-1)
+ , isolated(isolation_unknown)
+ {}
+ };
+
+ // Keeps turn indices per ring
+ typedef std::map<ring_identifier, merged_ring_properties > merge_map;
+ typedef std::map<signed_size_type, region_properties> region_connection_map;
+
typedef std::set<signed_size_type>::const_iterator set_iterator;
inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2,
@@ -62,135 +107,309 @@ struct traversal_switch_detector
, m_clusters(clusters)
, m_robust_policy(robust_policy)
, m_visitor(visitor)
- , m_region_id(0)
{
+ }
+
+ isolation_type get_isolation(region_properties const& properties,
+ signed_size_type parent_region_id,
+ const std::set<signed_size_type>& visited)
+ {
+ if (properties.isolated != isolation_unknown)
+ {
+ return properties.isolated;
+ }
+
+ bool all_colocated = true;
+ int unique_cluster_id = -1;
+ for (typename connection_map::const_iterator it = properties.connected_region_counts.begin();
+ all_colocated && it != properties.connected_region_counts.end(); ++it)
+ {
+ connection_properties const& cprop = it->second;
+ if (cprop.cluster_indices.size() != 1)
+ {
+ // Either no cluster (non colocated point), or more clusters
+ all_colocated = false;
+ }
+ int const cluster_id = *cprop.cluster_indices.begin();
+ if (cluster_id == -1)
+ {
+ all_colocated = false;
+ }
+ else if (unique_cluster_id == -1)
+ {
+ unique_cluster_id = cluster_id;
+ }
+ else if (unique_cluster_id != cluster_id)
+ {
+ all_colocated = false;
+ }
+ }
+ if (all_colocated)
+ {
+ return isolation_yes;
+ }
+
+
+ // It is isolated if there is only one connection, or if there are more connections but all
+ // of them are isolated themselves, or if there are more connections
+ // but they are all colocated
+ std::size_t non_isolation_count = 0;
+ bool child_not_isolated = false;
+ for (typename connection_map::const_iterator it = properties.connected_region_counts.begin();
+ it != properties.connected_region_counts.end(); ++it)
+ {
+ signed_size_type const region_id = it->first;
+ connection_properties const& cprop = it->second;
+
+ if (region_id == parent_region_id)
+ {
+ // Normal situation, skip its direct parent
+ continue;
+ }
+ if (visited.count(region_id) > 0)
+ {
+ // Find one of its ancestors again, this is a ring. Not isolated.
+ return isolation_no;
+ }
+ if (cprop.count > 1)
+ {
+ return isolation_no;
+ }
+
+ typename region_connection_map::iterator mit = m_connected_regions.find(region_id);
+ if (mit == m_connected_regions.end())
+ {
+ // Should not occur
+ continue;
+ }
+
+ std::set<signed_size_type> vis = visited;
+ vis.insert(parent_region_id);
+ region_properties& prop = mit->second;
+ if (prop.isolated == isolation_unknown)
+ {
+ isolation_type const iso = get_isolation(prop, properties.region_id, vis);
+ prop.isolated = iso;
+ if (iso == isolation_no)
+ {
+ child_not_isolated = true;
+ }
+ }
+ if (prop.isolated == isolation_no)
+ {
+ non_isolation_count++;
+ }
+ }
+
+ return child_not_isolated || non_isolation_count > 1 ? isolation_no : isolation_yes;
}
- static inline bool connects_same_zone(turn_type const& turn)
+ void get_isolated_regions()
{
+ for (typename region_connection_map::iterator it = m_connected_regions.begin();
+ it != m_connected_regions.end(); ++it)
+ {
+ region_properties& properties = it->second;
+ if (properties.isolated == isolation_unknown)
+ {
+ std::set<signed_size_type> visited;
+ properties.isolated = get_isolation(properties, properties.region_id, visited);
+ }
+ }
+ }
+
+ void assign_isolation()
+ {
+ for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
+ {
+ turn_type& turn = m_turns[turn_index];
+
+ for (int op_index = 0; op_index < 2; op_index++)
+ {
+ turn_operation_type& op = turn.operations[op_index];
+ typename region_connection_map::const_iterator mit = m_connected_regions.find(op.enriched.region_id);
+ if (mit != m_connected_regions.end())
+ {
+ region_properties const& prop = mit->second;
+ op.enriched.isolated = prop.isolated == isolation_yes;
+ }
+ }
+ }
+ }
+
+ void assign_regions()
+ {
+ for (typename merge_map::const_iterator it
+ = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
+ {
+ ring_identifier const& ring_id = it->first;
+ merged_ring_properties const& properties = it->second;
+
+ for (set_iterator sit = properties.turn_indices.begin();
+ sit != properties.turn_indices.end(); ++sit)
+ {
+ turn_type& turn = m_turns[*sit];
+
+ for (int i = 0; i < 2; i++)
+ {
+ turn_operation_type& op = turn.operations[i];
+ if (ring_id_by_seg_id(op.seg_id) == ring_id)
+ {
+ op.enriched.region_id = properties.region_id;
+ }
+ }
+ signed_size_type const& id0 = turn.operations[0].enriched.region_id;
+ signed_size_type const& id1 = turn.operations[1].enriched.region_id;
+ if (id0 != id1 && id0 != -1 && id1 != -1)
+ {
+ // Force insertion
+ m_connected_regions[id0].region_id = id0;
+ m_connected_regions[id1].region_id = id1;
+
+ connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1];
+ connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0];
+
+ if (turn.cluster_id < 0)
+ {
+ // Turn is not colocated, add reference to connection
+ prop0.count++;
+ prop1.count++;
+ }
+ else
+ {
+ // Turn is colocated, only add region reference if it was not yet registered
+ if (prop0.cluster_indices.count(turn.cluster_id) == 0)
+ {
+ prop0.count++;
+ }
+ if (prop1.cluster_indices.count(turn.cluster_id) == 0)
+ {
+ prop1.count++;
+ }
+ }
+ // Insert cluster-id (also -1 is inserted - reinsertion of
+ // same cluster id is OK)
+ prop0.cluster_indices.insert(turn.cluster_id);
+ prop1.cluster_indices.insert(turn.cluster_id);
+ }
+ }
+ }
+ }
+
+ inline bool connects_same_region(turn_type const& turn) const
+ {
+ if (turn.discarded)
+ {
+ // Discarded turns don't connect same region (otherwise discarded colocated uu turn
+ // could make a connection)
+ return false;
+ }
+
if (turn.cluster_id == -1)
{
- // If it is a uu/ii-turn (non clustered), it is never same zone
+ // If it is a uu/ii-turn (non clustered), it is never same region
return ! (turn.both(operation_union) || turn.both(operation_intersection));
}
- // It is a cluster, check zones of both operations
- return turn.operations[0].enriched.zone
- == turn.operations[1].enriched.zone;
+ if (operation_from_overlay<OverlayType>::value == operation_union)
+ {
+ // It is a cluster, check zones
+ // (assigned by sort_by_side/handle colocations) of both operations
+ return turn.operations[0].enriched.zone
+ == turn.operations[1].enriched.zone;
+ }
+
+ // If a cluster contains an ii/cc it is not same region (for intersection)
+ typename Clusters::const_iterator it = m_clusters.find(turn.cluster_id);
+ if (it == m_clusters.end())
+ {
+ // Should not occur
+ return true;
+ }
+
+ cluster_info const& cinfo = it->second;
+ for (set_iterator sit = cinfo.turn_indices.begin();
+ sit != cinfo.turn_indices.end(); ++sit)
+ {
+ turn_type const& cluster_turn = m_turns[*sit];
+ if (cluster_turn.both(operation_union)
+ || cluster_turn.both(operation_intersection))
+ {
+ return false;
+ }
+ }
+
+ // It is the same region
+ return false;
}
+
inline int get_region_id(turn_operation_type const& op) const
{
- std::map<ring_identifier, int>::const_iterator it
- = m_regions.find(ring_id_by_seg_id(op.seg_id));
- return it == m_regions.end() ? -1 : it->second;
+ return op.enriched.region_id;
}
- void create_region(ring_identifier const& ring_id, std::set<signed_size_type> const& ring_turn_indices, int region_id = -1)
+
+ void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id,
+ merged_ring_properties& properties, int region_id = -1)
{
- std::map<ring_identifier, int>::const_iterator it = m_regions.find(ring_id);
- if (it != m_regions.end())
+ if (properties.region_id > 0)
{
- // The ring is already gathered in a region, quit
+ // Already handled
return;
}
+
+ // Assign new id if this is a new region
if (region_id == -1)
{
- region_id = m_region_id++;
+ region_id = new_region_id++;
}
// Assign this ring to specified region
- m_regions[ring_id] = region_id;
+ properties.region_id = region_id;
+
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
std::cout << " ADD " << ring_id << " TO REGION " << region_id << std::endl;
#endif
// Find connecting rings, recursively
- for (set_iterator sit = ring_turn_indices.begin();
- sit != ring_turn_indices.end(); ++sit)
+ for (set_iterator sit = properties.turn_indices.begin();
+ sit != properties.turn_indices.end(); ++sit)
{
signed_size_type const turn_index = *sit;
turn_type const& turn = m_turns[turn_index];
- if (! connects_same_zone(turn))
+ if (! connects_same_region(turn))
{
// This is a non clustered uu/ii-turn, or a cluster connecting different 'zones'
continue;
}
- // This turn connects two rings (interior connected), create the
- // same region
+ // Union: This turn connects two rings (interior connected), create the region
+ // Intersection: This turn connects two rings, set same regions for these two rings
for (int op_index = 0; op_index < 2; op_index++)
{
turn_operation_type const& op = turn.operations[op_index];
ring_identifier connected_ring_id = ring_id_by_seg_id(op.seg_id);
if (connected_ring_id != ring_id)
{
- propagate_region(connected_ring_id, region_id);
+ propagate_region(new_region_id, connected_ring_id, region_id);
}
}
}
}
- void check_turns_per_ring(ring_identifier const& ring_id,
- std::set<signed_size_type> const& ring_turn_indices)
+ void propagate_region(signed_size_type& new_region_id,
+ ring_identifier const& ring_id, int region_id)
{
- bool only_turn_on_ring = true;
- if (ring_turn_indices.size() > 1)
- {
- // More turns on this ring. Only leave only_turn_on_ring true
- // if they are all of the same cluster
- int cluster_id = -1;
- for (set_iterator sit = ring_turn_indices.begin();
- sit != ring_turn_indices.end(); ++sit)
- {
- turn_type const& turn = m_turns[*sit];
- if (turn.cluster_id == -1)
- {
- // Unclustered turn - and there are 2 or more turns
- // so the ring has different turns
- only_turn_on_ring = false;
- break;
- }
-
- // Clustered turn, check if it is the first or same as previous
- if (cluster_id == -1)
- {
- cluster_id = turn.cluster_id;
- }
- else if (turn.cluster_id != cluster_id)
- {
- only_turn_on_ring = false;
- break;
- }
- }
- }
-
- // Assign result to matching operation (a turn is always on two rings)
- for (set_iterator sit = ring_turn_indices.begin();
- sit != ring_turn_indices.end(); ++sit)
- {
- turn_type& turn = m_turns[*sit];
- for (int i = 0; i < 2; i++)
- {
- turn_operation_type& op = turn.operations[i];
- if (ring_id_by_seg_id(op.seg_id) == ring_id)
- {
- op.enriched.only_turn_on_ring = only_turn_on_ring;
- }
- }
- }
- }
-
- void propagate_region(ring_identifier const& ring_id, int region_id)
- {
- std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it = m_turns_per_ring.find(ring_id);
+ typename merge_map::iterator it = m_turns_per_ring.find(ring_id);
if (it != m_turns_per_ring.end())
{
- create_region(ring_id, it->second, region_id);
+ create_region(new_region_id, ring_id, it->second, region_id);
}
}
+
void iterate()
{
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
@@ -199,26 +418,38 @@ struct traversal_switch_detector
// Collect turns per ring
m_turns_per_ring.clear();
- m_regions.clear();
- m_region_id = 1;
+ m_connected_regions.clear();
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
{
turn_type const& turn = m_turns[turn_index];
+ if (turn.discarded
+ && operation_from_overlay<OverlayType>::value == operation_intersection)
+ {
+ // Discarded turn (union currently still needs it to determine regions)
+ continue;
+ }
+
for (int op_index = 0; op_index < 2; op_index++)
{
turn_operation_type const& op = turn.operations[op_index];
- m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].insert(turn_index);
+ m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].turn_indices.insert(turn_index);
}
}
// All rings having turns are in the map. Now iterate them
- for (std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it
- = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
{
- create_region(it->first, it->second);
- check_turns_per_ring(it->first, it->second);
+ signed_size_type new_region_id = 1;
+ for (typename merge_map::iterator it
+ = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
+ {
+ create_region(new_region_id, it->first, it->second);
+ }
+
+ assign_regions();
+ get_isolated_regions();
+ assign_isolation();
}
// Now that all regions are filled, assign switch_source property
@@ -245,6 +476,10 @@ struct traversal_switch_detector
{
signed_size_type turn_index = *sit;
turn_type const& turn = m_turns[turn_index];
+ if (turn.colocated_ii && ! turn.colocated_uu)
+ {
+ continue;
+ }
for (int oi = 0; oi < 2; oi++)
{
int const region = get_region_id(turn.operations[oi]);
@@ -252,7 +487,7 @@ struct traversal_switch_detector
}
}
// Switch source if this cluster connects the same region
- cinfo.switch_source = regions.size() == 1;
+ cinfo.switch_source = regions.size() <= 1;
}
// Iterate through all uu/ii turns (non-clustered)
@@ -326,13 +561,10 @@ private:
Geometry2 const& m_geometry2;
Turns& m_turns;
Clusters& m_clusters;
+ merge_map m_turns_per_ring;
+ region_connection_map m_connected_regions;
RobustPolicy const& m_robust_policy;
Visitor& m_visitor;
-
- std::map<ring_identifier, int> m_regions;
- std::map<ring_identifier, std::set<signed_size_type> > m_turns_per_ring;
- int m_region_id;
-
};
}} // namespace detail::overlay