[algorithms][is_valid] re-structure and polish code a bit;

check relative containment of rings only for rings that are not
associated with turns
This commit is contained in:
Menelaos Karavelas
2014-06-27 13:36:48 +03:00
parent a156ba17dc
commit 31361135dc
2 changed files with 258 additions and 129 deletions

View File

@@ -64,13 +64,31 @@ private:
template <typename PolygonIterator>
static inline bool are_polygon_interiors_disjoint(PolygonIterator first,
PolygonIterator beyond)
template <typename PolygonIterator, typename TurnIterator>
static inline
bool are_polygon_interiors_disjoint(PolygonIterator polygons_first,
PolygonIterator polygons_beyond,
TurnIterator turns_first,
TurnIterator turns_beyond)
{
for (PolygonIterator it1 = first; it1 != beyond; ++it1)
std::set<int> multi_indices;
for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
{
for (PolygonIterator it2 = first; it2 != beyond; ++it2)
multi_indices.insert(tit->operations[0].seg_id.multi_index);
multi_indices.insert(tit->operations[0].other_id.multi_index);
}
int multi_index = 0;
for (PolygonIterator it1 = polygons_first; it1 != polygons_beyond;
++it1, ++multi_index)
{
if ( multi_indices.find(multi_index) != multi_indices.end() )
{
continue;
}
for (PolygonIterator it2 = polygons_first;
it2 != polygons_beyond; ++it2)
{
if ( it1 != it2
&&
@@ -104,35 +122,78 @@ private:
int const m_multi_index;
};
template <typename PolygonIterator, typename Turns>
static inline bool have_connected_interior(PolygonIterator first,
PolygonIterator beyond,
Turns const& turns)
template <typename Predicate>
struct has_property_per_polygon
{
int multi_index = 0;
for (PolygonIterator it = first; it != beyond; ++it, ++multi_index)
template <typename PolygonIterator, typename TurnIterator>
static inline bool apply(PolygonIterator polygons_first,
PolygonIterator polygons_beyond,
TurnIterator turns_first,
TurnIterator turns_beyond)
{
has_multi_index predicate(multi_index);
typedef boost::filter_iterator
<
has_multi_index,
typename boost::range_iterator<Turns const>::type
> turn_iterator;
turn_iterator turns_first(predicate, turns.begin(), turns.end());
turn_iterator turns_beyond(predicate, turns.end(), turns.end());
if ( !base::has_connected_interior(*it, turns_first, turns_beyond) )
int multi_index = 0;
for (PolygonIterator it = polygons_first; it != polygons_beyond;
++it, ++multi_index)
{
return false;
has_multi_index index_predicate(multi_index);
typedef boost::filter_iterator
<
has_multi_index, TurnIterator
> filtered_turn_iterator;
filtered_turn_iterator filtered_turns_first(index_predicate,
turns_first,
turns_beyond);
filtered_turn_iterator filtered_turns_beyond(index_predicate,
turns_beyond,
turns_beyond);
if ( !Predicate::apply(*it,
filtered_turns_first,
filtered_turns_beyond) )
{
return false;
}
}
return true;
}
return true;
};
template <typename PolygonIterator, typename TurnIterator>
static inline bool have_holes_inside(PolygonIterator polygons_first,
PolygonIterator polygons_beyond,
TurnIterator turns_first,
TurnIterator turns_beyond)
{
return has_property_per_polygon
<
typename base::has_holes_inside
>::apply(polygons_first, polygons_beyond,
turns_first, turns_beyond);
}
template <typename PolygonIterator, typename TurnIterator>
static inline bool have_connected_interior(PolygonIterator polygons_first,
PolygonIterator polygons_beyond,
TurnIterator turns_first,
TurnIterator turns_beyond)
{
return has_property_per_polygon
<
typename base::has_connected_interior
>::apply(polygons_first, polygons_beyond,
turns_first, turns_beyond);
}
public:
static inline bool apply(MultiPolygon const& multipolygon)
{
@@ -156,9 +217,8 @@ public:
debug_phase::apply(2);
typedef has_valid_self_turns<MultiPolygon> has_valid_turns;
typedef typename has_valid_turns::turn_type turn_type;
std::deque<turn_type> turns;
std::deque<typename has_valid_turns::turn_type> turns;
bool has_invalid_turns = !has_valid_turns::apply(multipolygon, turns);
debug_print_turns(turns.begin(), turns.end());
@@ -172,11 +232,10 @@ public:
// exterior and not one inside the other
debug_phase::apply(3);
if ( !check_iterator_range
<
typename base::has_holes_inside
>::apply(boost::begin(multipolygon),
boost::end(multipolygon)) )
if ( !have_holes_inside(boost::begin(multipolygon),
boost::end(multipolygon),
turns.begin(),
turns.end()) )
{
return false;
}
@@ -187,7 +246,8 @@ public:
if ( !have_connected_interior(boost::begin(multipolygon),
boost::end(multipolygon),
turns) )
turns.begin(),
turns.end()) )
{
return false;
}
@@ -196,7 +256,9 @@ public:
// check if polygon interiors are disjoint
debug_phase::apply(5);
return are_polygon_interiors_disjoint(boost::begin(multipolygon),
boost::end(multipolygon));
boost::end(multipolygon),
turns.begin(),
turns.end());
}
};

View File

@@ -17,6 +17,7 @@
#include <iterator>
#include <set>
#include <boost/assert.hpp>
#include <boost/range.hpp>
#include <boost/geometry/core/exterior_ring.hpp>
@@ -66,52 +67,6 @@ protected:
typedef debug_validity_phase<Polygon> debug_phase;
template <typename RingIterator, typename ExteriorRing>
static inline bool are_holes_inside(RingIterator first,
RingIterator beyond,
ExteriorRing const& exterior_ring)
{
for (RingIterator it = first; it != beyond; ++it)
{
if ( !geometry::covered_by(range::front(*it), exterior_ring) )
{
return false;
}
}
for (RingIterator it1 = first; it1 != beyond; ++it1)
{
for (RingIterator it2 = first; it2 != beyond; ++it2)
{
if ( it1 != it2
&& geometry::within(range::front(*it1), *it2) )
{
return false;
}
}
}
return true;
}
template <typename InteriorRings, typename ExteriorRing>
static inline bool are_holes_inside(InteriorRings const& interior_rings,
ExteriorRing const& exterior_ring)
{
return are_holes_inside(boost::begin(interior_rings),
boost::end(interior_rings),
exterior_ring);
}
struct has_holes_inside
{
static inline bool apply(Polygon const& polygon)
{
return are_holes_inside(geometry::interior_rings(polygon),
geometry::exterior_ring(polygon));
}
};
template <typename InteriorRings>
static bool has_valid_interior_rings(InteriorRings const& interior_rings)
@@ -130,64 +85,175 @@ protected:
boost::end(interior_rings));
}
static inline bool has_valid_rings(Polygon const& polygon)
struct has_valid_rings
{
typedef typename ring_type<Polygon>::type ring_type;
// check validity of exterior ring
debug_phase::apply(1);
if ( !detail::is_valid::is_valid_ring
<
ring_type,
AllowDuplicates,
false // do not check self intersections
>::apply(exterior_ring(polygon)) )
static inline bool apply(Polygon const& polygon)
{
return false;
typedef typename ring_type<Polygon>::type ring_type;
// check validity of exterior ring
debug_phase::apply(1);
if ( !detail::is_valid::is_valid_ring
<
ring_type,
AllowDuplicates,
false // do not check self intersections
>::apply(exterior_ring(polygon)) )
{
return false;
}
// check validity of interior rings
debug_phase::apply(2);
return has_valid_interior_rings(geometry::interior_rings(polygon));
}
};
template
<
typename RingIterator,
typename ExteriorRing,
typename TurnIterator
>
static inline bool are_holes_inside(RingIterator rings_first,
RingIterator rings_beyond,
ExteriorRing const& exterior_ring,
TurnIterator turns_first,
TurnIterator turns_beyond)
{
// collect the interior ring indices that have turns with the
// exterior ring
std::set<int> ring_indices;
for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
{
if ( tit->operations[0].seg_id.ring_index == -1 )
{
BOOST_ASSERT( tit->operations[0].other_id.ring_index != -1 );
ring_indices.insert(tit->operations[0].other_id.ring_index);
}
else if ( tit->operations[0].other_id.ring_index == -1 )
{
BOOST_ASSERT( tit->operations[0].seg_id.ring_index != -1 );
ring_indices.insert(tit->operations[0].seg_id.ring_index);
}
}
// check validity of interior rings
debug_phase::apply(2);
return has_valid_interior_rings(geometry::interior_rings(polygon));
}
template <typename TurnIterator>
static inline bool has_connected_interior(Polygon const& polygon,
TurnIterator first,
TurnIterator beyond)
{
typedef typename std::iterator_traits
<
TurnIterator
>::value_type turn_type;
typedef complement_graph<typename turn_type::point_type> graph;
graph g(geometry::num_interior_rings(polygon) + 1);
for (TurnIterator tit = first; tit != beyond; ++tit)
int ring_index = 0;
for (RingIterator it = rings_first; it != rings_beyond;
++it, ++ring_index)
{
typename graph::vertex_handle v1 = g.add_vertex
( tit->operations[0].seg_id.ring_index + 1 );
typename graph::vertex_handle v2 = g.add_vertex
( tit->operations[0].other_id.ring_index + 1 );
typename graph::vertex_handle vip = g.add_vertex(tit->point);
g.add_edge(v1, vip);
g.add_edge(v2, vip);
// do not examine interior rings that have turns with the
// exterior ring
if ( ring_indices.find(ring_index) == ring_indices.end()
&& !geometry::covered_by(range::front(*it), exterior_ring) )
{
return false;
}
}
debug_print_complement_graph(std::cout, g);
// collect all rings (exterior and/or interior) that have turns
for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
{
ring_indices.insert(tit->operations[0].seg_id.ring_index);
ring_indices.insert(tit->operations[0].other_id.ring_index);
}
return !g.has_cycles();
ring_index = 0;
for (RingIterator it1 = rings_first; it1 != rings_beyond;
++it1, ++ring_index)
{
// do not examine rings that are associated with turns
if ( ring_indices.find(ring_index) == ring_indices.end() )
{
for (RingIterator it2 = rings_first; it2 != rings_beyond; ++it2)
{
if ( it1 != it2
&& geometry::within(range::front(*it1), *it2) )
{
return false;
}
}
}
}
return true;
}
template
<
typename InteriorRings,
typename ExteriorRing,
typename TurnIterator
>
static inline bool are_holes_inside(InteriorRings const& interior_rings,
ExteriorRing const& exterior_ring,
TurnIterator first,
TurnIterator beyond)
{
return are_holes_inside(boost::begin(interior_rings),
boost::end(interior_rings),
exterior_ring,
first,
beyond);
}
struct has_holes_inside
{
template <typename TurnIterator>
static inline bool apply(Polygon const& polygon,
TurnIterator first,
TurnIterator beyond)
{
return are_holes_inside(geometry::interior_rings(polygon),
geometry::exterior_ring(polygon),
first,
beyond);
}
};
struct has_connected_interior
{
template <typename TurnIterator>
static inline bool apply(Polygon const& polygon,
TurnIterator first,
TurnIterator beyond)
{
typedef typename std::iterator_traits
<
TurnIterator
>::value_type turn_type;
typedef complement_graph<typename turn_type::point_type> graph;
graph g(geometry::num_interior_rings(polygon) + 1);
for (TurnIterator tit = first; tit != beyond; ++tit)
{
typename graph::vertex_handle v1 = g.add_vertex
( tit->operations[0].seg_id.ring_index + 1 );
typename graph::vertex_handle v2 = g.add_vertex
( tit->operations[0].other_id.ring_index + 1 );
typename graph::vertex_handle vip = g.add_vertex(tit->point);
g.add_edge(v1, vip);
g.add_edge(v2, vip);
}
debug_print_complement_graph(std::cout, g);
return !g.has_cycles();
}
};
public:
static inline bool apply(Polygon const& polygon)
{
if ( !has_valid_rings(polygon) )
if ( !has_valid_rings::apply(polygon) )
{
return false;
}
@@ -201,9 +267,8 @@ public:
debug_phase::apply(3);
typedef has_valid_self_turns<Polygon> has_valid_turns;
typedef typename has_valid_turns::turn_type turn_type;
std::deque<turn_type> turns;
std::deque<typename has_valid_turns::turn_type> turns;
bool has_invalid_turns = !has_valid_turns::apply(polygon, turns);
debug_print_turns(turns.begin(), turns.end());
@@ -215,7 +280,7 @@ public:
// check if all interior rings are inside the exterior ring
debug_phase::apply(4);
if ( !has_holes_inside::apply(polygon) )
if ( !has_holes_inside::apply(polygon, turns.begin(), turns.end()) )
{
return false;
}
@@ -223,7 +288,9 @@ public:
// check whether the interior of the polygon is a connected set
debug_phase::apply(5);
return has_connected_interior(polygon, turns.begin(), turns.end());
return has_connected_interior::apply(polygon,
turns.begin(),
turns.end());
}
};