From 31361135dc8a7d5842e283c51ae92ad729ea1941 Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Fri, 27 Jun 2014 13:36:48 +0300 Subject: [PATCH] [algorithms][is_valid] re-structure and polish code a bit; check relative containment of rings only for rings that are not associated with turns --- .../detail/is_valid/multipolygon.hpp | 130 ++++++--- .../algorithms/detail/is_valid/polygon.hpp | 257 +++++++++++------- 2 files changed, 258 insertions(+), 129 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index f7d2e506a..882c2c748 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -64,13 +64,31 @@ private: - template - static inline bool are_polygon_interiors_disjoint(PolygonIterator first, - PolygonIterator beyond) + template + 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 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 - static inline bool have_connected_interior(PolygonIterator first, - PolygonIterator beyond, - Turns const& turns) + + + template + struct has_property_per_polygon { - int multi_index = 0; - for (PolygonIterator it = first; it != beyond; ++it, ++multi_index) + template + 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::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 + 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 + 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 has_valid_turns; - typedef typename has_valid_turns::turn_type turn_type; - std::deque turns; + std::deque 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()); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index cdd1b2dc1..59b646a74 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -66,52 +67,6 @@ protected: typedef debug_validity_phase debug_phase; - template - 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 - 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 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::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::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 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 - 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 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 + 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 + static inline bool apply(Polygon const& polygon, + TurnIterator first, + TurnIterator beyond) + { + typedef typename std::iterator_traits + < + TurnIterator + >::value_type turn_type; + typedef complement_graph 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 has_valid_turns; - typedef typename has_valid_turns::turn_type turn_type; - std::deque turns; + std::deque 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()); } };