mirror of
https://github.com/boostorg/geometry.git
synced 2025-05-11 21:44:04 +00:00
Merge pull request #229 from mkaravel/feature/is_valid_reason
New algorithm: is valid with reason
This commit is contained in:
commit
a7b82e7dd0
@ -47,6 +47,7 @@
|
||||
[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/is_valid_message.cpp]
|
||||
[import src/examples/algorithms/num_geometries.cpp]
|
||||
[import src/examples/algorithms/num_interior_rings.cpp]
|
||||
[import src/examples/algorithms/num_points.cpp]
|
||||
|
@ -123,7 +123,10 @@
|
||||
[endsect]
|
||||
|
||||
[include generated/is_simple.qbk]
|
||||
|
||||
[section:is_valid is_valid]
|
||||
[include generated/is_valid.qbk]
|
||||
[endsect]
|
||||
|
||||
[section:length length]
|
||||
[include generated/length.qbk]
|
||||
|
32
doc/reference/algorithms/is_valid_with_message.qbk
Normal file
32
doc/reference/algorithms/is_valid_with_message.qbk
Normal file
@ -0,0 +1,32 @@
|
||||
[/============================================================================
|
||||
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__]
|
||||
|
||||
[include reference/status/is_valid_status.qbk]
|
||||
|
||||
[heading Complexity]
|
||||
Constant-time for points, segments, boxes and multi-points
|
||||
|
||||
Linear for linestrings and multi-linestrings
|
||||
|
||||
Linearithmic for rings
|
||||
|
||||
Currently, worst-case quadratic for polygons and multi-polygons
|
||||
|
||||
[heading Example]
|
||||
[is_valid_message]
|
||||
[is_valid_message_output]
|
||||
|
||||
[heading See also]
|
||||
* [link geometry.reference.algorithms.is_simple is_simple]
|
@ -20,6 +20,7 @@
|
||||
[*Additional functionality]
|
||||
|
||||
* New algorithm num_segments, returning the number of segments of a geometry
|
||||
* New overload for is_valid algorithm, that takes a string reference as a second argument and fills it with a message related to the validity or invalidity of the geometry
|
||||
|
||||
[*Improvements]
|
||||
|
||||
|
@ -54,6 +54,7 @@ exe intersects_linestring : intersects_linestring.cpp ;
|
||||
exe is_simple : is_simple.cpp ;
|
||||
|
||||
exe is_valid : is_valid.cpp ;
|
||||
exe is_valid_message : is_valid_message.cpp ;
|
||||
|
||||
exe length : length.cpp ;
|
||||
exe length_with_strategy : length_with_strategy.cpp ;
|
||||
|
57
doc/src/examples/algorithms/is_valid_message.cpp
Normal file
57
doc/src/examples/algorithms/is_valid_message.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
// QuickBook Example
|
||||
|
||||
// Copyright (c) 2015, 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_message
|
||||
//` Checks whether a geometry is valid and, if not valid, prints a message describing the reason
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/geometries/polygon.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,2 9,1 9,0 0),(2 9,9 2,9 9,2 9))"
|
||||
, poly);
|
||||
|
||||
std::string message;
|
||||
bool valid = boost::geometry::is_valid(poly, message);
|
||||
std::cout << "is valid? " << (valid ? "yes" : "no") << std::endl;
|
||||
if (! valid)
|
||||
{
|
||||
std::cout << "why not valid? " << message << std::endl;
|
||||
}
|
||||
/*<-*/ create_svg("is_valid_example.svg", poly); /*->*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[is_valid_message_output
|
||||
/*`
|
||||
Output:
|
||||
[pre
|
||||
is valid? no
|
||||
why not valid? Geometry has disconnected interior
|
||||
|
||||
[$img/algorithms/is_valid_example.png]
|
||||
|
||||
]
|
||||
|
||||
*/
|
||||
//]
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_simple/failure_policy.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/dispatch/is_simple.hpp>
|
||||
@ -38,11 +39,12 @@ struct is_simple_ring
|
||||
{
|
||||
static inline bool apply(Ring const& ring)
|
||||
{
|
||||
simplicity_failure_policy policy;
|
||||
return
|
||||
!detail::is_valid::has_duplicates
|
||||
<
|
||||
Ring, geometry::closure<Ring>::value
|
||||
>::apply(ring);
|
||||
>::apply(ring, policy);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015, 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_SIMPLE_FAILURE_POLICY_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_FAILURE_POLICY_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace is_simple
|
||||
{
|
||||
|
||||
|
||||
struct simplicity_failure_policy
|
||||
{
|
||||
template <validity_failure_type Failure>
|
||||
static inline bool apply()
|
||||
{
|
||||
return Failure == no_failure;
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data>
|
||||
static inline bool apply(Data const&)
|
||||
{
|
||||
return apply<Failure>();
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data1, typename Data2>
|
||||
static inline bool apply(Data1 const&, Data2 const&)
|
||||
{
|
||||
return apply<Failure>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::is_simple
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_FAILURE_POLICY_HPP
|
@ -43,6 +43,7 @@
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_spikes.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_simple/failure_policy.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/dispatch/is_simple.hpp>
|
||||
@ -233,14 +234,15 @@ struct is_simple_linestring
|
||||
{
|
||||
static inline bool apply(Linestring const& linestring)
|
||||
{
|
||||
simplicity_failure_policy policy;
|
||||
return ! detail::is_valid::has_duplicates
|
||||
<
|
||||
Linestring, closed
|
||||
>::apply(linestring)
|
||||
>::apply(linestring, policy)
|
||||
&& ! detail::is_valid::has_spikes
|
||||
<
|
||||
Linestring, closed
|
||||
>::apply(linestring)
|
||||
>::apply(linestring, policy)
|
||||
&& ! (CheckSelfIntersections && has_self_intersections(linestring));
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#include <boost/geometry/policies/compare.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_simple/failure_policy.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/dispatch/is_simple.hpp>
|
||||
|
||||
@ -48,7 +49,11 @@ struct is_simple_multipoint
|
||||
std::sort(boost::begin(mp), boost::end(mp),
|
||||
geometry::less<typename point_type<MultiPoint>::type>());
|
||||
|
||||
return !detail::is_valid::has_duplicates<MultiPoint, closed>::apply(mp);
|
||||
simplicity_failure_policy policy;
|
||||
return !detail::is_valid::has_duplicates
|
||||
<
|
||||
MultiPoint, closed
|
||||
>::apply(mp, policy);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
#include <boost/geometry/core/coordinate_dimension.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/dispatch/is_valid.hpp>
|
||||
|
||||
|
||||
@ -30,15 +31,22 @@ namespace detail { namespace is_valid
|
||||
template <typename Box, std::size_t I>
|
||||
struct has_valid_corners
|
||||
{
|
||||
static inline bool apply(Box const& box)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Box const& box, VisitPolicy& visitor)
|
||||
{
|
||||
if ( geometry::get<geometry::max_corner, I-1>(box)
|
||||
<=
|
||||
geometry::get<geometry::min_corner, I-1>(box) )
|
||||
if (math::equals(geometry::get<geometry::min_corner, I-1>(box),
|
||||
geometry::get<geometry::max_corner, I-1>(box)))
|
||||
{
|
||||
return false;
|
||||
return
|
||||
visitor.template apply<failure_wrong_topological_dimension>();
|
||||
}
|
||||
return has_valid_corners<Box, I-1>::apply(box);
|
||||
else if (geometry::get<geometry::min_corner, I-1>(box)
|
||||
>
|
||||
geometry::get<geometry::max_corner, I-1>(box))
|
||||
{
|
||||
return visitor.template apply<failure_wrong_corner_order>();
|
||||
}
|
||||
return has_valid_corners<Box, I-1>::apply(box, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -46,9 +54,10 @@ struct has_valid_corners
|
||||
template <typename Box>
|
||||
struct has_valid_corners<Box, 0>
|
||||
{
|
||||
static inline bool apply(Box const&)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Box const&, VisitPolicy& visitor)
|
||||
{
|
||||
return true;
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -50,8 +50,8 @@ struct debug_validity_phase<Polygon, polygon_tag>
|
||||
std::cout << "computing and analyzing turns..." << std::endl;
|
||||
break;
|
||||
case 4:
|
||||
std::cout << "checking if holes are inside the exterior ring..."
|
||||
<< std::endl;
|
||||
std::cout << "checking if interior rings are inside "
|
||||
<< "the exterior ring..." << std::endl;
|
||||
break;
|
||||
case 5:
|
||||
std::cout << "checking connectivity of interior..." << std::endl;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -15,8 +15,10 @@
|
||||
#include <boost/geometry/core/closure.hpp>
|
||||
|
||||
#include <boost/geometry/policies/compare.hpp>
|
||||
#include <boost/geometry/policies/is_valid/default_policy.hpp>
|
||||
|
||||
#include <boost/geometry/views/closeable_view.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
@ -30,7 +32,8 @@ namespace detail { namespace is_valid
|
||||
template <typename Range, closure_selector Closure>
|
||||
struct has_duplicates
|
||||
{
|
||||
static inline bool apply(Range const& range)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Range const& range, VisitPolicy& visitor)
|
||||
{
|
||||
typedef typename closeable_view<Range const, Closure>::type view_type;
|
||||
typedef typename boost::range_iterator<view_type const>::type iterator;
|
||||
@ -39,7 +42,7 @@ struct has_duplicates
|
||||
|
||||
if ( boost::size(view) < 2 )
|
||||
{
|
||||
return false;
|
||||
return ! visitor.template apply<no_failure>();
|
||||
}
|
||||
|
||||
geometry::equal_to<typename boost::range_value<Range>::type> equal;
|
||||
@ -50,10 +53,10 @@ struct has_duplicates
|
||||
{
|
||||
if ( equal(*it, *next) )
|
||||
{
|
||||
return true;
|
||||
return ! visitor.template apply<failure_duplicate_points>(*it);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return ! visitor.template apply<no_failure>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -13,15 +13,22 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
|
||||
#include <boost/geometry/core/point_type.hpp>
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/policies/is_valid/default_policy.hpp>
|
||||
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
#include <boost/geometry/views/closeable_view.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/equals.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp>
|
||||
#include <boost/geometry/io/dsv/write.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
@ -69,13 +76,17 @@ struct not_equal_to
|
||||
template <typename Range, closure_selector Closure>
|
||||
struct has_spikes
|
||||
{
|
||||
static inline bool apply(Range const& range)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Range const& range, VisitPolicy& visitor)
|
||||
{
|
||||
typedef not_equal_to<typename point_type<Range>::type> not_equal;
|
||||
|
||||
typedef typename closeable_view<Range const, Closure>::type view_type;
|
||||
typedef typename boost::range_iterator<view_type const>::type iterator;
|
||||
|
||||
bool const is_linear
|
||||
= boost::is_same<typename tag<Range>::type, linestring_tag>::value;
|
||||
|
||||
view_type const view(range);
|
||||
|
||||
iterator prev = boost::begin(view);
|
||||
@ -85,7 +96,7 @@ struct has_spikes
|
||||
{
|
||||
// the range has only one distinct point, so it
|
||||
// cannot have a spike
|
||||
return false;
|
||||
return ! visitor.template apply<no_failure>();
|
||||
}
|
||||
|
||||
iterator next = std::find_if(cur, boost::end(view), not_equal(*cur));
|
||||
@ -93,7 +104,7 @@ struct has_spikes
|
||||
{
|
||||
// the range has only two distinct points, so it
|
||||
// cannot have a spike
|
||||
return false;
|
||||
return ! visitor.template apply<no_failure>();
|
||||
}
|
||||
|
||||
while ( next != boost::end(view) )
|
||||
@ -102,7 +113,8 @@ struct has_spikes
|
||||
*next,
|
||||
*cur) )
|
||||
{
|
||||
return true;
|
||||
return
|
||||
! visitor.template apply<failure_spikes>(is_linear, *cur);
|
||||
}
|
||||
prev = cur;
|
||||
cur = next;
|
||||
@ -120,10 +132,18 @@ struct has_spikes
|
||||
not_equal(range::back(view)));
|
||||
iterator next =
|
||||
std::find_if(cur, boost::end(view), not_equal(*cur));
|
||||
return detail::point_is_spike_or_equal(*prev, *next, *cur);
|
||||
if (detail::point_is_spike_or_equal(*prev, *next, *cur))
|
||||
{
|
||||
return
|
||||
! visitor.template apply<failure_spikes>(is_linear, *cur);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ! visitor.template apply<no_failure>();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return ! visitor.template apply<no_failure>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -10,6 +10,11 @@
|
||||
#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 <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/point_type.hpp>
|
||||
|
||||
#include <boost/geometry/policies/predicate_based_interrupt_policy.hpp>
|
||||
@ -22,7 +27,6 @@
|
||||
|
||||
#include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
@ -64,8 +68,10 @@ public:
|
||||
> turn_type;
|
||||
|
||||
// returns true if all turns are valid
|
||||
template <typename Turns>
|
||||
static inline bool apply(Geometry const& geometry, Turns& turns)
|
||||
template <typename Turns, typename VisitPolicy>
|
||||
static inline bool apply(Geometry const& geometry,
|
||||
Turns& turns,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
rescale_policy_type robust_policy
|
||||
= geometry::get_rescale_policy<rescale_policy_type>(geometry);
|
||||
@ -80,7 +86,23 @@ public:
|
||||
turns,
|
||||
interrupt_policy);
|
||||
|
||||
return !interrupt_policy.has_intersections;
|
||||
if (interrupt_policy.has_intersections)
|
||||
{
|
||||
BOOST_ASSERT(! boost::empty(turns));
|
||||
return visitor.template apply<failure_self_intersections>(turns);
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if all turns are valid
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Geometry const& geometry, VisitPolicy& visitor)
|
||||
{
|
||||
std::vector<turn_type> turns;
|
||||
return apply(geometry, turns, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -10,6 +10,9 @@
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
#include <boost/variant/variant_fwd.hpp>
|
||||
@ -17,6 +20,9 @@
|
||||
#include <boost/geometry/geometries/concepts/check.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/dispatch/is_valid.hpp>
|
||||
#include <boost/geometry/policies/is_valid/default_policy.hpp>
|
||||
#include <boost/geometry/policies/is_valid/failing_reason_policy.hpp>
|
||||
#include <boost/geometry/policies/is_valid/failure_type_policy.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
@ -28,35 +34,52 @@ namespace resolve_variant {
|
||||
template <typename Geometry>
|
||||
struct is_valid
|
||||
{
|
||||
static inline bool apply(Geometry const& geometry)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Geometry const& geometry, VisitPolicy& visitor)
|
||||
{
|
||||
concept::check<Geometry const>();
|
||||
return dispatch::is_valid<Geometry>::apply(geometry);
|
||||
return dispatch::is_valid<Geometry>::apply(geometry, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
|
||||
struct is_valid<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
|
||||
{
|
||||
template <typename VisitPolicy>
|
||||
struct visitor : boost::static_visitor<bool>
|
||||
{
|
||||
visitor(VisitPolicy& policy) : m_policy(policy) {}
|
||||
|
||||
template <typename Geometry>
|
||||
bool operator()(Geometry const& geometry) const
|
||||
{
|
||||
return is_valid<Geometry>::apply(geometry);
|
||||
return is_valid<Geometry>::apply(geometry, m_policy);
|
||||
}
|
||||
|
||||
VisitPolicy& m_policy;
|
||||
};
|
||||
|
||||
template <typename VisitPolicy>
|
||||
static inline bool
|
||||
apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry)
|
||||
apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
|
||||
VisitPolicy& policy_visitor)
|
||||
{
|
||||
return boost::apply_visitor(visitor(), geometry);
|
||||
return boost::apply_visitor(visitor<VisitPolicy>(policy_visitor),
|
||||
geometry);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace resolve_variant
|
||||
|
||||
|
||||
// Undocumented for now
|
||||
template <typename Geometry, typename VisitPolicy>
|
||||
inline bool is_valid(Geometry const& geometry, VisitPolicy& visitor)
|
||||
{
|
||||
return resolve_variant::is_valid<Geometry>::apply(geometry, visitor);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief \brief_check{is valid (in the OGC sense)}
|
||||
\ingroup is_valid
|
||||
@ -70,7 +93,43 @@ multi-geometries with no elements are considered valid}
|
||||
template <typename Geometry>
|
||||
inline bool is_valid(Geometry const& geometry)
|
||||
{
|
||||
return resolve_variant::is_valid<Geometry>::apply(geometry);
|
||||
is_valid_default_policy<> policy_visitor;
|
||||
return is_valid(geometry, policy_visitor);
|
||||
}
|
||||
|
||||
|
||||
// Undocumented for now
|
||||
template <typename Geometry>
|
||||
inline bool is_valid(Geometry const& geometry, validity_failure_type& failure)
|
||||
{
|
||||
failure_type_policy<> policy_visitor;
|
||||
bool result = is_valid(geometry, policy_visitor);
|
||||
failure = policy_visitor.failure();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief \brief_check{is valid (in the OGC sense)}
|
||||
\ingroup is_valid
|
||||
\tparam Geometry \tparam_geometry
|
||||
\param geometry \param_geometry
|
||||
\param message A string containing a message stating if the geometry
|
||||
is valid or not, and if not valid a reason why
|
||||
\return \return_check{is valid (in the OGC sense), with one exception:
|
||||
multi-geometries with no elements are considered valid}
|
||||
|
||||
\qbk{distinguish,with message}
|
||||
\qbk{[include reference/algorithms/is_valid_with_message.qbk]}
|
||||
*/
|
||||
template <typename Geometry>
|
||||
inline bool is_valid(Geometry const& geometry, std::string& message)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
failing_reason_policy<> policy_visitor(stream);
|
||||
bool result = is_valid(geometry, policy_visitor);
|
||||
message = stream.str();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -72,6 +72,16 @@ template <typename Geometry, typename Tag = typename tag<Geometry>::type>
|
||||
struct is_acceptable_turn
|
||||
{};
|
||||
|
||||
template <typename Ring>
|
||||
struct is_acceptable_turn<Ring, ring_tag>
|
||||
{
|
||||
template <typename Turn>
|
||||
static inline bool apply(Turn const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Polygon>
|
||||
class is_acceptable_turn<Polygon, polygon_tag>
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/equals.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_spikes.hpp>
|
||||
#include <boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp>
|
||||
@ -36,11 +37,18 @@ namespace detail { namespace is_valid
|
||||
{
|
||||
|
||||
|
||||
template <typename Linestring, bool AllowSpikes>
|
||||
template <typename Linestring>
|
||||
struct is_valid_linestring
|
||||
{
|
||||
static inline bool apply(Linestring const& linestring)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Linestring const& linestring,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
if (boost::size(linestring) < 2)
|
||||
{
|
||||
return visitor.template apply<failure_few_points>();
|
||||
}
|
||||
|
||||
std::size_t num_distinct = detail::num_distinct_consecutive_points
|
||||
<
|
||||
Linestring,
|
||||
@ -49,14 +57,17 @@ struct is_valid_linestring
|
||||
not_equal_to<typename point_type<Linestring>::type>
|
||||
>::apply(linestring);
|
||||
|
||||
if ( num_distinct < 2u )
|
||||
if (num_distinct < 2u)
|
||||
{
|
||||
return false;
|
||||
return
|
||||
visitor.template apply<failure_wrong_topological_dimension>();
|
||||
}
|
||||
|
||||
return num_distinct == 2u
|
||||
|| AllowSpikes
|
||||
|| !has_spikes<Linestring, closed>::apply(linestring);
|
||||
if (num_distinct == 2u)
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
return ! has_spikes<Linestring, closed>::apply(linestring, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -84,9 +95,11 @@ namespace dispatch
|
||||
// By default, spikes are disallowed
|
||||
//
|
||||
// Reference: OGC 06-103r4 (6.1.6.1)
|
||||
template <typename Linestring, bool AllowSpikes>
|
||||
struct is_valid<Linestring, linestring_tag, AllowSpikes>
|
||||
: detail::is_valid::is_valid_linestring<Linestring, AllowSpikes>
|
||||
template <typename Linestring, bool AllowEmptyMultiGeometries>
|
||||
struct is_valid
|
||||
<
|
||||
Linestring, linestring_tag, AllowEmptyMultiGeometries
|
||||
> : detail::is_valid::is_valid_linestring<Linestring>
|
||||
{};
|
||||
|
||||
|
||||
@ -96,21 +109,47 @@ struct is_valid<Linestring, linestring_tag, AllowSpikes>
|
||||
// are on the boundaries of both elements.
|
||||
//
|
||||
// Reference: OGC 06-103r4 (6.1.8.1; Fig. 9)
|
||||
template <typename MultiLinestring, bool AllowSpikes>
|
||||
struct is_valid<MultiLinestring, multi_linestring_tag, AllowSpikes>
|
||||
template <typename MultiLinestring, bool AllowEmptyMultiGeometries>
|
||||
class is_valid
|
||||
<
|
||||
MultiLinestring, multi_linestring_tag, AllowEmptyMultiGeometries
|
||||
>
|
||||
{
|
||||
static inline bool apply(MultiLinestring const& multilinestring)
|
||||
private:
|
||||
template <typename VisitPolicy>
|
||||
struct per_linestring
|
||||
{
|
||||
per_linestring(VisitPolicy& policy) : m_policy(policy) {}
|
||||
|
||||
template <typename Linestring>
|
||||
inline bool apply(Linestring const& linestring) const
|
||||
{
|
||||
return detail::is_valid::is_valid_linestring
|
||||
<
|
||||
Linestring
|
||||
>::apply(linestring, m_policy);
|
||||
}
|
||||
|
||||
VisitPolicy& m_policy;
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(MultiLinestring const& multilinestring,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
if (AllowEmptyMultiGeometries && boost::empty(multilinestring))
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
|
||||
return detail::check_iterator_range
|
||||
<
|
||||
detail::is_valid::is_valid_linestring
|
||||
<
|
||||
typename boost::range_value<MultiLinestring>::type,
|
||||
AllowSpikes
|
||||
>,
|
||||
true // allow empty multilinestring
|
||||
per_linestring<VisitPolicy>,
|
||||
false // do not check for empty multilinestring (done above)
|
||||
>::apply(boost::begin(multilinestring),
|
||||
boost::end(multilinestring));
|
||||
boost::end(multilinestring),
|
||||
per_linestring<VisitPolicy>(visitor));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/within.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
@ -49,12 +50,11 @@ namespace detail { namespace is_valid
|
||||
{
|
||||
|
||||
|
||||
template <typename MultiPolygon, bool AllowDuplicates>
|
||||
template <typename MultiPolygon, bool AllowEmptyMultiGeometries>
|
||||
class is_valid_multipolygon
|
||||
: is_valid_polygon
|
||||
<
|
||||
typename boost::range_value<MultiPolygon>::type,
|
||||
AllowDuplicates,
|
||||
true // check only the validity of rings
|
||||
>
|
||||
{
|
||||
@ -62,18 +62,23 @@ private:
|
||||
typedef is_valid_polygon
|
||||
<
|
||||
typename boost::range_value<MultiPolygon>::type,
|
||||
AllowDuplicates,
|
||||
true
|
||||
> base;
|
||||
|
||||
|
||||
|
||||
template <typename PolygonIterator, typename TurnIterator>
|
||||
template
|
||||
<
|
||||
typename PolygonIterator,
|
||||
typename TurnIterator,
|
||||
typename VisitPolicy
|
||||
>
|
||||
static inline
|
||||
bool are_polygon_interiors_disjoint(PolygonIterator polygons_first,
|
||||
PolygonIterator polygons_beyond,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
TurnIterator turns_beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
// collect all polygons that have turns
|
||||
std::set<signed_index_type> multi_indices;
|
||||
@ -89,22 +94,29 @@ private:
|
||||
for (PolygonIterator it = polygons_first; it != polygons_beyond;
|
||||
++it, ++multi_index)
|
||||
{
|
||||
if ( multi_indices.find(multi_index) == multi_indices.end() )
|
||||
if (multi_indices.find(multi_index) == multi_indices.end())
|
||||
{
|
||||
polygon_iterators.push_back(it);
|
||||
}
|
||||
}
|
||||
|
||||
typename base::item_visitor visitor;
|
||||
typename base::item_visitor_type item_visitor;
|
||||
|
||||
geometry::partition
|
||||
<
|
||||
geometry::model::box<typename point_type<MultiPolygon>::type>,
|
||||
typename base::expand_box,
|
||||
typename base::overlaps_box
|
||||
>::apply(polygon_iterators, visitor);
|
||||
>::apply(polygon_iterators, item_visitor);
|
||||
|
||||
return !visitor.items_overlap;
|
||||
if (item_visitor.items_overlap)
|
||||
{
|
||||
return visitor.template apply<failure_intersecting_interiors>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -132,11 +144,17 @@ private:
|
||||
template <typename Predicate>
|
||||
struct has_property_per_polygon
|
||||
{
|
||||
template <typename PolygonIterator, typename TurnIterator>
|
||||
template
|
||||
<
|
||||
typename PolygonIterator,
|
||||
typename TurnIterator,
|
||||
typename VisitPolicy
|
||||
>
|
||||
static inline bool apply(PolygonIterator polygons_first,
|
||||
PolygonIterator polygons_beyond,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
TurnIterator turns_beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
signed_index_type multi_index = 0;
|
||||
for (PolygonIterator it = polygons_first; it != polygons_beyond;
|
||||
@ -157,9 +175,10 @@ private:
|
||||
turns_beyond,
|
||||
turns_beyond);
|
||||
|
||||
if ( !Predicate::apply(*it,
|
||||
if (! Predicate::apply(*it,
|
||||
filtered_turns_first,
|
||||
filtered_turns_beyond) )
|
||||
filtered_turns_beyond,
|
||||
visitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -170,49 +189,82 @@ private:
|
||||
|
||||
|
||||
|
||||
template <typename PolygonIterator, typename TurnIterator>
|
||||
template
|
||||
<
|
||||
typename PolygonIterator,
|
||||
typename TurnIterator,
|
||||
typename VisitPolicy
|
||||
>
|
||||
static inline bool have_holes_inside(PolygonIterator polygons_first,
|
||||
PolygonIterator polygons_beyond,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
TurnIterator turns_beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
return has_property_per_polygon
|
||||
<
|
||||
typename base::has_holes_inside
|
||||
>::apply(polygons_first, polygons_beyond,
|
||||
turns_first, turns_beyond);
|
||||
turns_first, turns_beyond, visitor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename PolygonIterator, typename TurnIterator>
|
||||
template
|
||||
<
|
||||
typename PolygonIterator,
|
||||
typename TurnIterator,
|
||||
typename VisitPolicy
|
||||
>
|
||||
static inline bool have_connected_interior(PolygonIterator polygons_first,
|
||||
PolygonIterator polygons_beyond,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
TurnIterator turns_beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
return has_property_per_polygon
|
||||
<
|
||||
typename base::has_connected_interior
|
||||
>::apply(polygons_first, polygons_beyond,
|
||||
turns_first, turns_beyond);
|
||||
turns_first, turns_beyond, visitor);
|
||||
}
|
||||
|
||||
|
||||
template <typename VisitPolicy>
|
||||
struct per_polygon
|
||||
{
|
||||
per_polygon(VisitPolicy& policy) : m_policy(policy) {}
|
||||
|
||||
template <typename Polygon>
|
||||
inline bool apply(Polygon const& polygon) const
|
||||
{
|
||||
return base::apply(polygon, m_policy);
|
||||
}
|
||||
|
||||
VisitPolicy& m_policy;
|
||||
};
|
||||
public:
|
||||
static inline bool apply(MultiPolygon const& multipolygon)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(MultiPolygon const& multipolygon,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
typedef debug_validity_phase<MultiPolygon> debug_phase;
|
||||
|
||||
if (AllowEmptyMultiGeometries && boost::empty(multipolygon))
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
|
||||
// check validity of all polygons ring
|
||||
debug_phase::apply(1);
|
||||
|
||||
if ( !detail::check_iterator_range
|
||||
if (! detail::check_iterator_range
|
||||
<
|
||||
base,
|
||||
true // allow empty multi-polygons
|
||||
per_polygon<VisitPolicy>,
|
||||
false // do not check for empty multipolygon (done above)
|
||||
>::apply(boost::begin(multipolygon),
|
||||
boost::end(multipolygon)) )
|
||||
boost::end(multipolygon),
|
||||
per_polygon<VisitPolicy>(visitor)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -224,10 +276,11 @@ public:
|
||||
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);
|
||||
bool has_invalid_turns =
|
||||
! has_valid_turns::apply(multipolygon, turns, visitor);
|
||||
debug_print_turns(turns.begin(), turns.end());
|
||||
|
||||
if ( has_invalid_turns )
|
||||
if (has_invalid_turns)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -237,10 +290,11 @@ public:
|
||||
// exterior and not one inside the other
|
||||
debug_phase::apply(3);
|
||||
|
||||
if ( !have_holes_inside(boost::begin(multipolygon),
|
||||
if (! have_holes_inside(boost::begin(multipolygon),
|
||||
boost::end(multipolygon),
|
||||
turns.begin(),
|
||||
turns.end()) )
|
||||
turns.end(),
|
||||
visitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -249,10 +303,11 @@ public:
|
||||
// check that each polygon's interior is connected
|
||||
debug_phase::apply(4);
|
||||
|
||||
if ( !have_connected_interior(boost::begin(multipolygon),
|
||||
if (! have_connected_interior(boost::begin(multipolygon),
|
||||
boost::end(multipolygon),
|
||||
turns.begin(),
|
||||
turns.end()) )
|
||||
turns.end(),
|
||||
visitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -263,7 +318,8 @@ public:
|
||||
return are_polygon_interiors_disjoint(boost::begin(multipolygon),
|
||||
boost::end(multipolygon),
|
||||
turns.begin(),
|
||||
turns.end());
|
||||
turns.end(),
|
||||
visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -282,9 +338,14 @@ namespace dispatch
|
||||
// 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>
|
||||
template <typename MultiPolygon, bool AllowEmptyMultiGeometries>
|
||||
struct is_valid
|
||||
<
|
||||
MultiPolygon, multi_polygon_tag, AllowEmptyMultiGeometries
|
||||
> : detail::is_valid::is_valid_multipolygon
|
||||
<
|
||||
MultiPolygon, AllowEmptyMultiGeometries
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/dispatch/is_valid.hpp>
|
||||
|
||||
|
||||
@ -30,9 +31,10 @@ namespace dispatch
|
||||
template <typename Point>
|
||||
struct is_valid<Point, point_tag>
|
||||
{
|
||||
static inline bool apply(Point const&)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Point const&, VisitPolicy& visitor)
|
||||
{
|
||||
return true;
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,14 +44,24 @@ struct is_valid<Point, point_tag>
|
||||
// (have identical coordinate values in X and Y)
|
||||
//
|
||||
// Reference: OGC 06-103r4 (6.1.5)
|
||||
template <typename MultiPoint>
|
||||
struct is_valid<MultiPoint, multi_point_tag>
|
||||
template <typename MultiPoint, bool AllowEmptyMultiGeometries>
|
||||
struct is_valid<MultiPoint, multi_point_tag, AllowEmptyMultiGeometries>
|
||||
{
|
||||
static inline bool apply(MultiPoint const& /*multipoint*/)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(MultiPoint const& multipoint,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
// we allow empty multi-geometries, so an empty multipoint
|
||||
// is considered valid
|
||||
return true;
|
||||
if (AllowEmptyMultiGeometries || boost::size(multipoint) > 0)
|
||||
{
|
||||
// we allow empty multi-geometries, so an empty multipoint
|
||||
// is considered valid
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we do not allow an empty multipoint
|
||||
return visitor.template apply<failure_few_points>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#include <boost/geometry/algorithms/disjoint.hpp>
|
||||
#include <boost/geometry/algorithms/expand.hpp>
|
||||
#include <boost/geometry/algorithms/num_interior_rings.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/within.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
|
||||
@ -63,51 +64,58 @@ namespace detail { namespace is_valid
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Polygon,
|
||||
bool AllowDuplicates,
|
||||
bool CheckRingValidityOnly = false
|
||||
>
|
||||
template <typename Polygon, bool CheckRingValidityOnly = false>
|
||||
class is_valid_polygon
|
||||
{
|
||||
protected:
|
||||
typedef debug_validity_phase<Polygon> debug_phase;
|
||||
|
||||
template <typename VisitPolicy>
|
||||
struct per_ring
|
||||
{
|
||||
per_ring(VisitPolicy& policy) : m_policy(policy) {}
|
||||
|
||||
template <typename Ring>
|
||||
inline bool apply(Ring const& ring) const
|
||||
{
|
||||
return detail::is_valid::is_valid_ring
|
||||
<
|
||||
Ring, false, true
|
||||
>::apply(ring, m_policy);
|
||||
}
|
||||
|
||||
template <typename InteriorRings>
|
||||
static bool has_valid_interior_rings(InteriorRings const& interior_rings)
|
||||
VisitPolicy& m_policy;
|
||||
};
|
||||
|
||||
template <typename InteriorRings, typename VisitPolicy>
|
||||
static bool has_valid_interior_rings(InteriorRings const& interior_rings,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
return
|
||||
detail::check_iterator_range
|
||||
<
|
||||
detail::is_valid::is_valid_ring
|
||||
<
|
||||
typename boost::range_value<InteriorRings>::type,
|
||||
AllowDuplicates,
|
||||
false, // do not check self-intersections
|
||||
true // indicate that the ring is interior
|
||||
>
|
||||
per_ring<VisitPolicy>,
|
||||
true // allow for empty interior ring range
|
||||
>::apply(boost::begin(interior_rings),
|
||||
boost::end(interior_rings));
|
||||
boost::end(interior_rings),
|
||||
per_ring<VisitPolicy>(visitor));
|
||||
}
|
||||
|
||||
struct has_valid_rings
|
||||
{
|
||||
static inline bool apply(Polygon const& polygon)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Polygon const& polygon, VisitPolicy& visitor)
|
||||
{
|
||||
typedef typename ring_type<Polygon>::type ring_type;
|
||||
|
||||
// check validity of exterior ring
|
||||
debug_phase::apply(1);
|
||||
|
||||
if ( !detail::is_valid::is_valid_ring
|
||||
if (! detail::is_valid::is_valid_ring
|
||||
<
|
||||
ring_type,
|
||||
AllowDuplicates,
|
||||
false // do not check self intersections
|
||||
>::apply(exterior_ring(polygon)) )
|
||||
>::apply(exterior_ring(polygon), visitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -115,7 +123,8 @@ protected:
|
||||
// check validity of interior rings
|
||||
debug_phase::apply(2);
|
||||
|
||||
return has_valid_interior_rings(geometry::interior_rings(polygon));
|
||||
return has_valid_interior_rings(geometry::interior_rings(polygon),
|
||||
visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -136,24 +145,24 @@ protected:
|
||||
template <typename Box, typename Iterator>
|
||||
static inline bool apply(Box const& box, Iterator const& it)
|
||||
{
|
||||
return !geometry::disjoint(*it, box);
|
||||
return ! geometry::disjoint(*it, box);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct item_visitor
|
||||
struct item_visitor_type
|
||||
{
|
||||
bool items_overlap;
|
||||
|
||||
item_visitor() : items_overlap(false) {}
|
||||
item_visitor_type() : items_overlap(false) {}
|
||||
|
||||
template <typename Item1, typename Item2>
|
||||
inline void apply(Item1 const& item1, Item2 const& item2)
|
||||
{
|
||||
if ( !items_overlap
|
||||
&& (geometry::within(*points_begin(*item1), *item2)
|
||||
|| geometry::within(*points_begin(*item2), *item1))
|
||||
)
|
||||
if (! items_overlap
|
||||
&& (geometry::within(*points_begin(*item1), *item2)
|
||||
|| geometry::within(*points_begin(*item2), *item1))
|
||||
)
|
||||
{
|
||||
items_overlap = true;
|
||||
}
|
||||
@ -166,27 +175,29 @@ protected:
|
||||
<
|
||||
typename RingIterator,
|
||||
typename ExteriorRing,
|
||||
typename TurnIterator
|
||||
typename TurnIterator,
|
||||
typename VisitPolicy
|
||||
>
|
||||
static inline bool are_holes_inside(RingIterator rings_first,
|
||||
RingIterator rings_beyond,
|
||||
ExteriorRing const& exterior_ring,
|
||||
TurnIterator turns_first,
|
||||
TurnIterator turns_beyond)
|
||||
TurnIterator turns_beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
// collect the interior ring indices that have turns with the
|
||||
// exterior ring
|
||||
std::set<signed_index_type> ring_indices;
|
||||
for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
|
||||
{
|
||||
if ( tit->operations[0].seg_id.ring_index == -1 )
|
||||
if (tit->operations[0].seg_id.ring_index == -1)
|
||||
{
|
||||
BOOST_ASSERT( tit->operations[1].seg_id.ring_index != -1 );
|
||||
BOOST_ASSERT(tit->operations[1].seg_id.ring_index != -1);
|
||||
ring_indices.insert(tit->operations[1].seg_id.ring_index);
|
||||
}
|
||||
else if ( tit->operations[1].seg_id.ring_index == -1 )
|
||||
else if (tit->operations[1].seg_id.ring_index == -1)
|
||||
{
|
||||
BOOST_ASSERT( tit->operations[0].seg_id.ring_index != -1 );
|
||||
BOOST_ASSERT(tit->operations[0].seg_id.ring_index != -1);
|
||||
ring_indices.insert(tit->operations[0].seg_id.ring_index);
|
||||
}
|
||||
}
|
||||
@ -197,10 +208,10 @@ protected:
|
||||
{
|
||||
// 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) )
|
||||
if (ring_indices.find(ring_index) == ring_indices.end()
|
||||
&& ! geometry::covered_by(range::front(*it), exterior_ring))
|
||||
{
|
||||
return false;
|
||||
return visitor.template apply<failure_interior_rings_outside>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +228,7 @@ protected:
|
||||
for (RingIterator it = rings_first; it != rings_beyond;
|
||||
++it, ++ring_index)
|
||||
{
|
||||
if ( ring_indices.find(ring_index) == ring_indices.end() )
|
||||
if (ring_indices.find(ring_index) == ring_indices.end())
|
||||
{
|
||||
ring_iterators.push_back(it);
|
||||
}
|
||||
@ -225,47 +236,59 @@ protected:
|
||||
|
||||
// call partition to check is interior rings are disjoint from
|
||||
// each other
|
||||
item_visitor visitor;
|
||||
item_visitor_type item_visitor;
|
||||
|
||||
geometry::partition
|
||||
<
|
||||
geometry::model::box<typename point_type<Polygon>::type>,
|
||||
expand_box,
|
||||
overlaps_box
|
||||
>::apply(ring_iterators, visitor);
|
||||
>::apply(ring_iterators, item_visitor);
|
||||
|
||||
return !visitor.items_overlap;
|
||||
if (item_visitor.items_overlap)
|
||||
{
|
||||
return visitor.template apply<failure_nested_interior_rings>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename InteriorRings,
|
||||
typename ExteriorRing,
|
||||
typename TurnIterator
|
||||
typename TurnIterator,
|
||||
typename VisitPolicy
|
||||
>
|
||||
static inline bool are_holes_inside(InteriorRings const& interior_rings,
|
||||
ExteriorRing const& exterior_ring,
|
||||
TurnIterator first,
|
||||
TurnIterator beyond)
|
||||
TurnIterator beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
return are_holes_inside(boost::begin(interior_rings),
|
||||
boost::end(interior_rings),
|
||||
exterior_ring,
|
||||
first,
|
||||
beyond);
|
||||
beyond,
|
||||
visitor);
|
||||
}
|
||||
|
||||
struct has_holes_inside
|
||||
{
|
||||
template <typename TurnIterator>
|
||||
template <typename TurnIterator, typename VisitPolicy>
|
||||
static inline bool apply(Polygon const& polygon,
|
||||
TurnIterator first,
|
||||
TurnIterator beyond)
|
||||
TurnIterator beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
return are_holes_inside(geometry::interior_rings(polygon),
|
||||
geometry::exterior_ring(polygon),
|
||||
first,
|
||||
beyond);
|
||||
beyond,
|
||||
visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -274,10 +297,11 @@ protected:
|
||||
|
||||
struct has_connected_interior
|
||||
{
|
||||
template <typename TurnIterator>
|
||||
template <typename TurnIterator, typename VisitPolicy>
|
||||
static inline bool apply(Polygon const& polygon,
|
||||
TurnIterator first,
|
||||
TurnIterator beyond)
|
||||
TurnIterator beyond,
|
||||
VisitPolicy& visitor)
|
||||
{
|
||||
typedef typename std::iterator_traits
|
||||
<
|
||||
@ -300,14 +324,22 @@ protected:
|
||||
|
||||
debug_print_complement_graph(std::cout, g);
|
||||
|
||||
return !g.has_cycles();
|
||||
if (g.has_cycles())
|
||||
{
|
||||
return visitor.template apply<failure_disconnected_interior>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static inline bool apply(Polygon const& polygon)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Polygon const& polygon, VisitPolicy& visitor)
|
||||
{
|
||||
if ( !has_valid_rings::apply(polygon) )
|
||||
if (! has_valid_rings::apply(polygon, visitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -323,10 +355,11 @@ public:
|
||||
typedef has_valid_self_turns<Polygon> has_valid_turns;
|
||||
|
||||
std::deque<typename has_valid_turns::turn_type> turns;
|
||||
bool has_invalid_turns = !has_valid_turns::apply(polygon, turns);
|
||||
bool has_invalid_turns
|
||||
= ! has_valid_turns::apply(polygon, turns, visitor);
|
||||
debug_print_turns(turns.begin(), turns.end());
|
||||
|
||||
if ( has_invalid_turns )
|
||||
if (has_invalid_turns)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -334,7 +367,9 @@ public:
|
||||
// check if all interior rings are inside the exterior ring
|
||||
debug_phase::apply(4);
|
||||
|
||||
if ( !has_holes_inside::apply(polygon, turns.begin(), turns.end()) )
|
||||
if (! has_holes_inside::apply(polygon,
|
||||
turns.begin(), turns.end(),
|
||||
visitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -344,7 +379,8 @@ public:
|
||||
|
||||
return has_connected_interior::apply(polygon,
|
||||
turns.begin(),
|
||||
turns.end());
|
||||
turns.end(),
|
||||
visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -362,9 +398,11 @@ namespace dispatch
|
||||
// A Polygon is always a simple geometric object provided that it is valid.
|
||||
//
|
||||
// Reference (for validity of Polygons): OGC 06-103r4 (6.1.11.1)
|
||||
template <typename Polygon, bool AllowSpikes, bool AllowDuplicates>
|
||||
struct is_valid<Polygon, polygon_tag, AllowSpikes, AllowDuplicates>
|
||||
: detail::is_valid::is_valid_polygon<Polygon, AllowDuplicates>
|
||||
template <typename Polygon, bool AllowEmptyMultiGeometries>
|
||||
struct is_valid
|
||||
<
|
||||
Polygon, polygon_tag, AllowEmptyMultiGeometries
|
||||
> : detail::is_valid::is_valid_polygon<Polygon>
|
||||
{};
|
||||
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <boost/geometry/core/closure.hpp>
|
||||
#include <boost/geometry/core/cs.hpp>
|
||||
#include <boost/geometry/core/point_order.hpp>
|
||||
@ -19,16 +21,22 @@
|
||||
|
||||
#include <boost/geometry/algorithms/equals.hpp>
|
||||
|
||||
#include <boost/geometry/views/reversible_view.hpp>
|
||||
#include <boost/geometry/views/closeable_view.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/area.hpp>
|
||||
#include <boost/geometry/algorithms/intersects.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_spikes.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp>
|
||||
#include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp>
|
||||
|
||||
#include <boost/geometry/strategies/area.hpp>
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
#include <boost/geometry/io/dsv/write.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
@ -42,18 +50,27 @@ namespace detail { namespace is_valid
|
||||
template <typename Ring, closure_selector Closure /* open */>
|
||||
struct is_topologically_closed
|
||||
{
|
||||
static inline bool apply(Ring const&)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Ring const&, VisitPolicy& visitor)
|
||||
{
|
||||
return true;
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ring>
|
||||
struct is_topologically_closed<Ring, closed>
|
||||
{
|
||||
static inline bool apply(Ring const& ring)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Ring const& ring, VisitPolicy& visitor)
|
||||
{
|
||||
return geometry::equals(range::front(ring), range::back(ring));
|
||||
if (geometry::equals(range::front(ring), range::back(ring)))
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitor.template apply<failure_not_closed>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -92,7 +109,8 @@ struct is_properly_oriented
|
||||
|
||||
typedef typename default_area_result<Ring>::type area_result_type;
|
||||
|
||||
static inline bool apply(Ring const& ring)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Ring const& ring, VisitPolicy& visitor)
|
||||
{
|
||||
typename ring_area_predicate
|
||||
<
|
||||
@ -101,7 +119,14 @@ struct is_properly_oriented
|
||||
|
||||
// Check area
|
||||
area_result_type const zero = area_result_type();
|
||||
return predicate(ring_area_type::apply(ring, strategy_type()), zero);
|
||||
if (predicate(ring_area_type::apply(ring, strategy_type()), zero))
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitor.template apply<failure_wrong_orientation>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -110,36 +135,55 @@ struct is_properly_oriented
|
||||
template
|
||||
<
|
||||
typename Ring,
|
||||
bool AllowDuplicates,
|
||||
bool CheckSelfIntersections = true,
|
||||
bool IsInteriorRing = false
|
||||
>
|
||||
struct is_valid_ring
|
||||
{
|
||||
static inline bool apply(Ring const& ring)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Ring const& ring, VisitPolicy& visitor)
|
||||
{
|
||||
// return invalid if any of the following condition holds:
|
||||
// (a) the ring's size is below the minimal one
|
||||
// (b) the ring is not topologically closed
|
||||
// (c) the ring has spikes
|
||||
// (d) the ring has duplicate points (if AllowDuplicates is false)
|
||||
// (e) the boundary of the ring has self-intersections
|
||||
// (f) the order of the points is inconsistent with the defined order
|
||||
// (b) the ring consists of at most two distinct points
|
||||
// (c) the ring is not topologically closed
|
||||
// (d) the ring has spikes
|
||||
// (e) the ring has duplicate points (if AllowDuplicates is false)
|
||||
// (f) the boundary of the ring has self-intersections
|
||||
// (g) the order of the points is inconsistent with the defined order
|
||||
//
|
||||
// Note: no need to check if the area is zero. If this is the
|
||||
// case, then the ring must have at least two spikes, which is
|
||||
// checked by condition (c).
|
||||
|
||||
closure_selector const closure = geometry::closure<Ring>::value;
|
||||
typedef typename closeable_view<Ring const, closure>::type view_type;
|
||||
|
||||
if (boost::size(ring)
|
||||
< core_detail::closure::minimum_ring_size<closure>::value)
|
||||
{
|
||||
return visitor.template apply<failure_few_points>();
|
||||
}
|
||||
|
||||
view_type const view(ring);
|
||||
if (detail::num_distinct_consecutive_points
|
||||
<
|
||||
view_type, 4u, true,
|
||||
not_equal_to<typename point_type<Ring>::type>
|
||||
>::apply(view)
|
||||
< 4u)
|
||||
{
|
||||
return
|
||||
visitor.template apply<failure_wrong_topological_dimension>();
|
||||
}
|
||||
|
||||
return
|
||||
( boost::size(ring)
|
||||
>= core_detail::closure::minimum_ring_size<closure>::value )
|
||||
&& is_topologically_closed<Ring, closure>::apply(ring)
|
||||
&& (AllowDuplicates || !has_duplicates<Ring, closure>::apply(ring))
|
||||
&& !has_spikes<Ring, closure>::apply(ring)
|
||||
&& !(CheckSelfIntersections && geometry::intersects(ring))
|
||||
&& is_properly_oriented<Ring, IsInteriorRing>::apply(ring);
|
||||
is_topologically_closed<Ring, closure>::apply(ring, visitor)
|
||||
&& ! has_duplicates<Ring, closure>::apply(ring, visitor)
|
||||
&& ! has_spikes<Ring, closure>::apply(ring, visitor)
|
||||
&& (! CheckSelfIntersections
|
||||
|| has_valid_self_turns<Ring>::apply(ring, visitor))
|
||||
&& is_properly_oriented<Ring, IsInteriorRing>::apply(ring, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -158,9 +202,11 @@ namespace dispatch
|
||||
// 6.1.7.1, for the definition of LinearRing)
|
||||
//
|
||||
// Reference (for polygon validity): OGC 06-103r4 (6.1.11.1)
|
||||
template <typename Ring, bool AllowSpikes, bool AllowDuplicates>
|
||||
struct is_valid<Ring, ring_tag, AllowSpikes, AllowDuplicates>
|
||||
: detail::is_valid::is_valid_ring<Ring, AllowDuplicates>
|
||||
template <typename Ring, bool AllowEmptyMultiGeometries>
|
||||
struct is_valid
|
||||
<
|
||||
Ring, ring_tag, AllowEmptyMultiGeometries
|
||||
> : detail::is_valid::is_valid_ring<Ring>
|
||||
{};
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014, Oracle and/or its affiliates.
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
#include <boost/geometry/algorithms/assign.hpp>
|
||||
#include <boost/geometry/algorithms/equals.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/dispatch/is_valid.hpp>
|
||||
|
||||
@ -40,13 +41,22 @@ namespace dispatch
|
||||
template <typename Segment>
|
||||
struct is_valid<Segment, segment_tag>
|
||||
{
|
||||
static inline bool apply(Segment const& segment)
|
||||
template <typename VisitPolicy>
|
||||
static inline bool apply(Segment const& segment, VisitPolicy& visitor)
|
||||
{
|
||||
typename point_type<Segment>::type p[2];
|
||||
detail::assign_point_from_index<0>(segment, p[0]);
|
||||
detail::assign_point_from_index<1>(segment, p[1]);
|
||||
|
||||
return !geometry::equals(p[0], p[1]);
|
||||
if(! geometry::equals(p[0], p[1]))
|
||||
{
|
||||
return visitor.template apply<no_failure>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return
|
||||
visitor.template apply<failure_wrong_topological_dimension>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,10 +27,8 @@ template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename tag<Geometry>::type,
|
||||
// for linear geometries: determines if spikes are allowed
|
||||
bool AllowSpikes = true,
|
||||
// for areal geometries: determines if duplicate points are allowed
|
||||
bool AllowDuplicates = true
|
||||
// for multi-geometries: determines if empty multi-geometries are allowed
|
||||
bool AllowEmptyMultiGeometries = true
|
||||
>
|
||||
struct is_valid
|
||||
: not_implemented<Geometry>
|
||||
|
38
include/boost/geometry/algorithms/validity_failure_type.hpp
Normal file
38
include/boost/geometry/algorithms/validity_failure_type.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015, 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_VALIDITY_FAILURE_TYPE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_VALIDITY_FAILURE_TYPE_HPP
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
enum validity_failure_type
|
||||
{
|
||||
no_failure = 0,
|
||||
failure_few_points = 10,
|
||||
failure_wrong_topological_dimension = 11,
|
||||
failure_spikes = 12,
|
||||
failure_duplicate_points = 13,
|
||||
failure_not_closed = 20, // for areal geometries
|
||||
failure_self_intersections = 21, // for areal geometries
|
||||
failure_wrong_orientation = 22, // for areal geometries
|
||||
failure_interior_rings_outside = 30, // for (multi-)polygons
|
||||
failure_nested_interior_rings = 31, // for (multi-)polygons
|
||||
failure_disconnected_interior = 32, // for (multi-)polygons
|
||||
failure_intersecting_interiors = 40, // for multi-polygons
|
||||
failure_wrong_corner_order = 50 // for boxes
|
||||
};
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_VALIDITY_FAILURE_TYPE_HPP
|
@ -1,13 +1,14 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
|
||||
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
|
||||
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
|
||||
// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
|
||||
|
||||
// This file was modified by Oracle on 2014.
|
||||
// Modifications copyright (c) 2014 Oracle and/or its affiliates.
|
||||
// This file was modified by Oracle on 2014, 2015.
|
||||
// Modifications copyright (c) 2014-2015 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
|
||||
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
|
||||
@ -71,6 +72,7 @@
|
||||
#include <boost/geometry/algorithms/intersects.hpp>
|
||||
#include <boost/geometry/algorithms/is_simple.hpp>
|
||||
#include <boost/geometry/algorithms/is_valid.hpp>
|
||||
#include <boost/geometry/algorithms/is_valid_reason.hpp>
|
||||
#include <boost/geometry/algorithms/length.hpp>
|
||||
#include <boost/geometry/algorithms/make.hpp>
|
||||
#include <boost/geometry/algorithms/num_geometries.hpp>
|
||||
|
59
include/boost/geometry/policies/is_valid/default_policy.hpp
Normal file
59
include/boost/geometry/policies/is_valid/default_policy.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015, 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_POLICIES_IS_VALID_DEFAULT_POLICY_HPP
|
||||
#define BOOST_GEOMETRY_POLICIES_IS_VALID_DEFAULT_POLICY_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
template <bool AllowDuplicates = true, bool AllowSpikes = true>
|
||||
class is_valid_default_policy
|
||||
{
|
||||
protected:
|
||||
static inline bool is_valid(validity_failure_type failure)
|
||||
{
|
||||
return failure == no_failure
|
||||
|| (AllowDuplicates && failure == failure_duplicate_points);
|
||||
}
|
||||
|
||||
static inline bool is_valid(validity_failure_type failure, bool is_linear)
|
||||
{
|
||||
return is_valid(failure)
|
||||
|| (is_linear && AllowSpikes && failure == failure_spikes);
|
||||
}
|
||||
|
||||
public:
|
||||
template <validity_failure_type Failure>
|
||||
static inline bool apply()
|
||||
{
|
||||
return is_valid(Failure);
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data>
|
||||
static inline bool apply(Data const&)
|
||||
{
|
||||
return is_valid(Failure);
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data1, typename Data2>
|
||||
static inline bool apply(Data1 const& data1, Data2 const&)
|
||||
{
|
||||
return is_valid(Failure, data1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_POLICIES_IS_VALID_DEFAULT_POLICY_HPP
|
@ -0,0 +1,218 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015, 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_POLICIES_IS_VALID_FAILING_REASON_POLICY_HPP
|
||||
#define BOOST_GEOMETRY_POLICIES_IS_VALID_FAILING_REASON_POLICY_HPP
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/geometry/io/dsv/write.hpp>
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
inline char const* validity_failure_type_message(validity_failure_type failure)
|
||||
{
|
||||
switch (failure)
|
||||
{
|
||||
case no_failure:
|
||||
return "Geometry is valid";
|
||||
case failure_few_points:
|
||||
return "Geometry has too few points";
|
||||
case failure_wrong_topological_dimension:
|
||||
return "Geometry has wrong topological dimension";
|
||||
case failure_not_closed:
|
||||
return "Geometry is defined as closed but is open";
|
||||
case failure_spikes:
|
||||
return "Geometry has spikes";
|
||||
case failure_self_intersections:
|
||||
return "Geometry has invalid self-intersections";
|
||||
case failure_wrong_orientation:
|
||||
return "Geometry has wrong orientation";
|
||||
case failure_interior_rings_outside:
|
||||
return "Geometry has interior rings defined outside the outer boundary";
|
||||
case failure_nested_interior_rings:
|
||||
return "Geometry has nested interior rings";
|
||||
case failure_disconnected_interior:
|
||||
return "Geometry has disconnected interior";
|
||||
case failure_intersecting_interiors:
|
||||
return "Multi-polygon has intersecting interiors";
|
||||
case failure_duplicate_points:
|
||||
return "Geometry has duplicate (consecutive) points";
|
||||
case failure_wrong_corner_order:
|
||||
return "Box has corners in wrong order";
|
||||
default: // to avoid -Wreturn-type warning
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <bool AllowDuplicates = true, bool AllowSpikes = true>
|
||||
class failing_reason_policy
|
||||
{
|
||||
private:
|
||||
static inline
|
||||
validity_failure_type transform_failure_type(validity_failure_type failure)
|
||||
{
|
||||
if (AllowDuplicates && failure == failure_duplicate_points)
|
||||
{
|
||||
return no_failure;
|
||||
}
|
||||
return failure;
|
||||
}
|
||||
|
||||
static inline
|
||||
validity_failure_type transform_failure_type(validity_failure_type failure,
|
||||
bool is_linear)
|
||||
{
|
||||
if (is_linear && AllowSpikes && failure == failure_spikes)
|
||||
{
|
||||
return no_failure;
|
||||
}
|
||||
return transform_failure_type(failure);
|
||||
}
|
||||
|
||||
inline void set_failure_message(validity_failure_type failure)
|
||||
{
|
||||
m_oss.str("");
|
||||
m_oss.clear();
|
||||
m_oss << validity_failure_type_message(failure);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
validity_failure_type Failure,
|
||||
typename Data1,
|
||||
typename Data2 = Data1,
|
||||
typename Dummy = void
|
||||
>
|
||||
struct process_data
|
||||
{
|
||||
static inline void apply(std::ostringstream&, Data1 const&)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void apply(std::ostringstream&,
|
||||
Data1 const&,
|
||||
Data2 const&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SpikePoint>
|
||||
struct process_data<failure_spikes, bool, SpikePoint>
|
||||
{
|
||||
static inline void apply(std::ostringstream& oss,
|
||||
bool is_linear,
|
||||
SpikePoint const& spike_point)
|
||||
{
|
||||
if (is_linear && AllowSpikes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
oss << ". A spike point was found with apex at "
|
||||
<< geometry::dsv(spike_point);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Turns>
|
||||
struct process_data<failure_self_intersections, Turns>
|
||||
{
|
||||
static inline
|
||||
void apply_to_segment_identifier(std::ostringstream& oss,
|
||||
segment_identifier seg_id)
|
||||
{
|
||||
oss << "{" << seg_id.source_index
|
||||
<< ", " << seg_id.multi_index
|
||||
<< ", " << seg_id.ring_index
|
||||
<< ", " << seg_id.segment_index
|
||||
<< "}";
|
||||
}
|
||||
|
||||
static inline void apply(std::ostringstream& oss,
|
||||
Turns const& turns)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
turn_type const& turn = range::front(turns);
|
||||
oss << ". A self-intersection point was found at "
|
||||
<< geometry::dsv(turn.point);
|
||||
|
||||
oss << "; method: " << method_char(turn.method)
|
||||
<< "; operations: "
|
||||
<< operation_char(turn.operations[0].operation)
|
||||
<< "/"
|
||||
<< operation_char(turn.operations[1].operation)
|
||||
<< "; segment IDs {source, multi, ring, segment}: ";
|
||||
apply_to_segment_identifier(oss, turn.operations[0].seg_id);
|
||||
oss << "/";
|
||||
apply_to_segment_identifier(oss, turn.operations[1].seg_id);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Point>
|
||||
struct process_data<failure_duplicate_points, Point>
|
||||
{
|
||||
static inline void apply(std::ostringstream& oss,
|
||||
Point const& point)
|
||||
{
|
||||
if (AllowDuplicates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
oss << ". Duplicate points were found near point "
|
||||
<< geometry::dsv(point);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
failing_reason_policy(std::ostringstream& oss)
|
||||
: m_oss(oss)
|
||||
{}
|
||||
|
||||
template <validity_failure_type Failure>
|
||||
inline bool apply()
|
||||
{
|
||||
validity_failure_type const failure = transform_failure_type(Failure);
|
||||
set_failure_message(failure);
|
||||
return failure == no_failure;
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data>
|
||||
inline bool apply(Data const& data)
|
||||
{
|
||||
validity_failure_type const failure = transform_failure_type(Failure);
|
||||
set_failure_message(failure);
|
||||
process_data<Failure, Data>::apply(m_oss, data);
|
||||
return failure == no_failure;
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data1, typename Data2>
|
||||
inline bool apply(Data1 const& data1, Data2 const& data2)
|
||||
{
|
||||
validity_failure_type const failure
|
||||
= transform_failure_type(Failure, data1);
|
||||
set_failure_message(failure);
|
||||
process_data<Failure, Data1, Data2>::apply(m_oss, data1, data2);
|
||||
return failure == no_failure;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream& m_oss;
|
||||
};
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_POLICIES_IS_VALID_FAILING_REASON_POLICY_HPP
|
@ -0,0 +1,83 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015, 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_POLICIES_IS_VALID_FAILURE_TYPE_POLICY_HPP
|
||||
#define BOOST_GEOMETRY_POLICIES_IS_VALID_FAILURE_TYPE_POLICY_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/validity_failure_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
// policy that simply keeps (and can return) the failure type
|
||||
template <bool AllowDuplicates = true, bool AllowSpikes = true>
|
||||
class failure_type_policy
|
||||
{
|
||||
private:
|
||||
static inline
|
||||
validity_failure_type transform_failure_type(validity_failure_type failure)
|
||||
{
|
||||
if (AllowDuplicates && failure == failure_duplicate_points)
|
||||
{
|
||||
return no_failure;
|
||||
}
|
||||
return failure;
|
||||
}
|
||||
|
||||
static inline
|
||||
validity_failure_type transform_failure_type(validity_failure_type failure,
|
||||
bool is_linear)
|
||||
{
|
||||
if (is_linear && AllowSpikes && failure == failure_spikes)
|
||||
{
|
||||
return no_failure;
|
||||
}
|
||||
return transform_failure_type(failure);
|
||||
}
|
||||
|
||||
public:
|
||||
failure_type_policy()
|
||||
: m_failure(no_failure)
|
||||
{}
|
||||
|
||||
template <validity_failure_type Failure>
|
||||
inline bool apply()
|
||||
{
|
||||
m_failure = transform_failure_type(Failure);
|
||||
return m_failure == no_failure;
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data>
|
||||
inline bool apply(Data const&)
|
||||
{
|
||||
return apply<Failure>();
|
||||
}
|
||||
|
||||
template <validity_failure_type Failure, typename Data1, typename Data2>
|
||||
inline bool apply(Data1 const& data1, Data2 const&)
|
||||
{
|
||||
m_failure = transform_failure_type(Failure, data1);
|
||||
return m_failure == no_failure;
|
||||
}
|
||||
|
||||
validity_failure_type failure() const
|
||||
{
|
||||
return m_failure;
|
||||
}
|
||||
|
||||
private:
|
||||
validity_failure_type m_failure;
|
||||
};
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_POLICIES_IS_VALID_FAILURE_TYPE_POLICY_HPP
|
@ -1,11 +1,11 @@
|
||||
# Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
#
|
||||
# Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
# Copyright (c) 2008-2014 Bruno Lalande, Paris, France.
|
||||
# Copyright (c) 2009-2014 Mateusz Loskot, London, UK.
|
||||
# Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
# Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
|
||||
# Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
|
||||
#
|
||||
# This file was modified by Oracle on 2014.
|
||||
# Modifications copyright (c) 2014, Oracle and/or its affiliates.
|
||||
# This file was modified by Oracle on 2014, 2015.
|
||||
# Modifications copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
#
|
||||
# Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
# Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
@ -30,6 +30,7 @@ test-suite boost-geometry-algorithms
|
||||
[ run for_each.cpp ]
|
||||
[ run is_simple.cpp : : : <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run is_valid.cpp : : : <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run is_valid_failure.cpp : : : <toolset>msvc:<cxxflags>/bigobj ]
|
||||
[ run length.cpp ]
|
||||
[ run make.cpp ]
|
||||
[ run multi_area.cpp ]
|
||||
|
@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE( test_is_valid_multilinestring )
|
||||
}
|
||||
|
||||
template <typename Point, bool AllowDuplicates>
|
||||
void test_open_rings()
|
||||
inline void test_open_rings()
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << std::endl << std::endl;
|
||||
@ -328,20 +328,22 @@ void test_open_rings()
|
||||
test::apply("r31", "POLYGON((1 0,1 1,0 1,0 0))", true);
|
||||
|
||||
// test cases coming from buffer
|
||||
test::apply
|
||||
("r32",
|
||||
"POLYGON((1.1713032141645456 -0.9370425713316364,\
|
||||
5.1713032141645456 4.0629574286683638,\
|
||||
4.7808688094430307 4.3753049524455756,\
|
||||
4.7808688094430307 4.3753049524455756,\
|
||||
0.7808688094430304 -0.6246950475544243,\
|
||||
0.7808688094430304 -0.6246950475544243))",
|
||||
AllowDuplicates);
|
||||
test::apply("r32",
|
||||
"POLYGON((1.1713032141645456 -0.9370425713316364,\
|
||||
5.1713032141645456 4.0629574286683638,\
|
||||
4.7808688094430307 4.3753049524455756,\
|
||||
4.7808688094430307 4.3753049524455756,\
|
||||
0.7808688094430304 -0.6246950475544243,\
|
||||
0.7808688094430304 -0.6246950475544243))",
|
||||
AllowDuplicates);
|
||||
|
||||
// wrong orientation
|
||||
test::apply("r33", "POLYGON((0 0,0 1,1 1))", false);
|
||||
}
|
||||
|
||||
|
||||
template <typename Point, bool AllowDuplicates>
|
||||
void test_closed_rings()
|
||||
inline void test_closed_rings()
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << std::endl << std::endl;
|
||||
@ -374,6 +376,13 @@ void test_closed_rings()
|
||||
// boundary not closed
|
||||
test::apply("r09c", "POLYGON((0 0,1 0,1 1,1 2))", false);
|
||||
test::apply("r10c", "POLYGON((0 0,1 0,1 0,1 1,1 1,1 2))", false);
|
||||
|
||||
// with spikes
|
||||
test::apply("r11c", "POLYGON((0 0,1 0,1 0,2 0,0 0))", false);
|
||||
test::apply("r12c", "POLYGON((0 0,1 0,1 1,2 2,0.5 0.5,0 1,0 0))", false);
|
||||
|
||||
// wrong orientation
|
||||
test::apply("r13c", "POLYGON((0 0,0 1,1 1,2 0,0 0))", false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_is_valid_ring )
|
||||
@ -389,7 +398,7 @@ BOOST_AUTO_TEST_CASE( test_is_valid_ring )
|
||||
}
|
||||
|
||||
template <typename Point, bool AllowDuplicates>
|
||||
void test_open_polygons()
|
||||
inline void test_open_polygons()
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << std::endl << std::endl;
|
||||
@ -568,12 +577,21 @@ void test_open_polygons()
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,9 9,9 1),(2 2,2 8,8 8,9 6,8 2))",
|
||||
false);
|
||||
test::apply("pg058",
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,9 9,9 1),(2 2,2 8,8 8,9 6,8 2))",
|
||||
false);
|
||||
test::apply("pg058a",
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,9 9,9 1),(2 2,8 2,9 6,8 8,2 8))",
|
||||
false);
|
||||
test::apply("pg059",
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,9 9,9 1),(2 2,2 8,8 8,9 6,8 2))",
|
||||
false);
|
||||
test::apply("pg059a",
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,9 1,9 9,1 9),(2 2,2 8,8 8,9 6,8 2))",
|
||||
false);
|
||||
test::apply("pg060",
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,1 9,9 9,9 1),(2 2,2 8,8 8,9 6,8 2))",
|
||||
false);
|
||||
test::apply("pg060a",
|
||||
"POLYGON((0 0,10 0,10 10,0 10),(1 1,9 1,9 9,1 9),(2 2,8 2,9 6,8 8,2 8))",
|
||||
false);
|
||||
// hole touches exterior ring at two points
|
||||
@ -705,9 +723,6 @@ inline void test_doc_example_polygon()
|
||||
#endif
|
||||
|
||||
typedef bg::model::polygon<Point> CCW_CG;
|
||||
|
||||
CCW_CG poly;
|
||||
|
||||
typedef validity_tester_areal<true> tester;
|
||||
typedef test_valid<tester, CCW_CG> test;
|
||||
|
||||
@ -727,7 +742,7 @@ BOOST_AUTO_TEST_CASE( test_is_valid_polygon )
|
||||
}
|
||||
|
||||
template <typename Point, bool AllowDuplicates>
|
||||
void test_open_multipolygons()
|
||||
inline void test_open_multipolygons()
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
std::cout << std::endl << std::endl;
|
||||
@ -903,7 +918,7 @@ BOOST_AUTO_TEST_CASE( test_is_valid_variant )
|
||||
polygon_type valid_polygon =
|
||||
from_wkt<polygon_type>("POLYGON((0 0,1 1,1 0,0 0))");
|
||||
polygon_type invalid_polygon =
|
||||
from_wkt<polygon_type>("POLYGON((0 0,1 1,1 0))");
|
||||
from_wkt<polygon_type>("POLYGON((0 0,2 2,2 0,1 0))");
|
||||
|
||||
vg = valid_linestring;
|
||||
test::apply("v01", vg, true);
|
||||
|
979
test/algorithms/is_valid_failure.cpp
Normal file
979
test/algorithms/is_valid_failure.cpp
Normal file
File diff suppressed because one or more lines are too long
@ -220,17 +220,39 @@ template <typename ValidityTester>
|
||||
struct validity_checker
|
||||
{
|
||||
template <typename Geometry>
|
||||
static inline bool apply(Geometry const& geometry,
|
||||
static inline bool apply(std::string const& case_id,
|
||||
Geometry const& geometry,
|
||||
bool expected_result,
|
||||
std::string const& case_id)
|
||||
std::string& reason)
|
||||
{
|
||||
bool valid = ValidityTester::apply(geometry);
|
||||
std::string const reason_valid
|
||||
= bg::validity_failure_type_message(bg::no_failure);
|
||||
reason = ValidityTester::reason(geometry);
|
||||
std::string reason_short = reason.substr(0, reason_valid.length());
|
||||
|
||||
BOOST_CHECK_MESSAGE(valid == expected_result,
|
||||
"case id: " << case_id
|
||||
<< ", Expected: " << expected_result
|
||||
<< ", detected: " << valid
|
||||
<< "; wkt: " << bg::wkt(geometry));
|
||||
|
||||
BOOST_CHECK_MESSAGE(reason != "",
|
||||
"case id (empty reason): " << case_id
|
||||
<< ", Expected: " << valid
|
||||
<< ", detected reason: " << reason
|
||||
<< "; wkt: " << bg::wkt(geometry));
|
||||
|
||||
BOOST_CHECK_MESSAGE((valid && reason == reason_valid)
|
||||
||
|
||||
(! valid && reason != reason_valid)
|
||||
||
|
||||
(! valid && reason_short != reason_valid),
|
||||
"case id (reason): " << case_id
|
||||
<< ", Expected: " << valid
|
||||
<< ", detected reason: " << reason
|
||||
<< "; wkt: " << bg::wkt(geometry));
|
||||
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
@ -246,6 +268,14 @@ struct default_validity_tester
|
||||
{
|
||||
return bg::is_valid(geometry);
|
||||
}
|
||||
|
||||
template <typename Geometry>
|
||||
static inline std::string reason(Geometry const& geometry)
|
||||
{
|
||||
std::string message;
|
||||
bg::is_valid(geometry, message);
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -255,12 +285,19 @@ 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);
|
||||
bool const irrelevant = true;
|
||||
bg::is_valid_default_policy<irrelevant, AllowSpikes> visitor;
|
||||
return bg::is_valid(geometry, visitor);
|
||||
}
|
||||
|
||||
template <typename Geometry>
|
||||
static inline std::string reason(Geometry const& geometry)
|
||||
{
|
||||
bool const irrelevant = true;
|
||||
std::ostringstream oss;
|
||||
bg::failing_reason_policy<irrelevant, AllowSpikes> visitor(oss);
|
||||
bg::is_valid(geometry, visitor);
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
@ -271,16 +308,19 @@ 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);
|
||||
bg::is_valid_default_policy<AllowDuplicates> visitor;
|
||||
return bg::is_valid(geometry, visitor);
|
||||
}
|
||||
|
||||
template <typename Geometry>
|
||||
static inline std::string reason(Geometry const& geometry)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
bg::failing_reason_policy<AllowDuplicates> visitor(oss);
|
||||
bg::is_valid(geometry, visitor);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -308,10 +348,11 @@ protected:
|
||||
std::cout << "=======" << std::endl;
|
||||
#endif
|
||||
|
||||
std::string reason;
|
||||
bool valid = validity_checker
|
||||
<
|
||||
ValidityTester
|
||||
>::apply(g, expected_result, case_id);
|
||||
>::apply(case_id, g, expected_result, reason);
|
||||
boost::ignore_unused(valid);
|
||||
|
||||
#ifdef BOOST_GEOMETRY_TEST_DEBUG
|
||||
@ -322,6 +363,7 @@ protected:
|
||||
std::cout << std::boolalpha;
|
||||
std::cout << "is valid? " << valid << std::endl;
|
||||
std::cout << "expected result: " << expected_result << std::endl;
|
||||
std::cout << "reason: " << reason << std::endl;
|
||||
std::cout << "=======" << std::endl;
|
||||
std::cout << std::noboolalpha;
|
||||
#endif
|
||||
@ -331,7 +373,6 @@ public:
|
||||
static inline void apply(std::string const& case_id,
|
||||
Geometry const& geometry,
|
||||
bool expected_result)
|
||||
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << case_id << "-original";
|
||||
@ -433,6 +474,7 @@ public:
|
||||
VariantGeometry const& vg,
|
||||
bool expected_result)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
base_type::base_test(case_id, vg, expected_result);
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user