mirror of
https://github.com/boostorg/geometry.git
synced 2025-05-11 13:34:10 +00:00
Merge pull request #75 from mkaravel/feature/is_simple
Feature/is simple
This commit is contained in:
commit
e9178b6a80
@ -181,6 +181,8 @@ INPUT = . .. ../../../../boost/geometry/core \
|
||||
../../../../boost/geometry/algorithms/detail/disjoint \
|
||||
../../../../boost/geometry/algorithms/detail/distance \
|
||||
../../../../boost/geometry/algorithms/detail/equals \
|
||||
../../../../boost/geometry/algorithms/detail/is_simple \
|
||||
../../../../boost/geometry/algorithms/detail/is_valid \
|
||||
../../../../boost/geometry/algorithms/detail/overlay \
|
||||
../../../../boost/geometry/algorithms/detail/relate \
|
||||
../../../../boost/geometry/algorithms/detail/sections \
|
||||
|
@ -40,6 +40,8 @@
|
||||
\defgroup interior_rings interior_rings: interior_rings
|
||||
\defgroup intersection intersection: calculate new geometry
|
||||
\defgroup intersects intersects: detect if a geometry self-intersects or if two geometries intersect
|
||||
\defgroup is_simple is_simple: detect if a geometry is simple
|
||||
\defgroup is_valid is_valid: detect if a geometry is valid
|
||||
\defgroup iterators iterators: iterators
|
||||
\defgroup length length: calculate length of a linear geometry
|
||||
\defgroup make make: construct geometries
|
||||
|
@ -44,6 +44,8 @@
|
||||
[import src/examples/algorithms/intersection_segment.cpp]
|
||||
[import src/examples/algorithms/intersection_poly_poly.cpp]
|
||||
[import src/examples/algorithms/intersects_linestring.cpp]
|
||||
[import src/examples/algorithms/is_simple.cpp]
|
||||
[import src/examples/algorithms/is_valid.cpp]
|
||||
[import src/examples/algorithms/num_geometries.cpp]
|
||||
[import src/examples/algorithms/num_interior_rings.cpp]
|
||||
[import src/examples/algorithms/num_points.cpp]
|
||||
|
@ -83,10 +83,11 @@ call_doxygen()
|
||||
algorithms = ["append", "assign", "make", "clear"
|
||||
, "area", "buffer", "centroid", "convert", "correct", "covered_by"
|
||||
, "convex_hull", "crosses", "difference", "disjoint", "distance"
|
||||
, "envelope", "equals", "expand", "for_each", "intersection", "intersects"
|
||||
, "length", "num_geometries", "num_interior_rings", "num_points"
|
||||
, "overlaps", "perimeter", "reverse", "simplify", "sym_difference"
|
||||
, "touches", "transform", "union", "unique", "within"]
|
||||
, "envelope", "equals", "expand", "for_each", "is_simple", "is_valid"
|
||||
, "intersection", "intersects", "length", "num_geometries"
|
||||
, "num_interior_rings", "num_points", "overlaps", "perimeter"
|
||||
, "reverse", "simplify", "sym_difference", "touches", "transform"
|
||||
, "union", "unique", "within"]
|
||||
|
||||
access_functions = ["get", "set", "exterior_ring", "interior_rings"
|
||||
, "num_points", "num_interior_rings", "num_geometries"]
|
||||
|
@ -323,6 +323,8 @@
|
||||
<member><link linkend="geometry.reference.algorithms.disjoint">disjoint</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.equals">equals</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.intersects">intersects</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.is_simple">is_simple</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.is_valid">is_valid</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.overlaps">overlaps</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.touches">touches</link></member>
|
||||
<member><link linkend="geometry.reference.algorithms.within">within</link></member>
|
||||
|
@ -121,6 +121,9 @@
|
||||
[include generated/intersects.qbk]
|
||||
[endsect]
|
||||
|
||||
[include generated/is_simple.qbk]
|
||||
[include generated/is_valid.qbk]
|
||||
|
||||
[section:length length]
|
||||
[include generated/length.qbk]
|
||||
[endsect]
|
||||
|
28
doc/reference/algorithms/is_simple.qbk
Normal file
28
doc/reference/algorithms/is_simple.qbk
Normal file
@ -0,0 +1,28 @@
|
||||
[/============================================================================
|
||||
Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
Licensed under the Boost Software License version 1.0.
|
||||
http://www.boost.org/users/license.html
|
||||
=============================================================================/]
|
||||
|
||||
[def __this_function__ is_simple]
|
||||
|
||||
[heading_conformance_ogc __this_function__..IsSimple]
|
||||
|
||||
[heading Complexity]
|
||||
Constant-time for points, segments and boxes
|
||||
|
||||
Linear for rings, polygons and multi-polygons
|
||||
|
||||
Worst-case quadratic for linestrings and multi-linestrings
|
||||
|
||||
[heading Example]
|
||||
[is_simple]
|
||||
[is_simple_output]
|
||||
|
||||
[heading See also]
|
||||
* [link geometry.reference.algorithms.is_valid is_valid]
|
26
doc/reference/algorithms/is_valid.qbk
Normal file
26
doc/reference/algorithms/is_valid.qbk
Normal file
@ -0,0 +1,26 @@
|
||||
[/============================================================================
|
||||
Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
Licensed under the Boost Software License version 1.0.
|
||||
http://www.boost.org/users/license.html
|
||||
=============================================================================/]
|
||||
|
||||
[def __this_function__ is_valid]
|
||||
|
||||
[heading_conformance_no_ogc __this_function__]
|
||||
|
||||
[heading Complexity]
|
||||
Constant-time for points, segments and boxes
|
||||
|
||||
Currently, worst-case quadratic for all other geometries
|
||||
|
||||
[heading Example]
|
||||
[is_valid]
|
||||
[is_valid_output]
|
||||
|
||||
[heading See also]
|
||||
* [link geometry.reference.algorithms.is_simple is_simple]
|
@ -50,6 +50,10 @@ exe intersection_segment : intersection_segment.cpp ;
|
||||
|
||||
exe intersects_linestring : intersects_linestring.cpp ;
|
||||
|
||||
exe is_simple : is_simple.cpp ;
|
||||
|
||||
exe is_valid : is_valid.cpp ;
|
||||
|
||||
exe length : length.cpp ;
|
||||
exe length_with_strategy : length_with_strategy.cpp ;
|
||||
|
||||
|
49
doc/src/examples/algorithms/create_svg_one.hpp
Normal file
49
doc/src/examples/algorithms/create_svg_one.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2011-2014 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2014.
|
||||
// Modifications copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// Code to create SVG for examples
|
||||
|
||||
#ifndef CREATE_SVG_ONE_HPP
|
||||
#define CREATE_SVG_ONE_HPP
|
||||
|
||||
#include <fstream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#if defined(HAVE_SVG)
|
||||
# include <boost/geometry/io/svg/svg_mapper.hpp>
|
||||
#endif
|
||||
|
||||
template <typename Geometry>
|
||||
void create_svg(std::string const& filename, Geometry const& g)
|
||||
{
|
||||
#if defined(HAVE_SVG)
|
||||
std::cout << std::endl << "[$img/algorithms/" << boost::replace_all_copy(filename, ".svg", ".png") << "]" << std::endl << std::endl;
|
||||
|
||||
typedef typename boost::geometry::point_type<Geometry>::type point_type;
|
||||
std::ofstream svg(filename.c_str());
|
||||
|
||||
boost::geometry::svg_mapper<point_type> mapper(svg, 400, 400);
|
||||
mapper.add(g);
|
||||
|
||||
mapper.map(g, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
|
||||
#else
|
||||
boost::ignore_unused(filename, g);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: convert manually from svg to png using Inkscape ctrl-shift-E
|
||||
// and copy png to html/img/algorithms/
|
||||
|
||||
|
||||
#endif // CREATE_SVG_ONE_HPP
|
||||
|
50
doc/src/examples/algorithms/is_simple.cpp
Normal file
50
doc/src/examples/algorithms/is_simple.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
// QuickBook Example
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
//[is_simple
|
||||
//` Checks whether a geometry is simple
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/geometries/linestring.hpp>
|
||||
#include <boost/geometry/geometries/multi_linestring.hpp>
|
||||
|
||||
#include <boost/geometry/io/wkt/wkt.hpp>
|
||||
/*<-*/ #include "create_svg_one.hpp" /*->*/
|
||||
|
||||
int main()
|
||||
{
|
||||
typedef boost::geometry::model::d2::point_xy<double> point_type;
|
||||
typedef boost::geometry::model::linestring<point_type> linestring_type;
|
||||
typedef boost::geometry::model::multi_linestring<linestring_type> multi_linestring_type;
|
||||
|
||||
multi_linestring_type multi_linestring;
|
||||
boost::geometry::read_wkt("MULTILINESTRING((0 0,0 10,10 10,10 0,0 0),(10 10,20 20))", multi_linestring);
|
||||
|
||||
std::cout << "is simple? "
|
||||
<< (boost::geometry::is_simple(multi_linestring) ? "yes" : "no")
|
||||
<< std::endl;
|
||||
/*<-*/ create_svg("is_simple_example.svg", multi_linestring); /*->*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[is_simple_output
|
||||
/*`
|
||||
Output:
|
||||
[pre
|
||||
is simple? no
|
||||
]
|
||||
|
||||
*/
|
||||
//]
|
48
doc/src/examples/algorithms/is_valid.cpp
Normal file
48
doc/src/examples/algorithms/is_valid.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
// QuickBook Example
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
//[is_valid
|
||||
//` Checks whether a geometry is valid
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/geometries/polygon.hpp>
|
||||
|
||||
#include <boost/geometry/io/wkt/wkt.hpp>
|
||||
/*<-*/ #include "create_svg_one.hpp" /*->*/
|
||||
|
||||
int main()
|
||||
{
|
||||
typedef boost::geometry::model::d2::point_xy<double> point_type;
|
||||
typedef boost::geometry::model::polygon<point_type> polygon_type;
|
||||
|
||||
polygon_type poly;
|
||||
boost::geometry::read_wkt(
|
||||
"POLYGON((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 2,0 0),(0 0,1 9,2 9,0 0),(2 9,9 2,9 9,2 9))"
|
||||
, poly);
|
||||
|
||||
std::cout << "is valid? " << (boost::geometry::is_valid(poly) ? "yes" : "no") << std::endl;
|
||||
/*<-*/ create_svg("is_valid_example.svg", poly); /*->*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[is_valid_output
|
||||
/*`
|
||||
Output:
|
||||
[pre
|
||||
is valid? no
|
||||
]
|
||||
|
||||
*/
|
||||
//]
|
@ -58,10 +58,19 @@ struct is_simple<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\brief \brief_check{is simple}
|
||||
\ingroup is_simple
|
||||
\tparam Geometry \tparam_geometry
|
||||
\param geometry \param_geometry
|
||||
\return \return_check{is simple}
|
||||
|
||||
\qbk{[include reference/algorithms/is_simple.qbk]}
|
||||
*/
|
||||
template <typename Geometry>
|
||||
inline bool is_simple(Geometry const& g)
|
||||
inline bool is_simple(Geometry const& geometry)
|
||||
{
|
||||
return resolve_variant::is_simple<Geometry>::apply(g);
|
||||
return resolve_variant::is_simple<Geometry>::apply(geometry);
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
#include <boost/geometry/policies/predicate_based_interrupt_policy.hpp>
|
||||
#include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
|
||||
#include <boost/geometry/policies/robustness/segment_ratio.hpp>
|
||||
|
||||
|
@ -15,6 +15,10 @@
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/addressof.hpp>
|
||||
|
||||
#include <boost/geometry/policies/compare.hpp>
|
||||
|
||||
@ -27,122 +31,106 @@ namespace detail { namespace is_valid
|
||||
|
||||
|
||||
template <typename TurnPoint>
|
||||
class graph_vertex
|
||||
class complement_graph_vertex
|
||||
{
|
||||
public:
|
||||
typedef typename std::set
|
||||
<
|
||||
graph_vertex<TurnPoint>
|
||||
>::const_iterator vertex_handle;
|
||||
complement_graph_vertex(std::size_t id)
|
||||
: m_id(id)
|
||||
, m_turn_point(NULL)
|
||||
{}
|
||||
|
||||
complement_graph_vertex(TurnPoint const* turn_point,
|
||||
std::size_t expected_id)
|
||||
: m_id(expected_id)
|
||||
, m_turn_point(turn_point)
|
||||
{}
|
||||
|
||||
inline std::size_t id() const { return m_id; }
|
||||
|
||||
inline bool operator<(complement_graph_vertex const& other) const
|
||||
{
|
||||
if ( m_turn_point != NULL && other.m_turn_point != NULL )
|
||||
{
|
||||
return geometry::less
|
||||
<
|
||||
TurnPoint
|
||||
>()(*m_turn_point, *other.m_turn_point);
|
||||
}
|
||||
if ( m_turn_point == NULL && other.m_turn_point == NULL )
|
||||
{
|
||||
return m_id < other.m_id;
|
||||
}
|
||||
return m_turn_point == NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
// the value of m_turn_point determines the type of the vertex
|
||||
// non-NULL: vertex corresponds to an IP
|
||||
// NULL : vertex corresponds to a hole or outer space, and the id
|
||||
// is the ring id of the corresponding ring of the polygon
|
||||
std::size_t m_id;
|
||||
TurnPoint const* m_turn_point;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename TurnPoint>
|
||||
class complement_graph
|
||||
{
|
||||
private:
|
||||
typedef complement_graph_vertex<TurnPoint> vertex;
|
||||
typedef std::set<vertex> vertex_container;
|
||||
|
||||
public:
|
||||
typedef typename vertex_container::const_iterator vertex_handle;
|
||||
|
||||
private:
|
||||
struct vertex_handle_less
|
||||
{
|
||||
bool operator()(vertex_handle v1, vertex_handle v2) const
|
||||
inline bool operator()(vertex_handle v1, vertex_handle v2) const
|
||||
{
|
||||
return v1->id() < v2->id();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<vertex_handle, vertex_handle_less> neighbor_container;
|
||||
typedef typename neighbor_container::const_iterator neighbor_iterator;
|
||||
|
||||
graph_vertex(int id, TurnPoint const& dummy = TurnPoint())
|
||||
: m_is_ip(false)
|
||||
, m_id(id)
|
||||
, m_turn_point(dummy)
|
||||
, m_neighbors()
|
||||
, m_parent()
|
||||
, m_visited(false)
|
||||
{}
|
||||
|
||||
graph_vertex(TurnPoint const& turn_point)
|
||||
: m_is_ip(true)
|
||||
, m_id(-1)
|
||||
, m_turn_point(turn_point)
|
||||
, m_neighbors()
|
||||
, m_parent()
|
||||
, m_visited(false)
|
||||
{}
|
||||
|
||||
int id() const { return m_id; }
|
||||
void id(int id) const { m_id = id; }
|
||||
int parent_id() const
|
||||
class has_cycles_dfs_data
|
||||
{
|
||||
if ( m_parent.second )
|
||||
public:
|
||||
has_cycles_dfs_data(std::size_t num_nodes)
|
||||
: m_visited(num_nodes)
|
||||
, m_parent_id(num_nodes)
|
||||
{}
|
||||
|
||||
inline int parent_id(vertex_handle v) const
|
||||
{
|
||||
return m_parent.first->id();
|
||||
return m_parent_id[v->id()];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// for DFS -- start
|
||||
bool visited() const { return m_visited; }
|
||||
void visited(bool value) const { m_visited = value; }
|
||||
|
||||
vertex_handle parent() const { return m_parent.first; }
|
||||
void parent(vertex_handle p) const { m_parent = std::make_pair(p, true); }
|
||||
void initialize_parent() const
|
||||
{
|
||||
m_parent = std::make_pair(vertex_handle(), false);
|
||||
}
|
||||
// for DFS -- end
|
||||
|
||||
void add_neighbor(vertex_handle v) const
|
||||
{
|
||||
m_neighbors.insert(v);
|
||||
}
|
||||
|
||||
neighbor_iterator neighbors_begin() const
|
||||
{
|
||||
return m_neighbors.begin();
|
||||
}
|
||||
|
||||
neighbor_iterator neighbors_end() const
|
||||
{
|
||||
return m_neighbors.end();
|
||||
}
|
||||
|
||||
bool operator<(graph_vertex const& other) const
|
||||
{
|
||||
if ( m_is_ip && other.m_is_ip )
|
||||
inline void set_parent_id(vertex_handle v, int id)
|
||||
{
|
||||
return geometry::less
|
||||
<
|
||||
TurnPoint
|
||||
>()(m_turn_point, other.m_turn_point);
|
||||
m_parent_id[v->id()] = id;
|
||||
}
|
||||
if ( !m_is_ip && !other.m_is_ip )
|
||||
|
||||
inline bool visited(vertex_handle v) const
|
||||
{
|
||||
return m_id < other.m_id;
|
||||
return m_visited[v->id()];
|
||||
}
|
||||
return other.m_is_ip;
|
||||
}
|
||||
|
||||
private:
|
||||
// the following bool determines the type of the vertex
|
||||
// true : vertex corresponds to an IP
|
||||
// false: vertex corresponds to a hole or outer space, and the id
|
||||
// is the ring id of the corresponding ring of the polygon
|
||||
bool m_is_ip;
|
||||
mutable int m_id;
|
||||
TurnPoint const& m_turn_point;
|
||||
mutable neighbor_container m_neighbors;
|
||||
mutable std::pair<vertex_handle, bool> m_parent; // for DFS
|
||||
mutable bool m_visited; // for DFS
|
||||
};
|
||||
inline void set_visited(vertex_handle v, bool value)
|
||||
{
|
||||
m_visited[v->id()] = value;
|
||||
}
|
||||
private:
|
||||
std::vector<bool> m_visited;
|
||||
std::vector<int> m_parent_id;
|
||||
};
|
||||
|
||||
|
||||
template <typename Vertex>
|
||||
class complement_graph
|
||||
{
|
||||
private:
|
||||
typedef std::set<Vertex> vertex_container;
|
||||
|
||||
public:
|
||||
typedef typename vertex_container::const_iterator vertex_handle;
|
||||
|
||||
private:
|
||||
bool has_cycles(vertex_handle start_vertex) const
|
||||
inline bool has_cycles(vertex_handle start_vertex,
|
||||
has_cycles_dfs_data& data) const
|
||||
{
|
||||
std::stack<vertex_handle> stack;
|
||||
stack.push(start_vertex);
|
||||
@ -152,20 +140,21 @@ private:
|
||||
vertex_handle v = stack.top();
|
||||
stack.pop();
|
||||
|
||||
v->visited(true);
|
||||
for (typename Vertex::neighbor_iterator nit = v->neighbors_begin();
|
||||
nit != v->neighbors_end(); ++nit)
|
||||
data.set_visited(v, true);
|
||||
for (typename neighbor_container::const_iterator nit
|
||||
= m_neighbors[v->id()].begin();
|
||||
nit != m_neighbors[v->id()].end(); ++nit)
|
||||
{
|
||||
if ( (*nit)->id() != v->parent_id() )
|
||||
if ( static_cast<int>((*nit)->id()) != data.parent_id(v) )
|
||||
{
|
||||
if ( !(*nit)->visited() )
|
||||
if ( data.visited(*nit) )
|
||||
{
|
||||
(*nit)->parent(v);
|
||||
stack.push(*nit);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
data.set_parent_id(*nit, static_cast<int>(v->id()));
|
||||
stack.push(*nit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,51 +168,58 @@ public:
|
||||
: m_num_rings(num_rings)
|
||||
, m_num_turns(0)
|
||||
, m_vertices()
|
||||
, m_neighbors(num_rings)
|
||||
{}
|
||||
|
||||
// inserts a ring vertex in the graph and returns its id
|
||||
vertex_handle add_vertex(int ring_id)
|
||||
// inserts a ring vertex in the graph and returns its handle
|
||||
// ring id's are zero-based (so the first interior ring has id 1)
|
||||
inline vertex_handle add_vertex(int id)
|
||||
{
|
||||
Vertex v(static_cast<std::size_t>(ring_id + 1));
|
||||
return m_vertices.insert(v).first;
|
||||
return m_vertices.insert(vertex(static_cast<std::size_t>(id))).first;
|
||||
}
|
||||
|
||||
// inserts an IP in the graph and returns its id
|
||||
template <typename TurnPoint>
|
||||
vertex_handle add_vertex(TurnPoint const& turn_point)
|
||||
inline vertex_handle add_vertex(TurnPoint const& turn_point)
|
||||
{
|
||||
Vertex v(turn_point);
|
||||
std::pair<vertex_handle, bool> res = m_vertices.insert(v);
|
||||
std::pair<vertex_handle, bool> res
|
||||
= m_vertices.insert(vertex(boost::addressof(turn_point),
|
||||
m_num_rings + m_num_turns)
|
||||
);
|
||||
|
||||
if ( res.second )
|
||||
{
|
||||
// a new element is inserted
|
||||
res.first->id( m_num_rings + m_num_turns );
|
||||
m_neighbors.push_back(neighbor_container());
|
||||
++m_num_turns;
|
||||
}
|
||||
return res.first;
|
||||
}
|
||||
|
||||
void add_edge(vertex_handle v1, vertex_handle v2)
|
||||
inline void add_edge(vertex_handle v1, vertex_handle v2)
|
||||
{
|
||||
BOOST_ASSERT( v1 != m_vertices.end() );
|
||||
BOOST_ASSERT( v2 != m_vertices.end() );
|
||||
v1->add_neighbor(v2);
|
||||
v2->add_neighbor(v1);
|
||||
m_neighbors[v1->id()].insert(v2);
|
||||
m_neighbors[v2->id()].insert(v1);
|
||||
}
|
||||
|
||||
bool has_cycles() const
|
||||
inline bool has_cycles() const
|
||||
{
|
||||
has_cycles_dfs_data data(m_vertices.size());
|
||||
|
||||
// initialize all vertices as non-visited and with no parent set
|
||||
for (vertex_handle it = m_vertices.begin();
|
||||
it != m_vertices.end(); ++it)
|
||||
{
|
||||
it->visited(false);
|
||||
it->initialize_parent();
|
||||
data.set_visited(it, false);
|
||||
data.set_parent_id(it, -1);
|
||||
}
|
||||
|
||||
// for each non-visited vertex, start a DFS from that vertex
|
||||
for (vertex_handle it = m_vertices.begin();
|
||||
it != m_vertices.end(); ++it)
|
||||
{
|
||||
if ( !it->visited() && has_cycles(it) )
|
||||
if ( !data.visited(it) && has_cycles(it, data) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -231,13 +227,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename OStream, typename V>
|
||||
template <typename OStream, typename TP>
|
||||
friend inline
|
||||
void debug_print_complement_graph(OStream&, complement_graph<V> const&);
|
||||
void debug_print_complement_graph(OStream&, complement_graph<TP> const&);
|
||||
|
||||
private:
|
||||
std::size_t m_num_rings, m_num_turns;
|
||||
vertex_container m_vertices;
|
||||
std::vector<neighbor_container> m_neighbors;
|
||||
};
|
||||
|
||||
|
||||
|
@ -22,11 +22,12 @@ namespace detail { namespace is_valid
|
||||
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
template <typename OutputStream, typename Vertex>
|
||||
inline void debug_print_complement_graph(OutputStream& os,
|
||||
complement_graph<Vertex> const& graph)
|
||||
template <typename OutputStream, typename TurnPoint>
|
||||
inline void
|
||||
debug_print_complement_graph(OutputStream& os,
|
||||
complement_graph<TurnPoint> const& graph)
|
||||
{
|
||||
typedef typename complement_graph<Vertex>::vertex_handle vertex_handle;
|
||||
typedef typename complement_graph<TurnPoint>::vertex_handle vertex_handle;
|
||||
|
||||
os << "num rings: " << graph.m_num_rings << std::endl;
|
||||
os << "vertex ids: {";
|
||||
@ -41,8 +42,12 @@ inline void debug_print_complement_graph(OutputStream& os,
|
||||
it != graph.m_vertices.end(); ++it)
|
||||
{
|
||||
os << "neighbors of " << it->id() << ": {";
|
||||
for (typename Vertex::neighbor_iterator nit = it->neighbors_begin();
|
||||
nit != it->neighbors_end(); ++nit)
|
||||
for (typename complement_graph
|
||||
<
|
||||
TurnPoint
|
||||
>::neighbor_container::const_iterator
|
||||
nit = graph.m_neighbors[it->id()].begin();
|
||||
nit != graph.m_neighbors[it->id()].end(); ++nit)
|
||||
{
|
||||
os << " " << (*nit)->id();
|
||||
}
|
||||
@ -50,9 +55,9 @@ inline void debug_print_complement_graph(OutputStream& os,
|
||||
}
|
||||
}
|
||||
#else
|
||||
template <typename OutputStream, typename Vertex>
|
||||
template <typename OutputStream, typename TurnPoint>
|
||||
void debug_print_complement_graph(OutputStream&,
|
||||
complement_graph<Vertex> const&)
|
||||
complement_graph<TurnPoint> const&)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -0,0 +1,93 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP
|
||||
|
||||
#include <boost/geometry/core/point_type.hpp>
|
||||
|
||||
#include <boost/geometry/policies/predicate_based_interrupt_policy.hpp>
|
||||
#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
|
||||
#include <boost/geometry/policies/robustness/get_rescale_policy.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/is_acceptable_turn.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace is_valid
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename IsAcceptableTurn = is_acceptable_turn<Geometry>
|
||||
>
|
||||
class has_valid_self_turns
|
||||
{
|
||||
private:
|
||||
typedef typename point_type<Geometry>::type point_type;
|
||||
|
||||
typedef typename geometry::rescale_policy_type
|
||||
<
|
||||
point_type
|
||||
>::type rescale_policy_type;
|
||||
|
||||
typedef detail::overlay::get_turn_info
|
||||
<
|
||||
detail::overlay::assign_null_policy
|
||||
> turn_policy;
|
||||
|
||||
public:
|
||||
typedef detail::overlay::turn_info
|
||||
<
|
||||
point_type,
|
||||
typename geometry::segment_ratio_type
|
||||
<
|
||||
point_type,
|
||||
rescale_policy_type
|
||||
>::type
|
||||
> turn_type;
|
||||
|
||||
// returns true if all turns are valid
|
||||
template <typename Turns>
|
||||
static inline bool apply(Geometry const& geometry, Turns& turns)
|
||||
{
|
||||
rescale_policy_type robust_policy
|
||||
= geometry::get_rescale_policy<rescale_policy_type>(geometry);
|
||||
|
||||
detail::overlay::stateless_predicate_based_interrupt_policy
|
||||
<
|
||||
IsAcceptableTurn
|
||||
> interrupt_policy;
|
||||
|
||||
geometry::self_turns<turn_policy>(geometry,
|
||||
robust_policy,
|
||||
turns,
|
||||
interrupt_policy);
|
||||
|
||||
return !interrupt_policy.has_intersections;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::is_valid
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP
|
@ -13,6 +13,7 @@
|
||||
#include <boost/geometry/algorithms/detail/is_valid/pointlike.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/linear.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/polygon.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/multipolygon.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/ring.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/segment.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/box.hpp>
|
||||
|
@ -57,11 +57,19 @@ struct is_valid<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
|
||||
} // namespace resolve_variant
|
||||
|
||||
|
||||
/*!
|
||||
\brief \brief_check{is valid (in the OGC sense)}
|
||||
\ingroup is_valid
|
||||
\tparam Geometry \tparam_geometry
|
||||
\param geometry \param_geometry
|
||||
\return \return_check{is valid (in the OGC sense)}
|
||||
|
||||
\qbk{[include reference/algorithms/is_valid.qbk]}
|
||||
*/
|
||||
template <typename Geometry>
|
||||
inline bool is_valid(Geometry const& g)
|
||||
inline bool is_valid(Geometry const& geometry)
|
||||
{
|
||||
return resolve_variant::is_valid<Geometry>::apply(g);
|
||||
return resolve_variant::is_valid<Geometry>::apply(geometry);
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,144 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IS_ACCEPTABLE_TURN_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IS_ACCEPTABLE_TURN_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/point_order.hpp>
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace is_valid
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
order_selector Order = geometry::point_order<Geometry>::value,
|
||||
typename Tag = typename tag<Geometry>::type
|
||||
>
|
||||
struct acceptable_operation
|
||||
{};
|
||||
|
||||
template <typename Polygon>
|
||||
struct acceptable_operation<Polygon, counterclockwise, polygon_tag>
|
||||
{
|
||||
static const detail::overlay::operation_type value =
|
||||
detail::overlay::operation_union;
|
||||
};
|
||||
|
||||
template <typename Polygon>
|
||||
struct acceptable_operation<Polygon, clockwise, polygon_tag>
|
||||
{
|
||||
static const detail::overlay::operation_type value =
|
||||
detail::overlay::operation_intersection;
|
||||
};
|
||||
|
||||
template <typename MultiPolygon>
|
||||
struct acceptable_operation<MultiPolygon, counterclockwise, multi_polygon_tag>
|
||||
{
|
||||
static const detail::overlay::operation_type value =
|
||||
detail::overlay::operation_intersection;
|
||||
};
|
||||
|
||||
template <typename MultiPolygon>
|
||||
struct acceptable_operation<MultiPolygon, clockwise, multi_polygon_tag>
|
||||
{
|
||||
static const detail::overlay::operation_type value =
|
||||
detail::overlay::operation_union;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename Geometry, typename Tag = typename tag<Geometry>::type>
|
||||
struct is_acceptable_turn
|
||||
{};
|
||||
|
||||
template <typename Polygon>
|
||||
class is_acceptable_turn<Polygon, polygon_tag>
|
||||
{
|
||||
protected:
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
template <typename Turn>
|
||||
static inline bool apply(Turn const& turn)
|
||||
{
|
||||
using namespace detail::overlay;
|
||||
|
||||
if ( turn.operations[0].seg_id.ring_index
|
||||
== turn.operations[0].other_id.ring_index )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
operation_type const op = acceptable_operation<Polygon>::value;
|
||||
|
||||
return check_turn(turn, method_touch_interior, op)
|
||||
|| check_turn(turn, method_touch, op)
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MultiPolygon>
|
||||
class is_acceptable_turn<MultiPolygon, multi_polygon_tag>
|
||||
: is_acceptable_turn<typename boost::range_value<MultiPolygon>::type>
|
||||
{
|
||||
private:
|
||||
typedef typename boost::range_value<MultiPolygon>::type polygon;
|
||||
typedef is_acceptable_turn<polygon> base;
|
||||
|
||||
public:
|
||||
template <typename Turn>
|
||||
static inline bool apply(Turn const& turn)
|
||||
{
|
||||
using namespace detail::overlay;
|
||||
|
||||
if ( turn.operations[0].seg_id.multi_index
|
||||
== turn.operations[0].other_id.multi_index )
|
||||
{
|
||||
return base::apply(turn);
|
||||
}
|
||||
|
||||
operation_type const op = acceptable_operation<MultiPolygon>::value;
|
||||
|
||||
return base::check_turn(turn, method_touch_interior, op)
|
||||
|| base::check_turn(turn, method_touch, op)
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::is_valid
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IS_ACCEPTABLE_TURN_HPP
|
@ -0,0 +1,292 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <boost/iterator/filter_iterator.hpp>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/exterior_ring.hpp>
|
||||
#include <boost/geometry/core/interior_rings.hpp>
|
||||
#include <boost/geometry/core/ring_type.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/within.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/polygon.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/dispatch/is_valid.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace is_valid
|
||||
{
|
||||
|
||||
|
||||
template <typename MultiPolygon, bool AllowDuplicates>
|
||||
class is_valid_multipolygon
|
||||
: is_valid_polygon
|
||||
<
|
||||
typename boost::range_value<MultiPolygon>::type,
|
||||
AllowDuplicates,
|
||||
true // check only the validity of rings
|
||||
>
|
||||
{
|
||||
private:
|
||||
typedef is_valid_polygon
|
||||
<
|
||||
typename boost::range_value<MultiPolygon>::type,
|
||||
AllowDuplicates,
|
||||
true
|
||||
> base;
|
||||
|
||||
|
||||
|
||||
template <typename PolygonIterator, typename TurnIterator>
|
||||
static inline
|
||||
bool are_polygon_interiors_disjoint(PolygonIterator polygons_first,
|
||||
PolygonIterator polygons_beyond,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
{
|
||||
std::set<int> multi_indices;
|
||||
for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
|
||||
{
|
||||
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
|
||||
&&
|
||||
geometry::within(range::front(exterior_ring(*it1)), *it2)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class has_multi_index
|
||||
{
|
||||
public:
|
||||
has_multi_index(int multi_index)
|
||||
: m_multi_index(multi_index)
|
||||
{}
|
||||
|
||||
template <typename Turn>
|
||||
inline bool operator()(Turn const& turn) const
|
||||
{
|
||||
return turn.operations[0].seg_id.multi_index == m_multi_index
|
||||
&& turn.operations[0].other_id.multi_index == m_multi_index;
|
||||
}
|
||||
|
||||
private:
|
||||
int const m_multi_index;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename Predicate>
|
||||
struct has_property_per_polygon
|
||||
{
|
||||
template <typename PolygonIterator, typename TurnIterator>
|
||||
static inline bool apply(PolygonIterator polygons_first,
|
||||
PolygonIterator polygons_beyond,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
{
|
||||
int multi_index = 0;
|
||||
for (PolygonIterator it = polygons_first; it != polygons_beyond;
|
||||
++it, ++multi_index)
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
typedef debug_validity_phase<MultiPolygon> debug_phase;
|
||||
|
||||
// check validity of all polygons ring
|
||||
debug_phase::apply(1);
|
||||
|
||||
if ( !detail::check_iterator_range
|
||||
<
|
||||
base,
|
||||
false // do not allow empty multi-polygons
|
||||
>::apply(boost::begin(multipolygon),
|
||||
boost::end(multipolygon)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// compute turns and check if all are acceptable
|
||||
debug_phase::apply(2);
|
||||
|
||||
typedef has_valid_self_turns<MultiPolygon> has_valid_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());
|
||||
|
||||
if ( has_invalid_turns )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check if each polygon's interior rings are inside the
|
||||
// exterior and not one inside the other
|
||||
debug_phase::apply(3);
|
||||
|
||||
if ( !have_holes_inside(boost::begin(multipolygon),
|
||||
boost::end(multipolygon),
|
||||
turns.begin(),
|
||||
turns.end()) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check that each polygon's interior is connected
|
||||
debug_phase::apply(4);
|
||||
|
||||
if ( !have_connected_interior(boost::begin(multipolygon),
|
||||
boost::end(multipolygon),
|
||||
turns.begin(),
|
||||
turns.end()) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check if polygon interiors are disjoint
|
||||
debug_phase::apply(5);
|
||||
return are_polygon_interiors_disjoint(boost::begin(multipolygon),
|
||||
boost::end(multipolygon),
|
||||
turns.begin(),
|
||||
turns.end());
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::is_valid
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DISPATCH
|
||||
namespace dispatch
|
||||
{
|
||||
|
||||
|
||||
// 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, bool AllowSpikes, bool AllowDuplicates>
|
||||
struct is_valid<MultiPolygon, multi_polygon_tag, AllowSpikes, AllowDuplicates>
|
||||
: detail::is_valid::is_valid_multipolygon<MultiPolygon, AllowDuplicates>
|
||||
{};
|
||||
|
||||
|
||||
} // namespace dispatch
|
||||
#endif // DOXYGEN_NO_DISPATCH
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
|
@ -10,36 +10,35 @@
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range.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/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
#include <boost/geometry/policies/predicate_based_interrupt_policy.hpp>
|
||||
#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
|
||||
#include <boost/geometry/policies/robustness/get_rescale_policy.hpp>
|
||||
|
||||
// MK:: the following include may not be needed
|
||||
#include <boost/geometry/algorithms/covered_by.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/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/detail/is_valid/has_valid_self_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/ring.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp>
|
||||
@ -56,110 +55,21 @@ 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, bool AllowDuplicates>
|
||||
template
|
||||
<
|
||||
typename Polygon,
|
||||
bool AllowDuplicates,
|
||||
bool CheckRingValidityOnly = false
|
||||
>
|
||||
class is_valid_polygon
|
||||
{
|
||||
private:
|
||||
template <typename RingIterator, typename ExteriorRing, typename IndexSet>
|
||||
static inline bool are_holes_inside(RingIterator first,
|
||||
RingIterator beyond,
|
||||
ExteriorRing const& 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;
|
||||
}
|
||||
}
|
||||
protected:
|
||||
typedef debug_validity_phase<Polygon> debug_phase;
|
||||
|
||||
// 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 InteriorRings, typename ExteriorRing, typename IndexSet>
|
||||
static inline bool are_holes_inside(InteriorRings const& interior_rings,
|
||||
ExteriorRing const& exterior_ring,
|
||||
IndexSet const& rings_with_turns)
|
||||
{
|
||||
return are_holes_inside(boost::begin(interior_rings),
|
||||
boost::end(interior_rings),
|
||||
exterior_ring,
|
||||
rings_with_turns);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct is_acceptable_turn
|
||||
{
|
||||
template <typename Turn>
|
||||
static inline bool apply(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)
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename InteriorRings>
|
||||
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,132 +85,217 @@ private:
|
||||
boost::end(interior_rings));
|
||||
}
|
||||
|
||||
struct has_valid_rings
|
||||
{
|
||||
static inline bool apply(Polygon const& polygon)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int ring_index = 0;
|
||||
for (RingIterator it = rings_first; it != rings_beyond;
|
||||
++it, ++ring_index)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
typedef typename point_type<Polygon>::type point_type;
|
||||
typedef typename ring_type<Polygon>::type ring_type;
|
||||
|
||||
typedef debug_validity_phase<Polygon> debug_phase;
|
||||
|
||||
// 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)) )
|
||||
if ( !has_valid_rings::apply(polygon) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check validity of interior rings
|
||||
debug_phase::apply(2);
|
||||
|
||||
if ( !are_valid_interior_rings(geometry::interior_rings(polygon)) )
|
||||
if ( CheckRingValidityOnly )
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// compute turns and check if all are acceptable
|
||||
debug_phase::apply(3);
|
||||
|
||||
typedef typename geometry::rescale_policy_type
|
||||
<
|
||||
point_type
|
||||
>::type rescale_policy_type;
|
||||
typedef has_valid_self_turns<Polygon> has_valid_turns;
|
||||
|
||||
typedef detail::overlay::turn_info
|
||||
<
|
||||
point_type,
|
||||
typename geometry::segment_ratio_type
|
||||
<
|
||||
point_type,
|
||||
rescale_policy_type
|
||||
>::type
|
||||
> turn_info;
|
||||
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());
|
||||
|
||||
typedef detail::overlay::get_turn_info
|
||||
<
|
||||
detail::overlay::assign_null_policy
|
||||
> turn_policy;
|
||||
|
||||
rescale_policy_type robust_policy
|
||||
= geometry::get_rescale_policy<rescale_policy_type>(polygon);
|
||||
|
||||
detail::overlay::stateless_predicate_based_interrupt_policy
|
||||
<
|
||||
is_acceptable_turn
|
||||
> interrupt_policy;
|
||||
|
||||
std::deque<turn_info> turns;
|
||||
geometry::self_turns<turn_policy>(polygon,
|
||||
robust_policy,
|
||||
turns,
|
||||
interrupt_policy);
|
||||
|
||||
if ( interrupt_policy.has_intersections )
|
||||
if ( has_invalid_turns )
|
||||
{
|
||||
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<int> rings_with_turns;
|
||||
for (typename std::deque<turn_info>::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) )
|
||||
if ( !has_holes_inside::apply(polygon, turns.begin(), turns.end()) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check whether the interior of the polygon is a connected set
|
||||
debug_phase::apply(5);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
debug_print_complement_graph(std::cout, g);
|
||||
|
||||
return !g.has_cycles();
|
||||
return has_connected_interior::apply(polygon,
|
||||
turns.begin(),
|
||||
turns.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace dispatch
|
||||
}} // namespace detail::is_valid
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
@ -319,37 +314,6 @@ struct is_valid<Polygon, polygon_tag, AllowSpikes, AllowDuplicates>
|
||||
{};
|
||||
|
||||
|
||||
// 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, bool AllowSpikes, bool AllowDuplicates>
|
||||
struct is_valid<MultiPolygon, multi_polygon_tag, AllowSpikes, AllowDuplicates>
|
||||
{
|
||||
static inline bool apply(MultiPolygon const& multipolygon)
|
||||
{
|
||||
if ( !detail::check_iterator_range
|
||||
<
|
||||
detail::is_valid::is_valid_polygon
|
||||
<
|
||||
typename boost::range_value<MultiPolygon>::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
|
||||
|
||||
|
@ -55,6 +55,8 @@
|
||||
#include <boost/geometry/algorithms/for_each.hpp>
|
||||
#include <boost/geometry/algorithms/intersection.hpp>
|
||||
#include <boost/geometry/algorithms/intersects.hpp>
|
||||
#include <boost/geometry/algorithms/is_simple.hpp>
|
||||
#include <boost/geometry/algorithms/is_valid.hpp>
|
||||
#include <boost/geometry/algorithms/length.hpp>
|
||||
#include <boost/geometry/algorithms/make.hpp>
|
||||
#include <boost/geometry/algorithms/num_geometries.hpp>
|
||||
|
@ -13,314 +13,11 @@
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/variant/variant.hpp>
|
||||
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <boost/geometry/core/closure.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/ring_type.hpp>
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/geometries/segment.hpp>
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
#include <boost/geometry/geometries/linestring.hpp>
|
||||
#include <boost/geometry/geometries/ring.hpp>
|
||||
#include <boost/geometry/geometries/polygon.hpp>
|
||||
#include <boost/geometry/geometries/multi_point.hpp>
|
||||
#include <boost/geometry/geometries/multi_linestring.hpp>
|
||||
#include <boost/geometry/geometries/multi_polygon.hpp>
|
||||
|
||||
#include <boost/geometry/strategies/strategies.hpp>
|
||||
|
||||
#include <boost/geometry/io/wkt/wkt.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/num_points.hpp>
|
||||
#include <boost/geometry/algorithms/is_valid.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
|
||||
#include "from_wkt.hpp"
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
#include "pretty_print_geometry.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
namespace bg = ::boost::geometry;
|
||||
|
||||
typedef bg::model::point<double, 2, bg::cs::cartesian> point_type;
|
||||
typedef bg::model::segment<point_type> segment_type;
|
||||
typedef bg::model::box<point_type> box_type;
|
||||
typedef bg::model::linestring<point_type> linestring_type;
|
||||
typedef bg::model::multi_linestring<linestring_type> multi_linestring_type;
|
||||
|
||||
// multi-geometries
|
||||
typedef bg::model::multi_point<point_type> multi_point_type;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// returns true if a geometry can be converted to closed
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type,
|
||||
bg::closure_selector Closure = bg::closure<Geometry>::value
|
||||
>
|
||||
struct is_convertible_to_closed
|
||||
{
|
||||
static inline bool apply(Geometry const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ring>
|
||||
struct is_convertible_to_closed<Ring, bg::ring_tag, bg::open>
|
||||
{
|
||||
static inline bool apply(Ring const& ring)
|
||||
{
|
||||
return boost::size(ring) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Polygon>
|
||||
struct is_convertible_to_closed<Polygon, bg::polygon_tag, bg::open>
|
||||
{
|
||||
typedef typename bg::ring_type<Polygon>::type ring_type;
|
||||
|
||||
template <typename InteriorRings>
|
||||
static inline
|
||||
bool apply_to_interior_rings(InteriorRings const& interior_rings)
|
||||
{
|
||||
return bg::detail::check_iterator_range
|
||||
<
|
||||
is_convertible_to_closed<ring_type>,
|
||||
true // allow empty iterator range
|
||||
>::apply(boost::begin(interior_rings),
|
||||
boost::end(interior_rings));
|
||||
}
|
||||
|
||||
static inline bool apply(Polygon const& polygon)
|
||||
{
|
||||
return boost::size(bg::exterior_ring(polygon)) > 0
|
||||
&& apply_to_interior_rings(bg::interior_rings(polygon));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MultiPolygon>
|
||||
struct is_convertible_to_closed<MultiPolygon, bg::multi_polygon_tag, bg::open>
|
||||
{
|
||||
typedef typename boost::range_value<MultiPolygon>::type polygon;
|
||||
|
||||
static inline bool apply(MultiPolygon const& multi_polygon)
|
||||
{
|
||||
return bg::detail::check_iterator_range
|
||||
<
|
||||
is_convertible_to_closed<polygon>,
|
||||
false // do not allow empty multi-polygon
|
||||
>::apply(boost::begin(multi_polygon),
|
||||
boost::end(multi_polygon));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// returns true if a geometry can be converted to cw
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type,
|
||||
bg::order_selector Order = bg::point_order<Geometry>::value
|
||||
>
|
||||
struct is_convertible_to_cw
|
||||
{
|
||||
static inline bool apply(Geometry const&)
|
||||
{
|
||||
return bg::point_order<Geometry>::value == bg::counterclockwise;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
struct default_validity_tester
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry,
|
||||
bool expected_result)
|
||||
{
|
||||
bool valid = bg::is_valid(geometry);
|
||||
BOOST_CHECK_MESSAGE( valid == expected_result,
|
||||
"Expected: " << expected_result
|
||||
<< " detected: " << valid
|
||||
<< " wkt: " << bg::wkt(geometry) );
|
||||
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
|
||||
template <bool AllowSpikes>
|
||||
struct validity_tester_linear
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry,
|
||||
bool expected_result)
|
||||
{
|
||||
bool valid = bg::dispatch::is_valid
|
||||
<
|
||||
Geometry,
|
||||
typename bg::tag<Geometry>::type,
|
||||
AllowSpikes
|
||||
>::apply(geometry);
|
||||
|
||||
BOOST_CHECK_MESSAGE( valid == expected_result,
|
||||
"Expected: " << expected_result
|
||||
<< " detected: " << valid
|
||||
<< " wkt: " << bg::wkt(geometry) );
|
||||
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
|
||||
template <bool AllowDuplicates>
|
||||
struct validity_tester_areal
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry,
|
||||
bool expected_result)
|
||||
{
|
||||
bool const irrelevant = true;
|
||||
|
||||
bool valid = bg::dispatch::is_valid
|
||||
<
|
||||
Geometry,
|
||||
typename bg::tag<Geometry>::type,
|
||||
irrelevant,
|
||||
AllowDuplicates
|
||||
>::apply(geometry);
|
||||
|
||||
BOOST_CHECK_MESSAGE( valid == expected_result,
|
||||
"Expected: " << expected_result
|
||||
<< " detected: " << valid
|
||||
<< " wkt: " << bg::wkt(geometry) );
|
||||
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename ValidityTester,
|
||||
typename Geometry,
|
||||
typename ClosedGeometry = Geometry,
|
||||
typename CWGeometry = Geometry,
|
||||
typename CWClosedGeometry = Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type
|
||||
>
|
||||
struct test_valid
|
||||
{
|
||||
template <typename G>
|
||||
static inline void base_test(G const& g, bool expected_result)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "=======" << std::endl;
|
||||
#endif
|
||||
|
||||
bool valid = ValidityTester::apply(g, expected_result);
|
||||
boost::ignore_unused(valid);
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "Geometry: ";
|
||||
pretty_print_geometry<G>::apply(std::cout, g);
|
||||
std::cout << std::endl;
|
||||
std::cout << "wkt: " << bg::wkt(g) << std::endl;
|
||||
std::cout << std::boolalpha;
|
||||
std::cout << "is valid? " << valid << std::endl;
|
||||
std::cout << "expected result: " << expected_result << std::endl;
|
||||
std::cout << "=======" << std::endl;
|
||||
std::cout << std::noboolalpha;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void apply(Geometry const& geometry, bool expected_result)
|
||||
{
|
||||
base_test(geometry, expected_result);
|
||||
|
||||
if ( is_convertible_to_closed<Geometry>::apply(geometry) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking closed geometry..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
ClosedGeometry closed_geometry;
|
||||
bg::convert(geometry, closed_geometry);
|
||||
base_test(closed_geometry, expected_result);
|
||||
}
|
||||
if ( is_convertible_to_cw<Geometry>::apply(geometry) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking cw open geometry..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
CWGeometry cw_geometry;
|
||||
bg::convert(geometry, cw_geometry);
|
||||
base_test(cw_geometry, expected_result);
|
||||
if ( is_convertible_to_closed<CWGeometry>::apply(cw_geometry) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking cw closed geometry..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
CWClosedGeometry cw_closed_geometry;
|
||||
bg::convert(cw_geometry, cw_closed_geometry);
|
||||
base_test(cw_closed_geometry, expected_result);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << std::endl << std::endl << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
template <typename VariantGeometry>
|
||||
struct test_valid_variant
|
||||
{
|
||||
static inline void apply(VariantGeometry const& vg, bool expected_result)
|
||||
{
|
||||
test_valid
|
||||
<
|
||||
default_validity_tester, VariantGeometry
|
||||
>::base_test(vg, expected_result);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
#include "test_is_valid.hpp"
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_is_valid_point )
|
||||
@ -834,6 +531,10 @@ void test_open_polygons()
|
||||
// "hole" is completely outside the exterior ring
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,10 0,10 10,0 10),(20 20,20 21,21 21,21 20))"),
|
||||
false);
|
||||
// two "holes" completely outside the exterior ring, that touch
|
||||
// each other
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,10 0,10 10,0 10),(20 0,25 10,21 0),(30 0,25 10,31 0))"),
|
||||
false);
|
||||
|
||||
// example from Norvald Ryeng
|
||||
test::apply(from_wkt<OG>("POLYGON((58 31,56.57 30,62 33),(35 9,28 14,31 16),(23 11,29 5,26 4))"),
|
||||
@ -878,6 +579,10 @@ void test_open_polygons()
|
||||
// 1st hole touches 2nd hole at two points
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,9 9,9 8,2 8,2 1),(2 5,5 8,5 5))"),
|
||||
false);
|
||||
// two holes completely inside exterior ring but touching each
|
||||
// other at a point
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,2 9),(1 1,9 2,9 1))"),
|
||||
true);
|
||||
// four holes, each two touching at different points
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,10 0,10 10,0 10),(0 10,2 1,1 1),(0 10,4 1,3 1),(10 10,9 1,8 1),(10 10,7 1,6 1))"),
|
||||
true);
|
||||
@ -889,6 +594,13 @@ void test_open_polygons()
|
||||
// fifth hole creating three disconnected components for the interior
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,10 0,10 10,0 10),(0 10,2 1,1 1),(0 10,4 1,3 1),(10 10,9 1,8 1),(10 10,7 1,6 1),(4 1,4 4,6 4,6 1,5 0))"),
|
||||
false);
|
||||
|
||||
// both examples: a polygon with one hole, where the hole contains
|
||||
// the exterior ring
|
||||
test::apply(from_wkt<OG>("POLYGON((0 0,1 0,1 1,0 1),(-10 -10,-10 10,10 10,10 -10))"),
|
||||
false);
|
||||
test::apply(from_wkt<OG>("POLYGON((-10 -10,1 0,1 1,0 1),(-10 -10,-10 10,10 10,10 -10))"),
|
||||
false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_is_valid_polygon )
|
||||
@ -929,13 +641,57 @@ void test_open_multipolygons()
|
||||
typedef validity_tester_areal<AllowDuplicates> tester;
|
||||
typedef test_valid<tester, OG, CG, CW_OG, CW_CG> test;
|
||||
|
||||
// not enough points
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON((()))"), false);
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0)),(()))"), false);
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,1 0)))"), false);
|
||||
|
||||
// two disjoint polygons
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,1 0,1 1,0 1)),((2 2,3 2,3 3,2 3)))"),
|
||||
true);
|
||||
|
||||
// two disjoint polygons with multiple points
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,1 0,1 0,1 1,0 1)),((2 2,3 2,3 3,3 3,2 3)))"),
|
||||
AllowDuplicates);
|
||||
|
||||
// two polygons touch at a point
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,1 0,1 1,0 1)),((1 1,2 1,2 2,1 2)))"),
|
||||
true);
|
||||
|
||||
// two polygons share a segment at a point
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,1.5 0,1.5 1,0 1)),((1 1,2 1,2 2,1 2)))"),
|
||||
false);
|
||||
|
||||
// one polygon inside another and boundaries touching
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,10 0,10 10,0 10)),((0 0,9 1,9 2)))"),
|
||||
false);
|
||||
|
||||
// one polygon inside another and boundaries not touching
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,10 0,10 10,0 10)),((1 1,9 1,9 2)))"),
|
||||
false);
|
||||
|
||||
// free space is disconnected
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,1 0,1 1,0 1)),((1 1,2 1,2 2,1 2)),((0 1,0 2,-1 2,-1 -1)),((1 2,1 3,0 3,0 2)))"),
|
||||
true);
|
||||
|
||||
// multi-polygon with a polygon inside the hole of another polygon
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,100 0,100 100,0 100),(1 1,1 99,99 99,99 1)),((2 2,98 2,98 98,2 98)))"),
|
||||
true);
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,100 0,100 100,0 100),(1 1,1 99,99 99,99 1)),((1 1,98 2,98 98,2 98)))"),
|
||||
true);
|
||||
|
||||
// test case suggested by Barend Gehrels: take two valid polygons P1 and
|
||||
// P2 with holes H1 and H2, respectively, and consider P2 to be
|
||||
// fully inside H1; now invalidate the multi-polygon by
|
||||
// considering H2 as a hole of P1 and H1 as a hole of P2; this
|
||||
// should be invalid
|
||||
//
|
||||
// first the valid case:
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,100 0,100 100,0 100),(1 1,1 99,99 99,99 1)),((2 2,98 2,98 98,2 98),(3 3,3 97,97 97,97 3)))"),
|
||||
true);
|
||||
// and the invalid case:
|
||||
test::apply(from_wkt<OG>("MULTIPOLYGON(((0 0,100 0,100 100,0 100),(3 3,3 97,97 97,97 3)),((2 2,98 2,98 98,2 98),(1 1,1 99,99 99,99 1)))"),
|
||||
false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_is_valid_multipolygon )
|
||||
|
407
test/algorithms/test_is_valid.hpp
Normal file
407
test/algorithms/test_is_valid.hpp
Normal file
@ -0,0 +1,407 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
// Unit Test
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
#ifndef BOOST_GEOMETRY_TEST_IS_VALID_HPP
|
||||
#define BOOST_GEOMETRY_TEST_IS_VALID_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/variant/variant.hpp>
|
||||
|
||||
#include <boost/geometry/core/closure.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/ring_type.hpp>
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/geometries/segment.hpp>
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
#include <boost/geometry/geometries/linestring.hpp>
|
||||
#include <boost/geometry/geometries/ring.hpp>
|
||||
#include <boost/geometry/geometries/polygon.hpp>
|
||||
#include <boost/geometry/geometries/multi_point.hpp>
|
||||
#include <boost/geometry/geometries/multi_linestring.hpp>
|
||||
#include <boost/geometry/geometries/multi_polygon.hpp>
|
||||
|
||||
#include <boost/geometry/strategies/strategies.hpp>
|
||||
|
||||
#include <boost/geometry/io/wkt/wkt.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/num_points.hpp>
|
||||
#include <boost/geometry/algorithms/is_valid.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
#include "pretty_print_geometry.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
namespace bg = ::boost::geometry;
|
||||
|
||||
typedef bg::model::point<double, 2, bg::cs::cartesian> point_type;
|
||||
typedef bg::model::segment<point_type> segment_type;
|
||||
typedef bg::model::box<point_type> box_type;
|
||||
typedef bg::model::linestring<point_type> linestring_type;
|
||||
typedef bg::model::multi_linestring<linestring_type> multi_linestring_type;
|
||||
typedef bg::model::multi_point<point_type> multi_point_type;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// returns true if a geometry can be converted to closed
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type,
|
||||
bg::closure_selector Closure = bg::closure<Geometry>::value
|
||||
>
|
||||
struct is_convertible_to_closed
|
||||
{
|
||||
static inline bool apply(Geometry const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ring>
|
||||
struct is_convertible_to_closed<Ring, bg::ring_tag, bg::open>
|
||||
{
|
||||
static inline bool apply(Ring const& ring)
|
||||
{
|
||||
return boost::size(ring) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Polygon>
|
||||
struct is_convertible_to_closed<Polygon, bg::polygon_tag, bg::open>
|
||||
{
|
||||
typedef typename bg::ring_type<Polygon>::type ring_type;
|
||||
|
||||
template <typename InteriorRings>
|
||||
static inline
|
||||
bool apply_to_interior_rings(InteriorRings const& interior_rings)
|
||||
{
|
||||
return bg::detail::check_iterator_range
|
||||
<
|
||||
is_convertible_to_closed<ring_type>
|
||||
>::apply(boost::begin(interior_rings),
|
||||
boost::end(interior_rings));
|
||||
}
|
||||
|
||||
static inline bool apply(Polygon const& polygon)
|
||||
{
|
||||
return boost::size(bg::exterior_ring(polygon)) > 0
|
||||
&& apply_to_interior_rings(bg::interior_rings(polygon));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MultiPolygon>
|
||||
struct is_convertible_to_closed<MultiPolygon, bg::multi_polygon_tag, bg::open>
|
||||
{
|
||||
typedef typename boost::range_value<MultiPolygon>::type polygon;
|
||||
|
||||
static inline bool apply(MultiPolygon const& multi_polygon)
|
||||
{
|
||||
return bg::detail::check_iterator_range
|
||||
<
|
||||
is_convertible_to_closed<polygon>,
|
||||
false // do not allow empty multi-polygon
|
||||
>::apply(boost::begin(multi_polygon),
|
||||
boost::end(multi_polygon));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// returns true if a geometry can be converted to cw
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type,
|
||||
bg::order_selector Order = bg::point_order<Geometry>::value
|
||||
>
|
||||
struct is_convertible_to_cw
|
||||
{
|
||||
static inline bool apply(Geometry const&)
|
||||
{
|
||||
return bg::point_order<Geometry>::value == bg::counterclockwise;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// returns true if geometry can be converted to polygon
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type
|
||||
>
|
||||
struct is_convertible_to_polygon
|
||||
{
|
||||
typedef Geometry type;
|
||||
static bool const value = false;
|
||||
};
|
||||
|
||||
template <typename Ring>
|
||||
struct is_convertible_to_polygon<Ring, bg::ring_tag>
|
||||
{
|
||||
typedef bg::model::polygon
|
||||
<
|
||||
typename bg::point_type<Ring>::type,
|
||||
bg::point_order<Ring>::value == bg::clockwise,
|
||||
bg::closure<Ring>::value == bg::closed
|
||||
> type;
|
||||
|
||||
static bool const value = true;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// returns true if geometry can be converted to multi-polygon
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type
|
||||
>
|
||||
struct is_convertible_to_multipolygon
|
||||
{
|
||||
typedef Geometry type;
|
||||
static bool const value = false;
|
||||
};
|
||||
|
||||
template <typename Ring>
|
||||
struct is_convertible_to_multipolygon<Ring, bg::ring_tag>
|
||||
{
|
||||
typedef bg::model::multi_polygon
|
||||
<
|
||||
typename is_convertible_to_polygon<Ring>::type
|
||||
> type;
|
||||
|
||||
static bool const value = true;
|
||||
};
|
||||
|
||||
template <typename Polygon>
|
||||
struct is_convertible_to_multipolygon<Polygon, bg::polygon_tag>
|
||||
{
|
||||
typedef bg::model::multi_polygon<Polygon> type;
|
||||
static bool const value = true;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
template <typename ValidityTester>
|
||||
struct validity_checker
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry,
|
||||
bool expected_result)
|
||||
{
|
||||
bool valid = ValidityTester::apply(geometry);
|
||||
BOOST_CHECK_MESSAGE( valid == expected_result,
|
||||
"Expected: " << expected_result
|
||||
<< " detected: " << valid
|
||||
<< " wkt: " << bg::wkt(geometry) );
|
||||
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
struct default_validity_tester
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry)
|
||||
{
|
||||
return bg::is_valid(geometry);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <bool AllowSpikes>
|
||||
struct validity_tester_linear
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry)
|
||||
{
|
||||
return bg::dispatch::is_valid
|
||||
<
|
||||
Geometry,
|
||||
typename bg::tag<Geometry>::type,
|
||||
AllowSpikes
|
||||
>::apply(geometry);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <bool AllowDuplicates>
|
||||
struct validity_tester_areal
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry)
|
||||
{
|
||||
bool const irrelevant = true;
|
||||
|
||||
return bg::dispatch::is_valid
|
||||
<
|
||||
Geometry,
|
||||
typename bg::tag<Geometry>::type,
|
||||
irrelevant,
|
||||
AllowDuplicates
|
||||
>::apply(geometry);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename ValidityTester,
|
||||
typename Geometry,
|
||||
typename ClosedGeometry = Geometry,
|
||||
typename CWGeometry = Geometry,
|
||||
typename CWClosedGeometry = Geometry,
|
||||
typename Tag = typename bg::tag<Geometry>::type
|
||||
>
|
||||
struct test_valid
|
||||
{
|
||||
template <typename G>
|
||||
static inline void base_test(G const& g, bool expected_result)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "=======" << std::endl;
|
||||
#endif
|
||||
|
||||
bool valid = validity_checker
|
||||
<
|
||||
ValidityTester
|
||||
>::apply(g, expected_result);
|
||||
boost::ignore_unused(valid);
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "Geometry: ";
|
||||
pretty_print_geometry<G>::apply(std::cout, g);
|
||||
std::cout << std::endl;
|
||||
std::cout << "wkt: " << bg::wkt(g) << std::endl;
|
||||
std::cout << std::boolalpha;
|
||||
std::cout << "is valid? " << valid << std::endl;
|
||||
std::cout << "expected result: " << expected_result << std::endl;
|
||||
std::cout << "=======" << std::endl;
|
||||
std::cout << std::noboolalpha;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void apply(Geometry const& geometry, bool expected_result)
|
||||
{
|
||||
base_test(geometry, expected_result);
|
||||
|
||||
if ( is_convertible_to_closed<Geometry>::apply(geometry) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking closed geometry..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
ClosedGeometry closed_geometry;
|
||||
bg::convert(geometry, closed_geometry);
|
||||
base_test(closed_geometry, expected_result);
|
||||
}
|
||||
if ( is_convertible_to_cw<Geometry>::apply(geometry) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking cw open geometry..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
CWGeometry cw_geometry;
|
||||
bg::convert(geometry, cw_geometry);
|
||||
base_test(cw_geometry, expected_result);
|
||||
if ( is_convertible_to_closed<CWGeometry>::apply(cw_geometry) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking cw closed geometry..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
CWClosedGeometry cw_closed_geometry;
|
||||
bg::convert(cw_geometry, cw_closed_geometry);
|
||||
base_test(cw_closed_geometry, expected_result);
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_convertible_to_polygon<Geometry>::value )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking geometry converted to polygon..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
typename is_convertible_to_polygon<Geometry>::type polygon;
|
||||
bg::convert(geometry, polygon);
|
||||
base_test(polygon, expected_result);
|
||||
}
|
||||
|
||||
if ( is_convertible_to_multipolygon<Geometry>::value )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << "...checking geometry converted to multi-polygon..."
|
||||
<< std::endl;
|
||||
#endif
|
||||
typename is_convertible_to_multipolygon
|
||||
<
|
||||
Geometry
|
||||
>::type multipolygon;
|
||||
|
||||
bg::convert(geometry, multipolygon);
|
||||
base_test(multipolygon, expected_result);
|
||||
}
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << std::endl << std::endl << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
template <typename VariantGeometry>
|
||||
struct test_valid_variant
|
||||
{
|
||||
static inline void apply(VariantGeometry const& vg, bool expected_result)
|
||||
{
|
||||
test_valid
|
||||
<
|
||||
default_validity_tester, VariantGeometry
|
||||
>::base_test(vg, expected_result);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_TEST_IS_VALID_HPP
|
Loading…
x
Reference in New Issue
Block a user