From 970016f9dcf647f80895fd2e17c3dd6ef27ad83e Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Sat, 14 Jun 2014 23:51:45 +0300 Subject: [PATCH] [algorithms][is_valid] add implementation for validity of polygons (and partial implementation for validity of multi-polygons) --- .../algorithms/detail/is_valid/polygon.hpp | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 6037d1609..8385c6269 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -10,6 +10,44 @@ #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 + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef GEOMETRY_TEST_DEBUG +#include +#endif + namespace boost { namespace geometry { @@ -19,6 +57,233 @@ namespace boost { namespace geometry 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 +struct is_valid_polygon +{ + template + static inline bool are_holes_inside(RingIterator first, + RingIterator beyond, + ExteriorRing exterior_ring, + IndexSet const& rings_with_turns) + { + int idx = 0; + for (RingIterator it = first; it != beyond; ++it, ++idx) + { + // 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) ) + { + 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) + { + if ( rings_with_turns.find(idx) == rings_with_turns.end() ) + { + for (RingIterator it2 = first; it2 != beyond; ++it2) + { + if ( it1 != it2 + && geometry::within(range::front(*it1), *it2) ) + { + return false; + } + } + } + } + return true; + } + + + + 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; + } + + template + static inline bool is_acceptable_turn(Turn const& turn) + { + 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) + ; + } + + + + static inline bool apply(Polygon const& polygon) + { + typedef typename point_type::type point_type; + typedef typename ring_type::type ring_type; + + // check validity of exterior ring +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "checking exterior ring..." << std::endl; +#endif + if ( !detail::is_valid::is_valid_ring + < + ring_type, false // do not check self intersections + >::apply(exterior_ring(polygon)) ) + { + return false; + } + + + // check validity of interior rings +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "checking interior rings..." << std::endl; +#endif + if ( geometry::num_interior_rings(polygon) > 0 + && !detail::check_iterator_range + < + // do not check self-intersections, indicate interior ring + detail::is_valid::is_valid_ring + >::apply(boost::begin(geometry::interior_rings(polygon)), + boost::end(geometry::interior_rings(polygon))) + ) + { + return false; + } + + + // compute turns and check if all are acceptable +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "computing and analyzing turns..." << std::endl; +#endif + typedef detail::overlay::turn_info + < + point_type, + geometry::segment_ratio + < + typename geometry::coordinate_type::type + > + > turn_info; + + typedef detail::overlay::get_turn_info + < + detail::disjoint::assign_disjoint_policy + > turn_policy; + + + std::deque turns; + detail::self_get_turn_points::no_interrupt_policy interrupt_policy; + + // MK:: change the no_rescale_policy to rescale-to-integer + geometry::self_turns(polygon, + detail::no_rescale_policy(), + turns, + interrupt_policy); +#if GEOMETRY_TEST_DEBUG + std::cout << "turns:"; + for (typename std::deque::const_iterator tit = turns.begin(); + tit != turns.end(); ++tit) + { + std::cout << " [" << geometry::method_char(tit->method); + std::cout << "," + << geometry::operation_char(tit->operations[0].operation); + std::cout << "/" + << geometry::operation_char(tit->operations[1].operation); + std::cout << " {" << tit->operations[0].seg_id.ring_index + << ", " << tit->operations[0].other_id.ring_index + << "}"; + std::cout << " " << geometry::dsv(tit->point); + std::cout << "] "; + } + std::cout << std::endl << std::endl; +#endif + + std::set rings_with_turns; + for (typename std::deque::const_iterator tit = turns.begin(); + tit != turns.end(); ++tit) + { + if ( !is_acceptable_turn(*tit) ) + { + return false; + } + 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 +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "checking if holes are inside the exterior ring..." + << std::endl; +#endif + if ( !are_holes_inside(boost::begin(geometry::interior_rings(polygon)), + boost::end(geometry::interior_rings(polygon)), + geometry::exterior_ring(polygon), + rings_with_turns) ) + { + return false; + } + + + // check whether the interior of the polygon is a connected set +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "checking connectivity of interior..." << std::endl; +#endif + typedef graph_vertex graph_vertex; + 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) + { + 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 vip = g.add_vertex(tit->point); + + g.add_edge(v1, vip); + g.add_edge(v2, vip); + } + +#ifdef GEOMETRY_TEST_DEBUG + g.print(); +#endif + return !g.has_cycles(); + } +}; + + }} // namespace dispatch #endif // DOXYGEN_NO_DETAIL @@ -27,6 +292,46 @@ namespace detail { namespace is_valid #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { + + +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for validity of Polygons): OGC 06-103r4 (§6.1.11.1) +template +struct is_valid + : detail::is_valid::is_valid_polygon +{}; + + +// 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 + < + dispatch::is_valid + < + typename boost::range_value::type + > + >::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