diff --git a/include/boost/geometry/algorithms/ogc/is_simple.hpp b/include/boost/geometry/algorithms/ogc/is_simple.hpp new file mode 100644 index 000000000..4b8660e49 --- /dev/null +++ b/include/boost/geometry/algorithms/ogc/is_simple.hpp @@ -0,0 +1,472 @@ +#ifndef BOOST_GEOMETRY_ALGORITHMS_OGC_IS_SIMPLE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_OGC_IS_SIMPLE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace geometry { namespace ogc +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template ::type> +struct is_simple + : not_implemented +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + + +template +struct has_spikes; + +template +struct has_spikes +{ + static inline bool apply(Range const& range) + { + if ( boost::size(range) < 3 ) + { + return false; + } + + BOOST_AUTO_TPL(it1, boost::begin(range)); + BOOST_AUTO_TPL(it2, ++boost::begin(range)); + BOOST_AUTO_TPL(it3, ++(++boost::begin(range))); + + for (; it3 != boost::end(range); ++it1, ++it2, ++it3) + { + if ( geometry::detail::point_is_spike_or_equal(*it3, *it1, *it2) ) + { + return true; + } + } + + return false; + } +}; + + +template +struct has_spikes +{ + static inline bool apply(Range const& range) + { + typedef typename closeable_view::type ClosedRange; + + ClosedRange crange(const_cast(range)); + return has_spikes::apply(crange); + } + +}; + + +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_range +{ + static inline bool apply(Range const& range) + { + if ( is_below_minimal_size::apply(range) ) + { + return false; + } + + Range other(range); + geometry::unique(other); + + if ( boost::size(other) == 1 ) + { + return true; + } + + if ( has_spikes::value>::apply(other) ) + { + return false; + } + + return !geometry::intersects(other); + } +}; + + + +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_multipoint +{ + static inline bool apply(MultiPoint const& multipoint) + { + if ( boost::size(multipoint) <= 1 ) + { + return true; + } + + MultiPoint mp(multipoint); + + typedef typename point_type::type Point; + + std::sort(boost::begin(mp), boost::end(mp), geometry::less()); + + geometry::equal_to equal; + + BOOST_AUTO_TPL(it_next, ++boost::begin(mp)); + BOOST_AUTO_TPL(it, boost::begin(mp)); + for (; it_next != boost::end(mp); ++it, ++it_next) + { + if ( equal(*it, *it_next) ) + { + return false; + } + } + return true; + } +}; + + +template +struct is_simple_multilinestring +{ + static inline bool apply(MultiLinestring const& multilinestring) + { + typedef typename boost::range_value::type Linestring; + typedef typename point_type::type Point; + + BOOST_AUTO_TPL(it, boost::begin(multilinestring)); + for (; it != boost::end(multilinestring); ++it) + { + if ( !dispatch::is_simple::apply(*it) ) + { + return false; + } + } + + + typedef geometry::detail::overlay::turn_info + < + Point + > turn_info; + + std::deque turns; + + typedef typename strategy_intersection + < + typename cs_tag::type, + MultiLinestring, + MultiLinestring, + Point + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + typedef geometry::detail::overlay::get_turn_info + < + geometry::detail::disjoint::assign_disjoint_policy + > TurnPolicy; + + geometry::detail::self_get_turn_points::no_interrupt_policy + interrupt_policy; + + geometry::detail::self_get_turn_points::get_turns + < + TurnPolicy + >::apply(multilinestring, + geometry::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::dsv(tit->point); + } + std::cout << std::endl << std::endl; + + if ( turns.size() == 0 ) + { + return true; + } + + std::vector endpoints; + 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; + + for (typename std::deque::const_iterator tit = turns.begin(); + tit != turns.end(); ++tit) + { + 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 ( ( !geometry::equals(*boost::begin(ls1), tit->point) + && !geometry::equals(*--boost::end(ls1), tit->point) ) + || + ( !geometry::equals(*boost::begin(ls2), tit->point) + && !geometry::equals(*--boost::end(ls2), tit->point) ) + ) + { + return false; + } + } + return true; + } + +}; + + +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 point is always simple +template +struct is_simple +{ + static inline bool apply(Point const&) + { + return true; + } +}; + + +// 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&) + { + return true; + } +}; + + +// 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&) + { + return true; + } +}; + + +// 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 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 +{}; + + +// 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 +{}; + + +// 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 +{}; + + +// 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::ogc + + + +#endif // BOOST_GEOMETRY_MYSQL_ALGORITHMS_IS_SIMPLE_HPP diff --git a/test/algorithms/is_simple.cpp b/test/algorithms/is_simple.cpp new file mode 100644 index 000000000..28e4fa9ce --- /dev/null +++ b/test/algorithms/is_simple.cpp @@ -0,0 +1,259 @@ +#include + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_simple +#endif + +#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::ogc::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 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), true); + 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; + + test_simple(from_wkt("LINESTRING()"), true); + test_simple(from_wkt("LINESTRING(0 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,0 0)"), true); + test_simple(from_wkt("LINESTRING(0 0,1 2)"), true); + 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,1 0,0.5 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 0,0.5 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 0,1.5 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,2 0,1.5 0,0.5 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,0.5 0,2 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,1 1,0.5 0,0 0)"), false); + test_simple(from_wkt("LINESTRING(0 0,1 0,1 1,0 0)"), true); + test_simple(from_wkt("LINESTRING(0 0,0 0,1 0,1 0,1 1,0 0)"), true); +} + + +BOOST_AUTO_TEST_CASE( test_is_simple_multilinestring ) +{ + typedef multi_linestring_type G; + + test_simple(from_wkt("MULTILINESTRING()"), true); + test_simple(from_wkt("MULTILINESTRING(())"), true); + test_simple(from_wkt("MULTILINESTRING((),(),())"), true); + test_simple(from_wkt("MULTILINESTRING((),(0 1,1 0))"), true); + 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))"), true); + test_simple(from_wkt("MULTILINESTRING((0 0),(1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,0 0),(1 0,1 0))"), true); + + test_simple(from_wkt("MULTILINESTRING((0 0),(0 0))"), false); + + 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); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(0 1,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(1 1,1 0))"), true); + 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),(0 1,1 1))"), + true); + + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(1 1,1 0),\ + (1 1,0 1,0.5,0.5))"), + false); +} + + + +#if 0 +BOOST_AUTO_TEST_CASE( test_is_simple_rest ) +{ + typedef multi_linestring_type mls; + typedef open_polygon_type op; + typedef closed_polygon_type cp; + typedef multi_point_type mpt; + typedef multi_polygon_type mpl; + + test_simple(from_wkt("MULTILINESTRING()"), true); + test_simple(from_wkt("MULTILINESTRING(())"), true); + test_simple(from_wkt("MULTILINESTRING((),(),())"), true); + test_simple(from_wkt("MULTILINESTRING((),(0 1,1 0))"), true); + test_simple(from_wkt("MULTILINESTRING((0 0),(0 1,1 0))"), true); + test_simple(from_wkt("MULTILINESTRING((0 0),(1 0))"), true); +#ifdef GEOMETRY_TEST_INCLUDE_FAILING_TESTS + // test_simple(from_wkt("MULTILINESTRING((0 0),(0 0))"), false); +#endif + 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); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(0 1,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,1 1),(1 1,1 0))"), false); + test_simple(from_wkt("MULTILINESTRING((0 0,2 0),(1 0,0 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),())"), 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("MULTIPOINT()"), 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); + + 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