Merge pull request #75 from mkaravel/feature/is_simple

Feature/is simple
This commit is contained in:
Adam Wulkiewicz 2014-07-01 17:27:01 +02:00
commit e9178b6a80
25 changed files with 1566 additions and 670 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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]

View File

@ -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"]

View File

@ -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>

View File

@ -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]

View 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]

View 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]

View File

@ -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 ;

View 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

View 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
]
*/
//]

View 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
]
*/
//]

View File

@ -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);
}

View File

@ -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>

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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 )

View 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