From 1f2041594a73bf5f05b1218458bdd40e829a2d6a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 14 Feb 2014 13:52:27 +0100 Subject: [PATCH 01/11] relate(L,L) added asserts to boundary_checker --- .../detail/relate/linear_linear.hpp | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index e7bac41b8..11c2cfc78 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -82,22 +82,33 @@ public: if ( !has_boundary ) return false; - if ( BoundaryQuery == boundary_front_explicit || BoundaryQuery == boundary_back_explicit ) + if ( BoundaryQuery == boundary_front_explicit ) + { + BOOST_ASSERT(this->template is_boundary(pt, sid)); return true; - - if ( BoundaryQuery == boundary_front ) + } + else if ( BoundaryQuery == boundary_back_explicit ) + { + BOOST_ASSERT(this->template is_boundary(pt, sid)); + return true; + } + else if ( BoundaryQuery == boundary_front ) + { return sid.segment_index == 0 && detail::equals::equals_point_point(pt, range::front(geometry)); - - if ( BoundaryQuery == boundary_back ) + } + else if ( BoundaryQuery == boundary_back ) + { return sid.segment_index + 2 == geometry::num_points(geometry) && detail::equals::equals_point_point(pt, range::back(geometry)); - - if ( BoundaryQuery == boundary_any ) + } + else if ( BoundaryQuery == boundary_any ) + { return sid.segment_index == 0 && detail::equals::equals_point_point(pt, range::front(geometry)) || sid.segment_index + 2 == geometry::num_points(geometry) && detail::equals::equals_point_point(pt, range::back(geometry)); + } BOOST_ASSERT(false); return false; From 868b54c6444529b0ae053af2255dd051d7b75f73 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 14 Feb 2014 14:01:20 +0100 Subject: [PATCH 02/11] relate() boundary_checker moved to separate file --- .../detail/relate/boundary_checker.hpp | 177 ++++++++++++++++++ .../detail/relate/linear_linear.hpp | 160 +--------------- 2 files changed, 183 insertions(+), 154 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp new file mode 100644 index 000000000..b237d1698 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -0,0 +1,177 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +enum boundary_query { boundary_front, boundary_back, boundary_front_explicit, boundary_back_explicit, boundary_any }; + +template ::type> +class boundary_checker {}; + +template +class boundary_checker +{ + typedef typename point_type::type point_type; + +public: + boundary_checker(Geometry const& g) + : has_boundary(! detail::equals::equals_point_point(range::front(g), range::back(g))) + , geometry(g) + {} + + template + bool is_boundary(point_type const& pt, segment_identifier const& sid) const + { + // TODO: replace with assert? + if ( boost::size(geometry) < 2 ) + return false; + + // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? + + if ( !has_boundary ) + return false; + + if ( BoundaryQuery == boundary_front_explicit ) + { + BOOST_ASSERT(this->template is_boundary(pt, sid)); + return true; + } + else if ( BoundaryQuery == boundary_back_explicit ) + { + BOOST_ASSERT(this->template is_boundary(pt, sid)); + return true; + } + else if ( BoundaryQuery == boundary_front ) + { + return sid.segment_index == 0 + && detail::equals::equals_point_point(pt, range::front(geometry)); + } + else if ( BoundaryQuery == boundary_back ) + { + return sid.segment_index + 2 == geometry::num_points(geometry) + && detail::equals::equals_point_point(pt, range::back(geometry)); + } + else if ( BoundaryQuery == boundary_any ) + { + return sid.segment_index == 0 + && detail::equals::equals_point_point(pt, range::front(geometry)) + || sid.segment_index + 2 == geometry::num_points(geometry) + && detail::equals::equals_point_point(pt, range::back(geometry)); + } + + BOOST_ASSERT(false); + return false; + } + +private: + bool has_boundary; + Geometry const& geometry; +}; + +template +class boundary_checker +{ + typedef typename point_type::type point_type; + +public: + boundary_checker(Geometry const& g) + : is_filled(false), geometry(g) + {} + + template + bool is_boundary(point_type const& pt, segment_identifier const& sid) + { + typedef typename boost::range_size::type size_type; + size_type multi_count = boost::size(geometry); + + // TODO: replace with assert? + if ( multi_count < 1 ) + return false; + + // TODO: for explicit parameters ASSERT could be used + + if ( BoundaryQuery == boundary_front || BoundaryQuery == boundary_front_explicit ) + { + if ( sid.segment_index != 0 ) + return false; + } + + if ( BoundaryQuery == boundary_back || BoundaryQuery == boundary_back_explicit ) + { + if ( sid.segment_index + 2 != geometry::num_points(geometry) ) + return false; + } + + if ( BoundaryQuery == boundary_any ) + { + if ( sid.segment_index != 0 && sid.segment_index + 2 != geometry::num_points(geometry) ) + return false; + } + + if ( ! is_filled ) + { + //boundary_points.clear(); + boundary_points.reserve(multi_count * 2); + + typedef typename boost::range_iterator::type multi_iterator; + for ( multi_iterator it = boost::begin(geometry) ; + it != boost::end(geometry) ; ++ it ) + { + // point - no boundary + if ( boost::size(*it) < 2 ) + continue; + + // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? + + boundary_points.push_back(range::front(*it)); + boundary_points.push_back(range::back(*it)); + } + + std::sort(boundary_points.begin(), boundary_points.end(), geometry::less()); + + is_filled = true; + } + + std::size_t equal_points_count + = boost::size( + std::equal_range(boundary_points.begin(), + boundary_points.end(), + pt, + geometry::less()) + ); + + return equal_points_count % 2 != 0 && equal_points_count > 0; // the number is odd and > 0 + + } + +private: + bool is_filled; + // TODO: store references/pointers instead of points? + std::vector boundary_points; + Geometry const& geometry; +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 11c2cfc78..c31142b44 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -9,15 +9,18 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP +#include +#include + #include #include #include -#include - -#include +#include namespace boost { namespace geometry { @@ -51,157 +54,6 @@ inline void update_result(result & res) update_result_dispatch::apply(res); } -// boundary_checker - -enum boundary_query { boundary_front, boundary_back, boundary_front_explicit, boundary_back_explicit, boundary_any }; - -template ::type> -class boundary_checker {}; - -template -class boundary_checker -{ - typedef typename point_type::type point_type; - -public: - boundary_checker(Geometry const& g) - : has_boundary(! detail::equals::equals_point_point(range::front(g), range::back(g))) - , geometry(g) - {} - - template - bool is_boundary(point_type const& pt, segment_identifier const& sid) const - { - // TODO: replace with assert? - if ( boost::size(geometry) < 2 ) - return false; - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - - if ( !has_boundary ) - return false; - - if ( BoundaryQuery == boundary_front_explicit ) - { - BOOST_ASSERT(this->template is_boundary(pt, sid)); - return true; - } - else if ( BoundaryQuery == boundary_back_explicit ) - { - BOOST_ASSERT(this->template is_boundary(pt, sid)); - return true; - } - else if ( BoundaryQuery == boundary_front ) - { - return sid.segment_index == 0 - && detail::equals::equals_point_point(pt, range::front(geometry)); - } - else if ( BoundaryQuery == boundary_back ) - { - return sid.segment_index + 2 == geometry::num_points(geometry) - && detail::equals::equals_point_point(pt, range::back(geometry)); - } - else if ( BoundaryQuery == boundary_any ) - { - return sid.segment_index == 0 - && detail::equals::equals_point_point(pt, range::front(geometry)) - || sid.segment_index + 2 == geometry::num_points(geometry) - && detail::equals::equals_point_point(pt, range::back(geometry)); - } - - BOOST_ASSERT(false); - return false; - } - -private: - bool has_boundary; - Geometry const& geometry; -}; - -template -class boundary_checker -{ - typedef typename point_type::type point_type; - -public: - boundary_checker(Geometry const& g) - : is_filled(false), geometry(g) - {} - - template - bool is_boundary(point_type const& pt, segment_identifier const& sid) - { - typedef typename boost::range_size::type size_type; - size_type multi_count = boost::size(geometry); - - // TODO: replace with assert? - if ( multi_count < 1 ) - return false; - - // TODO: for explicit parameters ASSERT could be used - - if ( BoundaryQuery == boundary_front || BoundaryQuery == boundary_front_explicit ) - { - if ( sid.segment_index != 0 ) - return false; - } - - if ( BoundaryQuery == boundary_back || BoundaryQuery == boundary_back_explicit ) - { - if ( sid.segment_index + 2 != geometry::num_points(geometry) ) - return false; - } - - if ( BoundaryQuery == boundary_any ) - { - if ( sid.segment_index != 0 && sid.segment_index + 2 != geometry::num_points(geometry) ) - return false; - } - - if ( ! is_filled ) - { - //boundary_points.clear(); - boundary_points.reserve(multi_count * 2); - - typedef typename boost::range_iterator::type multi_iterator; - for ( multi_iterator it = boost::begin(geometry) ; - it != boost::end(geometry) ; ++ it ) - { - // point - no boundary - if ( boost::size(*it) < 2 ) - continue; - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - - boundary_points.push_back(range::front(*it)); - boundary_points.push_back(range::back(*it)); - } - - std::sort(boundary_points.begin(), boundary_points.end(), geometry::less()); - - is_filled = true; - } - - std::size_t equal_points_count - = boost::size( - std::equal_range(boundary_points.begin(), - boundary_points.end(), - pt, - geometry::less()) - ); - - return equal_points_count % 2 != 0 && equal_points_count > 0; // the number is odd and > 0 - - } - -private: - bool is_filled; - // TODO: store references/pointers instead of points? - std::vector boundary_points; - Geometry const& geometry; -}; - // currently works only for linestrings template struct linear_linear From 2437538ccd17e44d2ac66fec0c3f80bc57e58749 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 14 Feb 2014 15:07:02 +0100 Subject: [PATCH 03/11] added point_in_point agnostic strategy and point_in_geometry() for Point and MultiPoint --- .../detail/within/point_in_geometry.hpp | 11 +++ .../detail/within/point_in_geometry.hpp | 24 ++++- .../strategies/agnostic/point_in_point.hpp | 88 +++++++++++++++++++ .../boost/geometry/strategies/strategies.hpp | 6 ++ 4 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 include/boost/geometry/strategies/agnostic/point_in_point.hpp diff --git a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp index ea10fc465..cbc024255 100644 --- a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp @@ -83,6 +83,17 @@ template {}; +template +struct point_in_geometry +{ + template static inline + int apply(Point1 const& point1, Point2 const& point2, Strategy const& strategy) + { + boost::ignore_unused_variable_warning(strategy); + return strategy.apply(point1, point2) ? 1 : -1; + } +}; + template struct point_in_geometry { diff --git a/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp index da8005512..970dfcd7d 100644 --- a/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013. -// Modifications copyright (c) 2013, Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013, 2014 Oracle and/or its affiliates. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -113,6 +113,26 @@ struct point_in_geometry } }; +template +struct point_in_geometry +{ + template static inline + int apply(Point const& point, Geometry const& geometry, Strategy const& strategy) + { + typedef typename boost::range_value::type point_type; + typedef typename boost::range_const_iterator::type iterator; + for ( iterator it = boost::begin(geometry) ; it != boost::end(geometry) ; ++it ) + { + int pip = point_in_geometry::apply(point, *it, strategy); + + if ( pip > 0 ) // inside + return 1; + } + + return -1; // outside + } +}; + }} // namespace detail_dispatch::within #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/strategies/agnostic/point_in_point.hpp b/include/boost/geometry/strategies/agnostic/point_in_point.hpp new file mode 100644 index 000000000..b45bad70f --- /dev/null +++ b/include/boost/geometry/strategies/agnostic/point_in_point.hpp @@ -0,0 +1,88 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POINT_HPP +#define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POINT_HPP + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + +template +< + typename Point1, typename Point2 +> +struct point_in_point +{ + static inline bool apply(Point1 const& point1, Point2 const& point2) + { + return detail::equals::equals_point_point(point1, point2); + } +}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + +template +struct default_strategy +{ + typedef strategy::within::point_in_point type; +}; + +template +struct default_strategy +{ + typedef strategy::within::point_in_point::type> type; +}; + +} // namespace services + +#endif + + +}} // namespace strategy::within + + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace strategy { namespace covered_by { namespace services +{ + +template +struct default_strategy +{ + typedef strategy::within::point_in_point type; +}; + +template +struct default_strategy +{ + typedef strategy::within::point_in_point::type> type; +}; + +}}} // namespace strategy::covered_by::services +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POINT_HPP diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 3aa9ab00f..70cb2d95f 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -4,6 +4,9 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -11,6 +14,8 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + #ifndef BOOST_GEOMETRY_STRATEGIES_STRATEGIES_HPP #define BOOST_GEOMETRY_STRATEGIES_STRATEGIES_HPP @@ -46,6 +51,7 @@ #include #include +#include #include #include From 551069e555ca1865a3689694a1cbbcd2899584d1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 14 Feb 2014 18:44:14 +0100 Subject: [PATCH 04/11] relate() added has_disjoint_sub_geometries check - not working for all special cases, should be replaced with e.g. for_each_disjoint_sub_geometry --- .../detail/relate/boundary_checker.hpp | 6 +- .../detail/relate/linear_linear.hpp | 64 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp index b237d1698..7db4963c7 100644 --- a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -51,12 +51,14 @@ public: if ( BoundaryQuery == boundary_front_explicit ) { - BOOST_ASSERT(this->template is_boundary(pt, sid)); +// TODO: temporarily disable + //BOOST_ASSERT(this->template is_boundary(pt, sid)); return true; } else if ( BoundaryQuery == boundary_back_explicit ) { - BOOST_ASSERT(this->template is_boundary(pt, sid)); +// TODO: temporarily disable + //BOOST_ASSERT(this->template is_boundary(pt, sid)); return true; } else if ( BoundaryQuery == boundary_front ) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index c31142b44..3d4653ca7 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -28,6 +28,52 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { +template ::type> +struct has_disjoint_sub_geometries {}; + +template +struct has_disjoint_sub_geometries +{ + template + static inline bool apply(TurnIt first, TurnIt last, Geometry const& /*geometry*/) + { + BOOST_ASSERT(first != last); + return false; + } +}; + +template +struct has_disjoint_sub_geometries +{ + template + static inline bool apply(TurnIt first, TurnIt last, Geometry const& geometry) + { + BOOST_ASSERT(first != last); + + std::size_t count = boost::size(geometry); + std::vector detected_intersections(count, false); + for ( TurnIt it = first ; it != last ; ++it ) + { + int multi_index = it->operations[OpId].seg_id.multi_index; + BOOST_ASSERT(multi_index >= 0); + std::size_t index = static_cast(multi_index); + BOOST_ASSERT(index < count); + detected_intersections[index] = true; + } + + for ( std::vector::iterator it = detected_intersections.begin() ; + it != detected_intersections.end() ; ++it ) + { + if ( *it == false ) + return true; + } + + return false; + } +}; + // update_result template @@ -114,6 +160,24 @@ struct linear_linear return res; } +// TODO: this works bout we don't know what should be set exactly +// replace it with something like: for_each_disjoint_subgeometry + if ( has_disjoint_sub_geometries<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1) ) + { +// TODO: +// check if there is an interior and/or boundary + res.template update(); + res.template update(); + } + + if ( has_disjoint_sub_geometries<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2) ) + { +// TODO: +// check if there is an interior and/or boundary + res.template update(); + res.template update(); + } + boundary_checker boundary_checker1(geometry1); boundary_checker boundary_checker2(geometry2); From 00e69bbea3d98c4cec156925a66910941c57de46 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 15 Feb 2014 01:21:57 +0100 Subject: [PATCH 05/11] relate(L,L) - restored asserts in boundary_checker, added proper calculation of seg_ids for the first and last point of a range --- .../detail/relate/boundary_checker.hpp | 6 ++--- .../detail/relate/linear_linear.hpp | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp index 7db4963c7..b237d1698 100644 --- a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -51,14 +51,12 @@ public: if ( BoundaryQuery == boundary_front_explicit ) { -// TODO: temporarily disable - //BOOST_ASSERT(this->template is_boundary(pt, sid)); + BOOST_ASSERT(this->template is_boundary(pt, sid)); return true; } else if ( BoundaryQuery == boundary_back_explicit ) { -// TODO: temporarily disable - //BOOST_ASSERT(this->template is_boundary(pt, sid)); + BOOST_ASSERT(this->template is_boundary(pt, sid)); return true; } else if ( BoundaryQuery == boundary_front ) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 3d4653ca7..9bf8473bf 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -469,9 +469,12 @@ struct linear_linear // if it's the first IP then the first point is outside if ( first_in_range ) { + segment_identifier first_seg_id = seg_id; + first_seg_id.segment_index = 0; + // if there is a boundary on the first point if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + (range::front(sub_geometry::get(geometry, first_seg_id)), first_seg_id) ) { update_result(res); } @@ -601,14 +604,19 @@ struct linear_linear // PREV MAY NOT BE VALID! TurnIt prev = last; --prev; - segment_identifier const& prev_seg_id = prev->operations[OpId].seg_id; - - // if there is a boundary on the last point - if ( boost::size(sub_geometry::get(geometry, prev_seg_id)) > 1 - && boundary_checker.template is_boundary - (range::back(sub_geometry::get(geometry, prev_seg_id)), prev_seg_id) ) + segment_identifier prev_seg_id = prev->operations[OpId].seg_id; + std::size_t points_count = boost::size(sub_geometry::get(geometry, prev_seg_id)); + + if ( points_count > 1 ) { - update_result(res); + prev_seg_id.segment_index = points_count - 2; + + // if there is a boundary on the last point + if ( boundary_checker.template is_boundary + (range::back(sub_geometry::get(geometry, prev_seg_id)), prev_seg_id) ) + { + update_result(res); + } } } } From 25ce65b28acb82c4f9a27aafe910df59926599bc Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 15 Feb 2014 19:00:26 +0100 Subject: [PATCH 06/11] relate(L,L) has_disjoint_sub_geometries simple check replaced by for_each_disjoint_linestring_if taking predicate checking linestrings --- .../detail/relate/linear_linear.hpp | 126 ++++++++++++++---- 1 file changed, 100 insertions(+), 26 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 9bf8473bf..886a9e572 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -28,27 +28,55 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { +enum linestring_alt { linestring_exterior, linestring_point, linestring_closed, linestring_open }; + +template +linestring_alt linestring_simple_analysis(Linestring const& ls) +{ + std::size_t count = boost::size(ls); + if ( count == 0 ) + return linestring_exterior; + else if ( count == 1 ) + return linestring_point; + else + { + bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); + if ( equal_fb ) + // TODO: here the rest of points should be checked if they're equal with the first one + // and if they are, then it's a point + return linestring_closed; + else + return linestring_open; + } +} + template ::type> -struct has_disjoint_sub_geometries {}; +struct for_each_disjoint_linestring_if {}; template -struct has_disjoint_sub_geometries +struct for_each_disjoint_linestring_if { - template - static inline bool apply(TurnIt first, TurnIt last, Geometry const& /*geometry*/) + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& /*geometry*/, + Pred & /*pred*/) { BOOST_ASSERT(first != last); + // TODO: check if has at least one valid turn - uix? + //return first != last; return false; } }; template -struct has_disjoint_sub_geometries +struct for_each_disjoint_linestring_if { - template - static inline bool apply(TurnIt first, TurnIt last, Geometry const& geometry) + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) { BOOST_ASSERT(first != last); @@ -63,15 +91,54 @@ struct has_disjoint_sub_geometries detected_intersections[index] = true; } + bool found = false; + for ( std::vector::iterator it = detected_intersections.begin() ; it != detected_intersections.end() ; ++it ) { + // if there were no intersections for this multi_index if ( *it == false ) - return true; + { + found = true; + bool cont = pred( + *(boost::begin(geometry) + + std::distance(detected_intersections.begin(), it))); + if ( !cont ) + break; + } } + return found; + } +}; + +template +class disjoint_linestring_pred +{ +public: + disjoint_linestring_pred(BoundaryChecker & boundary_checker) + : m_boundary_checker_ptr(boost::addressof(boundary_checker)) + , m_boundary('F') + , m_interior('F') + {} + + template + bool operator()(Linestring const& linestring) + { + // TODO: check if there is boundary and interior and interior's dimension + m_boundary = '0'; + m_interior = '1'; + // TODO: return false only if boundary == 0 and interior == 1 return false; } + + char get_boundary() const { return m_boundary; } + char get_interior() const { return m_interior; } + +private: + BoundaryChecker * m_boundary_checker_ptr; + char m_boundary; + char m_interior; }; // update_result @@ -160,26 +227,33 @@ struct linear_linear return res; } -// TODO: this works bout we don't know what should be set exactly -// replace it with something like: for_each_disjoint_subgeometry - if ( has_disjoint_sub_geometries<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1) ) - { -// TODO: -// check if there is an interior and/or boundary - res.template update(); - res.template update(); - } - - if ( has_disjoint_sub_geometries<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2) ) - { -// TODO: -// check if there is an interior and/or boundary - res.template update(); - res.template update(); - } - boundary_checker boundary_checker1(geometry1); boundary_checker boundary_checker2(geometry2); + + disjoint_linestring_pred< boundary_checker > pred1(boundary_checker1); + disjoint_linestring_pred< boundary_checker > pred2(boundary_checker2); + + if ( for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1) ) + { + if ( pred1.get_interior() == '1' ) + res.template update(); + else if ( pred1.get_interior() == '0' ) + res.template update(); + + if ( pred1.get_boundary() == '0' ) + res.template update(); + } + + if ( for_each_disjoint_linestring_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2) ) + { + if ( pred2.get_interior() == '1' ) + res.template update(); + else if ( pred2.get_interior() == '0' ) + res.template update(); + + if ( pred2.get_boundary() == '0' ) + res.template update(); + } // TODO: turns must be sorted and followed only if it's possible to go out and in on the same point // for linear geometries union operation must be detected which I guess would be quite often From 4815e000482595966ec5d51d49c8b60122ea052b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 15 Feb 2014 21:51:47 +0100 Subject: [PATCH 07/11] relate(L,L) disjoin linestrings contained in multilinestring properly handled. In the case of disjoint linestrings almost all special cases are handled non-simple linear rings, 1-point linestrings. Linestrings containing >1 equal points are for now treated as normal linear rings, this should probably be changed. --- .../detail/relate/boundary_checker.hpp | 71 +++++----- .../detail/relate/linear_linear.hpp | 124 ++++++++++-------- test/algorithms/relate.cpp | 7 + 3 files changed, 114 insertions(+), 88 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp index b237d1698..4cf882565 100644 --- a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -20,7 +20,7 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -enum boundary_query { boundary_front, boundary_back, boundary_front_explicit, boundary_back_explicit, boundary_any }; +enum boundary_query { boundary_front, boundary_back, boundary_any }; template ::type> @@ -33,33 +33,28 @@ class boundary_checker public: boundary_checker(Geometry const& g) - : has_boundary(! detail::equals::equals_point_point(range::front(g), range::back(g))) + : has_boundary( boost::size(g) >= 2 + && !detail::equals::equals_point_point(range::front(g), range::back(g)) ) , geometry(g) {} + template + bool is_endpoint_boundary(point_type const& pt) const + { + BOOST_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + && detail::equals::equals_point_point(pt, range::front(geometry)) + || (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + && detail::equals::equals_point_point(pt, range::back(geometry)) ); + return has_boundary; + } + template bool is_boundary(point_type const& pt, segment_identifier const& sid) const { - // TODO: replace with assert? - if ( boost::size(geometry) < 2 ) - return false; - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - if ( !has_boundary ) return false; - if ( BoundaryQuery == boundary_front_explicit ) - { - BOOST_ASSERT(this->template is_boundary(pt, sid)); - return true; - } - else if ( BoundaryQuery == boundary_back_explicit ) - { - BOOST_ASSERT(this->template is_boundary(pt, sid)); - return true; - } - else if ( BoundaryQuery == boundary_front ) + if ( BoundaryQuery == boundary_front ) { return sid.segment_index == 0 && detail::equals::equals_point_point(pt, range::front(geometry)); @@ -96,36 +91,50 @@ public: : is_filled(false), geometry(g) {} + template + bool is_endpoint_boundary(point_type const& pt) + { + return is_boundary_point(pt); + } + template bool is_boundary(point_type const& pt, segment_identifier const& sid) { - typedef typename boost::range_size::type size_type; - size_type multi_count = boost::size(geometry); - - // TODO: replace with assert? - if ( multi_count < 1 ) - return false; - // TODO: for explicit parameters ASSERT could be used - if ( BoundaryQuery == boundary_front || BoundaryQuery == boundary_front_explicit ) + if ( BoundaryQuery == boundary_front ) { if ( sid.segment_index != 0 ) return false; } - if ( BoundaryQuery == boundary_back || BoundaryQuery == boundary_back_explicit ) + if ( BoundaryQuery == boundary_back ) { - if ( sid.segment_index + 2 != geometry::num_points(geometry) ) + if ( sid.segment_index + 2 != geometry::num_points(sub_geometry::get(geometry, sid)) ) return false; } if ( BoundaryQuery == boundary_any ) { - if ( sid.segment_index != 0 && sid.segment_index + 2 != geometry::num_points(geometry) ) + if ( sid.segment_index != 0 + && sid.segment_index + 2 != geometry::num_points(sub_geometry::get(geometry, sid)) ) return false; } + return is_boundary_point(pt); + } + +private: + // First call O(NlogN) + // Each next call O(logN) + bool is_boundary_point(point_type const& pt) + { + typedef typename boost::range_size::type size_type; + size_type multi_count = boost::size(geometry); + + if ( multi_count < 1 ) + return false; + if ( ! is_filled ) { //boundary_points.clear(); @@ -159,10 +168,8 @@ public: ); return equal_points_count % 2 != 0 && equal_points_count > 0; // the number is odd and > 0 - } -private: bool is_filled; // TODO: store references/pointers instead of points? std::vector boundary_points; diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 886a9e572..9961869b9 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -28,27 +28,27 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -enum linestring_alt { linestring_exterior, linestring_point, linestring_closed, linestring_open }; - -template -linestring_alt linestring_simple_analysis(Linestring const& ls) -{ - std::size_t count = boost::size(ls); - if ( count == 0 ) - return linestring_exterior; - else if ( count == 1 ) - return linestring_point; - else - { - bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); - if ( equal_fb ) - // TODO: here the rest of points should be checked if they're equal with the first one - // and if they are, then it's a point - return linestring_closed; - else - return linestring_open; - } -} +//enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open }; +// +//template +//linestring_kind check_linestring_kind(Linestring const& ls) +//{ +// std::size_t count = boost::size(ls); +// if ( count == 0 ) +// return linestring_exterior; +// else if ( count == 1 ) +// return linestring_point; +// else +// { +// bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); +// if ( equal_fb ) +// // TODO: here the rest of points should be checked if they're equal with the first one +// // and if they are, then it's a point +// return linestring_closed; +// else +// return linestring_open; +// } +//} template bool operator()(Linestring const& linestring) { - // TODO: check if there is boundary and interior and interior's dimension - m_boundary = '0'; - m_interior = '1'; - // TODO: return false only if boundary == 0 and interior == 1 - return false; + std::size_t count = boost::size(linestring); + + if ( count > 1 ) + { + // TODO: if all points are the same it's 0d object - Point + + m_interior = '1'; + + if ( m_boundary_checker_ptr->template + is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker_ptr->template + is_endpoint_boundary(range::back(linestring)) ) + { + m_boundary = '0'; + + // break only when we detect dim(I)=1 and dim(B)=0 + // we may be certain that there won't be bigger dimensions + return false; + } + } + else if ( count == 1 ) + { + if ( m_interior != '1' ) + m_interior = '0'; + } + + return true; } char get_boundary() const { return m_boundary; } @@ -496,9 +518,8 @@ struct linear_linear segment_identifier const& prev_seg_id = prev->operations[op_id].seg_id; // if there is a boundary on the last point - if ( boost::size(sub_geometry::get(geometry, prev_seg_id)) > 1 - && boundary_checker.template is_boundary - (range::back(sub_geometry::get(geometry, prev_seg_id)), prev_seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::back(sub_geometry::get(geometry, prev_seg_id))) ) { update_result(res); } @@ -520,7 +541,7 @@ struct linear_linear { bool other_b = it->operations[other_op_id].operation == overlay::operation_blocked ? - other_boundary_checker.template is_boundary(it->point, other_id) : + other_boundary_checker.template is_endpoint_boundary(it->point) : other_boundary_checker.template is_boundary(it->point, other_id); // it's also the boundary of the other geometry if ( other_b ) @@ -543,12 +564,9 @@ struct linear_linear // if it's the first IP then the first point is outside if ( first_in_range ) { - segment_identifier first_seg_id = seg_id; - first_seg_id.segment_index = 0; - // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, first_seg_id)), first_seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { update_result(res); } @@ -568,11 +586,11 @@ struct linear_linear if ( op_blocked ) { // check if this is indeed the boundary point - if ( boundary_checker.template is_boundary(it->point, seg_id) ) + if ( boundary_checker.template is_endpoint_boundary(it->point) ) { bool other_b = it->operations[other_op_id].operation == overlay::operation_blocked ? - other_boundary_checker.template is_boundary(it->point, other_id) : + other_boundary_checker.template is_endpoint_boundary(it->point) : other_boundary_checker.template is_boundary(it->point, other_id); // it's also the boundary of the other geometry if ( other_b ) @@ -600,8 +618,8 @@ struct linear_linear if ( first_in_range ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { update_result(res); } @@ -612,7 +630,7 @@ struct linear_linear { bool this_b = op_blocked ? - boundary_checker.template is_boundary(it->point, seg_id) : + boundary_checker.template is_endpoint_boundary(it->point) : boundary_checker.template is_boundary(it->point, seg_id); // if current IP is on boundary of the geometry @@ -620,7 +638,7 @@ struct linear_linear { bool other_b = it->operations[other_op_id].operation == overlay::operation_blocked ? - other_boundary_checker.template is_boundary(it->point, other_id) : + other_boundary_checker.template is_endpoint_boundary(it->point) : other_boundary_checker.template is_boundary(it->point, other_id); // it's also the boundary of the other geometry if ( other_b ) @@ -636,8 +654,8 @@ struct linear_linear if ( first_in_range && op_blocked ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { update_result(res); } @@ -651,8 +669,8 @@ struct linear_linear if ( first_in_range ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { update_result(res); } @@ -678,19 +696,13 @@ struct linear_linear // PREV MAY NOT BE VALID! TurnIt prev = last; --prev; - segment_identifier prev_seg_id = prev->operations[OpId].seg_id; - std::size_t points_count = boost::size(sub_geometry::get(geometry, prev_seg_id)); + segment_identifier const& prev_seg_id = prev->operations[OpId].seg_id; - if ( points_count > 1 ) + // if there is a boundary on the last point + if ( boundary_checker.template is_endpoint_boundary + (range::back(sub_geometry::get(geometry, prev_seg_id))) ) { - prev_seg_id.segment_index = points_count - 2; - - // if there is a boundary on the last point - if ( boundary_checker.template is_boundary - (range::back(sub_geometry::get(geometry, prev_seg_id)), prev_seg_id) ) - { - update_result(res); - } + update_result(res); } } } diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index db040d355..4e81c61fe 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -184,7 +184,14 @@ void test_linestring_multi_linestring() typedef bg::model::linestring

ls; typedef bg::model::multi_linestring mls; + // 1 LS disjoint test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1))", "101FF0102"); + // 1 linear ring disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1,2 2,1 1))", "101FF01F2"); + // 2xLS forming non-simple linear ring disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1,2 2),(1 1,2 2))", "101FF01F2"); + // 1 1-point LS (a Point) disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1))", "101FF00F2"); } template From 9f541428962d88bc4305234730402e857f179053 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 16 Feb 2014 18:26:41 +0100 Subject: [PATCH 08/11] relate(L,L) handled some cases with 1-point linestrings involved (if 1-PtLS is disjoint or there are other Ls in the other MLs) --- .../detail/relate/boundary_checker.hpp | 8 +- .../detail/relate/linear_linear.hpp | 180 +++++++++++------- test/algorithms/relate.cpp | 19 +- 3 files changed, 136 insertions(+), 71 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp index 4cf882565..d5511b988 100644 --- a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -100,8 +100,6 @@ public: template bool is_boundary(point_type const& pt, segment_identifier const& sid) { - // TODO: for explicit parameters ASSERT could be used - if ( BoundaryQuery == boundary_front ) { if ( sid.segment_index != 0 ) @@ -144,11 +142,13 @@ private: for ( multi_iterator it = boost::begin(geometry) ; it != boost::end(geometry) ; ++ it ) { - // point - no boundary + // empty or point - no boundary if ( boost::size(*it) < 2 ) continue; - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? + // linear ring or point - no boundary + if ( equals::equals_point_point(range::front(*it), range::back(*it)) ) + continue; boundary_points.push_back(range::front(*it)); boundary_points.push_back(range::back(*it)); diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 9961869b9..c92dfdd04 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -28,27 +28,42 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -//enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open }; -// -//template -//linestring_kind check_linestring_kind(Linestring const& ls) -//{ -// std::size_t count = boost::size(ls); -// if ( count == 0 ) -// return linestring_exterior; -// else if ( count == 1 ) -// return linestring_point; -// else -// { -// bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); -// if ( equal_fb ) -// // TODO: here the rest of points should be checked if they're equal with the first one -// // and if they are, then it's a point -// return linestring_closed; -// else -// return linestring_open; -// } -//} +enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open }; + +template +linestring_kind check_linestring_kind(Linestring const& ls) +{ + std::size_t count = boost::size(ls); + if ( count == 0 ) + return linestring_exterior; + else if ( count == 1 ) + return linestring_point; + else + { + bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); + if ( equal_fb ) + { + typedef typename boost::range_iterator::type iterator; + iterator first = boost::begin(ls); + ++first; + iterator last = boost::end(ls); + --last; + for ( iterator it = first ; it != last ; ++it ) + { + if ( !equals::equals_point_point(range::front(ls), *it) ) + return linestring_closed; + } + + return linestring_point; + } + else + return linestring_open; + } +} + +// TODO: +// For 1-point linestrings or with all equal points turns won't be generated! +// Check for those degenerated cases may be connected with this one! template { template static inline bool apply(TurnIt first, TurnIt last, - Geometry const& /*geometry*/, - Pred & /*pred*/) + Geometry const& geometry, + Pred & pred) { - BOOST_ASSERT(first != last); - // TODO: check if has at least one valid turn - uix? - //return first != last; - return false; + if ( first != last ) + return false; + pred(geometry); + return true; } }; @@ -112,12 +127,19 @@ struct for_each_disjoint_linestring_if } }; -template +// Called in a loop for: +// Ls/Ls - worst O(N) - 1x point_in_geometry(MLs) +// Ls/MLs - worst O(N) - 1x point_in_geometry(MLs) +// MLs/Ls - worst O(N^2) - Bx point_in_geometry(Ls) +// MLs/MLs - worst O(N^2) - Bx point_in_geometry(Ls) +// TODO: later use spatial index +template class disjoint_linestring_pred { public: - disjoint_linestring_pred(BoundaryChecker & boundary_checker) + disjoint_linestring_pred(BoundaryChecker & boundary_checker, OtherGeometry const& other_geometry) : m_boundary_checker_ptr(boost::addressof(boundary_checker)) + , m_other_geometry(boost::addressof(other_geometry)) , m_boundary('F') , m_interior('F') {} @@ -125,18 +147,52 @@ public: template bool operator()(Linestring const& linestring) { - std::size_t count = boost::size(linestring); + linestring_kind lk = check_linestring_kind(linestring); - if ( count > 1 ) + if ( lk == linestring_point ) { - // TODO: if all points are the same it's 0d object - Point + if ( m_interior != '1' ) + { +// TODO: what if this point is touching e.g. a linestring's boundary +// it should then be analysed later + // check the relation + int pig = within::point_in_geometry(range::front(linestring), *m_other_geometry); + + // point inside + if ( pig > 0 ) + { + // TODO + // interior, interior + } + // point on boundary + else if ( pig == 0 ) + { + // TODO + // interior, boundary + } + // point outside + else + { + m_interior = '0'; + // interior, exterior + } + } + } + else if ( lk == linestring_closed ) + { + m_interior = '1'; + // interior, exterior + } + else if ( lk == linestring_open ) + { m_interior = '1'; + // check if there is a boundary if ( m_boundary_checker_ptr->template - is_endpoint_boundary(range::front(linestring)) + is_endpoint_boundary(range::front(linestring)) || m_boundary_checker_ptr->template - is_endpoint_boundary(range::back(linestring)) ) + is_endpoint_boundary(range::back(linestring)) ) { m_boundary = '0'; @@ -145,11 +201,6 @@ public: return false; } } - else if ( count == 1 ) - { - if ( m_interior != '1' ) - m_interior = '0'; - } return true; } @@ -159,6 +210,7 @@ public: private: BoundaryChecker * m_boundary_checker_ptr; + const OtherGeometry * m_other_geometry; char m_boundary; char m_interior; }; @@ -201,29 +253,29 @@ struct linear_linear // TODO: currently this only works for linestrings std::size_t s1 = boost::size(geometry1); std::size_t s2 = boost::size(geometry2); - if ( s1 == 0 || s2 == 0 ) - { - // throw on empty output? - return result("FFFFFFFFF"); - } - else if ( s1 == 1 && s2 == 1 ) - { -// TODO : not working for MultiLinestrings -// return point_point::apply(range::front(geometry1), range::front(geometry2)); - BOOST_ASSERT(false); - } - else if ( s1 == 1 /*&& s2 > 1*/ ) - { -// TODO : not working for MultiLinestrings -// return point_geometry::apply(range::front(geometry1), geometry2); - BOOST_ASSERT(false); - } - else if ( s2 == 1 /*&& s1 > 1*/ ) - { -// TODO : not working for MultiLinestrings -// return geometry_point::apply(geometry1, range::front(geometry2)); - BOOST_ASSERT(false); - } +// if ( s1 == 0 || s2 == 0 ) +// { +// // throw on empty output? +// return result("FFFFFFFFF"); +// } +// else if ( s1 == 1 && s2 == 1 ) +// { +//// TODO : not working for MultiLinestrings +//// return point_point::apply(range::front(geometry1), range::front(geometry2)); +// BOOST_ASSERT(false); +// } +// else if ( s1 == 1 /*&& s2 > 1*/ ) +// { +//// TODO : not working for MultiLinestrings +//// return point_geometry::apply(range::front(geometry1), geometry2); +// BOOST_ASSERT(false); +// } +// else if ( s2 == 1 /*&& s1 > 1*/ ) +// { +//// TODO : not working for MultiLinestrings +//// return geometry_point::apply(geometry1, range::front(geometry2)); +// BOOST_ASSERT(false); +// } // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? @@ -252,8 +304,8 @@ struct linear_linear boundary_checker boundary_checker1(geometry1); boundary_checker boundary_checker2(geometry2); - disjoint_linestring_pred< boundary_checker > pred1(boundary_checker1); - disjoint_linestring_pred< boundary_checker > pred2(boundary_checker2); + disjoint_linestring_pred, Geometry2> pred1(boundary_checker1, geometry2); + disjoint_linestring_pred, Geometry1> pred2(boundary_checker2, geometry1); if ( for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1) ) { diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index 4e81c61fe..b15b2bc3f 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -176,6 +176,13 @@ void test_linestring_linestring() test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(5 0,10 0,5 5,5 0)", "FF00F01F2"); //to_svg("LINESTRING(0 0,5 0)", "LINESTRING(5 0,10 0,5 5,0 0,5 0)", "test_relate_00.svg"); + + // 1-point LS (a Point) NOT disjoint + test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0)", "0F1FF0FF2"); + test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0)", "0F1FF0FF2"); + test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0,1 0)", "0F1FF0FF2"); + // Point/Point + test_geometry("LINESTRING(0 0)", "LINESTRING(0 0)", "0FFFFFFF2"); } template @@ -184,14 +191,20 @@ void test_linestring_multi_linestring() typedef bg::model::linestring

ls; typedef bg::model::multi_linestring mls; - // 1 LS disjoint + // LS disjoint test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1))", "101FF0102"); - // 1 linear ring disjoint + // linear ring disjoint test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1,2 2,1 1))", "101FF01F2"); // 2xLS forming non-simple linear ring disjoint test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1,2 2),(1 1,2 2))", "101FF01F2"); - // 1 1-point LS (a Point) disjoint + // 1-point LS (a Point) disjoint test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1))", "101FF00F2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,1 1))", "101FF00F2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,1 1,1 1))", "101FF00F2"); + // 1-point LS (a Point) NOT disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0))", "101FF0FF2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0))", "101FF0FF2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0,2 0))", "101FF0FF2"); } template From 99610ca1240016bb22c168ae7fba58ca533ad6a2 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 16 Feb 2014 22:22:13 +0100 Subject: [PATCH 09/11] relate(L,L) handled the rest of cases with 1-point linestrings involved, added interrupt member to relate result - it may be used to break the calculation --- .../detail/relate/linear_linear.hpp | 155 ++++++------------ .../algorithms/detail/relate/result.hpp | 15 +- .../detail/within/point_in_geometry.hpp | 8 +- test/algorithms/relate.cpp | 1 + 4 files changed, 68 insertions(+), 111 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index c92dfdd04..5bf84ccbd 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -133,15 +133,18 @@ struct for_each_disjoint_linestring_if // MLs/Ls - worst O(N^2) - Bx point_in_geometry(Ls) // MLs/MLs - worst O(N^2) - Bx point_in_geometry(Ls) // TODO: later use spatial index -template +template class disjoint_linestring_pred { + static const bool reverse_result = OpId != 0; + public: - disjoint_linestring_pred(BoundaryChecker & boundary_checker, OtherGeometry const& other_geometry) - : m_boundary_checker_ptr(boost::addressof(boundary_checker)) + disjoint_linestring_pred(result & res, BoundaryChecker & boundary_checker, OtherGeometry const& other_geometry) + : m_result_ptr(boost::addressof(res)) + , m_boundary_checker_ptr(boost::addressof(boundary_checker)) , m_other_geometry(boost::addressof(other_geometry)) - , m_boundary('F') - , m_interior('F') + , m_detected_mask_point(0) + , m_detected_open_boundary(false) {} template @@ -149,70 +152,64 @@ public: { linestring_kind lk = check_linestring_kind(linestring); - if ( lk == linestring_point ) + if ( lk == linestring_point ) // just an optimization { - if ( m_interior != '1' ) + if ( m_detected_mask_point != 7 ) { -// TODO: what if this point is touching e.g. a linestring's boundary -// it should then be analysed later - // check the relation int pig = within::point_in_geometry(range::front(linestring), *m_other_geometry); // point inside if ( pig > 0 ) { - // TODO - // interior, interior + update_result(*m_result_ptr); + m_detected_mask_point |= 1; } // point on boundary else if ( pig == 0 ) { - // TODO - // interior, boundary + update_result(*m_result_ptr); + m_detected_mask_point |= 2; } // point outside else { - m_interior = '0'; - // interior, exterior + update_result(*m_result_ptr); + m_detected_mask_point |= 4; } } } - else if ( lk == linestring_closed ) + // NOTE: For closed Linestrings I/I=1 could be set automatically + // but for MultiLinestrings endpoints of closed Linestrings must also be checked for boundary + else if ( lk == linestring_open || lk == linestring_closed ) { - m_interior = '1'; - // interior, exterior - } - else if ( lk == linestring_open ) - { - m_interior = '1'; - - // check if there is a boundary - if ( m_boundary_checker_ptr->template - is_endpoint_boundary(range::front(linestring)) - || m_boundary_checker_ptr->template - is_endpoint_boundary(range::back(linestring)) ) + if ( !m_detected_open_boundary ) // just an optimization { - m_boundary = '0'; + update_result(*m_result_ptr); - // break only when we detect dim(I)=1 and dim(B)=0 - // we may be certain that there won't be bigger dimensions - return false; + // check if there is a boundary + if ( m_boundary_checker_ptr->template + is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker_ptr->template + is_endpoint_boundary(range::back(linestring)) ) + { + update_result(*m_result_ptr); + + m_detected_open_boundary = true; + } } } - return true; + bool all_detected = m_detected_mask_point == 7 && m_detected_open_boundary; + return !all_detected && !m_result_ptr->interrupt; } - char get_boundary() const { return m_boundary; } - char get_interior() const { return m_interior; } - private: + result * m_result_ptr; BoundaryChecker * m_boundary_checker_ptr; const OtherGeometry * m_other_geometry; - char m_boundary; - char m_interior; + char m_detected_mask_point; + bool m_detected_open_boundary; }; // update_result @@ -250,45 +247,11 @@ struct linear_linear static inline result apply(Geometry1 const& geometry1, Geometry2 const& geometry2) { - // TODO: currently this only works for linestrings - std::size_t s1 = boost::size(geometry1); - std::size_t s2 = boost::size(geometry2); -// if ( s1 == 0 || s2 == 0 ) -// { -// // throw on empty output? -// return result("FFFFFFFFF"); -// } -// else if ( s1 == 1 && s2 == 1 ) -// { -//// TODO : not working for MultiLinestrings -//// return point_point::apply(range::front(geometry1), range::front(geometry2)); -// BOOST_ASSERT(false); -// } -// else if ( s1 == 1 /*&& s2 > 1*/ ) -// { -//// TODO : not working for MultiLinestrings -//// return point_geometry::apply(range::front(geometry1), geometry2); -// BOOST_ASSERT(false); -// } -// else if ( s2 == 1 /*&& s1 > 1*/ ) -// { -//// TODO : not working for MultiLinestrings -//// return geometry_point::apply(geometry1, range::front(geometry2)); -// BOOST_ASSERT(false); -// } - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - result res("FFFFFFFFT"); static const std::size_t dimension = geometry::dimension::value; if ( dimension < 10 ) res.template set(); - // TEMP - //bool has_boundary1 = ! detail::equals::equals_point_point(range::front(geometry1), range::back(geometry1)); - //bool has_boundary2 = ! detail::equals::equals_point_point(range::front(geometry2), range::back(geometry2)); - //handle_boundaries(res, geometry1, geometry2, has_boundary1, has_boundary2); - // get and analyse turns typedef typename turns::get_turns::turn_info turn_type; typedef typename std::vector::iterator turn_iterator; @@ -296,38 +259,20 @@ struct linear_linear turns::get_turns::apply(turns, geometry1, geometry2); - if ( turns.empty() ) - { - return res; - } - boundary_checker boundary_checker1(geometry1); + disjoint_linestring_pred<0, boundary_checker, Geometry2> pred1(res, boundary_checker1, geometry2); + for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( res.interrupt ) + return res; + boundary_checker boundary_checker2(geometry2); + disjoint_linestring_pred<1, boundary_checker, Geometry1> pred2(res, boundary_checker2, geometry1); + for_each_disjoint_linestring_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( res.interrupt ) + return res; - disjoint_linestring_pred, Geometry2> pred1(boundary_checker1, geometry2); - disjoint_linestring_pred, Geometry1> pred2(boundary_checker2, geometry1); - - if ( for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1) ) - { - if ( pred1.get_interior() == '1' ) - res.template update(); - else if ( pred1.get_interior() == '0' ) - res.template update(); - - if ( pred1.get_boundary() == '0' ) - res.template update(); - } - - if ( for_each_disjoint_linestring_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2) ) - { - if ( pred2.get_interior() == '1' ) - res.template update(); - else if ( pred2.get_interior() == '0' ) - res.template update(); - - if ( pred2.get_boundary() == '0' ) - res.template update(); - } + if ( turns.empty() ) + return res; // TODO: turns must be sorted and followed only if it's possible to go out and in on the same point // for linear geometries union operation must be detected which I guess would be quite often @@ -341,6 +286,9 @@ struct linear_linear geometry1, geometry2, boundary_checker1, boundary_checker2); } + + if ( res.interrupt ) + return res; { std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,1>()); @@ -788,6 +736,9 @@ struct linear_linear analyser.apply(res, first, it, last, geometry, other_geometry, boundary_checker, other_boundary_checker); + + if ( res.interrupt ) + return; } analyser.apply(res, first, last, last, diff --git a/include/boost/geometry/algorithms/detail/relate/result.hpp b/include/boost/geometry/algorithms/detail/relate/result.hpp index 49155cbdf..63a29e9f9 100644 --- a/include/boost/geometry/algorithms/detail/relate/result.hpp +++ b/include/boost/geometry/algorithms/detail/relate/result.hpp @@ -23,6 +23,9 @@ enum field { interior = 0, boundary = 1, exterior = 2 }; class result { public: + + static const bool interrupt = false; + result() { set('F'); @@ -64,12 +67,12 @@ public: return std::make_pair(array, array+9); } - void transpose() - { - std::swap(array[1], array[3]); - std::swap(array[2], array[6]); - std::swap(array[5], array[7]); - } + //void transpose() + //{ + // std::swap(array[1], array[3]); + // std::swap(array[2], array[6]); + // std::swap(array[5], array[7]); + //} private: char array[9]; diff --git a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp index cbc024255..9cf4b8a46 100644 --- a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp @@ -116,9 +116,11 @@ struct point_in_geometry else return 1; } -// else if ( count == 1 -// && detail::equals::equals_point_point(point, *boost::begin(linestring)) ) -// return 0; + else if ( count == 1 ) + { + if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) ) + return 1; + } return -1; } diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index b15b2bc3f..1f0eee59d 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -178,6 +178,7 @@ void test_linestring_linestring() //to_svg("LINESTRING(0 0,5 0)", "LINESTRING(5 0,10 0,5 5,0 0,5 0)", "test_relate_00.svg"); // 1-point LS (a Point) NOT disjoint + test_geometry("LINESTRING(1 0)", "LINESTRING(0 0,5 0)", "0FFFFF102"); test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0)", "0F1FF0FF2"); test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0)", "0F1FF0FF2"); test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0,1 0)", "0F1FF0FF2"); From 1e871f8ae6726d67a20007f63020531ad4c003ac Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 17 Feb 2014 17:37:46 +0100 Subject: [PATCH 10/11] relate() result refactored, result concept crystalized, prepared for compile-time interruptable result types --- .../detail/relate/linear_linear.hpp | 84 ++---- .../detail/relate/point_geometry.hpp | 252 ++++++++++-------- .../algorithms/detail/relate/relate.hpp | 28 +- .../algorithms/detail/relate/result.hpp | 123 ++++++--- 4 files changed, 287 insertions(+), 200 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 5bf84ccbd..0d9f57370 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -136,7 +136,7 @@ struct for_each_disjoint_linestring_if template class disjoint_linestring_pred { - static const bool reverse_result = OpId != 0; + static const bool transpose_result = OpId != 0; public: disjoint_linestring_pred(result & res, BoundaryChecker & boundary_checker, OtherGeometry const& other_geometry) @@ -162,19 +162,19 @@ public: // point inside if ( pig > 0 ) { - update_result(*m_result_ptr); + update(*m_result_ptr); m_detected_mask_point |= 1; } // point on boundary else if ( pig == 0 ) { - update_result(*m_result_ptr); + update(*m_result_ptr); m_detected_mask_point |= 2; } // point outside else { - update_result(*m_result_ptr); + update(*m_result_ptr); m_detected_mask_point |= 4; } } @@ -185,7 +185,7 @@ public: { if ( !m_detected_open_boundary ) // just an optimization { - update_result(*m_result_ptr); + update(*m_result_ptr); // check if there is a boundary if ( m_boundary_checker_ptr->template @@ -193,7 +193,7 @@ public: || m_boundary_checker_ptr->template is_endpoint_boundary(range::back(linestring)) ) { - update_result(*m_result_ptr); + update(*m_result_ptr); m_detected_open_boundary = true; } @@ -212,32 +212,6 @@ private: bool m_detected_open_boundary; }; -// update_result - -template -struct update_result_dispatch -{ - static inline void apply(result & res) - { - res.template update(); - } -}; - -template -struct update_result_dispatch -{ - static inline void apply(result & res) - { - res.template update(); - } -}; - -template -inline void update_result(result & res) -{ - update_result_dispatch::apply(res); -} - // currently works only for linestrings template struct linear_linear @@ -247,10 +221,8 @@ struct linear_linear static inline result apply(Geometry1 const& geometry1, Geometry2 const& geometry2) { - result res("FFFFFFFFT"); - static const std::size_t dimension = geometry::dimension::value; - if ( dimension < 10 ) - res.template set(); + result res; // FFFFFFFFF + set::value>(res);// FFFFFFFFd, d in [1,9] or T // get and analyse turns typedef typename turns::get_turns::turn_info turn_type; @@ -451,7 +423,7 @@ struct linear_linear { static const std::size_t op_id = OpId; static const std::size_t other_op_id = (OpId + 1) % 2; - static const bool reverse_result = OpId != 0; + static const bool transpose_result = OpId != 0; public: turns_analyser() @@ -497,7 +469,7 @@ struct linear_linear m_exit_watcher.reset_detected_exit(); // not the last IP - update_result(res); + update(res); } // fake exit point, reset state // in reality this will be op == overlay::operation_intersection @@ -521,7 +493,7 @@ struct linear_linear if ( boundary_checker.template is_endpoint_boundary (range::back(sub_geometry::get(geometry, prev_seg_id))) ) { - update_result(res); + update(res); } } @@ -534,7 +506,7 @@ struct linear_linear bool was_outside = m_exit_watcher.enter(it->point, other_id); // interiors overlaps - update_result(res); + update(res); // going inside on boundary point if ( boundary_checker.template is_boundary(it->point, seg_id) ) @@ -546,11 +518,11 @@ struct linear_linear // it's also the boundary of the other geometry if ( other_b ) { - update_result(res); + update(res); } else { - update_result(res); + update(res); } } // going inside on non-boundary point @@ -559,7 +531,7 @@ struct linear_linear // if we didn't enter in the past, we were outside if ( was_outside && !fake_enter_detected ) { - update_result(res); + update(res); // if it's the first IP then the first point is outside if ( first_in_range ) @@ -568,7 +540,7 @@ struct linear_linear if ( boundary_checker.template is_endpoint_boundary (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } @@ -595,11 +567,11 @@ struct linear_linear // it's also the boundary of the other geometry if ( other_b ) { - update_result(res); + update(res); } else { - update_result(res); + update(res); } } } @@ -607,12 +579,12 @@ struct linear_linear // we're outside else { - update_result(res); + update(res); // boundaries don't overlap - just an optimization if ( it->method == overlay::method_crosses ) { - update_result(res); + update(res); // it's the first point in range if ( first_in_range ) @@ -621,7 +593,7 @@ struct linear_linear if ( boundary_checker.template is_endpoint_boundary (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } @@ -643,11 +615,11 @@ struct linear_linear // it's also the boundary of the other geometry if ( other_b ) { - update_result(res); + update(res); } else { - update_result(res); + update(res); } // first IP on the last segment point - this means that the first point is outside @@ -657,14 +629,14 @@ struct linear_linear if ( boundary_checker.template is_endpoint_boundary (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } // boundaries don't overlap else { - update_result(res); + update(res); if ( first_in_range ) { @@ -672,7 +644,7 @@ struct linear_linear if ( boundary_checker.template is_endpoint_boundary (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } @@ -689,7 +661,7 @@ struct linear_linear || m_last_union ) { // for sure - update_result(res); + update(res); BOOST_ASSERT(first != last); // TODO: ERROR! @@ -702,7 +674,7 @@ struct linear_linear if ( boundary_checker.template is_endpoint_boundary (range::back(sub_geometry::get(geometry, prev_seg_id))) ) { - update_result(res); + update(res); } } } diff --git a/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp b/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp index 9e240d2bd..68b5acbfd 100644 --- a/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp @@ -14,8 +14,8 @@ #include // later change to equal/point_point.hpp #include -#include -#include +//#include +//#include #include @@ -30,104 +30,22 @@ struct point_point { static inline result apply(Point1 const& point1, Point2 const& point2) { + result res; + bool equal = detail::equals::equals_point_point(point1, point2); - if ( equal ) - return result("0FFFFFFFT"); + { + set(res); + set::value>(res); + } else - return result("FF0FFF0FT"); - } -}; - -// NOTE: Those tests should be consistent with within(Point, Box) and covered_by(Point, Box) -// There is no EPS used in those functions, values are compared using < or <= -// so comparing MIN and MAX in the same way should be fine - -template ::value> -struct box_has_interior -{ - static inline bool apply(Box const& box) - { - return geometry::get(box) < geometry::get(box) - && box_has_interior::apply(box); - } -}; - -template -struct box_has_interior -{ - static inline bool apply(Box const&) { return true; } -}; - -// NOTE: especially important here (see the NOTE above). - -template ::value> -struct box_has_equal_min_max -{ - static inline bool apply(Box const& box) - { - return geometry::get(box) == geometry::get(box) - && box_has_equal_min_max::apply(box); - } -}; - -template -struct box_has_equal_min_max -{ - static inline bool apply(Box const&) { return true; } -}; - -template -struct point_box -{ - static inline result apply(Point const& point, Box const& box) - { - if ( geometry::within(point, box) ) // this also means that the box has interior { - return result("0FFFFFTTT"); + set(res); + set(res); + set::value>(res); } - else if ( geometry::covered_by(point, box) ) // point is on the boundary - { - //if ( box_has_interior::apply(box) ) - //{ - // return result("F0FFFFTTT"); - //} - //else if ( box_has_equal_min_max::apply(box) ) // no boundary outside point - //{ - // return result("F0FFFFFFT"); - //} - //else // no interior outside point - //{ - // return result("F0FFFFFTT"); - //} - return result("F0FFFF**T"); - } - else - { - /*if ( box_has_interior::apply(box) ) - { - return result("FF0FFFTTT"); - } - else - { - return result("FF0FFFFTT"); - }*/ - return result("FF0FFF*TT"); - } - } -}; -template -struct box_point -{ - static inline result apply(Box const& box, Point const& point) - { - if ( geometry::within(point, box) ) - return result("0FTFFTFFT"); - else if ( geometry::covered_by(point, box) ) - return result("FF*0F*FFT"); - else - return result("FF*FFT0FT"); + return res; } }; @@ -137,17 +55,31 @@ struct point_geometry { static inline result apply(Point const& point, Geometry const& geometry) { + result res; + int pig = detail::within::point_in_geometry(point, geometry); // TODO: * - if geometry has interior and/or boundary // e.g. isn't 1-point linestring or linear ring or 0-area polygon - if ( pig < 0 ) // not within - return result("FF0FFF**T"); + if ( pig > 0 ) // within + { + set(res); + } else if ( pig == 0 ) - return result("F0FFFF**T"); - else // pig > 0 - within - return result("0FFFFF**T"); + { + set(res); + } + else // pig < 0 - not within + { + set(res); + } + + set(res); // TODO + set(res); // TODO + set::value>(res); + + return res; } }; @@ -157,20 +89,132 @@ struct geometry_point { static inline result apply(Geometry const& geometry, Point const& point) { + result res; + int pig = detail::within::point_in_geometry(point, geometry); // TODO: * - if geometry has interior and/or boundary // e.g. isn't 1-point linestring or linear ring or 0-area polygon - if ( pig < 0 ) // not within - return result("FF*FF*0FT"); + if ( pig > 0 ) // within + { + set(res); + } else if ( pig == 0 ) - return result("FF*0F*FFT"); - else // pig > 0 - within - return result("0F*FF*FFT"); + { + set(res); + } + else // pig < 0 - not within + { + set(res); + } + + set(res); // TODO + set(res); // TODO + set::value>(res); + + return res; } }; +// TODO: rewrite the folowing: + +//// NOTE: Those tests should be consistent with within(Point, Box) and covered_by(Point, Box) +//// There is no EPS used in those functions, values are compared using < or <= +//// so comparing MIN and MAX in the same way should be fine +// +//template ::value> +//struct box_has_interior +//{ +// static inline bool apply(Box const& box) +// { +// return geometry::get(box) < geometry::get(box) +// && box_has_interior::apply(box); +// } +//}; +// +//template +//struct box_has_interior +//{ +// static inline bool apply(Box const&) { return true; } +//}; +// +//// NOTE: especially important here (see the NOTE above). +// +//template ::value> +//struct box_has_equal_min_max +//{ +// static inline bool apply(Box const& box) +// { +// return geometry::get(box) == geometry::get(box) +// && box_has_equal_min_max::apply(box); +// } +//}; +// +//template +//struct box_has_equal_min_max +//{ +// static inline bool apply(Box const&) { return true; } +//}; +// +//template +//struct point_box +//{ +// static inline result apply(Point const& point, Box const& box) +// { +// result res; +// +// if ( geometry::within(point, box) ) // this also means that the box has interior +// { +// return result("0FFFFFTTT"); +// } +// else if ( geometry::covered_by(point, box) ) // point is on the boundary +// { +// //if ( box_has_interior::apply(box) ) +// //{ +// // return result("F0FFFFTTT"); +// //} +// //else if ( box_has_equal_min_max::apply(box) ) // no boundary outside point +// //{ +// // return result("F0FFFFFFT"); +// //} +// //else // no interior outside point +// //{ +// // return result("F0FFFFFTT"); +// //} +// return result("F0FFFF**T"); +// } +// else +// { +// /*if ( box_has_interior::apply(box) ) +// { +// return result("FF0FFFTTT"); +// } +// else +// { +// return result("FF0FFFFTT"); +// }*/ +// return result("FF0FFF*TT"); +// } +// +// return res; +// } +//}; +// +//template +//struct box_point +//{ +// static inline result apply(Box const& box, Point const& point) +// { +// if ( geometry::within(point, box) ) +// return result("0FTFFTFFT"); +// else if ( geometry::covered_by(point, box) ) +// return result("FF*0F*FFT"); +// else +// return result("FF*FFT0FT"); +// } +//}; + }} // namespace detail::relate #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/relate/relate.hpp b/include/boost/geometry/algorithms/detail/relate/relate.hpp index f473a81ae..cf955ce45 100644 --- a/include/boost/geometry/algorithms/detail/relate/relate.hpp +++ b/include/boost/geometry/algorithms/detail/relate/relate.hpp @@ -62,15 +62,15 @@ struct relate : detail::relate::point_point {}; -template -struct relate - : detail::relate::point_box -{}; - -template -struct relate - : detail::relate::box_point -{}; +//template +//struct relate +// : detail::relate::point_box +//{}; +// +//template +//struct relate +// : detail::relate::box_point +//{}; template struct relate @@ -92,6 +92,16 @@ struct relate : detail::relate::linear_linear {}; +template +struct relate + : detail::relate::linear_linear +{}; + +template +struct relate + : detail::relate::linear_linear +{}; + }} // namespace detail_dispatch::relate namespace detail { namespace relate { diff --git a/include/boost/geometry/algorithms/detail/relate/result.hpp b/include/boost/geometry/algorithms/detail/relate/result.hpp index 63a29e9f9..b639a08c9 100644 --- a/include/boost/geometry/algorithms/detail/relate/result.hpp +++ b/include/boost/geometry/algorithms/detail/relate/result.hpp @@ -26,58 +26,119 @@ public: static const bool interrupt = false; - result() + inline result() { - set('F'); - } - - result(const char * str) - { - ::memcpy(array, str, 9); + ::memset(array, 'F', 9); } template - char get() const + inline char get() const { return array[F1 * 3 + F2]; } template - void set() + inline void set() { array[F1 * 3 + F2] = V; } - template - void update() - { - BOOST_STATIC_ASSERT('0' <= D && D <= '9'); - char * c = array + F1 * 3 + F2; - if ( D > *c || *c > '9') - *c = D; - } - - void set(char v) - { - ::memset(array, v, 9); - } - - std::pair get_code() const + inline std::pair get_code() const { return std::make_pair(array, array+9); } - //void transpose() - //{ - // std::swap(array[1], array[3]); - // std::swap(array[2], array[6]); - // std::swap(array[5], array[7]); - //} - private: char array[9]; }; +template +inline void set(Result & res) +{ + res.template set(); +} + +template +inline void set(Result & res) +{ + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); +} + +template +inline void set(Result & res) +{ + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); +} + +template +inline void update(Result & res) +{ + BOOST_STATIC_ASSERT('0' <= D && D <= '9'); + char c = res.template get(); + if ( D > c || c > '9') + res.template set(); +} + +template +struct update_result_dispatch +{ + template + static inline void apply(Result & res) + { + update(res); + } +}; + +template +struct update_result_dispatch +{ + template + static inline void apply(Result & res) + { + update(res); + } +}; + +template +inline void update(Result & res) +{ + update_result_dispatch::apply(res); +} + +template +inline Result return_result() +{ + Result res; + set(res); + return res; +} + +template +struct result_dimension +{ + BOOST_STATIC_ASSERT(geometry::dimension::value >= 0); + static const char value + = ( geometry::dimension::value <= 9 ) ? + ( '0' + geometry::dimension::value ) : + 'T'; +}; + }} // namespace detail::relate #endif // DOXYGEN_NO_DETAIL From b03601fab868974e4a3d563449a608fcd77928dd Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 19 Feb 2014 02:26:15 +0100 Subject: [PATCH 11/11] get_turns(L,L) fixed 2x IPs generation for a special case of collinear, opposite last Ls segment --- .../detail/overlay/get_turn_info_ll.hpp | 7 +++++-- .../overlay/get_turns_linear_linear.cpp | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp index 8840952db..b6d5345d5 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp @@ -492,7 +492,8 @@ struct get_turn_info_linear_linear result_ignore_ip0 = !opposite ? // <=> ip_count == 1 || ip_count == 2 && !opposite append0_last : - (append0_last && p0j); + (append0_last && (p0j || q0_last && q1i)); + // NOTE: based on how collinear is calculated for opposite segments if ( append0_first || append0_last ) { @@ -533,7 +534,9 @@ struct get_turn_info_linear_linear result_ignore_ip1 = !opposite ? // <=> ip_count == 2 && !opposite append1_last : - (append1_last && q1j); + (append1_last && (q1j || p1_last && p0i)); + // NOTE: based on how collinear is calculated for opposite segments + // this condition is symmetric to the one above if ( append1_first || append1_last ) { diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 57584d41d..89876ab68 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -207,6 +207,8 @@ void test_all() test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(-1 -1,1 0,10 0,20 -1)", "mii", "txu"); test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(20 -1,10 0,1 0,-1 -1)", "miu", "txi"); + test_geometry("LINESTRING(-1 -1,1 0,10 0,20 -1)", "LINESTRING(0 0,10 0)", "mii", "tux"); + test_geometry("LINESTRING(20 -1,10 0,1 0,-1 -1)", "LINESTRING(0 0,10 0)", "mui", "tix"); test_geometry("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)", "LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)", @@ -221,6 +223,20 @@ void test_all() "LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)", expected("tuu")("ecc")("mii")("muu")("mii")("muu")("mii").vec); + test_geometry("LINESTRING(-1 0,1 0,2 1,3 2)", "LINESTRING(4 5,3 2,1 0,0 0)", "mix", "txi", "ecc"); + test_geometry("LINESTRING(4 5,3 2,1 0,0 0)", "LINESTRING(-1 0,1 0,2 1,3 2)", "mxi", "tix", "ecc"); + + test_geometry("LINESTRING(30 1,20 1,10 0,0 0)", "LINESTRING(1 1,2 0,3 1,20 1,25 1)", "mix", "tui", "muu"); + test_geometry("LINESTRING(1 1,2 0,3 1,20 1,25 1)", "LINESTRING(30 1,20 1,10 0,0 0)", "mxi", "tiu", "muu"); + + test_geometry("LINESTRING(0 0,30 0)", "LINESTRING(4 0,4 1,20 1,5 0,1 0)", "muu", "mui", "mix"); + test_geometry("LINESTRING(4 0,4 1,20 1,5 0,1 0)", "LINESTRING(0 0,30 0)", "muu", "miu", "mxi"); + + test_geometry("LINESTRING(30 0,0 0)", "LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", + expected("mui")("miu")("mui")("mix").vec); + test_geometry("LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", "LINESTRING(30 0,0 0)", + expected("miu")("mui")("miu")("mxi").vec); + //if ( boost::is_same::value ) //{ // to_svg("LINESTRING(0 0,1 0,2 0,2.5 0,3 1)", "LINESTRING(0 0,2 0,2.5 0,3 1)", "test11.svg");