diff --git a/include/boost/geometry/algorithms/detail/is_simple/has_duplicates.hpp b/include/boost/geometry/algorithms/detail/is_simple/has_duplicates.hpp new file mode 100644 index 000000000..0e4f78a68 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_simple/has_duplicates.hpp @@ -0,0 +1,66 @@ +// 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_SIMPLE_HAS_DUPLICATES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_HAS_DUPLICATES_HPP + +#include +#include + +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + +template +struct has_duplicates +{ + static inline bool apply(Range const& range) + { + typedef typename boost::range_value::type point; + typedef typename boost::range_iterator::type iterator; + + BOOST_ASSERT( boost::size(range) > 0 ); + + if ( boost::size(range) == 1 ) + { + return false; + } + + geometry::equal_to equal; + + iterator it = boost::begin(range); + iterator next = ++boost::begin(range); + for (; next != boost::end(range); ++it, ++next) + { + if ( equal(*it, *next) ) + { + return true; + } + } + return false; + } +}; + + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_HAS_DUPLICATES_HPP diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp new file mode 100644 index 000000000..68541d965 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -0,0 +1,312 @@ +// 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_SIMPLE_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_LINEAR_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +struct is_simple_range +{ + static inline bool apply(Range const& range) + { + // the second argument to apply refers to spikes: + // spikes are disallowed + if ( !dispatch::is_valid::apply(range, false) ) + { + return false; + } + + if ( has_duplicates::apply(range) ) + { + return false; + } + + return !geometry::intersects(range); + } +}; + + + +template +struct is_simple_multi_range +{ + static inline bool apply(MultiRange const& multi_range) + { + typedef typename boost::range_value::type range; + typedef typename boost::range_iterator + < + MultiRange const + >::type iterator; + + if ( boost::size(multi_range) == 0 ) + { + return false; + } + + for (iterator it = boost::begin(multi_range); + it != boost::end(multi_range); ++it) + { + if ( !is_simple_range::apply(*it) ) + { + return false; + } + } + return true; + } +}; + + + +template +struct is_simple_multilinestring +{ + template + static inline bool check_turn(Turn const& turn, + Method method, + Operation operation) + { + return turn.method == method + && turn.operations[0].operation == operation + && turn.operations[1].operation == operation; + } + + template + static inline bool is_acceptable_turn(Turn const& turn) + { + return check_turn(turn, + detail::overlay::method_none, + detail::overlay::operation_continue) + || check_turn(turn, + detail::overlay::method_touch, + detail::overlay::operation_intersection) + || check_turn(turn, + detail::overlay::method_touch, + detail::overlay::operation_blocked) + ; + } + + template + static inline bool is_endpoint_of(Point const& point, + Linestring const& linestring) + { + BOOST_ASSERT( boost::size(linestring) > 1 ); + return geometry::equals(point, *boost::begin(linestring)) + || geometry::equals(point, *boost::rbegin(linestring)); + } + + template + static inline bool have_same_endpoints(Linestring1 const& ls1, + Linestring2 const& ls2) + { + return + (geometry::equals(*boost::begin(ls1), *boost::begin(ls2)) + && geometry::equals(*boost::rbegin(ls1), *boost::rbegin(ls2))) + || + (geometry::equals(*boost::begin(ls1), *boost::rbegin(ls2)) + && geometry::equals(*boost::rbegin(ls1), *boost::begin(ls2))) + ; + } + + + + + static inline bool apply(MultiLinestring const& multilinestring) + { + if ( !is_simple_multi_range::apply(multilinestring) ) + { + return false; + } + + typedef typename boost::range_value::type linestring; + typedef typename point_type::type point_type; + typedef point_type point; + + typedef detail::overlay::turn_info + < + point_type, + geometry::segment_ratio + < + typename geometry::coordinate_type::type + > + > turn_info; + + std::deque turns; + + typedef detail::overlay::get_turn_info + < + detail::disjoint::assign_disjoint_policy + > turn_policy; + + detail::self_get_turn_points::no_interrupt_policy + interrupt_policy; + + detail::self_get_turn_points::get_turns + < + turn_policy + >::apply(multilinestring, + detail::no_rescale_policy(), + turns, + interrupt_policy); + + std::cout << "turns:"; + for (typename std::deque::const_iterator tit = turns.begin(); + tit != turns.end(); ++tit) + { + std::cout << " [" << geometry::method_char(tit->method); + std::cout << "," << geometry::operation_char(tit->operations[0].operation); + std::cout << "/" << geometry::operation_char(tit->operations[1].operation); + std::cout << " " << geometry::dsv(tit->point); + std::cout << "] "; + } + std::cout << std::endl << std::endl; + + if ( turns.size() == 0 ) + { + return true; + } + +#ifdef GEOMETRY_TEST_DEBUG + std::vector endpoints; + typename boost::range_iterator::type it; + for (it = boost::begin(multilinestring); + it != boost::end(multilinestring); ++it) + { + BOOST_ASSERT ( boost::size(*it) != 1 ); + if ( boost::size(*it) != 0 ) + { + endpoints.push_back( *boost::begin(*it) ); + endpoints.push_back( *(--boost::end(*it)) ); + } + } + + + std::sort(endpoints.begin(), endpoints.end(), geometry::less()); + + std::cout << "endpoints: "; + for (typename std::vector::iterator pit = endpoints.begin(); + pit != endpoints.end(); ++pit) + { + std::cout << " " << geometry::dsv(*pit); + } + std::cout << std::endl << std::endl; +#endif + + for (typename std::deque::const_iterator tit = turns.begin(); + tit != turns.end(); ++tit) + { +#if 0 + if ( !is_acceptable_turn(*tit) ) + { + return false; + } +#endif + linestring const& ls1 = + *(boost::begin(multilinestring) + + tit->operations[0].seg_id.multi_index); + linestring const& ls2 = + *(boost::begin(multilinestring) + + tit->operations[0].other_id.multi_index); + + if ( !is_endpoint_of(tit->point, ls1) + || !is_endpoint_of(tit->point, ls2) ) + { + return false; + } + +#if 1 + if ( boost::size(ls1) == 2 + && boost::size(ls2) == 2 + && have_same_endpoints(ls1, ls2) ) + { + return false; + } +#endif + } + return true; + } + +}; + + + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A linestring is a curve. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// +// Reference: OGC 06-103r4 (§6.1.6.1) +template +struct is_simple + : detail::is_simple::is_simple_range +{}; + + +// A MultiLinestring is a MultiCurve +// A MultiCurve is simple if all of its elements are simple and the +// only intersections between any two elements occur at Points that +// are on the boundaries of both elements. +// +// Reference: OGC 06-103r4 (§6.1.8.1; Fig. 9) +template +struct is_simple + : detail::is_simple::is_simple_multilinestring +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_LINEAR_HPP diff --git a/include/boost/geometry/algorithms/detail/is_simple/pointlike.hpp b/include/boost/geometry/algorithms/detail/is_simple/pointlike.hpp new file mode 100644 index 000000000..fa5cf8c2b --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_simple/pointlike.hpp @@ -0,0 +1,102 @@ +// 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_SIMPLE_POINTLIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_POINTLIKE_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +struct is_simple_multipoint +{ + static inline bool apply(MultiPoint const& multipoint) + { + typedef typename point_type::type Point; + typedef typename boost::range_iterator + < + MultiPoint const + >::type iterator; + + + if ( !dispatch::is_valid::apply(multipoint) ) + { + return false; + } + + MultiPoint mp(multipoint); + std::sort(boost::begin(mp), boost::end(mp), geometry::less()); + + return !has_duplicates::apply(mp); + } +}; + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A point is always simple +template +struct is_simple +{ + static inline bool apply(Point const&) + { + return true; + } +}; + + + +// A MultiPoint is simple if no two Points in the MultiPoint are equal +// (have identical coordinate values in X and Y) +// +// Reference: OGC 06-103r4 (§6.1.5) +template +struct is_simple + : detail::is_simple::is_simple_multipoint +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_POINTLIKE_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/box.hpp b/include/boost/geometry/algorithms/detail/is_valid/box.hpp new file mode 100644 index 000000000..488c8c6ca --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/box.hpp @@ -0,0 +1,86 @@ +// 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_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_BOX_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + +template +struct has_valid_corners +{ + static inline bool apply(Box const& box) + { + if ( geometry::get(box) + <= + geometry::get(box) ) + { + return false; + } + return has_valid_corners::apply(box); + } +}; + + +template +struct has_valid_corners +{ + static inline bool apply(Box const& box) + { + return true; + } +}; + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A box is always simple +// A box is a Polygon, and it satisfies the conditions for Polygon validity. +// +// The only thing we have to check is whether the max corner lies in +// the upper-right quadrant as defined by the min corner +// +// Reference (for polygon validity): OGC 06-103r4 (§6.1.11.1) +template +struct is_valid + : detail::is_valid::has_valid_corners::value> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_BOX_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp new file mode 100644 index 000000000..57cfe666e --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp @@ -0,0 +1,140 @@ +// 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_SPIKES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + +template +struct equal_to +{ + Point const& m_point; + + equal_to(Point const& point) + : m_point(point) + {} + + template + inline bool operator()(OtherPoint const& other) const + { + return geometry::equals(m_point, other); + } +}; + +template +struct not_equal_to +{ + Point const& m_point; + + not_equal_to(Point const& point) + : m_point(point) + {} + + template + inline bool operator()(OtherPoint const& other) const + { + return !geometry::equals(other, m_point); + } +}; + + + +template +struct has_spikes +{ + static inline bool apply(Range const& range) + { + typedef typename point_type::type point; + typedef typename boost::range_iterator::type iterator; + + typedef not_equal_to not_equal; + + BOOST_ASSERT( boost::size(range) > 2 ); + + iterator prev = boost::begin(range); + + iterator cur = std::find_if(prev, boost::end(range), not_equal(*prev)); + BOOST_ASSERT( cur != boost::end(range) ); + + iterator next = std::find_if(cur, boost::end(range), not_equal(*cur)); + BOOST_ASSERT( next != boost::end(range) ); + + while ( next != boost::end(range) ) + { + if ( geometry::detail::point_is_spike_or_equal(*prev, + *next, + *cur) ) + { + return true; + } + prev = cur; + cur = next; + next = std::find_if(cur, boost::end(range), not_equal(*cur)); + } + + if ( geometry::equals(*boost::begin(range), *boost::rbegin(range)) ) + { + iterator cur = boost::begin(range); + typename boost::range_reverse_iterator + < + Range const + >::type prev = std::find_if(boost::rbegin(range), + boost::rend(range), + not_equal(*boost::rbegin(range))); + iterator next = std::find_if(cur, boost::end(range), not_equal(*cur)); + return detail::point_is_spike_or_equal(*prev, *next, *cur); + } + + return false; + } +}; + + +template +struct has_spikes +{ + static inline bool apply(Range const& range) + { + typedef typename closeable_view::type closed_view_type; + + closed_view_type closed_range(const_cast(range)); + return has_spikes::apply(closed_range); + } + +}; + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/linestring.hpp b/include/boost/geometry/algorithms/detail/is_valid/linestring.hpp new file mode 100644 index 000000000..e458aefb2 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/linestring.hpp @@ -0,0 +1,159 @@ +// 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_LINESTRING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_LINESTRING_HPP + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +template +struct is_single_point +{ + static inline bool apply(Range const& range) + { + typedef typename point_type::type point; + + BOOST_ASSERT( boost::size(range) > 1 ); + + return std::find_if(++boost::begin(range), + boost::end(range), + not_equal_to(*boost::begin(range)) + ) + == boost::end(range); + } +}; + +template +struct has_three_distinct_points +{ + static inline bool apply(Range const& range) + { + typedef typename point_type::type point; + typedef typename boost::range_iterator::type iterator; + + BOOST_ASSERT( boost::size(range) > 1 ); + + iterator it1 = + std::find_if(boost::begin(range), + boost::end(range), + not_equal_to(*boost::begin(range))); + + BOOST_ASSERT( it1 != boost::end(range) ); + + iterator it2 = + std::find_if(it1, + boost::end(range), + not_equal_to(*it1)); + + return it2 != boost::end(range) + && !geometry::equals(*boost::begin(range), *it2); + } +}; + + +template +struct is_valid_linestring +{ + static inline bool apply(Linestring const& linestring, + bool allow_spikes) + { + std::size_t linestring_size = boost::size(linestring); + + if ( linestring_size < 2 ) + { + // if it has zero or one points is cannot be valid + return false; + } + + if ( is_single_point::apply(linestring) ) + { + // if it is an one-point linestring its dimension is 0, + // so not valid + return false; + } + + BOOST_ASSERT( boost::size(linestring) > 1 ); + + if ( !has_three_distinct_points::apply(linestring) ) + { + return !geometry::equals(*boost::begin(linestring), + *--boost::end(linestring)); + } + + return allow_spikes || !has_spikes::apply(linestring); + } +}; + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A linestring is a curve. +// A curve is 1-dimensional so it has to have at least two distinct +// points. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// +// There is an open issue as to whether spikes are allowed for +// linestrings; here we pass this as an additional parameter: allow_spikes +// If allow_spikes is set to true, spikes are allowed, false otherwise. +// By default, spikes are disallowed +// +// Reference: OGC 06-103r4 (§6.1.6.1) +template +struct is_valid +{ + static inline bool apply(Linestring const& linestring, + bool allow_spikes = false) + { + return detail::is_valid::is_valid_linestring + < + Linestring + >::apply(linestring, allow_spikes); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_LINESTRING_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/multi_range.hpp b/include/boost/geometry/algorithms/detail/is_valid/multi_range.hpp new file mode 100644 index 000000000..c8bad652f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/multi_range.hpp @@ -0,0 +1,82 @@ +// 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_MULTI_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTI_RANGE_HPP + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ +template +struct is_valid_multi_range +{ + template + static inline bool apply(RangeIterator first, RangeIterator beyond) + { + for (RangeIterator it = first; it != beyond; ++it) + { + if ( !dispatch::is_valid::apply(*it) ) + { + return false; + } + } + return first != beyond; + }; +}; + + +}} //namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A MultiLinestring is a MultiCurve +// A MultiCurve is simple if all of its elements are simple and the +// only intersections between any two elements occur at Points that +// are on the boundaries of both elements. +// +// Reference: OGC 06-103r4 (§6.1.8.1; Fig. 9) +template +struct is_valid +{ + static inline bool apply(MultiLinestring const& multilinestring) + { + return detail::is_valid::is_valid_multi_range + < + typename boost::range_value::type + >::apply(boost::begin(multilinestring), + boost::end(multilinestring)); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTI_RANGE_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp b/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp new file mode 100644 index 000000000..6cf0908dc --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp @@ -0,0 +1,63 @@ +// 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_POINTLIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POINTLIKE_HPP + +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A point is always simple +template +struct is_valid +{ + static inline bool apply(Point const&) + { + return true; + } +}; + + + +// A MultiPoint is simple if no two Points in the MultiPoint are equal +// (have identical coordinate values in X and Y) +// +// Reference: OGC 06-103r4 (§6.1.5) +template +struct is_valid +{ + static inline bool apply(MultiPoint const& multipoint) + { + return boost::size(multipoint) > 0; + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POINTLIKE_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp new file mode 100644 index 000000000..6037d1609 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -0,0 +1,36 @@ +// 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_POLYGON_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + +}} // namespace dispatch +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp new file mode 100644 index 000000000..fe69afb48 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -0,0 +1,172 @@ +// 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_RING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP + +#include +#include + +#include +#include + +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +// struct to check whether a ring is topologically closed +template +struct is_topologically_closed +{}; + +template +struct is_topologically_closed +{ + static inline bool apply(Ring const&) + { + return true; + } +}; + +template +struct is_topologically_closed +{ + static inline bool apply(Ring const& ring) + { + return geometry::equals(*boost::begin(ring), *boost::rbegin(ring)); + } +}; + + +// struct to check if the size is above the minimal one +// (3 for open, 4 for closed) +template +struct is_below_minimal_size +{}; + +template +struct is_below_minimal_size +{ + static inline bool apply(Ring const& ring) + { + return boost::size(ring) < 4; + } +}; + +template +struct is_below_minimal_size +{ + static inline bool apply(Ring const& ring) + { + return boost::size(ring) < 3; + } +}; + + + + + + +template +< + typename Ring, + order_selector PointOrder, + closure_selector Closure +> +struct is_valid_ring + : not_implemented +{}; + + +template +struct is_valid_ring +{ + static inline bool apply(Ring const& ring) + { + typedef typename reversible_view + < + Ring, iterate_reverse + >::type reversible_view_type; + + reversible_view_type reversed_ring(const_cast(ring)); + return is_valid_ring::apply(reversed_ring); + } +}; + +template +struct is_valid_ring +{ + static inline bool apply(Ring const& ring) + { + // return invalid if any of the following condition holds: + // (a) the ring's size is below the minimal one + // (b) the ring has less than three distinct points + // (c) the ring is not topologically closed + // (d) the ring has spikes + // + // 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 (d). + if ( is_below_minimal_size::apply(ring) + || !has_three_distinct_points::apply(ring) + || !is_topologically_closed::apply(ring) + || has_spikes::apply(ring) ) + { + return false; + } + + // now call self turns to compute self intersections, if any, + // and analyze them + } +}; + + +}} // namespace dispatch +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A Ring is a Polygon. +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for polygon validity): OGC 06-103r4 (§6.1.11.1) +template +struct is_valid + : detail::is_valid::is_valid_ring + < + Ring, + point_order::value, + closure::value + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP diff --git a/include/boost/geometry/algorithms/detail/is_valid/segment.hpp b/include/boost/geometry/algorithms/detail/is_valid/segment.hpp new file mode 100644 index 000000000..ccbe49d4f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/is_valid/segment.hpp @@ -0,0 +1,61 @@ +// 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_SEGMENT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_SEGMENT_HPP + +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A segment is a curve. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// A curve is 1-dimensional, hence we have to check is the two +// endpoints of the segment coincide, since in this case it is +// 0-dimensional. +// +// Reference: OGC 06-103r4 (§6.1.6.1) +template +struct is_valid +{ + static inline bool apply(Segment const& segment) + { + typename point_type::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]); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_SEGMENT_HPP diff --git a/include/boost/geometry/algorithms/dispatch/is_simple.hpp b/include/boost/geometry/algorithms/dispatch/is_simple.hpp new file mode 100644 index 000000000..2ac92256b --- /dev/null +++ b/include/boost/geometry/algorithms/dispatch/is_simple.hpp @@ -0,0 +1,38 @@ +// 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_DISPATCH_IS_SIMPLE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DISPATCH_IS_SIMPLE_HPP + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template ::type> +struct is_simple + : not_implemented +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DISPATCH_IS_SIMPLE_HPP diff --git a/include/boost/geometry/algorithms/dispatch/is_valid.hpp b/include/boost/geometry/algorithms/dispatch/is_valid.hpp new file mode 100644 index 000000000..1d9834a4a --- /dev/null +++ b/include/boost/geometry/algorithms/dispatch/is_valid.hpp @@ -0,0 +1,38 @@ +// 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_DISPATCH_IS_VALID_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DISPATCH_IS_VALID_HPP + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template ::type> +struct is_valid + : not_implemented +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DISPATCH_IS_VALID_HPP diff --git a/include/boost/geometry/algorithms/is_simple.hpp b/include/boost/geometry/algorithms/is_simple.hpp new file mode 100644 index 000000000..fbf355c96 --- /dev/null +++ b/include/boost/geometry/algorithms/is_simple.hpp @@ -0,0 +1,237 @@ +// 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_IS_SIMPLE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_IS_SIMPLE_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +#include + +#include +#include + + + +// check includes below +#include +#include +#include +#include +#include +#include +#include +#include + + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +< + typename Geometry, + bool AllowEmpty = true, + typename Tag = typename tag::type +> +struct is_below_minimal_size +{ + static inline bool apply(Geometry const&) + { + return false; + } +}; + + +template +struct is_below_minimal_size +{ + static inline bool apply(Linestring const& linestring) + { + return boost::size(linestring) == 1; + } +}; + + + + + +template +struct is_simple_polygon +{ + static inline bool apply(Polygon const& polygon) + { + typedef typename ring_type::type Ring; + + BOOST_AUTO_TPL(it, boost::begin(geometry::interior_rings(polygon))); + for (; it != boost::end(geometry::interior_rings(polygon)); ++it) + { + if ( !is_simple_range::apply(*it) ) + { + return false; + } + } + + return is_simple_range::apply(geometry::exterior_ring(polygon)); + } +}; + + + + + +template +struct is_simple_multigeometry +{ + static inline bool apply(MultiGeometry const& multigeometry) + { + typedef typename boost::range_value::type Geometry; + + BOOST_AUTO_TPL(it, boost::begin(multigeometry)); + for (; it != boost::end(multigeometry); ++it) + { + if ( !dispatch::is_simple::apply(*it) ) + { + return false; + } + } + + + if ( CheckIntersections ) + { + BOOST_AUTO_TPL(it1, boost::begin(multigeometry)); + it = it1; + for (; it != boost::end(multigeometry); ++it) + { + for (; it1 != boost::end(multigeometry); ++it1) + { + if ( geometry::intersects(*it, *it1) ) + { + return false; + } + } + } + } + return true; + } +}; + + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + + +// A segment is always simple. +// A segment is a curve. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// +// Reference: OGC 06-103r4 (§6.1.6.1) +template +struct is_simple +{ + static inline bool apply(Segment const& segment) + { + return dispatch::is_valid::apply(segment); + } +}; + + +// A box is always simple +// A box is a Polygon, and it satisfies the conditions for Polygon validity. +// +// Reference (for polygon validity): OGC 06-103r4 (§6.1.11.1) +template +struct is_simple +{ + static inline bool apply(Box const& box) + { + return dispatch::is_valid::apply(box); + } +}; + + + +// A Ring is a Polygon. +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for polygon validity): OGC 06-103r4 (§6.1.11.1) +template +struct is_simple + : detail::is_simple::is_simple_range +{}; + + +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for validity of Polygons): OGC 06-103r4 (§6.1.11.1) +template +struct is_simple + : detail::is_simple::is_simple_polygon +{}; + + + +// Not clear what the definition is. +// Right now we check that each element is simple (in fact valid), and +// that the MultiPolygon is also valid. +// +// Reference (for validity of MultiPolygons): OGC 06-103r4 (§6.1.14) +template +struct is_simple + : detail::is_simple::is_simple_multigeometry +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +template +inline bool is_simple(Geometry const& g) +{ + return dispatch::is_simple::apply(g); +} + + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_IS_SIMPLE_HPP diff --git a/include/boost/geometry/algorithms/is_valid.hpp b/include/boost/geometry/algorithms/is_valid.hpp new file mode 100644 index 000000000..3f0c457ec --- /dev/null +++ b/include/boost/geometry/algorithms/is_valid.hpp @@ -0,0 +1,147 @@ +// 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_IS_VALID_HPP +#define BOOST_GEOMETRY_ALGORITHMS_IS_VALID_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + + + +template +struct is_valid_polygon +{ + static inline bool apply(Polygon const& polygon) + { + typedef typename ring_type::type ring_type; + + if ( !dispatch::is_valid::apply(exterior_ring(polygon)) ) + { + return false; + } + + if ( !is_valid_multi_range::apply + (boost::begin(geometry::interior_rings(polygon)), + boost::end(geometry::interior_rings(polygon))) ) + { + return false; + } + + // MK::need check for self-intersection points; not done yet + return true; + } +}; + + + +template +struct is_valid_multipolygon +{ + static inline bool apply(MultiPolygon const& multipolygon) + { + typedef typename boost::range_value::type polygon_type; + + if ( !is_valid_multi_range + < + polygon_type + >::apply(boost::begin(multipolygon), + boost::end(multipolygon)) ) + { + return false; + } + + // MK::need to check that they are (almost) disjoint + return true; + } + +}; + + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for validity of Polygons): OGC 06-103r4 (§6.1.11.1) +template +struct is_valid + : detail::is_valid::is_valid_polygon +{}; + + + +// Not clear what the definition is. +// Right now we check that each element is simple (in fact valid), and +// that the MultiPolygon is also valid. +// +// Reference (for validity of MultiPolygons): OGC 06-103r4 (§6.1.14) +template +struct is_valid + : detail::is_valid::is_valid_multipolygon +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +template +inline bool is_valid(Geometry const& g) +{ + return dispatch::is_valid::apply(g); +} + + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_IS_VALID_HPP diff --git a/test/algorithms/is_simple.cpp b/test/algorithms/is_simple.cpp new file mode 100644 index 000000000..aa946d42e --- /dev/null +++ b/test/algorithms/is_simple.cpp @@ -0,0 +1,314 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +#include + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_simple +#endif + +#include + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace bg = ::boost::geometry; + +typedef bg::model::point point_type; +typedef bg::model::segment segment_type; +typedef bg::model::linestring linestring_type; +typedef bg::model::multi_linestring multi_linestring_type; +// ccw open and closed polygons +typedef bg::model::polygon open_polygon_type; +typedef bg::model::polygon closed_polygon_type; + +// multi-geometries +typedef bg::model::multi_point multi_point_type; +typedef bg::model::multi_polygon multi_polygon_type; + + +template +Geometry from_wkt(std::string const& wkt) +{ + Geometry g; + bg::read_wkt(wkt, g); + return g; +} + + + +template +Segment make_segment(double x1, double y1, double x2, double y2) +{ + typename boost::geometry::point_type::type p(x1, y1), q(x2, y2); + return Segment(p, q); +} + + +template +void test_simple(Geometry const& g, bool simple_geometry) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "=======" << std::endl; +#endif + + bool simple = bg::is_simple(g); + BOOST_CHECK(simple == simple_geometry); + +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "Geometry: " << bg::wkt(g) << std::endl; + std::cout << std::boolalpha; + std::cout << "is valid : " << bg::is_valid(g) << std::endl; + std::cout << "is simple: " << simple << std::endl; + std::cout << "expected result: " << simple_geometry << std::endl; + std::cout << "=======" << std::endl; + std::cout << std::endl << std::endl; + std::cout << std::noboolalpha; +#endif +} + +//=========================================================================== +//=========================================================================== +//=========================================================================== + +BOOST_AUTO_TEST_CASE( test_is_simple_point ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_simple: POINT " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef point_type G; + + test_simple(from_wkt("POINT(0 0)"), true); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_segment ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_simple: SEGMENT " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef segment_type G; + + test_simple(make_segment(0, 0, 0, 0), false); + test_simple(make_segment(0, 0, 1, 0), true); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_linestring ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_simple: LINESTRING " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef linestring_type G; + + // invalid linestrings + test_simple(from_wkt("LINESTRING()"), false); + test_simple(from_wkt("LINESTRING(0 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,0 0)"), false); + + // valid linestrings with multiple points + test_simple(from_wkt("LINESTRING(0 0,0 0,1 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,0 0,1 0,0 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,0 0,1 0,1 0,1 1,0 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 0,1 1,1 0,1 -1)"), false); + + // simple open linestrings + test_simple(from_wkt("LINESTRING(0 0,1 2)"), true); + test_simple(from_wkt("LINESTRING(0 0,1 2,2 3)"), true); + + // simple closed linestrings + test_simple(from_wkt("LINESTRING(0 0,1 0,1 1,0 0)"), true); + test_simple(from_wkt("LINESTRING(0 0,1 0,1 1,0 1,0 0)"), true); + + // non-simple linestrings + test_simple(from_wkt("LINESTRING(0 0,1 0,0 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 10,0.5 -1)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 1,1 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 1,0.5 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,2 0,1 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,3 0,5 0,1 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,3 0,5 0,4 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,3 0,5 0,4 0,2 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,3 0,2 0,5 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,2 0,2 2,1 0,0 0)"), false); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_multipoint ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_simple: MULTIPOINT " << std::endl; + std::cout << "************************************" << std::endl; +#endif + typedef multi_point_type G; + + test_simple(from_wkt("MULTIPOINT()"), false); + test_simple(from_wkt("MULTIPOINT(0 0)"), true); + test_simple(from_wkt("MULTIPOINT(0 0,1 0,1 1,0 1)"), true); + test_simple(from_wkt("MULTIPOINT(0 0,1 0,1 1,1 0,0 1)"), false); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_multilinestring ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_simple: MULTILINESTRING " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef multi_linestring_type G; + + // empty multilinestring + test_simple(from_wkt("MULTILINESTRING()"), false); + + // multilinestrings with empty linestrings + test_simple(from_wkt("MULTILINESTRING(())"), false); + test_simple(from_wkt("MULTILINESTRING((),(),())"), false); + test_simple(from_wkt("MULTILINESTRING((),(0 1,1 0))"), false); + + // multilinestrings with 1-point linestrings + test_simple(from_wkt("MULTILINESTRING((0 0),(0 1,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,0 0),(0 1,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0),(1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,0 0),(1 0,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0),(0 0))"), false); + + // multilinestrings with linestrings with spikes + test_simple(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(5 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(5 0,1 0,4 1))"), + false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(5 0,1 0,4 0))"), + false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(1 0,2 0))"), + false); + + + // simple multilinestrings + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(1 1,1 0))"), true); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(1 1,1 0),(0 1,1 1))"), + true); + test_simple(from_wkt("MULTILINESTRING((0 0,2 2),(0 0,1 0,2 0,2 2))"), true); + test_simple(from_wkt("MULTILINESTRING((0 0,2 2),(2 2,2 0,1 0,0 0))"), true); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0),(0 0,-1 0),\ + (1 0,2 0))"), + true); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0),(-1 0,0 0),\ + (2 0,1 0))"), + true); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0,1 1,0 1,0 0),(-1 0,0 0))"), + true); + + // non-simple multilinestrings + test_simple(from_wkt("MULTILINESTRING((0 0,2 2),(0 0,2 2))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,2 2),(2 2,0 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,2 2),\ + (0 0,1 0,1 1,2 0,2 2))"), + false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1,2 2),\ + (0 0,1 0,1 1,2 0,2 2))"), + false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1,2 2),(2 2,0 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(0 1,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,2 0),(1 0,0 1))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(1 1,1 0),\ + (1 1,0 1,0.5,0.5))"), + false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 0,1 1,0 1,0 0),(1 0,1 -1))"), + false); +} + + +#if 0 +BOOST_AUTO_TEST_CASE( test_is_simple_rest ) +{ + typedef open_polygon_type op; + typedef closed_polygon_type cp; + typedef multi_polygon_type mpl; + + test_simple(from_wkt("POLYGON(())"), true); + test_simple(from_wkt("POLYGON((),())"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,1 1),())"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,1 0,1 1),())"), true); + test_simple(from_wkt("POLYGON((0 0,1 0))"), false); + test_simple(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 1))"), false); + test_simple(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 0))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,1 1,1 0.5))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,1 1))"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,2 1,2 2,1 3))"), true); + test_simple(from_wkt("POLYGON((0 0,2 0,4 1,1 0))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,3 1,-1 2,3 3,3 4,0 4))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,3 1,0 2,3 3,3 4,0 4))"), false); + test_simple(from_wkt("POLYGON((0 0,10 0,10 10,0 10),\ + (1 1,1 2,2 2,2 1))"), false); + + test_simple(from_wkt("POLYGON(())"), true); + test_simple(from_wkt("POLYGON((),())"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,1 1,0 0),())"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,1 0,1 1,0 0),())"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,0 0))"), false); + test_simple(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 1,0 0))"), false); + test_simple(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 0,0 0))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,1 1,1 0.5,0 0))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,1 1,0 0))"), true); + test_simple(from_wkt("POLYGON((0 0,1 0,2 1,2 2,1 3,0 0))"), true); + test_simple(from_wkt("POLYGON((0 0,2 0,4 1,1 0,0 0))"), false); + test_simple(from_wkt("POLYGON((0 0,1 0,3 1,-1 2,3 3,3 4,0 4,0 0))"), + false); + test_simple(from_wkt("POLYGON((0 0,1 0,3 1,0 2,3 3,3 4,0 4,0 0))"), + false); + test_simple(from_wkt("POLYGON((0 0,10 0,10 10,0 10,0 0),\ + (1 1,1 2,2 2,2 1,1 1))"), false); + + + test_simple(from_wkt("MULTIPOLYGON()"), true); + test_simple(from_wkt("MULTIPOLYGON( ((),()) )"), true); + test_simple(from_wkt("MULTIPOLYGON( (()),(()) )"), true); + test_simple(from_wkt("MULTIPOLYGON( ((),()),(()) )"), true); + test_simple(from_wkt("MULTIPOLYGON( ((0 0),()),(()) )"), true); + test_simple(from_wkt("MULTIPOLYGON( ((0 0),()),((1 1)) )"), true); +#ifdef GEOMETRY_TEST_INCLUDE_FAILING_TESTS + // test_simple(from_wkt("MULTIPOLYGON( ((0 0),()),((0 0)) )"), false); +#endif + + test_simple(from_wkt("MULTIPOLYGON(((0 0,1 0,2 1,2 2,1 3)),\ + ((10 0,11 0,11 1)))"), true); + + test_simple(from_wkt("MULTIPOLYGON(((0 0,1 0,1 0,2 1,2 2,1 3)),\ + ((10 0,11 0,11 1,11 1)))"), true); + + test_simple(from_wkt("MULTIPOLYGON(((0 0,1 0,3 1,0 2,3 3,3 4,0 4)),\ + ((10 0,11 0,11 1)))"), false); +} +#endif diff --git a/test/algorithms/is_valid.cpp b/test/algorithms/is_valid.cpp new file mode 100644 index 000000000..6328ebb45 --- /dev/null +++ b/test/algorithms/is_valid.cpp @@ -0,0 +1,325 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +#include + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_valid +#endif + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace bg = ::boost::geometry; + +typedef bg::model::point point_type; +typedef bg::model::segment segment_type; +typedef bg::model::box box_type; +typedef bg::model::linestring linestring_type; +typedef bg::model::multi_linestring multi_linestring_type; +// ccw open and closed polygons +typedef bg::model::polygon open_polygon_type; +typedef bg::model::polygon closed_polygon_type; + +// multi-geometries +typedef bg::model::multi_point multi_point_type; +typedef bg::model::multi_polygon multi_polygon_type; + + +template +Geometry from_wkt(std::string const& wkt) +{ + Geometry g; + bg::read_wkt(wkt, g); + return g; +} + + + +template +Segment make_segment(double x1, double y1, double x2, double y2) +{ + typename boost::geometry::point_type::type p(x1, y1), q(x2, y2); + return Segment(p, q); +} + + +template +Box make_box(double x1, double y1, double x2, double y2) +{ + typename boost::geometry::point_type::type p(x1, y1), q(x2, y2); + return Box(p, q); +} + + +template +void test_valid(Geometry const& g, bool expected_result) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "=======" << std::endl; +#endif + + bool valid = bg::is_valid(g); + BOOST_CHECK( valid == expected_result ); + +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "Geometry: " << 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::endl << std::endl; + std::cout << std::noboolalpha; +#endif +} + +//=========================================================================== +//=========================================================================== +//=========================================================================== + +BOOST_AUTO_TEST_CASE( test_is_valid_point ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_valid: POINT " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef point_type G; + + test_valid(from_wkt("POINT(0 0)"), true); +} + +BOOST_AUTO_TEST_CASE( test_is_valid_segment ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_valid: SEGMENT " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef segment_type G; + + test_valid(make_segment(0, 0, 0, 0), false); + test_valid(make_segment(0, 0, 1, 0), true); +} + +BOOST_AUTO_TEST_CASE( test_is_valid_box ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_valid: BOX " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef box_type G; + + // boxes where the max corner and below and/or to the left of min corner + test_valid(make_box(0, 0, -1, 0), false); + test_valid(make_box(0, 0, 0, -1), false); + test_valid(make_box(0, 0, -1, -1), false); + + // boxes of zero area; they are not 2-dimensional, so invalid + test_valid(make_box(0, 0, 0, 0), false); + test_valid(make_box(0, 0, 1, 0), false); + test_valid(make_box(0, 0, 0, 1), false); + + test_valid(make_box(0, 0, 1, 1), true); +} + +BOOST_AUTO_TEST_CASE( test_is_valid_linestring ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_valid: LINESTRING " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef linestring_type G; + + // empty linestring + test_valid(from_wkt("LINESTRING()"), false); + + // 1-point linestrings + test_valid(from_wkt("LINESTRING(0 0)"), false); + test_valid(from_wkt("LINESTRING(0 0,0 0)"), false); + test_valid(from_wkt("LINESTRING(0 0,0 0,0 0)"), false); + + // 2-point linestrings + test_valid(from_wkt("LINESTRING(0 0,1 2)"), true); + test_valid(from_wkt("LINESTRING(0 0,1 2,1 2)"), true); + test_valid(from_wkt("LINESTRING(0 0,0 0,1 2,1 2)"), true); + test_valid(from_wkt("LINESTRING(0 0,0 0,0 0,1 2,1 2)"), true); + + // 3-point linestrings + test_valid(from_wkt("LINESTRING(0 0,1 0,2 10)"), true); + test_valid(from_wkt("LINESTRING(0 0,1 0,2 10,0 0)"), true); + test_valid(from_wkt("LINESTRING(0 0,10 0,10 10,5 0)"), true); + test_valid(from_wkt("LINESTRING(0 0,10 0,10 10,5 0,4 0)"), true); + test_valid(from_wkt("LINESTRING(0 0,10 0,10 10,5 0,4 0,3 0)"), true); + test_valid(from_wkt("LINESTRING(0 0,10 0,10 10,5 0,4 0,-1 0)"), true); + test_valid(from_wkt("LINESTRING(0 0,1 0,1 1,-1 1,-1 0,0 0)"), true); + + // should this be valid? we have two overlapping segments + test_valid(from_wkt("LINESTRING(0 0,1 0,1 1,-1 1,-1 0,0.5 0)"), true); + + // linestrings with spikes + static const bool accept_spikes = false; + test_valid(from_wkt("LINESTRING(0 0,1 2,0 0)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,1 2,1 2,0 0)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,0 0,1 2,1 2,0 0)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,0 0,0 0,1 2,1 2,0 0,0 0)"), + accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,10 0,5 0)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,10 0,10 10,5 0,0 0)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,10 0,10 10,5 0,4 0,6 0)"), + accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,1 0,1 1,5 5,4 4)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,1 0,1 1,5 5,4 4,6 6)"), accept_spikes); + test_valid(from_wkt("LINESTRING(0 0,1 0,1 1,5 5,4 4,4 0)"), accept_spikes); +} + +BOOST_AUTO_TEST_CASE( test_is_valid_multipoint ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_valid: MULTIPOINT " << std::endl; + std::cout << "************************************" << std::endl; +#endif + typedef multi_point_type G; + + test_valid(from_wkt("MULTIPOINT()"), false); + test_valid(from_wkt("MULTIPOINT(0 0,0 0)"), true); + test_valid(from_wkt("MULTIPOINT(0 0,1 0,1 1,0 1)"), true); + test_valid(from_wkt("MULTIPOINT(0 0,1 0,1 1,1 0,0 1)"), true); +} + +BOOST_AUTO_TEST_CASE( test_is_valid_multilinestring ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl; + std::cout << "************************************" << std::endl; + std::cout << " is_valid: MULTILINESTRING " << std::endl; + std::cout << "************************************" << std::endl; +#endif + + typedef multi_linestring_type G; + + // empty multilinestring + test_valid(from_wkt("MULTILINESTRING()"), false); + + // multilinestring with empty linestring(s) + test_valid(from_wkt("MULTILINESTRING(())"), false); + test_valid(from_wkt("MULTILINESTRING((),(),())"), false); + test_valid(from_wkt("MULTILINESTRING((),(0 1,1 0))"), false); + + // multilinestring with invalid linestrings + test_valid(from_wkt("MULTILINESTRING((0 0),(0 1,1 0))"), false); + test_valid(from_wkt("MULTILINESTRING((0 0,0 0),(0 1,1 0))"), false); + test_valid(from_wkt("MULTILINESTRING((0 0),(1 0))"), false); + test_valid(from_wkt("MULTILINESTRING((0 0,0 0),(1 0,1 0))"), false); + test_valid(from_wkt("MULTILINESTRING((0 0),(0 0))"), false); + test_valid(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(5 0))"), false); + test_valid(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(5 0,1 0,4 1))"), + false); + test_valid(from_wkt("MULTILINESTRING((0 0,1 0,0 0),(1 0,2 0))"), + false); + + // valid multilinestrings + test_valid(from_wkt("MULTILINESTRING((0 0,1 0,2 0),(5 0,1 0,4 1))"), + true); + test_valid(from_wkt("MULTILINESTRING((0 0,1 0,2 0),(1 0,2 0))"), + true); + test_valid(from_wkt("MULTILINESTRING((0 0,1 1),(0 1,1 0))"), true); + test_valid(from_wkt("MULTILINESTRING((0 0,1 1,2 2),(0 1,1 0,2 2))"), true); +} + +#if 0 +BOOST_AUTO_TEST_CASE( test_is_valid_rest ) +{ + typedef open_polygon_type op; + typedef closed_polygon_type cp; + typedef multi_polygon_type mpl; + + test_valid(from_wkt("POLYGON(())"), true); + test_valid(from_wkt("POLYGON((),())"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,1 1),())"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,1 0,1 1),())"), true); + test_valid(from_wkt("POLYGON((0 0,1 0))"), false); + test_valid(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 1))"), false); + test_valid(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 0))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,1 1,1 0.5))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,1 1))"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,2 1,2 2,1 3))"), true); + test_valid(from_wkt("POLYGON((0 0,2 0,4 1,1 0))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,3 1,-1 2,3 3,3 4,0 4))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,3 1,0 2,3 3,3 4,0 4))"), false); + test_valid(from_wkt("POLYGON((0 0,10 0,10 10,0 10),\ + (1 1,1 2,2 2,2 1))"), false); + + test_valid(from_wkt("POLYGON(())"), true); + test_valid(from_wkt("POLYGON((),())"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,1 1,0 0),())"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,1 0,1 1,0 0),())"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,0 0))"), false); + test_valid(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 1,0 0))"), false); + test_valid(from_wkt("POLYGON((0 0,2 0,0.5 0,0.5 0,0 0))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,1 1,1 0.5,0 0))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,1 1,0 0))"), true); + test_valid(from_wkt("POLYGON((0 0,1 0,2 1,2 2,1 3,0 0))"), true); + test_valid(from_wkt("POLYGON((0 0,2 0,4 1,1 0,0 0))"), false); + test_valid(from_wkt("POLYGON((0 0,1 0,3 1,-1 2,3 3,3 4,0 4,0 0))"), + false); + test_valid(from_wkt("POLYGON((0 0,1 0,3 1,0 2,3 3,3 4,0 4,0 0))"), + false); + test_valid(from_wkt("POLYGON((0 0,10 0,10 10,0 10,0 0),\ + (1 1,1 2,2 2,2 1,1 1))"), false); + + + test_valid(from_wkt("MULTIPOLYGON()"), true); + test_valid(from_wkt("MULTIPOLYGON( ((),()) )"), true); + test_valid(from_wkt("MULTIPOLYGON( (()),(()) )"), true); + test_valid(from_wkt("MULTIPOLYGON( ((),()),(()) )"), true); + test_valid(from_wkt("MULTIPOLYGON( ((0 0),()),(()) )"), true); + test_valid(from_wkt("MULTIPOLYGON( ((0 0),()),((1 1)) )"), true); +#ifdef GEOMETRY_TEST_INCLUDE_FAILING_TESTS + // test_valid(from_wkt("MULTIPOLYGON( ((0 0),()),((0 0)) )"), false); +#endif + + test_valid(from_wkt("MULTIPOLYGON(((0 0,1 0,2 1,2 2,1 3)),\ + ((10 0,11 0,11 1)))"), true); + + test_valid(from_wkt("MULTIPOLYGON(((0 0,1 0,1 0,2 1,2 2,1 3)),\ + ((10 0,11 0,11 1,11 1)))"), true); + + test_valid(from_wkt("MULTIPOLYGON(((0 0,1 0,3 1,0 2,3 3,3 4,0 4)),\ + ((10 0,11 0,11 1)))"), false); +} +#endif