[algorithms][is_valid] add implementation for validity of polygons

(and partial implementation for validity of multi-polygons)
This commit is contained in:
Menelaos Karavelas
2014-06-14 23:51:45 +03:00
parent 0d577e7d4c
commit 970016f9dc

View File

@@ -10,6 +10,44 @@
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
#include <algorithm>
#include <deque>
#include <set>
#include <boost/range.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/exterior_ring.hpp>
#include <boost/geometry/core/interior_rings.hpp>
#include <boost/geometry/core/point_order.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/ring_type.hpp>
#include <boost/geometry/util/range.hpp>
#include <boost/geometry/policies/compare.hpp>
#include <boost/geometry/policies/robustness/segment_ratio.hpp>
#include <boost/geometry/algorithms/num_interior_rings.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
#include <boost/geometry/algorithms/detail/disjoint/linear_linear.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp>
#include <boost/geometry/algorithms/detail/is_valid/ring.hpp>
#include <boost/geometry/algorithms/detail/is_valid/complement_graph.hpp>
#include <boost/geometry/algorithms/dispatch/is_valid.hpp>
#ifdef GEOMETRY_TEST_DEBUG
#include <boost/geometry/io/dsv/write.hpp>
#endif
namespace boost { namespace geometry
{
@@ -19,6 +57,233 @@ namespace boost { namespace geometry
namespace detail { namespace is_valid
{
template <order_selector Order /* counterclockwise */>
struct acceptable_operation
{
static const detail::overlay::operation_type value =
detail::overlay::operation_union;
};
template <>
struct acceptable_operation<clockwise>
{
static const detail::overlay::operation_type value =
detail::overlay::operation_intersection;
};
template <typename Polygon>
struct is_valid_polygon
{
template <typename RingIterator, typename ExteriorRing, typename IndexSet>
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 <typename Turn, typename Method, typename Operation>
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 <typename Turn>
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<Polygon>::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<Polygon>::type point_type;
typedef typename ring_type<Polygon>::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<ring_type, false, true>
>::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<point_type>::type
>
> turn_info;
typedef detail::overlay::get_turn_info
<
detail::disjoint::assign_disjoint_policy
> turn_policy;
std::deque<turn_info> turns;
detail::self_get_turn_points::no_interrupt_policy interrupt_policy;
// MK:: change the no_rescale_policy to rescale-to-integer
geometry::self_turns<turn_policy>(polygon,
detail::no_rescale_policy(),
turns,
interrupt_policy);
#if GEOMETRY_TEST_DEBUG
std::cout << "turns:";
for (typename std::deque<turn_info>::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<int> rings_with_turns;
for (typename std::deque<turn_info>::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<typename turn_info::point_type> graph_vertex;
typedef complement_graph<graph_vertex> graph;
graph g(geometry::num_interior_rings(polygon) + 1);
for (typename std::deque<turn_info>::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 <typename Polygon>
struct is_valid<Polygon, polygon_tag>
: detail::is_valid::is_valid_polygon<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 <typename MultiPolygon>
struct is_valid<MultiPolygon, multi_polygon_tag>
{
static inline bool apply(MultiPolygon const& multipolygon)
{
if ( !detail::check_iterator_range
<
dispatch::is_valid
<
typename boost::range_value<MultiPolygon>::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