From 7df83d69a75f3cb86fafb06308c0dbc856a74a57 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 27 Mar 2014 16:35:43 +0100 Subject: [PATCH] [relate] add support for no-IP casesin A/A, fix for_each_disjoint_geometry_if for Multi Geometries. Fix a bug in for_each_disjoint_geometry_if resulting in assert failure if no turns are generated. This affects L/L, L/A and A/A. Enable all combinations of Areal geometries. Add ignoring of empty disjoint geometries in L/A and A/A. Add tests for disjoint geometries. --- .../algorithms/detail/relate/areal_areal.hpp | 83 +++++++++++-------- .../detail/relate/follow_helpers.hpp | 40 ++++++++- .../algorithms/detail/relate/linear_areal.hpp | 13 ++- .../algorithms/detail/relate/relate.hpp | 43 ++++++++++ test/algorithms/relate.cpp | 35 ++++++++ 5 files changed, 176 insertions(+), 38 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp index 065cb0cca..294dd3f39 100644 --- a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp @@ -31,29 +31,58 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { +// WARNING! +// TODO: In the worst case calling this Pred in a loop for MultiPolygon/MultiPolygon may take O(NM) +// Use the rtree in this case! + // may be used to set EI and EB for an Areal geometry for which no turns were generated -template +template class no_turns_aa_pred { public: - no_turns_aa_pred(Result & res) + no_turns_aa_pred(OtherAreal const& other_areal, Result & res) : m_result_ptr(boost::addressof(res)) + , m_other_areal_ptr(boost::addressof(other_areal)) + , m_flags(0) {} template bool operator()(Areal const& areal) { - // TODO: - // add an assertion for empty/invalid geometries + typedef typename geometry::point_type::type point_type; + point_type pt; + bool ok = boost::geometry::point_on_border(pt, areal); - update(*m_result_ptr); - update(*m_result_ptr); + // TODO: for now ignore, later throw an exception? + if ( !ok ) + return true; + + // check if the areal is inside the other_areal + int pig = detail::within::point_in_geometry(pt, *m_other_areal_ptr); + //BOOST_ASSERT( pig != 0 ); + + // inside + if ( pig > 0 ) + { + update(*m_result_ptr); + update(*m_result_ptr); + m_flags |= 1; + } + // outside + else + { + update(*m_result_ptr); + update(*m_result_ptr); + m_flags |= 2; + } - return false; + return m_flags != 3; } private: Result * m_result_ptr; + const OtherAreal * m_other_areal_ptr; + int m_flags; }; // The implementation of an algorithm calculating relate() for A/A @@ -90,32 +119,20 @@ struct areal_areal turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy); if ( result.interrupt ) return; -// -// boundary_checker boundary_checker1(geometry1); -// no_turns_la_linestring_pred -// < -// Geometry2, -// Result, -// boundary_checker, -// TransposeResult -// > pred1(geometry2, result, boundary_checker1); -// for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); -// if ( result.interrupt ) -// return; -// -// no_turns_la_areal_pred pred2(result); -// for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); -// if ( result.interrupt ) -// return; -// -// if ( turns.empty() ) -// return; -// -// // This is set here because in the case if empty Areal geometry were passed -// // those shouldn't be set -// set(result);// FFFFFF2Fd -// if ( result.interrupt ) -// return; + + no_turns_aa_pred pred1(geometry2, result); + for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( result.interrupt ) + return; + + no_turns_aa_pred pred2(geometry1, result); + for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( result.interrupt ) + return; + + if ( turns.empty() ) + return; + // // { // // for different multi or same ring id: x, u, i, c diff --git a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp index 941d8a5a6..10977f8b4 100644 --- a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -14,6 +14,8 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP +#include + namespace boost { namespace geometry { @@ -55,6 +57,39 @@ struct for_each_disjoint_geometry_if static inline bool apply(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred) + { + if ( first != last ) + return for_turns(first, last, geometry, pred); + else + return for_empty(geometry, pred); + } + + template + static inline bool for_empty(Geometry const& geometry, + Pred & pred) + { + typedef typename boost::range_iterator::type iterator; + + bool found = false; + + // O(N) + // check predicate for each contained geometry without generated turn + for ( iterator it = boost::begin(geometry) ; + it != boost::end(geometry) ; ++it ) + { + found = true; + bool cont = pred(*it); + if ( !cont ) + break; + } + + return true; + } + + template + static inline bool for_turns(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) { BOOST_ASSERT(first != last); @@ -84,9 +119,8 @@ struct for_each_disjoint_geometry_if if ( *it == false ) { found = true; - bool cont = pred( - *(boost::begin(geometry) - + std::distance(detected_intersections.begin(), it))); + bool cont = pred(range::at(geometry, + std::distance(detected_intersections.begin(), it))); if ( !cont ) break; } diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index f2e41a583..a8ad0b272 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -31,7 +31,8 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -// TODO: In the worst case for MultiLinestring/MultiPolygon this is O(NM) +// WARNING! +// TODO: In the worst case calling this Pred in a loop for MultiLinestring/MultiPolygon may take O(NM) // Use the rtree in this case! // may be used to set IE and BE for a Linear geometry for which no turns were generated @@ -118,7 +119,15 @@ public: bool operator()(Areal const& areal) { // TODO: - // add an assertion for empty/invalid geometries + // handle empty/invalid geometries in a different way than this: + + typedef typename geometry::point_type::type point_type; + point_type dummy; + bool ok = boost::geometry::point_on_border(dummy, areal); + + // TODO: for now ignore, later throw an exception? + if ( !ok ) + return true; update(*m_result_ptr); update(*m_result_ptr); diff --git a/include/boost/geometry/algorithms/detail/relate/relate.hpp b/include/boost/geometry/algorithms/detail/relate/relate.hpp index 07ea10138..e4253daa1 100644 --- a/include/boost/geometry/algorithms/detail/relate/relate.hpp +++ b/include/boost/geometry/algorithms/detail/relate/relate.hpp @@ -102,6 +102,7 @@ struct relate : detail::relate::geometry_point {}; + template struct relate : detail::relate::linear_linear @@ -122,6 +123,7 @@ struct relate {}; + template struct relate : detail::relate::linear_areal @@ -178,11 +180,52 @@ struct relate {}; + template struct relate : detail::relate::areal_areal {}; +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + +template +struct relate + : detail::relate::areal_areal +{}; + }} // namespace detail_dispatch::relate namespace detail { namespace relate { diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index 66a6b25fc..b9e0d2565 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -615,6 +615,11 @@ void test_multi_linestring_multi_polygon() test_geometry("MULTILINESTRING((0 0,10 0,10 10),(10 10,0 10,0 0),(20 20,50 50,20 80,20 20))", "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", "F11FFF2F2"); + + // disjoint + test_geometry("MULTILINESTRING((20 20,30 30),(30 30,40 40))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "FF1FF0212"); } template @@ -640,6 +645,9 @@ void polygon_polygon() test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", "POLYGON((5 5,5 10,6 5,5 5))", "212F01FF2"); + test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", + "POLYGON((5 5,5 6,6 6,6 5,5 5))", + "212FF1FF2"); // overlapping test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", @@ -653,6 +661,32 @@ void polygon_polygon() test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", "POLYGON((10 10,10 5,10 0,5 0,0 0,0 10,5 10,10 10))", "2FFF1FFF2"); + + // disjoint + test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", + "POLYGON((0 20,0 30,10 30,10 20,0 20))", + "FF2FF1212"); +} + +template +void multi_polygon_multi_polygon() +{ + typedef bg::model::polygon

poly; + typedef bg::model::multi_polygon mpoly; + + test_geometry("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "MULTIPOLYGON(((5 5,5 10,6 10,6 5,5 5)),((0 20,0 30,10 30,10 20,0 20)))", + "212F11212"); + test_geometry("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((0 20,0 30,10 30,10 20,0 20)))", + "MULTIPOLYGON(((5 5,5 10,6 10,6 5,5 5)))", + "212F11FF2"); + + test_geometry("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "MULTIPOLYGON(((5 5,5 6,6 6,6 5,5 5)),((0 20,0 30,10 30,10 20,0 20)))", + "212FF1212"); + test_geometry("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((0 20,0 30,10 30,10 20,0 20)))", + "MULTIPOLYGON(((5 5,5 6,6 6,6 5,5 5)))", + "212FF1FF2"); } template @@ -669,6 +703,7 @@ void test_all() test_linestring_multi_polygon

(); test_multi_linestring_multi_polygon

(); polygon_polygon

(); + multi_polygon_multi_polygon

(); } int test_main( int , char* [] )