From 0ca5d2f13a9a4ef4f19e152a5aff800038ad6ffb Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Wed, 19 Feb 2014 13:37:04 +0200 Subject: [PATCH] re-arranged code so that linestrings are not transformed to multilinestrings; added implementations for all four pairs of LS/MLS combinations; added unit tests that were missing for new combinations; --- .../detail/difference/linear_linear.hpp | 225 +++++++ .../detail/overlay/intersection_insert.hpp | 24 +- .../detail/turns/follow_linear_linear.hpp | 573 +++++++++++++++--- test/algorithms/difference1.cpp | 52 ++ 4 files changed, 788 insertions(+), 86 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/difference/linear_linear.hpp b/include/boost/geometry/algorithms/detail/difference/linear_linear.hpp index fc9c1b21e..ec49a8451 100644 --- a/include/boost/geometry/algorithms/detail/difference/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/difference/linear_linear.hpp @@ -30,6 +30,231 @@ namespace detail { namespace difference { +//=========================================================================== +//=========================================================================== +//=========================================================================== + + +template +struct difference_no_intersections; + + +template +struct difference_no_intersections +{ + template + static inline OutputIterator apply(LineString const& linestring, + OutputIterator oit) + { + LineStringOut ls_out; + geometry::convert(linestring, ls_out); + *oit++ = ls_out; + return oit; + } +}; + + +template +struct difference_no_intersections + < + LineStringOut, MultiLineString, multi_linestring_tag + > +{ + template + static inline OutputIterator apply(MultiLineString const& multilinestring, + OutputIterator oit) + { + BOOST_AUTO_TPL(it, boost::begin(multilinestring)); + for (; it != boost::end(multilinestring); ++it) + { + LineStringOut ls_out; + geometry::convert(*it, ls_out); + *oit++ = ls_out; + } + return oit; + } +}; + +template +struct linear_linear_linestring +{ + typedef typename point_type::type PointOut; + typedef overlay::traversal_turn_info turn_info; + typedef std::vector Turns; + typedef typename Turns::iterator TurnIt; + typedef detail::get_turns::no_interrupt_policy InterruptPolicy; + + + struct AssignPolicy + { + static bool const include_no_turn = false; + static bool const include_degenerate = false; + static bool const include_opposite = false; + + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& info, Point1 const& p1, Point2 const& p2, + IntersectionInfo const& ii, DirInfo const& di) + { + overlay::calculate_distance_policy::apply(info, p1, p2, ii, di); + } + }; + + + struct IsContinueTurn + { + template + bool operator()(Turn const& turn) const + { + if ( turn.method != overlay::method_collinear && + turn.method != overlay::method_equal ) + { + return false; + } + overlay::operation_type op[2]; + op[0] = turn.operations[0].operation; + op[1] = turn.operations[1].operation; + + return + (op[0] == overlay::operation_continue || + op[0] == overlay::operation_opposite) && + (op[1] == overlay::operation_continue || + op[1] == overlay::operation_opposite); + } + }; + + + template + static inline void filter_turns(Turns& turns) + { + typedef typename Turns::iterator TurnIt; + + TurnIt new_end = std::remove_if(turns.begin(), turns.end(), + IsContinueTurn()); + turns.resize( std::distance(turns.begin(), new_end) ); + } + + + template + < + typename Linear1, typename Linear2, + typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + OutputIterator oit, + Strategy const& ) + { + typedef geometry::model::multi_linestring + < + LinestringOut + > MultiLinestringOut; + + // MultiLinestringOut mls1, mls2; + // geometry::convert(multilinestring1, mls1); + // geometry::convert(multilinestring2, mls2); + + // assert( boost::size(mls1) > 0 ); + // assert( boost::size(mls2) > 0 ); + + + // canonical::apply(ls1); + // canonical::apply(ls2); + + // typedef typename point_type::type PointOut; + +#if 0 + typedef //overlay::assign_null_policy + overlay::calculate_distance_policy AssignPolicy; +#endif + // typedef //overlay::assign_null_policy + // detail::union_::assign_union_policy AssignPolicy; + + // typedef detail::disjoint::disjoint_interrupt_policy InterruptPolicy; + + Turns turns, reverse_turns; + + geometry::detail::relate::turns::get_turns + < + Linear1, + Linear2, + detail::get_turns::get_turn_info_type + < + Linear1, + Linear2, + AssignPolicy + > + >::apply(turns, linear1, linear2); + + Linear2 linear2_reverse = linear2; + geometry::reverse(linear2_reverse); + geometry::detail::relate::turns::get_turns + < + Linear1, + Linear2, + detail::get_turns::get_turn_info_type + < + Linear1, + Linear2, + AssignPolicy + > + >::apply(reverse_turns, linear1, linear2_reverse); + + if ( turns.empty() ) + { + // the two linestrings are disjoint; we return the first as is; + // canonical::apply(mls1); +#ifdef GEOMETRY_TEST_DEBUG + std::cout << "NO INTERSECTIONS" << std::endl; +#endif + // MK:: need to think about this + // std::copy(linear1.begin(), linear1.end(), oit); + oit = difference_no_intersections + < + LinestringOut, Linear1, typename tag::type + >::apply(linear1, oit); + return oit; + } + + // remove turns that have no added value +#if 1 + filter_turns(turns); + filter_turns(reverse_turns); +#endif + + // sort by seg_id, distance, and operation + typedef detail::turns::less_seg_dist_other_op<> less; + std::sort(boost::begin(turns), boost::end(turns), less()); + + typedef + detail::turns::less_seg_dist_other_op > rev_less; + std::sort(boost::begin(reverse_turns), boost::end(reverse_turns), + rev_less()); + +#ifdef GEOMETRY_TEST_DEBUG + detail::turns::print_turns(linear1, linear2, turns); + std::cout << std::endl << std::endl; + detail::turns::print_turns(linear1, linear2_reverse, reverse_turns); +#endif + + return detail::turns::following::follow + < + LinestringOut, + Linear1, + Linear2, + overlay_difference + >::apply(linear1, linear2, turns, reverse_turns, oit); + } +}; + + + //=========================================================================== //=========================================================================== //=========================================================================== diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index f98ffd81a..ee695b282 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -527,7 +527,7 @@ struct intersection_insert Reverse1, Reverse2, ReverseOut, linestring_tag, linestring_tag, linestring_tag, false, false, false - > : detail::difference::linestring_linestring_linestring + > : detail::difference::linear_linear_linestring {}; @@ -545,7 +545,25 @@ struct intersection_insert Reverse1, Reverse2, ReverseOut, linestring_tag, multi_linestring_tag, linestring_tag, false, false, false - > : detail::difference::linestring_multilinestring_linestring + > : detail::difference::linear_linear_linestring +{}; + + +template +< + typename MultiLineString, typename LineString, + typename LineStringOut, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiLineString, LineString, + LineStringOut, + overlay_difference, + Reverse1, Reverse2, ReverseOut, + multi_linestring_tag, linestring_tag, linestring_tag, + false, false, false + > : detail::difference::linear_linear_linestring {}; @@ -563,7 +581,7 @@ struct intersection_insert Reverse1, Reverse2, ReverseOut, multi_linestring_tag, multi_linestring_tag, linestring_tag, false, false, false - > : detail::difference::multilinestring_multilinestring_linestring + > : detail::difference::linear_linear_linestring {}; diff --git a/include/boost/geometry/algorithms/detail/turns/follow_linear_linear.hpp b/include/boost/geometry/algorithms/detail/turns/follow_linear_linear.hpp index d5fea80e7..ddd28ac2a 100644 --- a/include/boost/geometry/algorithms/detail/turns/follow_linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/turns/follow_linear_linear.hpp @@ -117,18 +117,184 @@ static inline bool is_leaving(Turn const& turn, Operation const& op, template < typename LineStringOut, - typename MultiLineString1, - typename MultiLineString2, + typename LineString1, + typename LineString2, overlay_type OverlayType > -class follow +class follow_linestring_linestring_linestring { protected: typedef typename point_type::type PointOut; typedef overlay::traversal_turn_info turn_info; - typedef typename boost::range_value::type LineString1; - typedef typename boost::range_value::type LineString2; + typedef typename boost::range_iterator + < + typename turn_info::container_type + >::type turn_operation_iterator_type; + + typedef detail::overlay::following::action_selector action; + + template + < + typename TurnIt, + typename TurnOpIt, + typename SegmentIdentifier, + typename OutputIterator + > + static inline OutputIterator + process_turn(TurnIt it, TurnIt it_r, + TurnOpIt iit, TurnOpIt iit_r, + bool& first, bool& entered, + std::size_t& enter_count, + LineString1 const& ls1, LineString2 const& ls2, + LineStringOut& current_piece, + SegmentIdentifier& current_segment_id, + OutputIterator oit) + { + if ( is_entering(*it, *iit) ) + { +#ifdef GEOMETRY_TEST_DEBUG + detail::overlay::debug_traverse(*it, *iit, "-> Entering"); +#endif + + entered = true; + if ( enter_count == 0 ) + { + action::enter(current_piece, ls1, current_segment_id, + iit->seg_id.segment_index, + it->point, *iit, oit); + } + ++enter_count; + } + else if ( is_staying_inside(*it, *iit, entered, first, ls1, ls2) ) + { +#ifdef GEOMETRY_TEST_DEBUG + detail::overlay::debug_traverse(*it, *iit, "-> Staying inside"); +#endif + + entered = true; + } + else if ( is_leaving(*it, *iit, *iit_r, entered, first, ls1, ls2) ) + { +#ifdef GEOMETRY_TEST_DEBUG + detail::overlay::debug_traverse(*it, *iit, "-> Leaving"); +#endif + + --enter_count; + if ( enter_count == 0 ) + { + entered = false; + action::leave(current_piece, ls1, current_segment_id, + iit->seg_id.segment_index, + it->point, *iit, oit); + } + } + first = false; + return oit; + } + + template + < + typename SegmentIdentifier, + typename OutputIterator + > + static inline OutputIterator + process_end(bool entered, + LineString1 const& linestring1, + SegmentIdentifier const& current_segment_id, + LineStringOut& current_piece, + OutputIterator oit) + { + if ( action::is_entered(entered) ) + { + geometry::copy_segments(linestring1, current_segment_id, + boost::size(linestring1) - 1, + current_piece); + } + + // Output the last one, if applicable + if (::boost::size(current_piece) > 1) + { + *oit++ = current_piece; + } + + return oit; + } + +public: + template + static inline OutputIterator apply(LineString1 const& linestring1, + LineString2 const& linestring2, + Turns& turns, + Turns& reverse_turns, + OutputIterator oit) + { + BOOST_CHECK( boost::size(turns) == boost::size(reverse_turns) ); + + typedef typename Turns::iterator TurnIt; + + // Iterate through all intersection points (they are + // ordered along the each line) + + LineStringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + + bool entered = false; + bool first = true; + // bool first_turn = true; + std::size_t enter_count = 0; + + TurnIt it = boost::begin(turns); + TurnIt it_r = boost::begin(reverse_turns); + for (; it != boost::end(turns); ++it, ++it_r) + { + turn_operation_iterator_type iit = boost::begin(it->operations); + turn_operation_iterator_type iit_r = boost::begin(it_r->operations); + ++iit_r; + + oit = process_turn(it, it_r, iit, iit_r, + first, entered, enter_count, + linestring1, linestring2, + current_piece, current_segment_id, + oit); + } + + BOOST_CHECK( enter_count == 0 ); + + return process_end(entered, linestring1, + current_segment_id, current_piece, + oit); + } +}; + + + +template +< + typename LineStringOut, + typename LineString, + typename MultiLineString, + overlay_type OverlayType +> +class follow_linestring_multilinestring_linestring + : follow_linestring_linestring_linestring + < + LineStringOut, + LineString, + typename boost::range_value::type, + OverlayType + > +{ +protected: + typedef typename boost::range_value::type LineString2; + + typedef follow_linestring_linestring_linestring + < + LineStringOut, LineString, LineString2, OverlayType + > Base; + + typedef typename point_type::type PointOut; + typedef overlay::traversal_turn_info turn_info; typedef typename boost::range_iterator < @@ -139,8 +305,94 @@ protected: public: template - static inline OutputIterator apply(MultiLineString1& multilinestring1, - MultiLineString2& multilinestring2, + static inline OutputIterator apply(LineString const& linestring, + MultiLineString const& multilinestring, + Turns& turns, + Turns& reverse_turns, + OutputIterator oit) + { + BOOST_CHECK( boost::size(turns) == boost::size(reverse_turns) ); + + typedef typename Turns::iterator TurnIt; + + // Iterate through all intersection points (they are + // ordered along the each line) + + LineStringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + + bool entered = false; + bool first = true; + std::size_t enter_count = 0; + + TurnIt it = boost::begin(turns); + TurnIt it_r = boost::begin(reverse_turns); + for (; it != boost::end(turns); ++it, ++it_r) + { + turn_operation_iterator_type iit = boost::begin(it->operations); + turn_operation_iterator_type iit_r = boost::begin(it_r->operations); + ++iit_r; + + LineString2 const* ls2 = + &*(boost::begin(multilinestring) + iit->other_id.multi_index); + + oit = Base::process_turn(it, it_r, iit, iit_r, + first, entered, enter_count, + linestring, *ls2, + current_piece, current_segment_id, + oit); + } + + BOOST_CHECK( enter_count == 0 ); + + return Base::process_end(entered, linestring, + current_segment_id, current_piece, + oit); + } +}; + + + + + +template +< + typename LineStringOut, + typename MultiLineString, + typename LineString, + overlay_type OverlayType +> +class follow_multilinestring_linestring_linestring + : follow_linestring_linestring_linestring + < + LineStringOut, + typename boost::range_value::type, + LineString, + OverlayType + > +{ +protected: + typedef typename boost::range_value::type LineString1; + + typedef follow_linestring_linestring_linestring + < + LineStringOut, LineString1, LineString, OverlayType + > Base; + + typedef typename point_type::type PointOut; + typedef overlay::traversal_turn_info turn_info; + + typedef typename boost::range_iterator + < + typename turn_info::container_type + >::type turn_operation_iterator_type; + + typedef detail::overlay::following::action_selector action; + +public: + template + static inline OutputIterator apply(MultiLineString const& multilinestring, + LineString const& linestring, Turns& turns, Turns& reverse_turns, OutputIterator oit) @@ -162,7 +414,7 @@ public: std::size_t enter_count = 0; // dummy initialization - LineString1& ls1 = *boost::begin(multilinestring1); + LineString1 const* ls1 = &*boost::begin(multilinestring); TurnIt it = boost::begin(turns); TurnIt it_r = boost::begin(reverse_turns); @@ -177,101 +429,256 @@ public: if ( first_turn ) { first_turn = false; - current_multi_id = iit->seg_id.multi_index; - ls1 = *(boost::begin(multilinestring1) + current_multi_id); } else { - if (action::is_entered(entered)) - { - geometry::copy_segments(ls1, current_segment_id, - boost::size(ls1) - 1, - current_piece); - } - - // Output the last one, if applicable - if (::boost::size(current_piece) > 1) - { - *oit++ = current_piece; - } + oit = Base::process_end(entered, *ls1, + current_segment_id, current_piece, + oit); + // reset values first = true; entered = false; enter_count = 0; - current_segment_id = geometry::segment_identifier(0, -1, -1, -1); + current_segment_id + = geometry::segment_identifier(0, -1, -1, -1); geometry::clear(current_piece); - current_multi_id = iit->seg_id.multi_index; - ls1 = *(boost::begin(multilinestring1) + current_multi_id); } + current_multi_id = iit->seg_id.multi_index; + ls1 = &*(boost::begin(multilinestring) + current_multi_id); } - LineString2 const& ls2 = - *(boost::begin(multilinestring2) + iit->other_id.multi_index); - - if ( is_entering(*it, *iit) ) - { -#ifdef GEOMETRY_TEST_DEBUG - detail::overlay::debug_traverse(*it, *iit, "-> Entering"); -#endif - - entered = true; - if ( enter_count == 0 ) - { - action::enter(current_piece, ls1, current_segment_id, - iit->seg_id.segment_index, - it->point, *iit, oit); - } - ++enter_count; - } - else if ( is_staying_inside(*it, *iit, entered, first, ls1, ls2) ) - { -#ifdef GEOMETRY_TEST_DEBUG - detail::overlay::debug_traverse(*it, *iit, "-> Staying inside"); -#endif - - entered = true; - } - else if ( is_leaving(*it, *iit, *iit_r, entered, first, ls1, ls2) ) - { -#ifdef GEOMETRY_TEST_DEBUG - detail::overlay::debug_traverse(*it, *iit, "-> Leaving"); -#endif - - --enter_count; - if ( enter_count == 0 ) - { - entered = false; - action::leave(current_piece, ls1, current_segment_id, - iit->seg_id.segment_index, - it->point, *iit, oit); - } - } - first = false; + oit = Base::process_turn(it, it_r, iit, iit_r, + first, entered, enter_count, + *ls1, linestring, + current_piece, current_segment_id, + oit); } -#ifdef GEOMETRY_TEST_DEBUG - std::cout << "*** enter count: " << enter_count << std::endl; -#endif BOOST_CHECK( enter_count == 0 ); - if (action::is_entered(entered)) - { - geometry::copy_segments(ls1, current_segment_id, - boost::size(ls1) - 1, - current_piece); - } - // Output the last one, if applicable - if (::boost::size(current_piece) > 1) - { - *oit++ = current_piece; - } - - return oit; + return Base::process_end(entered, *ls1, + current_segment_id, current_piece, + oit); } }; +template +< + typename LineStringOut, + typename MultiLineString1, + typename MultiLineString2, + overlay_type OverlayType +> +class follow_multilinestring_multilinestring_linestring + : follow_linestring_linestring_linestring + < + LineStringOut, + typename boost::range_value::type, + typename boost::range_value::type, + OverlayType + > +{ +protected: + typedef typename boost::range_value::type LineString1; + typedef typename boost::range_value::type LineString2; + + typedef follow_linestring_linestring_linestring + < + LineStringOut, LineString1, LineString2, OverlayType + > Base; + + typedef typename point_type::type PointOut; + typedef overlay::traversal_turn_info turn_info; + + typedef typename boost::range_iterator + < + typename turn_info::container_type + >::type turn_operation_iterator_type; + + typedef detail::overlay::following::action_selector action; + +public: + template + static inline OutputIterator apply(MultiLineString1 const& multilinestring1, + MultiLineString2 const& multilinestring2, + Turns& turns, + Turns& reverse_turns, + OutputIterator oit) + { + BOOST_CHECK( boost::size(turns) == boost::size(reverse_turns) ); + + typedef typename Turns::iterator TurnIt; + + // Iterate through all intersection points (they are + // ordered along the each line) + + LineStringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + int current_multi_id = -1; + + bool entered = false; + bool first = true; + bool first_turn = true; + std::size_t enter_count = 0; + + // dummy initialization + LineString1 const* ls1 = &*boost::begin(multilinestring1); + + TurnIt it = boost::begin(turns); + TurnIt it_r = boost::begin(reverse_turns); + for (; it != boost::end(turns); ++it, ++it_r) + { + turn_operation_iterator_type iit = boost::begin(it->operations); + turn_operation_iterator_type iit_r = boost::begin(it_r->operations); + ++iit_r; + + if ( iit->seg_id.multi_index != current_multi_id ) + { + if ( first_turn ) + { + first_turn = false; + } + else + { + oit = Base::process_end(entered, *ls1, + current_segment_id, current_piece, + oit); + + // reset values + first = true; + entered = false; + enter_count = 0; + current_segment_id + = geometry::segment_identifier(0, -1, -1, -1); + geometry::clear(current_piece); + } + current_multi_id = iit->seg_id.multi_index; + ls1 = &*(boost::begin(multilinestring1) + current_multi_id); + } + + LineString2 const* ls2 = + &*(boost::begin(multilinestring2) + iit->other_id.multi_index); + + oit = Base::process_turn(it, it_r, iit, iit_r, + first, entered, enter_count, + *ls1, *ls2, + current_piece, current_segment_id, + oit); + } + + BOOST_CHECK( enter_count == 0 ); + + return Base::process_end(entered, *ls1, + current_segment_id, current_piece, + oit); + } +}; + + + +template +< + typename LineStringOut, + typename Geometry1, + typename Geometry2, + overlay_type OverlayType, + typename Tag1 = typename tag::type, + typename Tag2 = typename tag::type +> +struct follow_dispatch + : not_implemented +{}; + + +template +< + typename LineStringOut, + typename Linestring1, + typename Linestring2, + overlay_type OverlayType +> +struct follow_dispatch + < + LineStringOut, Linestring1, Linestring2, + OverlayType, linestring_tag, linestring_tag + > : follow_linestring_linestring_linestring + < + LineStringOut, Linestring1, Linestring2, OverlayType + > +{}; + + +template +< + typename LineStringOut, + typename Linestring, + typename MultiLinestring, + overlay_type OverlayType +> +struct follow_dispatch + < + LineStringOut, Linestring, MultiLinestring, + OverlayType, linestring_tag, multi_linestring_tag + > : follow_linestring_multilinestring_linestring + < + LineStringOut, Linestring, MultiLinestring, OverlayType + > +{}; + + + +template +< + typename LineStringOut, + typename MultiLinestring, + typename Linestring, + overlay_type OverlayType +> +struct follow_dispatch + < + LineStringOut, MultiLinestring, Linestring, + OverlayType, multi_linestring_tag, linestring_tag + > : follow_multilinestring_linestring_linestring + < + LineStringOut, MultiLinestring, Linestring, OverlayType + > +{}; + + + +template +< + typename LineStringOut, + typename MultiLinestring1, + typename MultiLinestring2, + overlay_type OverlayType +> +struct follow_dispatch + < + LineStringOut, MultiLinestring1, MultiLinestring2, + OverlayType, multi_linestring_tag, multi_linestring_tag + > : follow_multilinestring_multilinestring_linestring + < + LineStringOut, MultiLinestring1, MultiLinestring2, OverlayType + > +{}; + + + +template +< + typename LineStringOut, + typename Geometry1, + typename Geometry2, + overlay_type OverlayType +> +struct follow + : follow_dispatch +{}; + } // namespace following diff --git a/test/algorithms/difference1.cpp b/test/algorithms/difference1.cpp index 54e7d51b9..71913052e 100644 --- a/test/algorithms/difference1.cpp +++ b/test/algorithms/difference1.cpp @@ -545,6 +545,58 @@ BOOST_AUTO_TEST_CASE( test_difference_linestring_multilinestring ) +BOOST_AUTO_TEST_CASE( test_difference_multilinestring_linestring ) +{ +#ifdef GEOMETRY_TEST_DEBUG + std::cout << std::endl << std::endl << std::endl; + std::cout << "*** MULTILINESTRING / LINESTRING DIFFERENCE ***" + << std::endl; + std::cout << std::endl; +#endif + + typedef linestring_type L; + typedef multi_linestring_type ML; + + typedef test_difference_of_geometries tester; + + // disjoint linestrings + tester() + (from_wkt("MULTILINESTRING((0 0,10 0,20 1),(1 0,7 0))"), + from_wkt("LINESTRING(1 1,2 2,4 3)"), + from_wkt("MULTILINESTRING((0 0,10 0,20 1),(1 0,7 0))"), + "mlldf01" + ); + + tester() + (from_wkt("MULTILINESTRING((0 0,10 0,20 1),(1 0,7 0))"), + from_wkt("LINESTRING(1 1,2 0,4 0)"), + from_wkt("MULTILINESTRING((0 0,2 0),(4 0,10 0,20 1),\ + (1 0,2 0),(4 0,7 0))"), + "mlldf02" + ); + + tester() + (from_wkt("MULTILINESTRING((0 0,101 0))"), + from_wkt("LINESTRING(-1 -1,1 0,101 0,200 -1)"), + from_wkt("MULTILINESTRING((0 0,1 0))"), + "mlldf03" + ); + + tester() + (from_wkt("MULTILINESTRING((0 0,20 0))"), + from_wkt("LINESTRING(0 1,1 0,19 0,20 1,19 1,18 0,2 0,\ + 1 1,2 1,3 0,17 0,18 1,17 1,16 0,4 0,3 1)"), + from_wkt("MULTILINESTRING((0 0,1 0),(19 0,20 0))"), + "mlldf04" + ); +} + + + + + + + BOOST_AUTO_TEST_CASE( test_difference_multilinestring_multilinestring ) { #ifdef GEOMETRY_TEST_DEBUG