diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 87f4590d6..b9f3e9ca0 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -10,36 +10,34 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP +#include + #include #include +#include #include #include #include #include -#include -#include #include #include #include -#include -#include -#include - +// MK:: the following include may not be needed +#include #include #include #include -#include -#include -#include - -#include #include +#include +#include +#include + #include #include #include @@ -56,110 +54,67 @@ namespace detail { namespace is_valid { -template -struct acceptable_operation -{ - static const detail::overlay::operation_type value = - detail::overlay::operation_union; -}; - -template <> -struct acceptable_operation -{ - static const detail::overlay::operation_type value = - detail::overlay::operation_intersection; -}; - - - -template +template +< + typename Polygon, + bool AllowDuplicates, + bool CheckRingValidityOnly = false +> class is_valid_polygon { -private: - template +protected: + typedef debug_validity_phase debug_phase; + + + template static inline bool are_holes_inside(RingIterator first, RingIterator beyond, - ExteriorRing const& exterior_ring, - IndexSet const& rings_with_turns) + ExteriorRing const& exterior_ring) { - int idx = 0; - for (RingIterator it = first; it != beyond; ++it, ++idx) + for (RingIterator it = first; it != beyond; ++it) { - // check only rings whose index is not associated to any turn - if ( rings_with_turns.find(idx) == rings_with_turns.end() - && !geometry::within(range::front(*it), exterior_ring) ) + if ( !geometry::covered_by(range::front(*it), exterior_ring) ) { return false; } } - // for those rings that do not have any associated turns, - // check if they lie inside another ring - idx = 0; - for (RingIterator it1 = first; it1 != beyond; ++it1, ++idx) + for (RingIterator it1 = first; it1 != beyond; ++it1) { - if ( rings_with_turns.find(idx) == rings_with_turns.end() ) + for (RingIterator it2 = first; it2 != beyond; ++it2) { - for (RingIterator it2 = first; it2 != beyond; ++it2) + if ( it1 != it2 + && geometry::within(range::front(*it1), *it2) ) { - if ( it1 != it2 - && geometry::within(range::front(*it1), *it2) ) - { - return false; - } + return false; } } } return true; } - template + template static inline bool are_holes_inside(InteriorRings const& interior_rings, - ExteriorRing const& exterior_ring, - IndexSet const& rings_with_turns) + ExteriorRing const& exterior_ring) { return are_holes_inside(boost::begin(interior_rings), boost::end(interior_rings), - exterior_ring, - rings_with_turns); + exterior_ring); } - - template - static inline bool check_turn(Turn const& turn, - Method method, - Operation operation) - { - return turn.method == method - && turn.operations[0].operation == operation - && turn.operations[1].operation == operation; - } - - struct is_acceptable_turn - { - template - static inline bool apply(Turn const& turn) + struct has_holes_inside + { + static inline bool apply(Polygon const& polygon) { - if ( turn.operations[0].seg_id.ring_index - == turn.operations[0].other_id.ring_index ) - { - return false; - } - - detail::overlay::operation_type const op = acceptable_operation - < - geometry::point_order::value - >::value; - - return check_turn(turn, detail::overlay::method_touch_interior, op) - || check_turn(turn, detail::overlay::method_touch, op) - ; + return are_holes_inside(geometry::interior_rings(polygon), + geometry::exterior_ring(polygon)); } }; + template - static bool are_valid_interior_rings(InteriorRings const& interior_rings) + static bool has_valid_interior_rings(InteriorRings const& interior_rings) { return detail::check_iterator_range @@ -175,16 +130,10 @@ private: boost::end(interior_rings)); } - - -public: - static inline bool apply(Polygon const& polygon) + static inline bool has_valid_rings(Polygon const& polygon) { - typedef typename point_type::type point_type; typedef typename ring_type::type ring_type; - typedef debug_validity_phase debug_phase; - // check validity of exterior ring debug_phase::apply(1); @@ -201,92 +150,34 @@ public: // check validity of interior rings debug_phase::apply(2); - if ( !are_valid_interior_rings(geometry::interior_rings(polygon)) ) - { - return false; - } + return has_valid_interior_rings(geometry::interior_rings(polygon)); + } - // compute turns and check if all are acceptable - debug_phase::apply(3); - typedef typename geometry::rescale_policy_type + static inline std::size_t get_vertex_id(int ring_id) + { + return static_cast(ring_id + 1); + } + + template + static inline bool has_connected_interior(Polygon const& polygon, + TurnIterator first, + TurnIterator beyond) + { + typedef typename std::iterator_traits < - point_type - >::type rescale_policy_type; - - typedef detail::overlay::turn_info - < - point_type, - typename geometry::segment_ratio_type - < - point_type, - rescale_policy_type - >::type - > turn_info; - - typedef detail::overlay::get_turn_info - < - detail::overlay::assign_null_policy - > turn_policy; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(polygon); - - detail::overlay::stateless_predicate_based_interrupt_policy - < - is_acceptable_turn - > interrupt_policy; - - std::deque turns; - geometry::self_turns(polygon, - robust_policy, - turns, - interrupt_policy); - - if ( interrupt_policy.has_intersections ) - { - return false; - } - - debug_print_turns(turns.begin(), turns.end()); - - // put the ring id's that are associated with turns in a - // container with fast lookup (std::set) - std::set rings_with_turns; - for (typename std::deque::const_iterator tit = turns.begin(); - tit != turns.end(); ++tit) - { - rings_with_turns.insert(tit->operations[0].seg_id.ring_index); - rings_with_turns.insert(tit->operations[0].other_id.ring_index); - } - - - // check if all interior rings are inside the exterior ring - debug_phase::apply(4); - - if ( !are_holes_inside(geometry::interior_rings(polygon), - geometry::exterior_ring(polygon), - rings_with_turns) ) - { - return false; - } - - - // check whether the interior of the polygon is a connected set - debug_phase::apply(5); - - typedef graph_vertex graph_vertex; - typedef complement_graph graph; + TurnIterator + >::value_type turn_type; + typedef complement_graph graph; graph g(geometry::num_interior_rings(polygon) + 1); - for (typename std::deque::const_iterator tit = turns.begin(); - tit != turns.end(); ++tit) + for (TurnIterator tit = first; tit != beyond; ++tit) { - typename graph::vertex_handle v1 - = g.add_vertex(tit->operations[0].seg_id.ring_index); - typename graph::vertex_handle v2 - = g.add_vertex(tit->operations[0].other_id.ring_index); + typename graph::vertex_handle v1 = g.add_vertex + ( get_vertex_id(tit->operations[0].seg_id.ring_index) ); + typename graph::vertex_handle v2 = g.add_vertex + ( get_vertex_id(tit->operations[0].other_id.ring_index) ); typename graph::vertex_handle vip = g.add_vertex(tit->point); g.add_edge(v1, vip); @@ -297,10 +188,52 @@ public: return !g.has_cycles(); } + +public: + static inline bool apply(Polygon const& polygon) + { + if ( !has_valid_rings(polygon) ) + { + return false; + } + + if ( CheckRingValidityOnly ) + { + return true; + } + + // compute turns and check if all are acceptable + debug_phase::apply(3); + + typedef has_valid_self_turns has_valid_turns; + typedef typename has_valid_turns::turn_type turn_type; + + std::deque turns; + bool has_invalid_turns = !has_valid_turns::apply(polygon, turns); + debug_print_turns(turns.begin(), turns.end()); + + if ( has_invalid_turns ) + { + return false; + } + + // check if all interior rings are inside the exterior ring + debug_phase::apply(4); + + if ( !has_holes_inside::apply(polygon) ) + { + return false; + } + + // check whether the interior of the polygon is a connected set + debug_phase::apply(5); + + return has_connected_interior(polygon, turns.begin(), turns.end()); + } }; -}} // namespace dispatch +}} // namespace detail::is_valid #endif // DOXYGEN_NO_DETAIL @@ -319,37 +252,6 @@ struct is_valid {}; -// Not clear what the definition is. -// Right now we check that each element is simple (in fact valid), and -// that the MultiPolygon is also valid. -// -// Reference (for validity of MultiPolygons): OGC 06-103r4 (ยง6.1.14) -template -struct is_valid -{ - static inline bool apply(MultiPolygon const& multipolygon) - { - if ( !detail::check_iterator_range - < - detail::is_valid::is_valid_polygon - < - typename boost::range_value::type, - AllowDuplicates - >, - false // do not allow empty multi-polygons - >::apply(boost::begin(multipolygon), - boost::end(multipolygon)) ) - { - return false; - } - - // MK::need to check that they are (almost) disjoint - return true; - } - -}; - - } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH