From 4a4dbf45129b528d0e245c296452a3a6f74f23c2 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 15 Apr 2014 16:28:35 +0200 Subject: [PATCH] [relate][touches] Implement touches() for L/L and L/A using relate(), fix error in L/A. Enable touches() for other pairs of Geometries. Implement ready-to-use in the implementations of relate() less comparators of operations. Change the less comparator for L/A. Fix: handling of the entry/interior for some turn with MultiID which comes before union for some other turn with different MultiID. Finish the support for boost::mpl::vector<> as a complex StaticMask. --- .../algorithms/detail/relate/linear_areal.hpp | 86 ++---- .../detail/relate/linear_linear.hpp | 18 +- .../algorithms/detail/relate/relate.hpp | 27 +- .../algorithms/detail/relate/result.hpp | 54 +++- .../algorithms/detail/relate/turns.hpp | 32 +- include/boost/geometry/algorithms/touches.hpp | 279 +++--------------- test/algorithms/relate.cpp | 16 + test/algorithms/touches.cpp | 15 +- 8 files changed, 204 insertions(+), 323 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index 880f1e483..d1ec480f5 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -239,13 +239,7 @@ struct linear_areal { // for different multi or same ring id: x, u, i, c // for same multi and different ring id: c, i, u, x - typedef turns::less - < - 0, turns::less_greater_op_for_other_same_m_diff_r - < - turns::op_to_int<0,2,3,1,4,0> - > - > less; + typedef turns::less<0, turns::less_op_linear_areal> less; std::sort(turns.begin(), turns.end(), less()); turns_analyser analyser; @@ -342,13 +336,7 @@ struct linear_areal else { // u, c - typedef turns::less - < - 1, turns::less_greater_op_for_other_same_m_diff_r - < - turns::op_to_int<0,1,0,0,2,0> - > - > less; + typedef turns::less<1, turns::less_op_areal_linear> less; std::sort(it, next, less()); // analyse @@ -460,10 +448,11 @@ struct linear_areal public: turns_analyser() - : m_previous_turn_ptr(0) + : m_previous_turn_ptr(NULL) , m_previous_operation(overlay::operation_none) , m_boundary_counter(0) , m_interior_detected(false) + , m_first_interior_other_id_ptr(NULL) {} template operations[op_id].seg_id; - //segment_identifier const& other_id = it->operations[other_op_id].seg_id; + segment_identifier const& other_id = it->operations[other_op_id].seg_id; const bool first_in_range = m_seg_watcher.update(seg_id); @@ -548,33 +537,20 @@ struct linear_areal { m_interior_detected = false; } + else if ( op == overlay::operation_union ) + { +// TODO: this probably is not a good way of handling the interiors/enters +// the solution similar to exit_watcher would be more robust +// all enters should be kept and handled. +// maybe integrate it with the exit_watcher -> enter_exit_watcher + if ( m_first_interior_other_id_ptr + && m_first_interior_other_id_ptr->multi_index == other_id.multi_index ) + { + m_interior_detected = false; + } + } } - // if the new linestring started just now, - // but the previous one went out on the previous point, - // we must check if the boundary of the previous segment is outside - // NOTE: couldn't it be integrated with the handling of the union above? - // THIS IS REDUNDANT WITH THE HANDLING OF THE END OF THE RANGE - //if ( first_in_range - // && ! fake_enter_detected - // && m_previous_operation == overlay::operation_union ) - //{ - // BOOST_ASSERT(it != first); - // BOOST_ASSERT(m_previous_turn_ptr); - - // segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - - // bool prev_back_b = is_endpoint_on_boundary( - // range::back(sub_geometry::get(geometry, prev_seg_id)), - // boundary_checker); - - // // if there is a boundary on the last point - // if ( prev_back_b ) - // { - // update(res); - // } - //} - // i/u, c/u if ( op == overlay::operation_intersection || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection @@ -592,9 +568,15 @@ struct linear_areal // interiors overlaps //update(res); - // don't update now - // we might enter a boundary of some other ring on the same IP - m_interior_detected = true; +// TODO: think about the implementation of the more robust version +// this way only the first enter will be handled + if ( !m_interior_detected ) + { + // don't update now + // we might enter a boundary of some other ring on the same IP + m_interior_detected = true; + m_first_interior_other_id_ptr = boost::addressof(other_id); + } } } else // operation_boundary @@ -780,7 +762,8 @@ struct linear_areal // here, the possible exit is the real one // we know that we entered and now we exit if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT - ||*/ m_previous_operation == overlay::operation_union ) + ||*/ m_previous_operation == overlay::operation_union + && !m_interior_detected ) { // for sure update(res); @@ -801,10 +784,12 @@ struct linear_areal } } // we might enter some Areal and didn't go out, - else if ( m_previous_operation == overlay::operation_intersection ) + else if ( m_previous_operation == overlay::operation_intersection + || m_interior_detected ) { // just in case update(res); + m_interior_detected = false; BOOST_ASSERT(first != last); BOOST_ASSERT(m_previous_turn_ptr); @@ -822,14 +807,6 @@ struct linear_areal } } - // handle the interior overlap - if ( m_interior_detected ) - { - // just in case - update(res); - m_interior_detected = false; - } - BOOST_ASSERT_MSG(m_previous_operation != overlay::operation_continue, "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)"); @@ -967,6 +944,7 @@ struct linear_areal overlay::operation_type m_previous_operation; unsigned m_boundary_counter; bool m_interior_detected; + const segment_identifier * m_first_interior_other_id_ptr; }; // call analyser.apply() for each turn in range diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 177161f79..32795e63b 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -264,14 +264,7 @@ struct linear_linear || may_update(result) || may_update(result) ) { - // x, u, i, c - typedef turns::less - < - 0, turns::less_greater_op_for_other_same_m_diff_r - < - turns::op_to_int<0,2,3,1,4,0> - > - > less; + typedef turns::less<0, turns::less_op_linear_linear> less; std::sort(turns.begin(), turns.end(), less()); turns_analyser analyser; @@ -291,14 +284,7 @@ struct linear_linear || may_update(result) || may_update(result) ) { - // x, u, i, c - typedef turns::less - < - 1, turns::less_greater_op_for_other_same_m_diff_r - < - turns::op_to_int<0,2,3,1,4,0> - > - > less; + typedef turns::less<1, turns::less_op_linear_linear> less; std::sort(turns.begin(), turns.end(), less()); turns_analyser analyser; diff --git a/include/boost/geometry/algorithms/detail/relate/relate.hpp b/include/boost/geometry/algorithms/detail/relate/relate.hpp index 52b966f42..946653452 100644 --- a/include/boost/geometry/algorithms/detail/relate/relate.hpp +++ b/include/boost/geometry/algorithms/detail/relate/relate.hpp @@ -198,19 +198,22 @@ struct interruption_enabled detail_dispatch::relate::relate::interruption_enabled; }; -template +template ::value> struct result_handler_type : not_implemented {}; template -struct result_handler_type +struct result_handler_type { typedef matrix_handler type; }; template -struct result_handler_type +struct result_handler_type { typedef mask_handler < @@ -224,7 +227,7 @@ struct result_handler_type }; template -struct result_handler_type > +struct result_handler_type, false> { typedef mask_handler < @@ -241,7 +244,7 @@ template -struct result_handler_type > +struct result_handler_type, false> { typedef static_mask_handler < @@ -254,6 +257,20 @@ struct result_handler_type type; }; +template +struct result_handler_type +{ + typedef static_mask_handler + < + StaticSequence, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + template inline typename result_handler_type diff --git a/include/boost/geometry/algorithms/detail/relate/result.hpp b/include/boost/geometry/algorithms/detail/relate/result.hpp index 270661af2..a4f571f94 100644 --- a/include/boost/geometry/algorithms/detail/relate/result.hpp +++ b/include/boost/geometry/algorithms/detail/relate/result.hpp @@ -620,8 +620,8 @@ public: // static_should_handle_element -template -struct static_should_handle_element +template +struct static_should_handle_element_dispatch { static const char mask_el = StaticMask::template get::value; static const bool value = mask_el == 'F' @@ -629,6 +629,56 @@ struct static_should_handle_element || ( mask_el >= '0' && mask_el <= '9' ); }; +template +struct static_should_handle_element_sequence +{ + typedef typename boost::mpl::deref::type StaticMask; + + static const bool value + = static_should_handle_element_dispatch + < + StaticMask, + F1, F2, + boost::mpl::is_sequence::value + >::value + || static_should_handle_element_sequence + < + typename boost::mpl::next::type, + Last, + F1, F2 + >::value; +}; + +template +struct static_should_handle_element_sequence +{ + static const bool value = false; +}; + +template +struct static_should_handle_element_dispatch +{ + static const bool value + = static_should_handle_element_sequence + < + typename boost::mpl::begin::type, + typename boost::mpl::end::type, + F1, F2 + >::value; +}; + +template +struct static_should_handle_element +{ + static const bool value + = static_should_handle_element_dispatch + < + StaticMask, + F1, F2, + boost::mpl::is_sequence::value + >::value; +}; + // static_interrupt template diff --git a/include/boost/geometry/algorithms/detail/relate/turns.hpp b/include/boost/geometry/algorithms/detail/relate/turns.hpp index 68eade8f0..b040fddba 100644 --- a/include/boost/geometry/algorithms/detail/relate/turns.hpp +++ b/include/boost/geometry/algorithms/detail/relate/turns.hpp @@ -135,28 +135,48 @@ struct op_to_int } }; -template > -struct less_greater_op_for_other_same_m_diff_r +template +struct less_op_xxx_linear { template inline bool operator()(Op const& left, Op const& right) { static OpToInt op_to_int; + return op_to_int(left) < op_to_int(right); + } +}; + +struct less_op_linear_linear + : less_op_xxx_linear< op_to_int<0,2,3,1,4,0> > +{}; + +struct less_op_linear_areal +{ + template + inline bool operator()(Op const& left, Op const& right) + { + static turns::op_to_int<0,2,3,1,4,0> op_to_int_xuic; + static turns::op_to_int<0,3,2,1,4,0> op_to_int_xiuc; if ( left.other_id.multi_index == right.other_id.multi_index ) { if ( left.other_id.ring_index == right.other_id.ring_index ) - return op_to_int(left) < op_to_int(right); + return op_to_int_xuic(left) < op_to_int_xuic(right); else - return op_to_int(left) > op_to_int(right); + return op_to_int_xiuc(left) < op_to_int_xiuc(right); } else { - return op_to_int(left) < op_to_int(right); + //return op_to_int_xuic(left) < op_to_int_xuic(right); + return left.other_id.multi_index < right.other_id.multi_index; } } }; +struct less_op_areal_linear + : less_op_xxx_linear< op_to_int<0,1,0,0,2,0> > +{}; + struct less_op_areal_areal { template @@ -201,7 +221,7 @@ struct less_op_areal_areal // sort turns by G1 - source_index == 0 by: // seg_id -> distance -> operation template > + typename LessOp = less_op_xxx_linear< op_to_int<> > > struct less { BOOST_STATIC_ASSERT(OpId < 2); diff --git a/include/boost/geometry/algorithms/touches.hpp b/include/boost/geometry/algorithms/touches.hpp index a34ac777e..d1e2d0171 100644 --- a/include/boost/geometry/algorithms/touches.hpp +++ b/include/boost/geometry/algorithms/touches.hpp @@ -40,6 +40,7 @@ namespace boost { namespace geometry namespace detail { namespace touches { + struct areal_interrupt_policy { static bool const enabled = true; @@ -117,134 +118,6 @@ struct areal_interrupt_policy } }; -template -struct linear_areal_interrupt_policy -{ - static bool const enabled = true; - bool found_touch; - bool found_not_touch; - - Linear const& linear; - - // dummy variable required by self_get_turn_points::get_turns - static bool const has_intersections = false; - - inline bool result() - { - return found_touch && !found_not_touch; - } - - inline linear_areal_interrupt_policy(Linear const& l) - : found_touch(false), found_not_touch(false), linear(l) - {} - - template - inline bool apply(Range const& range) - { - // if already rejected (temp workaround?) - if ( found_not_touch ) - return true; - - typedef typename boost::range_iterator::type iterator; - for ( iterator it = boost::begin(range) ; it != boost::end(range) ; ++it ) - { - if ( it->operations[0].operation == overlay::operation_intersection ) - { - if ( it->operations[1].operation == overlay::operation_union && - is_turn_on_last_point(*it) ) - { - found_touch = true; - continue; - } - else - { - found_not_touch = true; - return true; - } - } - - switch(it->method) - { - case overlay::method_crosses: - found_not_touch = true; - return true; - case overlay::method_equal: - case overlay::method_touch: - case overlay::method_touch_interior: - case overlay::method_collinear: - if ( ok_for_touch(*it) ) - { - found_touch = true; - } - else - { - found_not_touch = true; - return true; - } - break; - case overlay::method_none : - case overlay::method_disjoint : - case overlay::method_error : - break; - } - } - - return false; - } - - template - inline bool ok_for_touch(Turn const& turn) - { - return turn.both(overlay::operation_union) - || turn.both(overlay::operation_blocked) - || turn.combination(overlay::operation_union, overlay::operation_blocked) - - || turn.both(overlay::operation_continue) - || turn.combination(overlay::operation_union, overlay::operation_intersection) - ; - } - - template - inline bool is_turn_on_last_point(Turn const& turn) - { - typename sub_range_return_type::type - g = sub_range(linear, turn.operations[0].seg_id); - - std::size_t s = boost::size(g); - - if ( s == 0 ) - return false; // shouldn't return here - else if ( s == 1 ) - return equals::equals_point_point(turn.point, *boost::begin(g)); // nor here - else - return equals::equals_point_point(turn.point, *(boost::end(g)-1)); - } -}; - -template -struct turns_count_interrupt_policy -{ - static bool const enabled = true; - std::size_t turns_count; - - // dummy variable required by self_get_turn_points::get_turns - static bool const has_intersections = false; - - inline turns_count_interrupt_policy() - : turns_count(0) - {} - - template - inline bool apply(Range const& range) - { - turns_count += boost::size(range); - if ( Max < turns_count ) - return true; - return false; - } -}; - - template struct check_each_ring_for_within { @@ -261,16 +134,13 @@ struct check_each_ring_for_within { typename geometry::point_type::type p; geometry::point_on_border(p, range); - if (geometry::within(p, m_geometry)) + if ( !has_within && geometry::within(p, m_geometry) ) { has_within = true; } } }; -// TODO: currently this function checks if a point of at least one range from g2 is within g1 -// shouldn't it check the opposite? - template inline bool rings_containing(FirstGeometry const& geometry1, SecondGeometry const& geometry2) @@ -311,96 +181,29 @@ struct areal_areal } }; -template -struct linear_areal + +struct use_point_in_geometry { - static inline - bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) - { - typedef detail::overlay::turn_info - < - typename geometry::point_type::type - > turn_info; - - typedef detail::overlay::get_turn_info - < - detail::overlay::assign_null_policy - > policy_type; - - std::deque turns; - detail::touches::linear_areal_interrupt_policy policy(geometry1); - boost::geometry::get_turns - < - detail::overlay::do_reverse::value>::value, - detail::overlay::do_reverse::value>::value, - detail::overlay::assign_null_policy - >(geometry1, geometry2, detail::no_rescale_policy(), turns, policy); - - return policy.result() - && ! geometry::detail::touches::rings_containing(geometry2, geometry1); - } -}; - -template -struct point_geometry -{ - static inline - bool apply(Point const& point, Geometry const& geometry) + template + static inline bool apply(Point const& point, Geometry const& geometry) { return detail::within::point_in_geometry(point, geometry) == 0; } }; -template -struct linestring_linestring +struct use_relate { - static inline - bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + template + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) { - std::size_t s1 = boost::size(geometry1); - std::size_t s2 = boost::size(geometry2); - // TODO: throw on empty input? - if ( s1 == 0 || s2 == 0 ) - return false; - - typedef detail::overlay::turn_info - < - typename geometry::point_type::type - > turn_info; - - typedef detail::overlay::get_turn_info - < - detail::overlay::assign_null_policy - > policy_type; - - std::deque turns; - turns_count_interrupt_policy<2> policy; - boost::geometry::get_turns + typedef typename + detail::relate::static_mask_touches_type < - detail::overlay::do_reverse::value>::value, - detail::overlay::do_reverse::value>::value, - detail::overlay::assign_null_policy - >(geometry1, geometry2, detail::no_rescale_policy(), turns, policy); + Geometry1, + Geometry2 + >::type static_mask; - if ( 2 < policy.turns_count ) - return false; - else if ( 2 == policy.turns_count ) - { - return ( detail::equals::equals_point_point(turns[0].point, *(boost::end(geometry1)-1)) - && detail::equals::equals_point_point(turns[1].point, *(boost::end(geometry2)-1)) ) - || ( detail::equals::equals_point_point(turns[0].point, *(boost::end(geometry2)-1)) - && detail::equals::equals_point_point(turns[1].point, *(boost::end(geometry1)-1)) ); - } - else if ( 1 == policy.turns_count ) - { - return detail::equals::equals_point_point(turns[0].point, *(boost::end(geometry1)-1)) - || detail::equals::equals_point_point(turns[0].point, *(boost::end(geometry2)-1)); - } - else - { - return detail::within::point_in_geometry(*boost::begin(geometry1), geometry2) >= 0 - || detail::within::point_in_geometry(*boost::begin(geometry2), geometry1) >= 0; - } + return detail::relate::relate(geometry1, geometry2); } }; @@ -417,8 +220,8 @@ template typename Geometry1, typename Geometry2, typename Tag1 = typename tag::type, typename Tag2 = typename tag::type, - typename CastedTag1 = typename tag_cast::type, - typename CastedTag2 = typename tag_cast::type, + typename CastedTag1 = typename tag_cast::type, + typename CastedTag2 = typename tag_cast::type, bool Reverse = reverse_dispatch::type::value > struct touches : not_implemented @@ -440,10 +243,10 @@ struct touches } }; -// touches(Pt, Pt), touches(Pt, MPt), touches(MPt, MPt) +// P/P + template -struct touches - : detail::touches::point_geometry +struct touches { static inline bool apply(Geometry1 const& , Geometry2 const& ) { @@ -451,38 +254,36 @@ struct touches } }; -// touches(Point, Linear), touches(Point, Areal) +// P/* + template -struct touches - : detail::touches::point_geometry +struct touches + : detail::touches::use_point_in_geometry {}; // TODO: support touches(MPt, Linear/Areal) -// touches(Linestring, Linestring) -template -struct touches - : detail::touches::linestring_linestring +// L/L + +template +struct touches + : detail::touches::use_relate {}; -// TODO: support touches(MLs, MLs) and touches(Ls, MLs) +// L/A -// touches(Linear, Areal) -template -struct touches - : detail::touches::linear_areal +template +struct touches + : detail::touches::use_relate {}; -// e.g. for touches(Poly, MLs) -template -struct touches -{ - static inline bool apply(Areal const& areal, Linear const& linear) - { - return detail::touches::linear_areal::apply(linear, areal); - } -}; -// touches(Areal, Areal) +template +struct touches + : detail::touches::use_relate +{}; + +// A/A + template struct touches : detail::touches::areal_areal diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index d1bf8961a..afeeb1074 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -561,6 +561,10 @@ void test_linestring_polygon() test_geometry("LINESTRING(2 10,5 10,5 5,6 5,5 10,8 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11FF0F212"); + // self-IP with a hole -> B to I to B to E + test_geometry("LINESTRING(0 0,3 3)", "POLYGON((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0))", + "FF1F00212"); + // ccw { typedef bg::model::polygon ccwpoly; @@ -620,6 +624,18 @@ void test_linestring_multi_polygon() test_geometry("LINESTRING(0 0,10 0,10 10,0 10,0 0)", "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((20 0,20 10,30 20,20 0)))", "F1FFFF212"); + + // self-IP polygon with a hole and second polygon with a hole -> B to I to B to B to I to B to E + test_geometry("LINESTRING(0 0,3 3)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0)),((0 0,2 8,8 8,8 2,0 0),(0 0,7 3,7 7,3 7,0 0)))", + "FF1F00212"); + // self-IP polygon with a hole and second polygon -> B to I to B to B to I + test_geometry("LINESTRING(0 0,3 3)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0)),((0 0,2 8,8 8,8 2,0 0)))", + "1FF00F212"); + test_geometry("LINESTRING(0 0,3 3)", + "MULTIPOLYGON(((0 0,2 8,8 8,8 2,0 0)),((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0)))", + "1FF00F212"); } template diff --git a/test/algorithms/touches.cpp b/test/algorithms/touches.cpp index 551b30bb1..d5219082e 100644 --- a/test/algorithms/touches.cpp +++ b/test/algorithms/touches.cpp @@ -14,12 +14,13 @@ #include #include - +#include template void test_all() { + typedef bg::model::ring

ring; typedef bg::model::polygon

polygon; typedef bg::model::linestring

linestring; typedef bg::model::multi_polygon mpolygon; @@ -134,6 +135,7 @@ void test_all() ); // Point-Polygon + test_touches("POINT(40 50)", "POLYGON((40 40,40 60,60 60,60 40,40 40))", true); test_touches("POINT(40 50)", "POLYGON((40 40,40 60,60 60,60 40,40 40))", true); test_touches("POINT(60 60)", "POLYGON((40 40,40 60,60 60,60 40,40 40))", true); test_touches("POINT(50 50)", "POLYGON((40 40,40 60,60 60,60 40,40 40))", false); @@ -179,13 +181,24 @@ void test_all() test_touches("LINESTRING(0 0,1 1,2 2)", "LINESTRING(1 1,1 2,2 2,2 0)", true); test_touches("LINESTRING(2 2,1 1,0 0)", "LINESTRING(1 1,1 2,2 2,2 0)", true); + test_touches("LINESTRING(0 0,1 1,0 1)", "LINESTRING(1 1,2 2,1 2,1 1)", false); + + test_touches("LINESTRING(0 0,1 1,0 1)", "MULTILINESTRING((1 1,2 2),(1 2,1 1))", false); + //Linestring-Polygon test_touches("LINESTRING(10 0,15 5,10 10,5 15,5 10,0 10,5 15,5 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", true); test_touches("LINESTRING(5 10,5 15,0 10,5 10,5 15,10 10,15 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", true); test_touches("LINESTRING(5 10,5 15,0 10,5 10,5 15,10 10,5 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", false); + test_touches("LINESTRING(0 15,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", false); test_touches("LINESTRING(0 15,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", false); test_touches("LINESTRING(0 15,5 10,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", false); test_touches("LINESTRING(10 15,5 10,0 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", false); + + test_touches("LINESTRING(0 0,3 3)", "POLYGON((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0))", true); + + test_touches("LINESTRING(-1 -1,3 3)", "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0)))", true); + + test_touches("MULTILINESTRING((0 0,11 11))", "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 9,1 9,0 0)))", false); }