From eac0dc71bb87d23de189a7fefac6111553473e70 Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Thu, 29 Jan 2015 23:45:46 +0200 Subject: [PATCH] [algorithms][is_simple] optimize the computation of turns for multilinestrings: instead of computing the turns for each linestring (through bg::intersects) and then again for the entire multilinestring, avoid computing the turns for the individual linestrings in the multilinestring and compute and process all multilinestring turns together; besides the optimization this approach fixes a bug in bg::intersects in the context of simplicity testing: bg::intersects cannot detect the intersection occuring when a boundary point of linestring is also an internal point of the linestring, as in LINESTRING(4 1,10 8,4 6,4 1,10 5,10 3), for example; --- .../algorithms/detail/is_simple/linear.hpp | 327 ++++++++++-------- 1 file changed, 188 insertions(+), 139 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 90810d27e..c4487c445 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,8 +30,10 @@ #include #include +#include #include +#include #include #include @@ -54,128 +57,210 @@ namespace detail { namespace is_simple { +template +inline bool check_segment_indices(Turn const& turn, + signed_index_type last_index) +{ + return + (turn.operations[0].seg_id.segment_index == 0 + && turn.operations[1].seg_id.segment_index == last_index) + || + (turn.operations[0].seg_id.segment_index == 0 + && turn.operations[1].seg_id.segment_index == last_index); +} + + +template ::type> +class is_acceptable_turn + : not_implemented +{}; + +template +class is_acceptable_turn +{ +public: + is_acceptable_turn(Linestring const& linestring) + : m_linestring(linestring) + , m_is_closed(geometry::equals(range::front(linestring), + range::back(linestring))) + {} + + template + inline bool apply(Turn const& turn) const + { + BOOST_ASSERT(boost::size(m_linestring) > 1); + return m_is_closed + && turn.method == overlay::method_none + && check_segment_indices(turn, boost::size(m_linestring) - 2) + && geometry::equals(range::front(m_linestring), turn.point); + } + +private: + Linestring const& m_linestring; + bool const m_is_closed; +}; + +template +class is_acceptable_turn +{ +private: + typedef typename boost::range_value::type linestring_type; + typedef is_acceptable_turn base_type; + + template + static inline bool is_boundary_point_of(Point const& point, + Linestring const& linestring) + { + BOOST_ASSERT(boost::size(linestring) > 1); + return + ! geometry::equals(range::front(linestring), + range::back(linestring)) + && + (geometry::equals(point, range::front(linestring)) + || geometry::equals(point, range::back(linestring))); + } + + template + static inline bool is_closing_point_of(Turn const& turn, + Linestring const& linestring) + { + BOOST_ASSERT(boost::size(linestring) > 1); + return + turn.method == overlay::method_none + && + check_segment_indices(turn, boost::size(linestring) - 2) + && + geometry::equals(range::front(linestring), range::back(linestring)) + && + geometry::equals(range::front(linestring), turn.point) + ; + } + + template + static inline bool have_same_boundary_points(Linestring1 const& ls1, + Linestring2 const& ls2) + { + return + geometry::equals(range::front(ls1), range::front(ls2)) + ? + geometry::equals(range::back(ls1), range::back(ls2)) + : + (geometry::equals(range::front(ls1), range::back(ls2)) + && + geometry::equals(range::back(ls1), range::front(ls2))) + ; + } + +public: + is_acceptable_turn(MultiLinestring const& multilinestring) + : m_multilinestring(multilinestring) + {} + + template + inline bool apply(Turn const& turn) const + { + linestring_type const& ls1 = + range::at(m_multilinestring, turn.operations[0].seg_id.multi_index); + + linestring_type const& ls2 = + range::at(m_multilinestring, turn.operations[1].seg_id.multi_index); + + if (turn.operations[0].seg_id.multi_index + == turn.operations[1].seg_id.multi_index) + { + return is_closing_point_of(turn, ls1); + } + + return + is_boundary_point_of(turn.point, ls1) + && is_boundary_point_of(turn.point, ls2) + && + ( boost::size(ls1) != 2 + || boost::size(ls2) != 2 + || ! have_same_boundary_points(ls1, ls2) ); + } + +private: + MultiLinestring const& m_multilinestring; +}; + + +template +inline bool has_self_intersections(Linear const& linear) +{ + typedef typename point_type::type point_type; + + // compute self turns + typedef detail::overlay::turn_info + < + point_type, + geometry::segment_ratio + < + typename geometry::coordinate_type::type + > + > turn_info; + + std::deque turns; + + typedef detail::overlay::get_turn_info + < + detail::disjoint::assign_disjoint_policy + > turn_policy; + + is_acceptable_turn predicate(linear); + detail::overlay::predicate_based_interrupt_policy + < + is_acceptable_turn + > interrupt_policy(predicate); + + detail::self_get_turn_points::get_turns + < + turn_policy + >::apply(linear, + detail::no_rescale_policy(), + turns, + interrupt_policy); + + detail::is_valid::debug_print_turns(turns.begin(), turns.end()); + debug_print_boundary_points(linear); + + return interrupt_policy.has_intersections; +} + + template struct is_simple_linestring { static inline bool apply(Linestring const& linestring) { - return !detail::is_valid::has_duplicates + return ! detail::is_valid::has_duplicates < Linestring, closed >::apply(linestring) - && !detail::is_valid::has_spikes + && ! detail::is_valid::has_spikes < Linestring, closed >::apply(linestring) - && !(CheckSelfIntersections && geometry::intersects(linestring)); + && ! (CheckSelfIntersections && has_self_intersections(linestring)); } }; - template -class is_simple_multilinestring +struct is_simple_multilinestring { -private: - class is_acceptable_turn - { - private: - template - static inline bool is_boundary_point_of(Point const& point, - Linestring const& linestring) - { - BOOST_ASSERT( boost::size(linestring) > 1 ); - return - !geometry::equals(range::front(linestring), - range::back(linestring)) - && - ( geometry::equals(point, range::front(linestring)) - || geometry::equals(point, range::back(linestring)) ); - } - - template - static inline bool is_closing_point_of(Point const& point, - Linestring const& linestring) - { - BOOST_ASSERT( boost::size(linestring) > 1 ); - return - geometry::equals(range::front(linestring), - range::back(linestring)) - && - geometry::equals(range::front(linestring), point) - ; - } - - template - static inline bool have_same_boundary_points(Linestring1 const& ls1, - Linestring2 const& ls2) - { - return - geometry::equals(range::front(ls1), range::front(ls2)) - ? - geometry::equals(range::back(ls1), range::back(ls2)) - : - (geometry::equals(range::front(ls1), range::back(ls2)) - && - geometry::equals(range::back(ls1), range::front(ls2)) - ) - ; - } - - public: - is_acceptable_turn(MultiLinestring const& multilinestring) - : m_multilinestring(multilinestring) - {} - - template - inline bool apply(Turn const& turn) const - { - typedef typename boost::range_value - < - MultiLinestring - >::type linestring; - - linestring const& ls1 = - range::at(m_multilinestring, - turn.operations[0].seg_id.multi_index); - - linestring const& ls2 = - range::at(m_multilinestring, - turn.operations[1].seg_id.multi_index); - - if (turn.operations[0].seg_id.multi_index - == turn.operations[1].seg_id.multi_index) - { - BOOST_ASSERT(is_closing_point_of(turn.point, ls1)); - return true; - } - - return - is_boundary_point_of(turn.point, ls1) - && is_boundary_point_of(turn.point, ls2) - && - ( boost::size(ls1) != 2 - || boost::size(ls2) != 2 - || !have_same_boundary_points(ls1, ls2) ); - } - - private: - MultiLinestring const& m_multilinestring; - }; - - -public: static inline bool apply(MultiLinestring const& multilinestring) { - typedef typename boost::range_value::type linestring; - typedef typename point_type::type point_type; - typedef point_type point; - - // check each of the linestrings for simplicity - if ( !detail::check_iterator_range + // but do not compute self-intersections yet; these will be + // computed for the entire multilinestring + if ( ! detail::check_iterator_range < - is_simple_linestring, + is_simple_linestring + < + typename boost::range_value::type, + false // do not compute self-intersections + >, false // do not allow empty multilinestring >::apply(boost::begin(multilinestring), boost::end(multilinestring)) @@ -184,44 +269,8 @@ public: return false; } - - // compute self turns - typedef detail::overlay::turn_info - < - point_type, - geometry::segment_ratio - < - typename geometry::coordinate_type::type - > - > turn_info; - - std::deque turns; - - typedef detail::overlay::get_turn_info - < - detail::disjoint::assign_disjoint_policy - > turn_policy; - - is_acceptable_turn predicate(multilinestring); - detail::overlay::predicate_based_interrupt_policy - < - is_acceptable_turn - > interrupt_policy(predicate); - - detail::self_get_turn_points::get_turns - < - turn_policy - >::apply(multilinestring, - detail::no_rescale_policy(), - turns, - interrupt_policy); - - detail::is_valid::debug_print_turns(turns.begin(), turns.end()); - debug_print_boundary_points(multilinestring); - - return !interrupt_policy.has_intersections; + return ! has_self_intersections(multilinestring); } - };