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;
This commit is contained in:
Menelaos Karavelas
2014-02-19 13:37:04 +02:00
parent 1f9ccb6698
commit 0ca5d2f13a
4 changed files with 788 additions and 86 deletions

View File

@@ -30,6 +30,231 @@ namespace detail { namespace difference
{
//===========================================================================
//===========================================================================
//===========================================================================
template <typename LineStringOut, typename Geometry, typename GeometryTag>
struct difference_no_intersections;
template <typename LineStringOut, typename LineString>
struct difference_no_intersections<LineStringOut, LineString, linestring_tag>
{
template <typename OutputIterator>
static inline OutputIterator apply(LineString const& linestring,
OutputIterator oit)
{
LineStringOut ls_out;
geometry::convert(linestring, ls_out);
*oit++ = ls_out;
return oit;
}
};
template <typename LineStringOut, typename MultiLineString>
struct difference_no_intersections
<
LineStringOut, MultiLineString, multi_linestring_tag
>
{
template <typename OutputIterator>
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 <typename LinestringOut>
struct linear_linear_linestring
{
typedef typename point_type<LinestringOut>::type PointOut;
typedef overlay::traversal_turn_info<PointOut> turn_info;
typedef std::vector<turn_info> 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 <typename Turn>
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 <typename Turns>
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<LinestringOut>::apply(ls1);
// canonical<LinestringOut>::apply(ls2);
// typedef typename point_type<LinestringOut>::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<MultiLinestringOut>::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<Linear1>::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<std::greater<int> > 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);
}
};
//===========================================================================
//===========================================================================
//===========================================================================

View File

@@ -527,7 +527,7 @@ struct intersection_insert
Reverse1, Reverse2, ReverseOut,
linestring_tag, linestring_tag, linestring_tag,
false, false, false
> : detail::difference::linestring_linestring_linestring<LineStringOut>
> : detail::difference::linear_linear_linestring<LineStringOut>
{};
@@ -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<LineStringOut>
> : detail::difference::linear_linear_linestring<LineStringOut>
{};
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<LineStringOut>
{};
@@ -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<LineStringOut>
> : detail::difference::linear_linear_linestring<LineStringOut>
{};

View File

@@ -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<LineStringOut>::type PointOut;
typedef overlay::traversal_turn_info<PointOut> turn_info;
typedef typename boost::range_value<MultiLineString1>::type LineString1;
typedef typename boost::range_value<MultiLineString2>::type LineString2;
typedef typename boost::range_iterator
<
typename turn_info::container_type
>::type turn_operation_iterator_type;
typedef detail::overlay::following::action_selector<OverlayType> 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<false>(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 <typename Turns, typename OutputIterator>
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<MultiLineString>::type,
OverlayType
>
{
protected:
typedef typename boost::range_value<MultiLineString>::type LineString2;
typedef follow_linestring_linestring_linestring
<
LineStringOut, LineString, LineString2, OverlayType
> Base;
typedef typename point_type<LineStringOut>::type PointOut;
typedef overlay::traversal_turn_info<PointOut> turn_info;
typedef typename boost::range_iterator
<
@@ -139,8 +305,94 @@ protected:
public:
template <typename Turns, typename OutputIterator>
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<MultiLineString>::type,
LineString,
OverlayType
>
{
protected:
typedef typename boost::range_value<MultiLineString>::type LineString1;
typedef follow_linestring_linestring_linestring
<
LineStringOut, LineString1, LineString, OverlayType
> Base;
typedef typename point_type<LineStringOut>::type PointOut;
typedef overlay::traversal_turn_info<PointOut> turn_info;
typedef typename boost::range_iterator
<
typename turn_info::container_type
>::type turn_operation_iterator_type;
typedef detail::overlay::following::action_selector<OverlayType> action;
public:
template <typename Turns, typename OutputIterator>
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<false>(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<false>(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<MultiLineString1>::type,
typename boost::range_value<MultiLineString2>::type,
OverlayType
>
{
protected:
typedef typename boost::range_value<MultiLineString1>::type LineString1;
typedef typename boost::range_value<MultiLineString2>::type LineString2;
typedef follow_linestring_linestring_linestring
<
LineStringOut, LineString1, LineString2, OverlayType
> Base;
typedef typename point_type<LineStringOut>::type PointOut;
typedef overlay::traversal_turn_info<PointOut> turn_info;
typedef typename boost::range_iterator
<
typename turn_info::container_type
>::type turn_operation_iterator_type;
typedef detail::overlay::following::action_selector<OverlayType> action;
public:
template <typename Turns, typename OutputIterator>
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<Geometry1>::type,
typename Tag2 = typename tag<Geometry2>::type
>
struct follow_dispatch
: not_implemented<LineStringOut, Geometry1, Geometry2>
{};
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<LineStringOut, Geometry1, Geometry2, OverlayType>
{};
} // namespace following

View File

@@ -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<ML, L, ML> tester;
// disjoint linestrings
tester()
(from_wkt<ML>("MULTILINESTRING((0 0,10 0,20 1),(1 0,7 0))"),
from_wkt<L>("LINESTRING(1 1,2 2,4 3)"),
from_wkt<ML>("MULTILINESTRING((0 0,10 0,20 1),(1 0,7 0))"),
"mlldf01"
);
tester()
(from_wkt<ML>("MULTILINESTRING((0 0,10 0,20 1),(1 0,7 0))"),
from_wkt<L>("LINESTRING(1 1,2 0,4 0)"),
from_wkt<ML>("MULTILINESTRING((0 0,2 0),(4 0,10 0,20 1),\
(1 0,2 0),(4 0,7 0))"),
"mlldf02"
);
tester()
(from_wkt<ML>("MULTILINESTRING((0 0,101 0))"),
from_wkt<L>("LINESTRING(-1 -1,1 0,101 0,200 -1)"),
from_wkt<ML>("MULTILINESTRING((0 0,1 0))"),
"mlldf03"
);
tester()
(from_wkt<ML>("MULTILINESTRING((0 0,20 0))"),
from_wkt<L>("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<ML>("MULTILINESTRING((0 0,1 0),(19 0,20 0))"),
"mlldf04"
);
}
BOOST_AUTO_TEST_CASE( test_difference_multilinestring_multilinestring )
{
#ifdef GEOMETRY_TEST_DEBUG