From 12797dbfb7626746ced362adc77f38d048fe1d43 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 20 Apr 2014 01:36:08 +0200 Subject: [PATCH] [get_turns][relate] Handle spikes for L/L !opposite collinear and equal turns. 2 Turns are generated for a spike, one with operation_blocked the second one with operation_intersection. relate_linear_linear is not working yet, for now it is only ensured that boundaries will not be checked for those turns, which would result with assert failure. TODO: opposite collinear and equal, touches c/c and endpoints intersecting a spike + the adaptation of relate(L,L). --- .../overlay/get_turn_info_for_endpoint.hpp | 69 ++++++ .../detail/overlay/get_turn_info_ll.hpp | 196 +++++++++--------- .../detail/relate/linear_linear.hpp | 8 +- .../overlay/get_turns_linear_linear.cpp | 24 +++ test/algorithms/relate_linear_linear.cpp | 29 ++- 5 files changed, 219 insertions(+), 107 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp index 78a1e715b..816c7bedf 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp @@ -21,6 +21,8 @@ namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay { +// TURN_OPERATION + enum turn_position { position_middle, position_front, position_back }; struct turn_operation_linear @@ -35,6 +37,73 @@ struct turn_operation_linear bool is_collinear; // valid only for Linear geometry }; +// IS_SPIKE + +template +inline bool is_spike_of_collinear(model::referring_segment const& s1, + model::referring_segment const& s2) +{ + typedef strategy_intersection + < + typename cs_tag::type, Point, Point, Point + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + typename strategy::return_type result = strategy::apply(s1, s2); + + return result.template get<0>().count == 2; +} + +template +inline bool is_spike_p(side_calculator const& side_calc, + model::referring_segment const& p1, + model::referring_segment const& p2) +{ + if ( side_calc.pk_wrt_p1() == 0 ) + { + int const qk_p1 = side_calc.qk_wrt_p1(); + int const qk_p2 = side_calc.qk_wrt_p2(); + + if ( qk_p1 == -qk_p2 ) + { + if ( qk_p1 == 0 ) + { + return is_spike_of_collinear(p1, p2); + } + + return true; + } + } + + return false; +} + +template +inline bool is_spike_q(side_calculator const& side_calc, + model::referring_segment const& q1, + model::referring_segment const& q2) +{ + if ( side_calc.qk_wrt_q1() == 0 ) + { + int const pk_q1 = side_calc.pk_wrt_q1(); + int const pk_q2 = side_calc.pk_wrt_q2(); + + if ( pk_q1 == -pk_q2 ) + { + if ( pk_q1 == 0 ) + { + return is_spike_of_collinear(q1, q2); + } + + return true; + } + } + + return false; +} + + // SEGMENT_INTERSECTION RESULT // C H0 H1 A0 A1 O IP1 IP2 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 7408d13c4..d4d055fb0 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 @@ -22,65 +22,11 @@ namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay { -template > -class spike_detector -{ -public: - explicit spike_detector(SideCalc const& side_calc) - : m_side_calc(side_calc) - {} - - inline bool is_spike_p() const - { - if ( m_side_calc.pk_wrt_p1() == 0 ) - { - int const qk_p1 = m_side_calc.qk_wrt_p1(); - int const qk_p2 = m_side_calc.qk_wrt_p2(); - - if ( qk_p1 == -qk_p2 ) - { - if ( qk_p1 == 0 ) - { - // TODO check additional things - } - - return true; - } - } - - return false; - } - - inline bool is_spike_q() const - { - if ( m_side_calc.qk_wrt_q1() == 0 ) - { - int const pk_q1 = m_side_calc.pk_wrt_q1(); - int const pk_q2 = m_side_calc.pk_wrt_q2(); - - if ( pk_q1 == -pk_q2 ) - { - if ( pk_q1 == 0 ) - { - // TODO check additional things - } - - return true; - } - } - - return false; - } - -private: - SideCalc const& m_side_calc; -}; - template struct get_turn_info_linear_linear { + static const bool handle_spikes = true; + template < typename Point1, @@ -222,6 +168,8 @@ struct get_turn_info_linear_linear replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation); + // TODO: HANDLE SPIKES! + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } @@ -247,28 +195,26 @@ struct get_turn_info_linear_linear // or collinear-and-ending at intersection point equal::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>(), side_calc); + // for spikes u/i or i/u is returned replacer_of_method_and_operations_ec replacer(method_touch); replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); - // TODO: This isn't correct handling, hence commented out - /*spike_detector spike_detect(side_calc); - if ( tp.operations[0].operation == operation_union - && spike_detect.is_spike_p()) - { - tp.operations[0].operation = operation_continue; - } - if ( tp.operations[1].operation == operation_union - && spike_detect.is_spike_q()) - { - tp.operations[1].operation = operation_continue; - }*/ - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); - *out++ = tp; + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, side_calc, p1, p2, q1, q2, + is_p_last, is_q_last, + method_touch, operation_union, out) ) + { + *out++ = tp; // no spikes + } } else { + // TODO: HANDLE SPIKES! + equal_opposite < TurnInfo, @@ -295,56 +241,57 @@ struct get_turn_info_linear_linear tp.operations[1].is_collinear = true; if (! result.template get<1>().opposite) - { + { + method_type method_replace = method_touch_interior; + operation_type spike_op = operation_continue; + if (result.template get<1>().arrival[0] == 0) { // Collinear, but similar thus handled as equal equal::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>(), side_calc); + // for spikes u/i or i/u is returned // NOTE: don't change the method only if methods are WRT IPs, not segments! // (currently this approach is used) // override assigned method //tp.method = method_collinear; - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); - - // TODO: This isn't correct handling, hence commented out - /*spike_detector spike_detect(side_calc); - if ( tp.operations[0].operation == operation_union - && spike_detect.is_spike_p()) - { - tp.operations[0].operation = operation_continue; - } - if ( tp.operations[1].operation == operation_union - && spike_detect.is_spike_q()) - { - tp.operations[1].operation = operation_continue; - }*/ + method_replace = method_touch; + spike_op = operation_union; } else { collinear::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>(), side_calc); + // for spikes c,c/c is returned - replacer_of_method_and_operations_ec replacer(method_touch_interior); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); - - // TEST - //spike_detector spike_detect(side_calc); - //spike_detect.is_spike_p(); - //spike_detect.is_spike_q(); + //method_replace = method_touch_interior; + //spike_op = operation_continue; } + replacer_of_method_and_operations_ec replacer(method_replace); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); - *out++ = tp; + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, side_calc, p1, p2, q1, q2, + is_p_last, is_q_last, + method_replace, spike_op, out) ) + { + // no spikes + *out++ = tp; + } } else { // If this always 'm' ? replacer_of_method_and_operations_ec replacer(method_touch_interior); + // TODO: HANDLE SPIKES! + collinear_opposite < TurnInfo, @@ -405,6 +352,67 @@ struct get_turn_info_linear_linear return out; } + template + static inline bool append_collinear_spikes(TurnInfo & tp, + SideCalc const& side_calc, + SegmentP const& p1, SegmentP const& p2, + SegmentQ const& q1, SegmentQ const& q2, + bool is_p_last, bool is_q_last, + method_type method, operation_type spike_op, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = tp.operations[0].operation == spike_op + && ! is_p_last + && is_spike_p(side_calc, p1, p2); + bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && is_spike_q(side_calc, q1, q2); + + if ( is_p_spike && is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + else if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + *out++ = tp; + + return true; + } + else if ( is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + + return false; + } + static inline void replace_method_and_operations_tm(method_type & method, operation_type & op0, operation_type & op1) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 46e4726f2..62f30a688 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -466,8 +466,9 @@ struct linear_linear // interiors overlaps update(res); - - bool this_b = is_ip_on_boundary(it->point, + + bool this_b = it->operations[op_id].position == overlay::position_front // ignore spikes! + && is_ip_on_boundary(it->point, it->operations[op_id], boundary_checker, seg_id); @@ -539,7 +540,8 @@ struct linear_linear // possibly going out right now if ( ! was_outside && is_collinear ) { - if ( op_blocked ) + if ( op_blocked + && it->operations[op_id].position == overlay::position_back ) // ignore spikes! { // check if this is indeed the boundary point // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index fdafbdae2..dfdb6fe4d 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -109,6 +109,30 @@ void test_all() test_geometry("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,5 0,4 1)", expected("mii")("muu")("muu")); + // spike - c2 + test_geometry("LINESTRING(2 2,4 4,1 1)", "LINESTRING(0 0,4 4,6 3)", + expected("mii")("txu")("tiu")("mxi")); + // spike - e + test_geometry("LINESTRING(0 0,4 4,1 1)", "LINESTRING(0 0,4 4,6 3)", + expected("tii")("txu")("tiu")("mxi")); + // spike - c1 + test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(0 0,4 4,6 3)", + expected("tii")("mxu")("miu")("mxi")); + // opposite e + //test_geometry("LINESTRING(4 4,0 0,2 2)", "LINESTRING(0 0,4 4,6 3)", + // expected("tiu")("txi")("tii")("mxu")); + + // TODO: + //test_geometry("LINESTRING(0 0,2 0,1 0)", "LINESTRING(0 1,0 0,2 0)", "1FF00F102"); + //test_geometry("LINESTRING(2 0,0 0,1 0)", "LINESTRING(0 1,0 0,2 0)", "1FF00F102"); + + //test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(3 0,3 3,3 1)", "0F1FF0102"); + //test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(2 0,2 3,2 1)", "0F1FF0102"); + //test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(2 0,2 2,2 1)", "0F1FF0102"); + + //test_geometry("LINESTRING(0 0,2 2,3 3,4 4)", "LINESTRING(0 0,1 1,4 4)", "1FFF0FFF2"); + + //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"); diff --git a/test/algorithms/relate_linear_linear.cpp b/test/algorithms/relate_linear_linear.cpp index 73b5e7b51..663a96d93 100644 --- a/test/algorithms/relate_linear_linear.cpp +++ b/test/algorithms/relate_linear_linear.cpp @@ -69,18 +69,27 @@ void test_linestring_linestring() // test_geometry("LINESTRING(0 0,5 0,3 0,6 0)", "LINESTRING(0 0,6 0)", true); // test_geometry("LINESTRING(0 0,2 2,3 3,1 1)", "LINESTRING(0 0,3 3,6 3)", true); - // SPIKES! CURRENTLY NOT HANDLED - //test_geometry("LINESTRING(0 0,2 2,3 3,1 1,5 3)", "LINESTRING(0 0,3 3,6 3)", "1F100F102"); - //test_geometry("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(0 0,3 3,6 3)", "1F100F102"); - //test_geometry("LINESTRING(0 0,2 2,3 3,1 1,5 3)", "LINESTRING(6 3,3 3,0 0)", "1F100F102"); - //test_geometry("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(6 3,3 3,0 0)", "1F100F102"); + // SPIKES! + test_geometry("LINESTRING(0 0,2 2,3 3,1 1,5 3)", "LINESTRING(0 0,3 3,6 3)", "1F100F102"); + test_geometry("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(0 0,3 3,6 3)", "1F100F102"); + test_geometry("LINESTRING(0 0,2 2,3 3,1 1,5 3)", "LINESTRING(6 3,3 3,0 0)", "1F100F102"); + test_geometry("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(6 3,3 3,0 0)", "1F100F102"); - // spikes - // FOR NOW DISABLED - /*test_geometry("LINESTRING(0 0,10 0)", - "LINESTRING(1 0,9 0,2 0)", "101FF0FF2"); + test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(1 0,9 0,2 0)", "101FF0FF2"); test_geometry("LINESTRING(0 0,2 2,3 3,1 1)", "LINESTRING(0 0,3 3,6 3)", "1FF00F102"); - test_geometry("LINESTRING(0 0,2 2,3 3,1 1)", "LINESTRING(0 0,4 4,6 3)", "1FF00F102");*/ + // TODO: REWRITE MATRICES + // BEGIN + /*test_geometry("LINESTRING(0 0,2 2,3 3,1 1)", "LINESTRING(0 0,4 4,6 3)", "1FF00F102"); + + test_geometry("LINESTRING(0 0,2 0,1 0)", "LINESTRING(0 1,0 0,2 0)", "1FF00F102"); + test_geometry("LINESTRING(2 0,0 0,1 0)", "LINESTRING(0 1,0 0,2 0)", "1FF00F102"); + + test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(3 0,3 3,3 1)", "0F1FF0102"); + test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(2 0,2 3,2 1)", "0F1FF0102"); + test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(2 0,2 2,2 1)", "0F1FF0102"); + + test_geometry("LINESTRING(0 0,2 2,3 3,4 4)", "LINESTRING(0 0,1 1,4 4)", "1FFF0FFF2");*/ + // END test_geometry("LINESTRING(0 0,2 2,3 3,4 4)", "LINESTRING(0 0,1 1,4 4)", "1FFF0FFF2");