From 288c7e6980986cc49b5f18f42ffc1c40e5445c6c Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 1 May 2014 12:37:54 +0200 Subject: [PATCH 01/10] [get_turns] Fix for L/L spike vs. endpoint for the same direction segments. Add failing test cases for spikes touching internal point --- .../overlay/get_turn_info_for_endpoint.hpp | 61 +++++++++++-------- .../overlay/get_turns_linear_linear.cpp | 16 +++++ 2 files changed, 52 insertions(+), 25 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 ceb291691..fca64e3ed 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 @@ -484,20 +484,20 @@ struct get_turn_info_for_endpoint if ( append_first || append_last ) { - bool handled = handle_internal(pi, pj, pk, qi, qj, qk, - is_p_first_ip, is_p_last_ip, - is_q_first_ip, is_q_last_ip, - ip_info.is_qi, ip_info.is_qj, - tp_model, inters.result(), ip_index, - p_operation, q_operation); + bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk, + is_p_first_ip, is_p_last_ip, + is_q_first_ip, is_q_last_ip, + ip_info.is_qi, ip_info.is_qj, + tp_model, inters, ip_index, + p_operation, q_operation); if ( !handled ) { - handle_internal(qi, qj, qk, pi, pj, pk, - is_q_first_ip, is_q_last_ip, - is_p_first_ip, is_p_last_ip, - ip_info.is_pi, ip_info.is_pj, - tp_model, inters.result(), ip_index, - q_operation, p_operation); + handle_internal<1>(qi, qj, qk, pi, pj, pk, + is_q_first_ip, is_q_last_ip, + is_p_first_ip, is_p_last_ip, + ip_info.is_pi, ip_info.is_pj, + tp_model, inters, ip_index, + q_operation, p_operation); } if ( p_operation != operation_none ) @@ -546,16 +546,17 @@ struct get_turn_info_for_endpoint // TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment // however now it's lazily calculated and then it would be always calculated - template static inline bool handle_internal(Point1 const& i1, Point1 const& j1, Point1 const& /*k1*/, Point2 const& i2, Point2 const& j2, Point2 const& k2, bool first1, bool last1, bool first2, bool last2, bool ip_i2, bool ip_j2, TurnInfo const& tp_model, - IntersectionResult const& result, int ip_index, + IntersectionInfo const& inters, int ip_index, operation_type & op1, operation_type & op2) { boost::ignore_unused_variable_warning(ip_index); @@ -568,7 +569,7 @@ struct get_turn_info_for_endpoint #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR // may this give false positives for INTs? typename IntersectionResult::point_type const& - inters_pt = result.template get<0>().intersections[ip_index]; + inters_pt = inters.i_info().intersections[ip_index]; BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); #endif @@ -591,11 +592,16 @@ struct get_turn_info_for_endpoint if ( operations_both(operations, operation_continue) ) { - // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! - bool opposite = result.template get<1>().opposite; + if ( op1 != operation_union + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool opposite = inters.d_info().opposite; - op1 = operation_intersection; - op2 = opposite ? operation_union : operation_intersection; + op1 = operation_intersection; + op2 = opposite ? operation_union : operation_intersection; + } } else { @@ -613,7 +619,7 @@ struct get_turn_info_for_endpoint #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR // may this give false positives for INTs? typename IntersectionResult::point_type const& - inters_pt = result.template get<0>().intersections[ip_index]; + inters_pt = inters.i_info().intersections[ip_index]; BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); #endif @@ -636,11 +642,16 @@ struct get_turn_info_for_endpoint if ( operations_both(operations, operation_continue) ) { - // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! - bool second_going_out = result.template get<0>().count > 1; + if ( op1 != operation_blocked + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool second_going_out = inters.i_info().count > 1; - op1 = operation_blocked; - op2 = second_going_out ? operation_union : operation_intersection; + op1 = operation_blocked; + op2 = second_going_out ? operation_union : operation_intersection; + } } else { diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 57a7e1af4..1b65472a6 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -252,6 +252,22 @@ void test_all() test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,-2 0),(11 0,10 0,12 0))", expected("tuu")("txu")); + // spike vs internal + test_geometry("MULTILINESTRING((1 0,1 1,2 1))", + "MULTILINESTRING((0 1,1 1,0 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,2 1))", + "MULTILINESTRING((1 2,1 1,1 2))", + expected("tuu")); + test_geometry("MULTILINESTRING((0 0,1 0,0 0))", + "MULTILINESTRING((2 0,1 0,2 0))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,2 1))", + "MULTILINESTRING((0 2,1 1,0 2))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,2 1))", + "MULTILINESTRING((2 0,1 1,2 0))", + expected("tuu")); // non-spike similar test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,2 0))", From d4d7bf9657131b3f4d9255c528eadf7f8d3a8b0b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 3 May 2014 02:40:21 +0200 Subject: [PATCH 02/10] [get_turns] Fix the generation of some special cases of touch-spike-turns for L/L and L/A --- .../detail/overlay/get_turn_info_la.hpp | 83 +++++++++++++- .../detail/overlay/get_turn_info_ll.hpp | 103 +++++++++++++++++- .../overlay/get_turns_linear_areal.cpp | 24 ++++ .../overlay/get_turns_linear_linear.cpp | 24 +++- 4 files changed, 223 insertions(+), 11 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp index 003bac689..c1c5bed66 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -152,11 +152,62 @@ struct get_turn_info_linear_areal tp.operations[0].is_collinear = true; } + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // Only P spikes are valid for L/A + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + if ( inters.is_spike_p() ) + { + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + tp.operations[0].is_collinear = true; + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation); - // this function assumes that 'u' must be set for a spike bool ignore_spike = calculate_spike_operation(tp.operations[0].operation, inters, is_p_last); @@ -166,7 +217,7 @@ struct get_turn_info_linear_areal if ( ! handle_spikes || ignore_spike - || ! append_opposite_spikes( // for 'i' or 'c' + || ! append_opposite_spikes( // for 'i' or 'c' i??? tp, inters, is_p_last, is_q_last, out) ) { *out++ = tp; @@ -344,18 +395,38 @@ struct get_turn_info_linear_areal IntersectionInfo const& inters, bool is_p_last) { - bool is_p_spike = op == operation_union + bool is_p_spike = op == operation_union || op == operation_intersection && ! is_p_last && inters.is_spike_p(); - // we don't know where the spike is going since for both directions 'u' is set if ( is_p_spike ) { - if ( inters.sides().pk_wrt_q1() < 0 && inters.sides().pk_wrt_q2() < 0 ) + bool going_in = false, going_out = false; + + int const pk_q1 = inters.sides().pk_wrt_q1(); + int const pk_q2 = inters.sides().pk_wrt_q2(); + + if ( inters.sides().qk_wrt_q1() <= 0 ) // Q turning R or C + { + going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both + going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them + } + else + { + going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them + going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both + } + + if ( going_in ) { op = operation_intersection; return true; } + else if ( going_out ) + { + op = operation_union; + return true; + } } return false; @@ -416,7 +487,7 @@ struct get_turn_info_linear_areal { bool is_p_spike = ( Version == append_touches ? ( tp.operations[0].operation == operation_continue - || tp.operations[0].operation == operation_intersection ) : + || tp.operations[0].operation == operation_intersection ) : // i ??? true ) && ! is_p_last && inters.is_spike_p(); 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 4f1cc5232..e7426257e 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 @@ -143,14 +143,109 @@ struct get_turn_info_linear_linear touch::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info(), inters.sides()); - if ( tp.operations[0].operation == operation_blocked ) + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked + && tp.operations[1].operation == operation_blocked ) { - tp.operations[1].is_collinear = true; + // two touching spikes on the same line + if ( inters.is_spike_p() && inters.is_spike_q() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + } } - if ( tp.operations[1].operation == operation_blocked ) + else if ( tp.operations[0].operation == operation_blocked ) { - tp.operations[0].is_collinear = true; + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + else + { + tp.operations[1].is_collinear = true; + } } + else if ( tp.operations[1].operation == operation_blocked ) + { + // a spike on Q on the same line with P1 + if ( inters.is_spike_q() ) + { + if ( inters.sides().pk_wrt_q1() == 0 ) + { + tp.operations[1].is_collinear = true; + } + else + { + tp.operations[1].operation = operation_union; + } + } + else + { + tp.operations[0].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + bool const is_p = inters.is_spike_p(); + bool const is_q = inters.is_spike_q(); + + if ( is_p || is_q ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + if ( is_p ) + { + tp.operations[0].is_collinear = true; + } + } + + if ( inters.sides().qk_wrt_p2() == 0 ) + { + tp.operations[1].operation = operation_continue; // will be converted to i + if ( is_q ) + { + tp.operations[1].is_collinear = true; + } + } + } + } + + // workarounds for touch<> not taking spikes into account ends here replace_method_and_operations_tm(tp.method, tp.operations[0].operation, diff --git a/test/algorithms/overlay/get_turns_linear_areal.cpp b/test/algorithms/overlay/get_turns_linear_areal.cpp index 804516afb..0b1d624b6 100644 --- a/test/algorithms/overlay/get_turns_linear_areal.cpp +++ b/test/algorithms/overlay/get_turns_linear_areal.cpp @@ -131,6 +131,30 @@ void test_all() // opposite - neq neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "POLYGON((-2 -2,-1 -1,3 3,5 5,6 3,-2 -2))", expected("mcu")("mxc")("mcc")("mxu")); + + // spike vs internal + test_geometry("LINESTRING(0 1,1 1,0 1)", // -- + "POLYGON((1 0,1 1,2 1,1 0))", + expected("tuu")); + test_geometry("LINESTRING(1 2,1 1,1 2)", // | + "POLYGON((1 0,1 1,2 1,1 0))", + expected("tuu")); + test_geometry("LINESTRING(0 2,1 1,0 2)", + "POLYGON((1 0,1 1,2 1,1 0))", + expected("tuu")); + test_geometry("LINESTRING(2 0,1 1,2 0)", + "POLYGON((1 0,1 1,2 1,2 0,1 0))", + expected("tiu")("tiu")("txu")); // TODO: should spike point be duplicated? + test_geometry("LINESTRING(0 0,1 1,0 0)", // / + "POLYGON((1 0,1 1,2 1,1 0))", + expected("tuu")); + test_geometry("LINESTRING(2 2,1 1,2 2)", // / + "POLYGON((1 0,1 1,2 1,1 0))", + expected("tuu")); + + test_geometry("LINESTRING(2 1,1 1,2 1)", + "POLYGON((1 0,1 1,2 1,1 0))", + expected("tcu")("txc")("tcc")("txu")); } int test_main(int, char* []) diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 1b65472a6..3a7b9efed 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -252,7 +252,7 @@ void test_all() test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,-2 0),(11 0,10 0,12 0))", expected("tuu")("txu")); - // spike vs internal + // internal vs spike test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((0 1,1 1,0 1))", expected("tuu")); @@ -268,6 +268,28 @@ void test_all() test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((2 0,1 1,2 0))", expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,2 1))", + "MULTILINESTRING((2 1,1 1,2 1))", + expected("txi")("tix")("tii")("txx")); + // spike vs internal + test_geometry("MULTILINESTRING((0 1,1 1,0 1))", + "MULTILINESTRING((1 0,1 1,2 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 2,1 1,1 2))", + "MULTILINESTRING((1 0,1 1,2 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((2 0,1 0,2 0))", + "MULTILINESTRING((0 0,1 0,0 0))", + expected("tuu")); + test_geometry("MULTILINESTRING((0 2,1 1,0 2))", + "MULTILINESTRING((1 0,1 1,2 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((2 0,1 1,2 0))", + "MULTILINESTRING((1 0,1 1,2 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((2 1,1 1,2 1))", + "MULTILINESTRING((1 0,1 1,2 1))", + expected("tix")("txi")("tii")("txx")); // non-spike similar test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,2 0))", From 6abe245eb7fed6f2ff424504e42ef0dfcd09cc33 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 3 May 2014 02:47:58 +0200 Subject: [PATCH 03/10] [get_turns] Fix invalid condition get_turn_info_la --- .../geometry/algorithms/detail/overlay/get_turn_info_la.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp index c1c5bed66..3eab8e60e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -395,7 +395,7 @@ struct get_turn_info_linear_areal IntersectionInfo const& inters, bool is_p_last) { - bool is_p_spike = op == operation_union || op == operation_intersection + bool is_p_spike = ( op == operation_union || op == operation_intersection ) && ! is_p_last && inters.is_spike_p(); From 7192e6cbf66b86860a70639c355e94d762e35cfc Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 3 May 2014 04:02:44 +0200 Subject: [PATCH 04/10] [get_turns] Add more tests for t-spikes for L/L --- .../overlay/get_turns_linear_linear.cpp | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 3a7b9efed..181dfaa97 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -252,7 +252,7 @@ void test_all() test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,-2 0),(11 0,10 0,12 0))", expected("tuu")("txu")); - // internal vs spike + // internal turning R vs spike test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((0 1,1 1,0 1))", expected("tuu")); @@ -271,7 +271,23 @@ void test_all() test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((2 1,1 1,2 1))", expected("txi")("tix")("tii")("txx")); - // spike vs internal + // internal turning L vs spike + test_geometry("MULTILINESTRING((1 0,1 1,0 1))", + "MULTILINESTRING((2 1,1 1,2 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,0 1))", + "MULTILINESTRING((1 2,1 1,1 2))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,0 1))", + "MULTILINESTRING((2 2,1 1,2 2))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,0 1))", + "MULTILINESTRING((0 0,1 1,0 0))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 0,1 1,0 1))", + "MULTILINESTRING((0 1,1 1,0 1))", + expected("txi")("tix")("tii")("txx")); + // spike vs internal turning R test_geometry("MULTILINESTRING((0 1,1 1,0 1))", "MULTILINESTRING((1 0,1 1,2 1))", expected("tuu")); @@ -290,6 +306,22 @@ void test_all() test_geometry("MULTILINESTRING((2 1,1 1,2 1))", "MULTILINESTRING((1 0,1 1,2 1))", expected("tix")("txi")("tii")("txx")); + // spike vs internal turning L + test_geometry("MULTILINESTRING((2 1,1 1,2 1))", + "MULTILINESTRING((1 0,1 1,0 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((1 2,1 1,1 2))", + "MULTILINESTRING((1 0,1 1,0 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((2 2,1 1,2 2))", + "MULTILINESTRING((1 0,1 1,0 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((1 0,1 1,0 1))", + expected("tuu")); + test_geometry("MULTILINESTRING((0 1,1 1,0 1))", + "MULTILINESTRING((1 0,1 1,0 1))", + expected("tix")("txi")("tii")("txx")); // non-spike similar test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,2 0))", From 8775e65b2076ae62e3507f5b04bca315948021cd Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 3 May 2014 04:11:22 +0200 Subject: [PATCH 05/10] [get_turns] Add tests for t-spikes vs collinear internal pt for L/L --- test/algorithms/overlay/get_turns_linear_linear.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 181dfaa97..7197e9725 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -322,6 +322,14 @@ void test_all() test_geometry("MULTILINESTRING((0 1,1 1,0 1))", "MULTILINESTRING((1 0,1 1,0 1))", expected("tix")("txi")("tii")("txx")); + // spike vs internal collinear + test_geometry("MULTILINESTRING((0 1,1 1,0 1))", + "MULTILINESTRING((2 1,1 1,0 1))", + expected("tix")("txi")("tii")("txx")); + // internal collinear vs spike + test_geometry("MULTILINESTRING((2 1,1 1,0 1))", + "MULTILINESTRING((0 1,1 1,0 1))", + expected("txi")("tix")("tii")("txx")); // non-spike similar test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,2 0))", From 47d581304e52a174475388bd888d11db4815c0a2 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 3 May 2014 13:57:08 +0200 Subject: [PATCH 06/10] [relate][test] Enable tests for 2-Point degenerated Linestrings --- test/algorithms/relate_linear_linear.cpp | 59 +++++++++++------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/test/algorithms/relate_linear_linear.cpp b/test/algorithms/relate_linear_linear.cpp index ed5852aa7..6b484ed5c 100644 --- a/test/algorithms/relate_linear_linear.cpp +++ b/test/algorithms/relate_linear_linear.cpp @@ -172,12 +172,11 @@ void test_linestring_linestring() test_geometry("LINESTRING(1 0,1 6)", "LINESTRING(0 0,5 0,5 5,0 5)", "0F10F0102"); // point-size Linestring - // FOR NOW DISABLED, THE ROBUSTNESS UPGRADES BROKE POINT-SIZED LINESTRINGS - /*test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(0 0,5 0)", "0FFFFF102"); + test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(0 0,5 0)", "0FFFFF102"); test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(1 0,5 0)", "F0FFFF102"); test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(0 0,1 0)", "F0FFFF102"); test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(1 0,1 0)", "0FFFFFFF2"); - test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(0 0,0 0)", "FF0FFF0F2");*/ + test_geometry("LINESTRING(1 0,1 0)", "LINESTRING(0 0,0 0)", "FF0FFF0F2"); //to_svg("LINESTRING(0 0,5 0)", "LINESTRING(5 0,10 0,5 5,5 0)", "test_relate_00.svg"); @@ -238,32 +237,31 @@ void test_linestring_multi_linestring() //test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0,2 0))", "101FF0FF2"); // point-like - // FOR NOW DISABLED, THE ROBUSTNESS UPGRADES BROKE POINT-SIZED LINESTRINGS - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((0 0, 1 0),(2 0, 2 0))", // |------| * - // "101F00FF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((0 0, 1 0),(1 0, 1 0))", // |------* - // "101F00FF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((5 0, 1 0),(1 0, 1 0))", // *-------| - // "101F00FF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((0 0, 1 0),(5 0, 5 0))", // |------| * - // "10100FFF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((0 0, 1 0),(0 0, 0 0))", // *------| - // "101000FF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((4 0, 5 0),(5 0, 5 0))", // |------* - // "101000FF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((1 0, 2 0),(0 0, 0 0))", // * |------| - // "1010F0FF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((0 0, 1 0),(2 0, 2 0))", // |------| * + "101F00FF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((0 0, 1 0),(1 0, 1 0))", // |------* + "101F00FF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((5 0, 1 0),(1 0, 1 0))", // *-------| + "101F00FF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((0 0, 1 0),(5 0, 5 0))", // |------| * + "10100FFF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((0 0, 1 0),(0 0, 0 0))", // *------| + "101000FF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((4 0, 5 0),(5 0, 5 0))", // |------* + "101000FF2"); + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((1 0, 2 0),(0 0, 0 0))", // * |------| + "1010F0FF2"); - //test_geometry("LINESTRING(0 0, 5 0)", // |--------------| - // "MULTILINESTRING((2 0, 2 0),(2 0, 2 2))", // * - // "001FF0102"); // | + test_geometry("LINESTRING(0 0, 5 0)", // |--------------| + "MULTILINESTRING((2 0, 2 0),(2 0, 2 2))", // * + "001FF0102"); // | // for consistency test_geometry("LINESTRING(0 0, 5 0)", // |--------------| @@ -318,8 +316,7 @@ void test_multi_linestring_multi_linestring() "1F1F0F1F2"); // point-like - // FOR NOW DISABLED, THE ROBUSTNESS UPGRADES BROKE POINT-SIZED LINESTRINGS - /*test_geometry("MULTILINESTRING((0 0, 0 0),(1 1, 1 1))", + test_geometry("MULTILINESTRING((0 0, 0 0),(1 1, 1 1))", "MULTILINESTRING((0 0, 0 0))", "0F0FFFFF2"); test_geometry("MULTILINESTRING((0 0, 0 0),(1 1, 1 1))", @@ -327,7 +324,7 @@ void test_multi_linestring_multi_linestring() "0FFFFFFF2"); test_geometry("MULTILINESTRING((0 0, 0 0),(1 1, 1 1))", "MULTILINESTRING((2 2, 2 2),(3 3, 3 3))", - "FF0FFF0F2");*/ + "FF0FFF0F2"); } template From 7b2a4cb5896ae7fbaa8cd7a6b93e239859605caa Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 3 May 2014 14:41:33 +0200 Subject: [PATCH 07/10] [intersection] fix segments_intersection_ratios adding the new one_degenerate method --- .../policies/relate/intersection_ratios.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/boost/geometry/policies/relate/intersection_ratios.hpp b/include/boost/geometry/policies/relate/intersection_ratios.hpp index 0e4506eda..81ecba0b5 100644 --- a/include/boost/geometry/policies/relate/intersection_ratios.hpp +++ b/include/boost/geometry/policies/relate/intersection_ratios.hpp @@ -99,6 +99,24 @@ struct segments_intersection_ratios { return return_type(); } + + template + static inline return_type one_degenerate(Segment const& , + Ratio const& ratio, bool a_degenerate) + { + return_type result; + if (a_degenerate) + { + // IP lies on ratio w.r.t. segment b + result.assign(Ratio::zero(), ratio); + } + else + { + result.assign(ratio, Ratio::zero()); + } + return result; + } + }; From bff1c65b0af7f2bed34cab06116f343b4e8c9b4b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 3 May 2014 14:41:47 +0200 Subject: [PATCH 08/10] fixed comment --- include/boost/geometry/iterators/dispatch/point_iterator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/iterators/dispatch/point_iterator.hpp b/include/boost/geometry/iterators/dispatch/point_iterator.hpp index c07f3a865..938dfd8eb 100644 --- a/include/boost/geometry/iterators/dispatch/point_iterator.hpp +++ b/include/boost/geometry/iterators/dispatch/point_iterator.hpp @@ -38,7 +38,7 @@ struct points_end -} // namespace core_dispatch +} // namespace dispatch #endif // DOXYGEN_NO_DISPATCH From e6ef0b0328734bb95b2e3baca346b004a12fa83f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 4 May 2014 01:26:12 +0200 Subject: [PATCH 09/10] [get_turns] Use RobustPolicy in a unified way in all three get_turn_info* policies (A/A, L/A, and L/L). Newly introduced intersection_info containing IntersectionResult, SideCalculator and Point refs is used also in get_turn_info for A/A. Robust points are stored only if RobustPolicy is not no_rescale_policy. This commit also fixes an error in get_turn_info - swapped_side_calc created for method_touch_interior ('m') should use robust points just like the original one. --- .../detail/overlay/get_turn_info.hpp | 125 ++----- .../overlay/get_turn_info_for_endpoint.hpp | 180 +--------- .../detail/overlay/get_turn_info_helpers.hpp | 336 ++++++++++++++++++ .../detail/overlay/get_turn_info_la.hpp | 76 ++-- .../detail/overlay/get_turn_info_ll.hpp | 14 +- 5 files changed, 447 insertions(+), 284 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index 376f5a71f..20ccd9208 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -21,7 +21,7 @@ #include #include - +#include // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) @@ -60,36 +60,6 @@ public: namespace detail { namespace overlay { -template -struct side_calculator -{ - typedef boost::geometry::strategy::side::side_by_triangle<> side; // todo: get from coordinate system - - inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, - Qi const& qi, Qj const& qj, Qk const& qk) - : m_pi(pi), m_pj(pj), m_pk(pk) - , m_qi(qi), m_qj(qj), m_qk(qk) - {} - - inline int pk_wrt_p1() const { return side::apply(m_pi, m_pj, m_pk); } - inline int pk_wrt_q1() const { return side::apply(m_qi, m_qj, m_pk); } - inline int qk_wrt_p1() const { return side::apply(m_pi, m_pj, m_qk); } - inline int qk_wrt_q1() const { return side::apply(m_qi, m_qj, m_qk); } - - inline int pk_wrt_q2() const { return side::apply(m_qj, m_qk, m_pk); } - inline int qk_wrt_p2() const { return side::apply(m_pj, m_pk, m_qk); } - - Pi const& m_pi; - Pj const& m_pj; - Pk const& m_pk; - Qi const& m_qi; - Qj const& m_qj; - Qk const& m_qk; -}; - struct base_turn_handler { // Returns true if both sides are opposite @@ -947,41 +917,12 @@ struct get_turn_info RobustPolicy const& robust_policy, OutputIterator out) { - typedef typename geometry::robust_point_type - < - Point1, RobustPolicy - >::type robust_point_type; + typedef intersection_info + inters_info; - robust_point_type pi_rob, pj_rob, pk_rob, qi_rob, qj_rob, qk_rob; - geometry::recalculate(pi_rob, pi, robust_policy); - geometry::recalculate(pj_rob, pj, robust_policy); - geometry::recalculate(pk_rob, pk, robust_policy); - geometry::recalculate(qi_rob, qi, robust_policy); - geometry::recalculate(qj_rob, qj, robust_policy); - geometry::recalculate(qk_rob, qk, robust_policy); + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); - typedef model::referring_segment segment_type1; - typedef model::referring_segment segment_type2; - segment_type1 p1(pi, pj); - segment_type2 q1(qi, qj); - - side_calculator side_calc(pi_rob, pj_rob, pk_rob, qi_rob, qj_rob, qk_rob); - - typedef strategy_intersection - < - typename cs_tag::type, - Point1, - Point2, - typename TurnInfo::point_type, - RobustPolicy - > si; - - typedef typename si::segment_intersection_strategy_type strategy; - - typename strategy::return_type result = strategy::apply(p1, q1, - robust_policy, pi_rob, pj_rob, qi_rob, qj_rob); - - char const method = result.template get<1>().how; + char const method = inters.d_info().how; // Copy, to copy possibly extended fields TurnInfo tp = tp_model; @@ -993,11 +934,10 @@ struct get_turn_info case 'f' : // collinear, "from" case 's' : // starts from the middle if (AssignPolicy::include_no_turn - && result.template get<0>().count > 0) + && inters.i_info().count > 0) { - only_convert::apply(tp, - result.template get<0>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + only_convert::apply(tp, inters.i_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; @@ -1013,29 +953,34 @@ struct get_turn_info > policy; // If Q (1) arrives (1) - if (result.template get<1>().arrival[1] == 1) + if ( inters.d_info().arrival[1] == 1 ) { policy::template apply<0>(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), - side_calc); + tp, inters.i_info(), inters.d_info(), + inters.sides()); } else { // Swap p/q - side_calculator swapped_side_calc(qi, qj, qk, pi, pj, pk); + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); policy::template apply<1>(qi, qj, qk, pi, pj, pk, - tp, result.template get<0>(), result.template get<1>(), + tp, inters.i_info(), inters.d_info(), swapped_side_calc); } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 'i' : { crosses::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; @@ -1043,20 +988,20 @@ struct get_turn_info { // Both touch (both arrive there) touch::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info(), inters.sides()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 'e': { - if (! result.template get<1>().opposite) + if ( ! inters.d_info().opposite ) { // Both equal // 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); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info(), inters.sides()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } else @@ -1066,21 +1011,21 @@ struct get_turn_info TurnInfo, AssignPolicy >::apply(pi, qi, - tp, out, result.template get<0>(), result.template get<1>()); + tp, out, inters.i_info(), inters.d_info()); } } break; case 'c' : { // Collinear - if (! result.template get<1>().opposite) + if ( ! inters.d_info().opposite ) { - if (result.template get<1>().arrival[0] == 0) + if ( inters.d_info().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); + tp, inters.i_info(), inters.d_info(), inters.sides()); // override assigned method tp.method = method_collinear; @@ -1088,10 +1033,10 @@ struct get_turn_info else { collinear::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); + tp, inters.i_info(), inters.d_info(), inters.sides()); } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } else @@ -1101,7 +1046,7 @@ struct get_turn_info TurnInfo, AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, - tp, out, result.template get<0>(), result.template get<1>(), side_calc); + tp, out, inters.i_info(), inters.d_info(), inters.sides()); } } break; @@ -1110,8 +1055,8 @@ struct get_turn_info // degenerate points if (AssignPolicy::include_degenerate) { - only_convert::apply(tp, result.template get<0>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + only_convert::apply(tp, inters.i_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } } 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 fca64e3ed..4b910dff5 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 @@ -15,177 +15,13 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP #include +#include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay { -// TURN_OPERATION - -enum turn_position { position_middle, position_front, position_back }; - -template -struct turn_operation_linear - : public turn_operation -{ - turn_operation_linear() - : position(position_middle) - , is_collinear(false) - {} - - turn_position position; - bool is_collinear; // valid only for Linear geometry -}; - -template -class intersection_info -{ - typedef typename strategy_intersection - < - typename cs_tag::type, - Point1, - Point2, - TurnPoint, - RobustPolicy - >::segment_intersection_strategy_type strategy; - -public: - typedef model::referring_segment segment_type1; - typedef model::referring_segment segment_type2; - typedef side_calculator side_calculator_type; - - typedef typename strategy::return_type result_type; - typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info - typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info - - intersection_info(Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, - RobustPolicy const& robust_policy) - : m_result(strategy::apply(segment_type1(pi, pj), - segment_type2(qi, qj), - robust_policy)) - , m_side_calc(pi, pj, pk, qi, qj, qk) - , m_robust_policy(robust_policy) - {} - - inline Point1 const& pi() const { return m_side_calc.m_pi; } - inline Point1 const& pj() const { return m_side_calc.m_pj; } - inline Point1 const& pk() const { return m_side_calc.m_pk; } - - inline Point2 const& qi() const { return m_side_calc.m_qi; } - inline Point2 const& qj() const { return m_side_calc.m_qj; } - inline Point2 const& qk() const { return m_side_calc.m_qk; } - - inline side_calculator_type const& sides() const { return m_side_calc; } - inline result_type const& result() const { return m_result; } - inline i_info_type const& i_info() const { return m_result.template get<0>(); } - inline d_info_type const& d_info() const { return m_result.template get<1>(); } - - // TODO: not it's more like is_spike_ip_p - inline bool is_spike_p() const - { - if ( m_side_calc.pk_wrt_p1() == 0 ) - { - if ( ! is_ip_j<0>() ) - return false; - - 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 ) - { - return is_spike_of_collinear(pi(), pj(), pk()); - } - - return true; - } - } - - return false; - } - - // TODO: not it's more like is_spike_ip_q - inline bool is_spike_q() const - { - if ( m_side_calc.qk_wrt_q1() == 0 ) - { - if ( ! is_ip_j<1>() ) - return false; - - 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 ) - { - return is_spike_of_collinear(qi(), qj(), qk()); - } - - return true; - } - } - - return false; - } - -private: - template - inline bool is_spike_of_collinear(Point const& i, Point const& j, Point const& k) const - { - typedef model::referring_segment seg_t; - - typedef strategy_intersection - < - typename cs_tag::type, Point, Point, Point, RobustPolicy - > si; - - typedef typename si::segment_intersection_strategy_type strategy; - - typename strategy::return_type result - = strategy::apply(seg_t(i, j), seg_t(j, k), m_robust_policy); - - return result.template get<0>().count == 2; - } - - template - bool is_ip_j() const - { - int arrival = d_info().arrival[OpId]; - bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0; - - if ( same_dirs ) - { - if ( i_info().count == 2 ) - { - if ( ! d_info().opposite ) - { - return arrival != -1; - } - else - { - return arrival != -1; - } - } - else - { - return arrival == 0; - } - } - else - { - return arrival == 1; - } - } - - result_type m_result; - side_calculator_type m_side_calc; - RobustPolicy const& m_robust_policy; -}; - // SEGMENT_INTERSECTION RESULT // C H0 H1 A0 A1 O IP1 IP2 @@ -485,6 +321,8 @@ struct get_turn_info_for_endpoint if ( append_first || append_last ) { bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk, + inters.rpi(), inters.rpj(), inters.rpk(), + inters.rqi(), inters.rqj(), inters.rqk(), is_p_first_ip, is_p_last_ip, is_q_first_ip, is_q_last_ip, ip_info.is_qi, ip_info.is_qj, @@ -493,6 +331,8 @@ struct get_turn_info_for_endpoint if ( !handled ) { handle_internal<1>(qi, qj, qk, pi, pj, pk, + inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk(), is_q_first_ip, is_q_last_ip, is_p_first_ip, is_p_last_ip, ip_info.is_pi, ip_info.is_pj, @@ -549,11 +389,15 @@ struct get_turn_info_for_endpoint template static inline bool handle_internal(Point1 const& i1, Point1 const& j1, Point1 const& /*k1*/, Point2 const& i2, Point2 const& j2, Point2 const& k2, + RobustPoint1 const& ri1, RobustPoint1 const& rj1, RobustPoint1 const& /*rk1*/, + RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2, bool first1, bool last1, bool first2, bool last2, bool ip_i2, bool ip_j2, TurnInfo const& tp_model, IntersectionInfo const& inters, int ip_index, @@ -582,7 +426,8 @@ struct get_turn_info_for_endpoint } else if ( ip_j2 ) { - side_calculator side_calc(i2, i1, j1, i2, j2, k2); + side_calculator + side_calc(ri2, ri1, rj1, ri2, rj2, rk2); std::pair operations = operations_of_equal(side_calc); @@ -632,7 +477,8 @@ struct get_turn_info_for_endpoint } else if ( ip_j2 ) { - side_calculator side_calc(i2, j1, i1, i2, j2, k2); + side_calculator + side_calc(ri2, rj1, ri1, ri2, rj2, rk2); std::pair operations = operations_of_equal(side_calc); diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp new file mode 100644 index 000000000..6b3c54d5f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -0,0 +1,336 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP + +#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +enum turn_position { position_middle, position_front, position_back }; + +template +struct turn_operation_linear + : public turn_operation +{ + turn_operation_linear() + : position(position_middle) + , is_collinear(false) + {} + + turn_position position; + bool is_collinear; // valid only for Linear geometry +}; + +template +struct side_calculator +{ + typedef boost::geometry::strategy::side::side_by_triangle<> side; // todo: get from coordinate system + + inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, + Qi const& qi, Qj const& qj, Qk const& qk) + : m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline int pk_wrt_p1() const { return side::apply(m_pi, m_pj, m_pk); } + inline int pk_wrt_q1() const { return side::apply(m_qi, m_qj, m_pk); } + inline int qk_wrt_p1() const { return side::apply(m_pi, m_pj, m_qk); } + inline int qk_wrt_q1() const { return side::apply(m_qi, m_qj, m_qk); } + + inline int pk_wrt_q2() const { return side::apply(m_qj, m_qk, m_pk); } + inline int qk_wrt_p2() const { return side::apply(m_pj, m_pk, m_qk); } + + Pi const& m_pi; + Pj const& m_pj; + Pk const& m_pk; + Qi const& m_qi; + Qj const& m_qj; + Qk const& m_qk; +}; + +template +struct robust_points +{ + typedef typename geometry::robust_point_type + < + Point1, RobustPolicy + >::type robust_point1_type; + // TODO: define robust_point2_type using Point2? + typedef robust_point1_type robust_point2_type; + + inline robust_points(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + { + geometry::recalculate(m_rpi, pi, robust_policy); + geometry::recalculate(m_rpj, pj, robust_policy); + geometry::recalculate(m_rpk, pk, robust_policy); + geometry::recalculate(m_rqi, qi, robust_policy); + geometry::recalculate(m_rqj, qj, robust_policy); + geometry::recalculate(m_rqk, qk, robust_policy); + } + + robust_point1_type m_rpi, m_rpj, m_rpk; + robust_point2_type m_rqi, m_rqj, m_rqk; +}; + +template +class intersection_info_base + : private robust_points +{ + typedef robust_points base_t; + +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef typename base_t::robust_point1_type robust_point1_type; + typedef typename base_t::robust_point2_type robust_point2_type; + + typedef side_calculator side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_side_calc(base_t::m_rpi, base_t::m_rpj, base_t::m_rpk, + base_t::m_rqi, base_t::m_rqj, base_t::m_rqk) + , m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline Point1 const& pi() const { return m_pi; } + inline Point1 const& pj() const { return m_pj; } + inline Point1 const& pk() const { return m_pk; } + + inline Point2 const& qi() const { return m_qi; } + inline Point2 const& qj() const { return m_qj; } + inline Point2 const& qk() const { return m_qk; } + + inline robust_point1_type const& rpi() const { return base_t::m_rpi; } + inline robust_point1_type const& rpj() const { return base_t::m_rpj; } + inline robust_point1_type const& rpk() const { return base_t::m_rpk; } + + inline robust_point2_type const& rqi() const { return base_t::m_rqi; } + inline robust_point2_type const& rqj() const { return base_t::m_rqj; } + inline robust_point2_type const& rqk() const { return base_t::m_rqk; } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; + + point1_type const& m_pi; + point1_type const& m_pj; + point1_type const& m_pk; + point2_type const& m_qi; + point2_type const& m_qj; + point2_type const& m_qk; +}; + +template +class intersection_info_base +{ +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef Point1 robust_point1_type; + typedef Point2 robust_point2_type; + + typedef side_calculator side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + no_rescale_policy const& robust_policy) + : m_side_calc(pi, pj, pk, qi, qj, qk) + {} + + inline Point1 const& pi() const { return m_side_calc.m_pi; } + inline Point1 const& pj() const { return m_side_calc.m_pj; } + inline Point1 const& pk() const { return m_side_calc.m_pk; } + + inline Point2 const& qi() const { return m_side_calc.m_qi; } + inline Point2 const& qj() const { return m_side_calc.m_qj; } + inline Point2 const& qk() const { return m_side_calc.m_qk; } + + inline Point1 const& rpi() const { return pi(); } + inline Point1 const& rpj() const { return pj(); } + inline Point1 const& rpk() const { return pk(); } + + inline Point2 const& rqi() const { return qi(); } + inline Point2 const& rqj() const { return qj(); } + inline Point2 const& rqk() const { return qk(); } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; +}; + + +template +class intersection_info + : public intersection_info_base +{ + typedef intersection_info_base base_t; + + typedef typename strategy_intersection + < + typename cs_tag::type, + Point1, + Point2, + TurnPoint, + RobustPolicy + >::segment_intersection_strategy_type strategy; + +public: + typedef model::referring_segment segment_type1; + typedef model::referring_segment segment_type2; + typedef typename base_t::side_calculator_type side_calculator_type; + + typedef typename strategy::return_type result_type; + typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info + typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info + + intersection_info(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_result(strategy::apply(segment_type1(pi, pj), + segment_type2(qi, qj), + robust_policy)) + , m_robust_policy(robust_policy) + {} + + inline result_type const& result() const { return m_result; } + inline i_info_type const& i_info() const { return m_result.template get<0>(); } + inline d_info_type const& d_info() const { return m_result.template get<1>(); } + + // TODO: it's more like is_spike_ip_p + inline bool is_spike_p() const + { + if ( base_t::sides().pk_wrt_p1() == 0 ) + { + if ( ! is_ip_j<0>() ) + return false; + + int const qk_p1 = base_t::sides().qk_wrt_p1(); + int const qk_p2 = base_t::sides().qk_wrt_p2(); + + if ( qk_p1 == -qk_p2 ) + { + if ( qk_p1 == 0 ) + { + return is_spike_of_collinear(base_t::pi(), base_t::pj(), base_t::pk()); + } + + return true; + } + } + + return false; + } + + // TODO: it's more like is_spike_ip_q + inline bool is_spike_q() const + { + if ( base_t::sides().qk_wrt_q1() == 0 ) + { + if ( ! is_ip_j<1>() ) + return false; + + int const pk_q1 = base_t::sides().pk_wrt_q1(); + int const pk_q2 = base_t::sides().pk_wrt_q2(); + + if ( pk_q1 == -pk_q2 ) + { + if ( pk_q1 == 0 ) + { + return is_spike_of_collinear(base_t::qi(), base_t::qj(), base_t::qk()); + } + + return true; + } + } + + return false; + } + +private: + template + inline bool is_spike_of_collinear(Point const& i, Point const& j, Point const& k) const + { + typedef model::referring_segment seg_t; + + typedef strategy_intersection + < + typename cs_tag::type, Point, Point, Point, RobustPolicy + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + typename strategy::return_type result + = strategy::apply(seg_t(i, j), seg_t(j, k), m_robust_policy); + + return result.template get<0>().count == 2; + } + + template + bool is_ip_j() const + { + int arrival = d_info().arrival[OpId]; + bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0; + + if ( same_dirs ) + { + if ( i_info().count == 2 ) + { + if ( ! d_info().opposite ) + { + return arrival != -1; + } + else + { + return arrival != -1; + } + } + else + { + return arrival == 0; + } + } + else + { + return arrival == 1; + } + } + + result_type m_result; + RobustPolicy const& m_robust_policy; +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp index 3eab8e60e..121963b66 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -47,8 +47,10 @@ struct get_turn_info_linear_areal RobustPolicy const& robust_policy, OutputIterator out) { - intersection_info - inters(pi, pj, pk, qi, qj, qk, robust_policy); + typedef intersection_info + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); char const method = inters.d_info().how; @@ -64,7 +66,7 @@ struct get_turn_info_linear_areal get_turn_info_for_endpoint( pi, pj, pk, qi, qj, qk, is_p_first, is_p_last, is_q_first, is_q_last, - tp_model, inters.result(), method_none, out); + tp_model, inters, method_none, out); break; case 'd' : // disjoint: never do anything @@ -75,7 +77,7 @@ struct get_turn_info_linear_areal if ( get_turn_info_for_endpoint( pi, pj, pk, qi, qj, qk, is_p_first, is_p_last, is_q_first, is_q_last, - tp_model, inters.result(), method_touch_interior, out) ) + tp_model, inters, method_touch_interior, out) ) { // do nothing } @@ -96,7 +98,12 @@ struct get_turn_info_linear_areal else { // Swap p/q - side_calculator swapped_side_calc(qi, qj, qk, pi, pj, pk); + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); policy::template apply<1>(qi, qj, qk, pi, pj, pk, tp, inters.i_info(), inters.d_info(), swapped_side_calc); @@ -138,7 +145,7 @@ struct get_turn_info_linear_areal if ( get_turn_info_for_endpoint( pi, pj, pk, qi, qj, qk, is_p_first, is_p_last, is_q_first, is_q_last, - tp_model, inters.result(), method_touch, out) ) + tp_model, inters, method_touch, out) ) { // do nothing } @@ -230,7 +237,7 @@ struct get_turn_info_linear_areal if ( get_turn_info_for_endpoint( pi, pj, pk, qi, qj, qk, is_p_first, is_p_last, is_q_first, is_q_last, - tp_model, inters.result(), method_equal, out) ) + tp_model, inters, method_equal, out) ) { // do nothing } @@ -277,7 +284,7 @@ struct get_turn_info_linear_areal if ( get_turn_info_for_endpoint( pi, pj, pk, qi, qj, qk, is_p_first, is_p_last, is_q_first, is_q_last, - tp_model, inters.result(), method_collinear, out) ) + tp_model, inters, method_collinear, out) ) { // do nothing } @@ -621,7 +628,7 @@ struct get_turn_info_linear_areal typename Point1, typename Point2, typename TurnInfo, - typename IntersectionResult, + typename IntersectionInfo, typename OutputIterator> static inline bool get_turn_info_for_endpoint( Point1 const& pi, Point1 const& pj, Point1 const& pk, @@ -629,14 +636,14 @@ struct get_turn_info_linear_areal bool is_p_first, bool is_p_last, bool is_q_first, bool is_q_last, TurnInfo const& tp_model, - IntersectionResult const& result, + IntersectionInfo const& inters, method_type /*method*/, OutputIterator out) { namespace ov = overlay; typedef ov::get_turn_info_for_endpoint get_info_e; - const std::size_t ip_count = result.template get<0>().count; + const std::size_t ip_count = inters.i_info().count; // no intersection points if ( ip_count == 0 ) return false; @@ -648,11 +655,11 @@ struct get_turn_info_linear_areal if ( !is_p_first && !is_p_last ) return false; - linear_intersections intersections(pi, qi, result, is_p_last, is_q_last); + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); linear_intersections::ip_info const& ip0 = intersections.template get<0>(); linear_intersections::ip_info const& ip1 = intersections.template get<1>(); - const bool opposite = result.template get<1>().opposite; + const bool opposite = inters.d_info().opposite; // ANALYSE AND ASSIGN FIRST @@ -675,7 +682,13 @@ struct get_turn_info_linear_areal if ( ip0.is_qj ) { - side_calculator side_calc(qi, pi, pj, qi, qj, qk); + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rqj(), inters.rqk()); std::pair operations = get_info_e::operations_of_equal(side_calc); @@ -687,9 +700,18 @@ struct get_turn_info_linear_areal } else { - side_calculator side_calc(qi, pi, pj, qi, pi, qj); + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rpi(), inters.rqj()); std::pair operations = get_info_e::operations_of_equal(side_calc); @@ -704,17 +726,17 @@ struct get_turn_info_linear_areal // equals<> or collinear<> will assign the second point, // we'd like to assign the first one - base_turn_handler::assign_point(tp, tp.method, result.template get<0>(), 0); + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0); // NOTE: not really needed especially for the first point // for which there is no preceding segment (but consistent with the L/L) - if ( result.template get<0>().count > 1 ) + if ( inters.i_info().count > 1 ) { //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); tp.operations[0].is_collinear = true; } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } @@ -727,7 +749,7 @@ struct get_turn_info_linear_areal { TurnInfo tp = tp_model; - if ( result.template get<0>().count > 1 ) + if ( inters.i_info().count > 1 ) { //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); tp.operations[0].is_collinear = true; @@ -735,7 +757,13 @@ struct get_turn_info_linear_areal } else //if ( result.template get<0>().count == 1 ) { - side_calculator side_calc(qi, pj, pi, qi, qj, qk); + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpj(), inters.rpi(), + inters.rqi(), inters.rqj(), inters.rqk()); std::pair operations = get_info_e::operations_of_equal(side_calc); @@ -760,9 +788,9 @@ struct get_turn_info_linear_areal // equals<> or collinear<> will assign the second point, // we'd like to assign the first one std::size_t ip_index = ip_count > 1 ? 1 : 0; - base_turn_handler::assign_point(tp, tp.method, result.template get<0>(), ip_index); + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; return true; 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 e7426257e..4ff20d6cc 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 @@ -44,8 +44,10 @@ struct get_turn_info_linear_linear RobustPolicy const& robust_policy, OutputIterator out) { - intersection_info - inters(pi, pj, pk, qi, qj, qk, robust_policy); + typedef intersection_info + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); char const method = inters.d_info().how; @@ -93,7 +95,13 @@ struct get_turn_info_linear_linear else { // Swap p/q - side_calculator swapped_side_calc(qi, qj, qk, pi, pj, pk); + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + policy::template apply<1>(qi, qj, qk, pi, pj, pk, tp, inters.i_info(), inters.d_info(), swapped_side_calc); From aea578c602c7bea5238baca5af566d811d68cbe1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 4 May 2014 22:31:13 +0200 Subject: [PATCH 10/10] [get_turns] Fix turn operation is_collinear values for L/L and L/A. is_collinear for the first endpoint of L is now unified and is always equal to FALSE. Add tests of is_collinear. It is tested by passing 4th and 5th char in the expected turn string (= means collinear, + means non-collinear), e.g. "mxi=+" for L/L means that L1 ends in this IP and was collinear before meeting the Point, L2 is going "inside" intersection and wasn't collinear before. Replace method_and_operation_replacer with turn_transformer. --- .../detail/overlay/get_turn_info.hpp | 12 +- .../overlay/get_turn_info_for_endpoint.hpp | 30 ++- .../detail/overlay/get_turn_info_la.hpp | 58 ++-- .../detail/overlay/get_turn_info_ll.hpp | 50 ++-- .../overlay/get_turns_linear_areal.cpp | 82 +++--- .../overlay/get_turns_linear_linear.cpp | 254 ++++++++++-------- test/algorithms/overlay/test_get_turns.hpp | 126 +++++---- 7 files changed, 344 insertions(+), 268 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index 20ccd9208..6a7b92306 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -711,7 +711,7 @@ private : } public: - static inline void empty_replacer(method_type &, operation_type &, operation_type &) {} + static inline void empty_transformer(TurnInfo &) {} template < @@ -734,7 +734,7 @@ public: DirInfo const& dir_info, SidePolicy const& side) { - apply(pi, pj, pk, qi, qj, qk, tp_model, out, intersection_info, dir_info, side, empty_replacer); + apply(pi, pj, pk, qi, qj, qk, tp_model, out, intersection_info, dir_info, side, empty_transformer); } public: @@ -746,7 +746,7 @@ public: typename IntersectionInfo, typename DirInfo, typename SidePolicy, - typename MethodAndOperationsReplacer + typename TurnTransformer > static inline void apply( Point1 const& pi, Point1 const& pj, Point1 const& pk, @@ -759,7 +759,7 @@ public: IntersectionInfo const& intersection_info, DirInfo const& dir_info, SidePolicy const& side, - MethodAndOperationsReplacer method_and_operations_replacer, + TurnTransformer turn_transformer, bool const is_pk_valid = true, bool const is_qk_valid = true) { TurnInfo tp = tp_model; @@ -769,7 +769,7 @@ public: && is_pk_valid && set_tp<0>(pi, pj, pk, side.pk_wrt_p1(), true, qi, qj, side.pk_wrt_q1(), tp, intersection_info) ) { - method_and_operations_replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + turn_transformer(tp); AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; @@ -780,7 +780,7 @@ public: && is_qk_valid && set_tp<1>(qi, qj, qk, side.qk_wrt_q1(), false, pi, pj, side.qk_wrt_p1(), tp, intersection_info) ) { - method_and_operations_replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + turn_transformer(tp); AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; 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 4b910dff5..cd07e784a 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 @@ -356,9 +356,9 @@ struct get_turn_info_for_endpoint && inters.is_spike_p() ) { assign(pi, qi, inters.result(), ip_index, method, operation_blocked, q_operation, - p_pos, q_pos, tp_model, out); + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); assign(pi, qi, inters.result(), ip_index, method, operation_intersection, q_operation, - p_pos, q_pos, tp_model, out); + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); } // Q is spike and should be handled else if ( !is_q_last @@ -367,15 +367,15 @@ struct get_turn_info_for_endpoint && inters.is_spike_q() ) { assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_blocked, - p_pos, q_pos, tp_model, out); + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_intersection, - p_pos, q_pos, tp_model, out); + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); } // no spikes else { assign(pi, qi, inters.result(), ip_index, method, p_operation, q_operation, - p_pos, q_pos, tp_model, out); + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out); } } } @@ -541,6 +541,8 @@ struct get_turn_info_for_endpoint method_type method, operation_type op0, operation_type op1, turn_position pos0, turn_position pos1, + bool is_p_first_ip, bool is_q_first_ip, + bool is_p_spike, bool is_q_spike, TurnInfo const& tp_model, OutputIterator out) { @@ -555,13 +557,23 @@ struct get_turn_info_for_endpoint tp.operations[0].position = pos0; tp.operations[1].position = pos1; - // NOTE: this probably shouldn't be set for the first point - // for which there is no preceding segment if ( result.template get<0>().count > 1 ) { + // NOTE: is_collinear is NOT set for the first endpoint + // for which there is no preceding segment + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); - tp.operations[0].is_collinear = true; - tp.operations[1].is_collinear = true; + if ( ! is_p_first_ip ) + { + tp.operations[0].is_collinear = op0 != operation_intersection + || is_p_spike; + } + + if ( ! is_q_first_ip ) + { + tp.operations[1].is_collinear = op1 != operation_intersection + || is_q_spike; + } } else //if ( result.template get<0>().count == 1 ) { diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp index 121963b66..9556dbfaa 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -252,8 +252,8 @@ struct get_turn_info_linear_areal equal::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info(), inters.sides()); - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + turn_transformer_ec transformer(method_touch); + transformer(tp); // TODO: move this into the append_xxx and call for each turn? AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); @@ -315,8 +315,8 @@ struct get_turn_info_linear_areal //version = append_collinear; } - replacer_of_method_and_operations_ec replacer(method_replace); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + turn_transformer_ec transformer(method_replace); + transformer(tp); // TODO: move this into the append_xxx and call for each turn? AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); @@ -333,7 +333,7 @@ struct get_turn_info_linear_areal else { // Is this always 'm' ? - replacer_of_method_and_operations_ec replacer(method_touch_interior); + turn_transformer_ec transformer(method_touch_interior); // conditionally handle spikes if ( handle_spikes ) @@ -352,7 +352,7 @@ struct get_turn_info_linear_areal AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, tp, out, inters.i_info(), inters.d_info(), - inters.sides(), replacer); + inters.sides(), transformer); } } } @@ -511,13 +511,13 @@ struct get_turn_info_linear_areal if ( Version == append_touches ) { tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = ??? + //tp.operations[1].is_collinear = false; tp.method = method_touch; } else { - //tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = true; + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; BOOST_ASSERT(inters.i_info().count > 1); base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); @@ -573,25 +573,29 @@ struct get_turn_info_linear_areal } template - class replacer_of_method_and_operations_ec + class turn_transformer_ec { public: - explicit replacer_of_method_and_operations_ec(method_type method_t_or_m) + explicit turn_transformer_ec(method_type method_t_or_m) : m_method(method_t_or_m) {} - void operator()(method_type & method, - operation_type & op0, - operation_type & op1) const + template + void operator()(Turn & turn) const { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + // NOTE: probably only if methods are WRT IPs, not segments! if ( IsFront || op0 == operation_intersection || op0 == operation_union || op1 == operation_intersection || op1 == operation_union ) { - method = m_method; + turn.method = m_method; } + turn.operations[0].is_collinear = op0 != operation_blocked; + // Assuming G1 is always Linear if ( op0 == operation_blocked ) { @@ -720,21 +724,18 @@ struct get_turn_info_linear_areal tp.operations[1].operation = operations.second; } - replacer_of_method_and_operations_ec replacer(replaced_method); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + turn_transformer_ec transformer(replaced_method); + transformer(tp); } // equals<> or collinear<> will assign the second point, // we'd like to assign the first one base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0); - // NOTE: not really needed especially for the first point - // for which there is no preceding segment (but consistent with the L/L) - if ( inters.i_info().count > 1 ) - { - //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); - tp.operations[0].is_collinear = true; - } + // NOTE: is_collinear is not set for the first endpoint of L + // for which there is no preceding segment + // here is_p_first_ip == true + tp.operations[0].is_collinear = false; AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; @@ -771,13 +772,10 @@ struct get_turn_info_linear_areal tp.operations[0].operation = operations.first; tp.operations[1].operation = operations.second; - replacer_of_method_and_operations_ec replacer(method_none); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + turn_transformer_ec transformer(method_none); + transformer(tp); - if ( tp.both(operation_continue) ) - { - tp.operations[0].is_collinear = true; - } + tp.operations[0].is_collinear = tp.both(operation_continue); } tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior; 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 4ff20d6cc..4f0384877 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 @@ -293,8 +293,9 @@ struct get_turn_info_linear_linear equal::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info(), inters.sides()); - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + // transform turn + turn_transformer_ec transformer(method_touch); + transformer(tp); // TODO: move this into the append_xxx and call for each turn? AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); @@ -334,6 +335,7 @@ struct get_turn_info_linear_linear } else { + // NOTE: this is for spikes since those are set in the turn_transformer_ec tp.operations[0].is_collinear = true; tp.operations[1].is_collinear = true; @@ -360,8 +362,9 @@ struct get_turn_info_linear_linear //spike_op = operation_continue; } - replacer_of_method_and_operations_ec replacer(method_replace); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + // transform turn + turn_transformer_ec transformer(method_replace); + transformer(tp); // TODO: move this into the append_xxx and call for each turn? AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); @@ -380,7 +383,7 @@ struct get_turn_info_linear_linear else { // If this always 'm' ? - replacer_of_method_and_operations_ec replacer(method_touch_interior); + turn_transformer_ec transformer(method_touch_interior); // conditionally handle spikes if ( handle_spikes ) @@ -400,7 +403,7 @@ struct get_turn_info_linear_linear AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, tp, out, inters.i_info(), inters.d_info(), inters.sides(), - replacer, !is_p_last, !is_q_last); + transformer, !is_p_last, !is_q_last); } } } @@ -544,13 +547,13 @@ struct get_turn_info_linear_linear if ( Version == append_touches ) { tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = ??? + tp.operations[1].is_collinear = false; tp.method = method_touch; } - else + else // Version == append_collinear_opposite { - //tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = true; + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; BOOST_ASSERT(inters.i_info().count > 1); @@ -575,14 +578,14 @@ struct get_turn_info_linear_linear { if ( Version == append_touches ) { - //tp.operations[0].is_collinear = ??? + tp.operations[0].is_collinear = false; tp.operations[1].is_collinear = true; tp.method = method_touch; } - else + else // Version == append_collinear_opposite { - //tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = true; + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; BOOST_ASSERT(inters.i_info().count > 0); @@ -646,17 +649,19 @@ struct get_turn_info_linear_linear } } - class replacer_of_method_and_operations_ec + class turn_transformer_ec { public: - explicit replacer_of_method_and_operations_ec(method_type method_t_or_m) + explicit turn_transformer_ec(method_type method_t_or_m) : m_method(method_t_or_m) {} - void operator()(method_type & method, - operation_type & op0, - operation_type & op1) const + template + void operator()(Turn & turn) const { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + BOOST_ASSERT(op0 != operation_blocked || op1 != operation_blocked ); if ( op0 == operation_blocked ) @@ -680,8 +685,13 @@ struct get_turn_info_linear_linear if ( op0 == operation_intersection || op0 == operation_union || op1 == operation_intersection || op1 == operation_union ) { - method = m_method; + turn.method = m_method; } + +// TODO: is this correct? +// it's equivalent to comparing to operation_blocked at the beginning of the function + turn.operations[0].is_collinear = op0 != operation_intersection; + turn.operations[1].is_collinear = op1 != operation_intersection; } private: diff --git a/test/algorithms/overlay/get_turns_linear_areal.cpp b/test/algorithms/overlay/get_turns_linear_areal.cpp index 0b1d624b6..4ab28cbde 100644 --- a/test/algorithms/overlay/get_turns_linear_areal.cpp +++ b/test/algorithms/overlay/get_turns_linear_areal.cpp @@ -31,130 +31,132 @@ void test_all() test_geometry("LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - expected("miu")("iuu")("tcc")("tuu")("mcu")("mic")("muu")("tiu")("mcu")("mic")("mcc")("miu")("mxu").vec); + expected("miu+")("iuu+")("tcc+")("tuu=")("mcu+")("mic=")("muu+") + ("tiu+")("mcu+")("mic=")("mcc+")("miu=")("mxu+")); test_geometry("LINESTRING(5 0,5 5,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - "miu", "mxu"); + "miu+", "mxu+"); test_geometry("LINESTRING(0 0,5 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - "tiu", "txu"); + "tiu+", "txu+"); test_geometry("LINESTRING(0 0,5 0,5 5,10 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - expected("tcu")("mic")("mcc")("txu").vec); + expected("tcu+")("mic=")("mcc+")("txu=")); test_geometry("LINESTRING(10 0,5 0,5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - expected("tcc")("miu")("mcu")("txc").vec); + expected("tcc+")("miu=")("mcu+")("txc=")); test_geometry("LINESTRING(0 0,10 0,10 10)", "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - expected("tcu")("mic")("mcu")("mic")("mxu").vec); + expected("tcu+")("mic=")("mcu+")("mic=")("mxu+")); test_geometry("LINESTRING(11 1,10 0,0 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - "tcc", "txu"); + "tcc+", "txu="); test_geometry("LINESTRING(0 0,10 0,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - "tcu", "tuc"); + "tcu+", "tuc="); test_geometry("LINESTRING(10 0,0 0,-1 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - "tcc", "tuu"); + "tcc+", "tuu="); // true hole test_geometry("LINESTRING(9 1,10 5,9 9)", "POLYGON((0 0,0 10,10 10,10 5,10 0,0 0)(2 2,10 5,2 8,2 2))", - expected("tiu")("tiu").vec); + expected("tiu+")("tiu+")); test_geometry("LINESTRING(10 1,10 5,10 9)", "POLYGON((0 0,0 10,10 10,10 5,10 0,0 0)(2 2,10 5,2 8,2 2))", - expected("mcu")("ecc")("tiu")("mxc").vec); + expected("mcu+")("ecc=")("tiu+")("mxc=")); // fake hole test_geometry("LINESTRING(9 1,10 5,9 9)", "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", - expected("tuu")("tiu").vec); + expected("tuu+")("tiu+")); test_geometry("LINESTRING(10 1,10 5,10 9)", "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", - expected("mcu")("tuc")("tcu")("mxc").vec); + expected("mcu+")("tuc=")("tcu+")("mxc=")); + // true hole test_geometry("LINESTRING(10 1,10 5,2 2)", "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", - expected("mcu")("mic")("tcu")("txc")); + expected("mcu+")("mic=")("tcu+")("txc=")); test_geometry("LINESTRING(10 1,10 5,2 8)", "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", - expected("mcu")("mic")("tcc")("txu")); + expected("mcu+")("mic=")("tcc+")("txu=")); // SPIKE - NON-ENDPOINT - NON-OPPOSITE // spike - neq eq test_geometry("LINESTRING(2 2,4 4,1 1)", "POLYGON((0 0,4 4,6 3,6 0,0 0))", - expected("mcc")("txu")("tcu")("mxc")); + expected("mcc+")("txu=")("tcu=")("mxc=")); // spike - eq eq test_geometry("LINESTRING(0 0,4 4,1 1)", "POLYGON((0 0,4 4,6 3,6 0,0 0))", - expected("tcc")("txu")("tcu")("mxc")); + expected("tcc+")("txu=")("tcu=")("mxc=")); // spike - eq neq test_geometry("LINESTRING(0 0,3 3,1 1)", "POLYGON((0 0,4 4,6 3,6 0,0 0))", - expected("tcc")("mxu")("mcu")("mxc")); + expected("tcc+")("mxu=")("mcu=")("mxc=")); // spike - neq neq test_geometry("LINESTRING(1 1,3 3,2 2)", "POLYGON((0 0,4 4,6 3,6 0,0 0))", - expected("mcc")("mxu")("mcu")("mxc")); + expected("mcc+")("mxu=")("mcu=")("mxc=")); // spike - out neq test_geometry("LINESTRING(0 0,3 3,2 2)", "POLYGON((1 1,4 4,6 3,6 0,1 1))", - expected("mcc")("mxu")("mcu")("mxc")); + expected("mcc+")("mxu=")("mcu=")("mxc=")); // spike - out eq test_geometry("LINESTRING(0 0,4 4,2 2)", "POLYGON((1 1,4 4,6 3,6 0,1 1))", - expected("mcc")("txu")("tcu")("mxc")); + expected("mcc+")("txu=")("tcu=")("mxc=")); // spike - out out/eq test_geometry("LINESTRING(0 0,4 4,2 2)", "POLYGON((1 0,4 4,6 3,1 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(0 0,4 4,2 2)", "POLYGON((0 1,4 4,6 3,6 0,-1 -1,0 1))", - expected("tiu")); + expected("tiu+")); // spike - out out/neq test_geometry("LINESTRING(0 0,4 4,2 2)", "POLYGON((4 0,4 5,6 3,4 0))", - expected("muu")); + expected("muu+")); test_geometry("LINESTRING(0 0,4 4,2 2)", "POLYGON((0 4,5 4,6 3,6 0,-1 -1,0 4))", - expected("miu")); + expected("miu+")); test_geometry("LINESTRING(0 1,1 1,0 1)", "POLYGON((0 0,3 3,3 0,0 0))", - expected("muu")); + expected("muu+")); test_geometry("LINESTRING(0 1,3 3,0 1)", "POLYGON((0 0,3 3,3 0,0 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(0 1,0 0,0 1)", "POLYGON((0 0,3 3,3 0,0 0))", - expected("tuu")); + expected("tuu+")); // SPIKE - NON-ENDPOINT - OPPOSITE // opposite - eq eq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "POLYGON((-1 -1,0 0,4 4,6 3,-1 -1))", - expected("tcu")("txc")("tcc")("mxu")); + expected("tcu+")("txc=")("tcc=")("mxu=")); // opposite - neq eq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "POLYGON((-1 -1,0 0,5 5,6 3,-1 -1))", - expected("mcu")("txc")("tcc")("mxu")); + expected("mcu+")("txc=")("tcc=")("mxu=")); // opposite - eq, neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "POLYGON((-2 -2,-1 -1,4 4,6 3,-2 -2))", - expected("tcu")("mxc")("mcc")("mxu")); + expected("tcu+")("mxc=")("mcc=")("mxu=")); // opposite - neq neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "POLYGON((-2 -2,-1 -1,3 3,6 3,-2 -2))", - expected("mcu")("mxc")("mcc")("mxu")); + expected("mcu+")("mxc=")("mcc=")("mxu=")); // opposite - neq neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "POLYGON((-2 -2,-1 -1,3 3,5 5,6 3,-2 -2))", - expected("mcu")("mxc")("mcc")("mxu")); + expected("mcu+")("mxc=")("mcc=")("mxu=")); // spike vs internal test_geometry("LINESTRING(0 1,1 1,0 1)", // -- "POLYGON((1 0,1 1,2 1,1 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(1 2,1 1,1 2)", // | "POLYGON((1 0,1 1,2 1,1 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(0 2,1 1,0 2)", "POLYGON((1 0,1 1,2 1,1 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(2 0,1 1,2 0)", "POLYGON((1 0,1 1,2 1,2 0,1 0))", - expected("tiu")("tiu")("txu")); // TODO: should spike point be duplicated? + expected("tiu+")("tiu+")("txu+")); // TODO: should spike point be duplicated? test_geometry("LINESTRING(0 0,1 1,0 0)", // / "POLYGON((1 0,1 1,2 1,1 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(2 2,1 1,2 2)", // / "POLYGON((1 0,1 1,2 1,1 0))", - expected("tuu")); + expected("tuu+")); test_geometry("LINESTRING(2 1,1 1,2 1)", "POLYGON((1 0,1 1,2 1,1 0))", - expected("tcu")("txc")("tcc")("txu")); + expected("tcu+")("txc=")("tcc=")("txu=")); } int test_main(int, char* []) diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 7197e9725..e8ff512c6 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -32,179 +32,189 @@ void test_all() typedef bg::model::linestring ls; typedef bg::model::multi_linestring mls; - test_geometry("LINESTRING(0 0,2 0)", "LINESTRING(0 0,2 0)", "tii", "txx"); - test_geometry("LINESTRING(0 0,2 0)", "LINESTRING(2 0,0 0)", "tix", "txi"); + // NOTE: currently for the first endpoint of the Linestring on collinear segment + // is_collinear flags are set to FALSE! + // E.g. in the first test tii++, NOT tii== + + test_geometry("LINESTRING(0 0,2 0)", "LINESTRING(0 0,2 0)", "tii++", "txx=="); + test_geometry("LINESTRING(0 0,2 0)", "LINESTRING(2 0,0 0)", "tix+=", "txi=+"); - test_geometry("LINESTRING(1 0,1 1)", "LINESTRING(0 0,1 0,2 0)", "tuu"); - test_geometry("LINESTRING(1 0,0 0)", "LINESTRING(0 0,1 0,2 0)", "txi", "tiu"); - test_geometry("LINESTRING(1 0,2 0)", "LINESTRING(0 0,1 0,2 0)", "tii", "txx"); - test_geometry("LINESTRING(1 1,1 0)", "LINESTRING(0 0,1 0,2 0)", "txu"); - test_geometry("LINESTRING(0 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "tii", "txu"); - test_geometry("LINESTRING(2 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "txi", "tix"); + test_geometry("LINESTRING(1 0,1 1)", "LINESTRING(0 0,1 0,2 0)", "tuu++"); + test_geometry("LINESTRING(1 0,0 0)", "LINESTRING(0 0,1 0,2 0)", "txi=+", "tiu+="); + test_geometry("LINESTRING(1 0,2 0)", "LINESTRING(0 0,1 0,2 0)", "tii++", "txx=="); + test_geometry("LINESTRING(1 1,1 0)", "LINESTRING(0 0,1 0,2 0)", "txu++"); + test_geometry("LINESTRING(0 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "tii++", "txu=="); + test_geometry("LINESTRING(2 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "txi=+", "tix+="); - test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,1 1)", "tuu"); - test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,0 0)", "tix", "tui"); - test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,2 0)", "tii", "txx"); - test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 1,1 0)", "tux"); - test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(0 0,1 0)", "tii", "tux"); - test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(2 0,1 0)", "tix", "txi"); + test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,1 1)", "tuu++"); + test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,0 0)", "tix+=", "tui=+"); + test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,2 0)", "tii++", "txx=="); + test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 1,1 0)", "tux++"); + test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(0 0,1 0)", "tii++", "tux=="); + test_geometry("LINESTRING(0 0,1 0,2 0)", "LINESTRING(2 0,1 0)", "tix+=", "txi=+"); - test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,3 0,3 1)", "mii", "ccc", "muu"); - test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,3 0,3 -1)", "mii", "ccc", "muu"); - test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,1 0,1 1)", "miu", "mui"); - test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,1 0,1 -1)", "miu", "mui"); - test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,4 0,4 1)", "tii", "ccc", "tuu"); - test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,4 0,4 -1)", "tii", "ccc", "tuu"); - test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,2 0,2 1)", "tiu", "tui"); - test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,2 0,2 -1)", "tiu", "tui"); + test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,3 0,3 1)", "mii++", "ccc==", "muu=="); + test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,3 0,3 -1)", "mii++", "ccc==", "muu=="); + test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,1 0,1 1)", "miu+=", "mui=+"); + test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,1 0,1 -1)", "miu+=", "mui=+"); + test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,4 0,4 1)", "tii++", "ccc==", "tuu=="); + test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,4 0,4 -1)", "tii++", "ccc==", "tuu=="); + test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,2 0,2 1)", "tiu+=", "tui=+"); + test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,2 0,2 -1)", "tiu+=", "tui=+"); - test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,2 0,3 0,3 1)", "mii", "ecc", "muu"); + // same as above - TODO: reverse manually or automatically for all tests + /*test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,2 0,3 0,3 1)", "mii", "ecc", "muu"); test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,2 0,3 0,3 -1)", "mii", "ecc", "muu"); test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,2 0,1 0,1 1)", "miu", "ecc", "mui"); test_geometry("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,2 0,1 0,1 -1)", "miu", "ecc", "mui"); test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,3 0,4 0,4 1)", "tii", "ecc", "tuu"); test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,3 0,4 0,4 -1)", "tii", "ecc", "tuu"); test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,3 0,2 0,2 1)", "tiu", "ecc", "tui"); - test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,3 0,2 0,2 -1)", "tiu", "ecc", "tui"); + test_geometry("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,3 0,2 0,2 -1)", "tiu", "ecc", "tui");*/ - test_geometry("LINESTRING(0 0,1 0,2 1,3 5,4 0)", "LINESTRING(1 0,2 1,3 5)", "tii", "ecc", "tux"); - test_geometry("LINESTRING(0 0,1 0,2 1,3 5,4 0)", "LINESTRING(3 5,2 1,1 0)", "tix", "ecc", "tui"); - test_geometry("LINESTRING(1 0,2 1,3 5)", "LINESTRING(0 0,1 0,2 1,3 5,4 0)", "txu", "ecc", "tii"); - test_geometry("LINESTRING(3 5,2 1,1 0)", "LINESTRING(0 0,1 0,2 1,3 5,4 0)", "tiu", "ecc", "txi"); + test_geometry("LINESTRING(0 0,1 0,2 1,3 5,4 0)", "LINESTRING(1 0,2 1,3 5)", "tii++", "ecc==", "tux=="); + test_geometry("LINESTRING(0 0,1 0,2 1,3 5,4 0)", "LINESTRING(3 5,2 1,1 0)", "tix+=", "ecc==", "tui=+"); + test_geometry("LINESTRING(1 0,2 1,3 5)", "LINESTRING(0 0,1 0,2 1,3 5,4 0)", "txu==", "ecc==", "tii++"); + test_geometry("LINESTRING(3 5,2 1,1 0)", "LINESTRING(0 0,1 0,2 1,3 5,4 0)", "tiu+=", "ecc==", "txi=+"); - test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(-1 -1,1 0,10 0,20 -1)", "mii", "txu"); - test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(20 -1,10 0,1 0,-1 -1)", "miu", "txi"); - test_geometry("LINESTRING(-1 -1,1 0,10 0,20 -1)", "LINESTRING(0 0,10 0)", "mii", "tux"); - test_geometry("LINESTRING(20 -1,10 0,1 0,-1 -1)", "LINESTRING(0 0,10 0)", "mui", "tix"); + test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(-1 -1,1 0,10 0,20 -1)", "mii++", "txu=="); + test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(20 -1,10 0,1 0,-1 -1)", "miu+=", "txi=+"); + test_geometry("LINESTRING(-1 -1,1 0,10 0,20 -1)", "LINESTRING(0 0,10 0)", "mii++", "tux=="); + test_geometry("LINESTRING(20 -1,10 0,1 0,-1 -1)", "LINESTRING(0 0,10 0)", "mui=+", "tix+="); test_geometry("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)", "LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)", - expected("tii")("ecc")("muu")("mii")("muu")("mii")("mux")); + expected("tii++")("ecc==")("muu==")("mii++")("muu==")("mii++")("mux==")); test_geometry("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)", "LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)", - expected("tiu")("ecc")("mui")("miu")("mui")("miu")("mui")); + expected("tiu+=")("ecc==")("mui=+")("miu+=")("mui=+")("miu+=")("mui=+")); test_geometry("LINESTRING(31 0,15 0,10 5,5 5,4 0,1 0,0 0,-1 1)", "LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)", - expected("tui")("ecc")("miu")("mui")("miu")("mui")("mix")); + expected("tui=+")("ecc==")("miu+=")("mui=+")("miu+=")("mui=+")("mix+=")); test_geometry("LINESTRING(31 0,15 0,10 5,5 5,4 0,1 0,0 0,-1 1)", "LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)", - expected("tuu")("ecc")("mii")("muu")("mii")("muu")("mii")); + expected("tuu==")("ecc==")("mii++")("muu==")("mii++")("muu==")("mii++")); - test_geometry("LINESTRING(-1 0,1 0,2 1,3 2)", "LINESTRING(4 5,3 2,1 0,0 0)", "mix", "txi", "ecc"); - test_geometry("LINESTRING(4 5,3 2,1 0,0 0)", "LINESTRING(-1 0,1 0,2 1,3 2)", "mxi", "tix", "ecc"); + test_geometry("LINESTRING(-1 0,1 0,2 1,3 2)", "LINESTRING(4 5,3 2,1 0,0 0)", "mix+=", "txi=+", "ecc=="); + test_geometry("LINESTRING(4 5,3 2,1 0,0 0)", "LINESTRING(-1 0,1 0,2 1,3 2)", "mxi=+", "tix+=", "ecc=="); - test_geometry("LINESTRING(30 1,20 1,10 0,0 0)", "LINESTRING(1 1,2 0,3 1,20 1,25 1)", "mix", "tui", "muu"); - test_geometry("LINESTRING(1 1,2 0,3 1,20 1,25 1)", "LINESTRING(30 1,20 1,10 0,0 0)", "mxi", "tiu", "muu"); + test_geometry("LINESTRING(30 1,20 1,10 0,0 0)", "LINESTRING(1 1,2 0,3 1,20 1,25 1)", "mix+=", "tui=+", "muu++"); + test_geometry("LINESTRING(1 1,2 0,3 1,20 1,25 1)", "LINESTRING(30 1,20 1,10 0,0 0)", "mxi=+", "tiu+=", "muu++"); - test_geometry("LINESTRING(0 0,30 0)", "LINESTRING(4 0,4 1,20 1,5 0,1 0)", "muu", "mui", "mix"); - test_geometry("LINESTRING(4 0,4 1,20 1,5 0,1 0)", "LINESTRING(0 0,30 0)", "muu", "miu", "mxi"); + test_geometry("LINESTRING(0 0,30 0)", "LINESTRING(4 0,4 1,20 1,5 0,1 0)", "muu++", "mui=+", "mix+="); + test_geometry("LINESTRING(4 0,4 1,20 1,5 0,1 0)", "LINESTRING(0 0,30 0)", "muu++", "miu+=", "mxi=+"); test_geometry("LINESTRING(30 0,0 0)", "LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", - expected("mui")("miu")("mui")("mix")); + expected("mui=+")("miu+=")("mui=+")("mix+=")); test_geometry("LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", "LINESTRING(30 0,0 0)", - expected("miu")("mui")("miu")("mxi")); + expected("miu+=")("mui=+")("miu+=")("mxi=+")); test_geometry("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)", - expected("mii")("iuu")("muu")); + expected("mii++")("iuu++")("muu==")); test_geometry("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,5 0,4 1)", - expected("mii")("muu")("muu")); + expected("mii++")("muu++")("muu==")); + + // non-collinear + test_geometry("LINESTRING(0 1,0 0)", "LINESTRING(0 0,1 0,2 0)", "txu++"); + test_geometry("LINESTRING(0 1,0 0,1 1)", "LINESTRING(0 0,1 0,2 0)", "tuu++"); + test_geometry("LINESTRING(0 1,1 0,2 1)", "LINESTRING(0 0,1 0,2 0)", "tuu++"); // SPIKE - NON-ENDPOINT - NON-OPPOSITE // spike - neq eq test_geometry("LINESTRING(2 2,4 4,1 1)", "LINESTRING(0 0,4 4,6 3)", - expected("mii")("txu")("tiu")("mxi")); + expected("mii++")("txu==")("tiu==")("mxi=+")); // spike - eq eq test_geometry("LINESTRING(0 0,4 4,1 1)", "LINESTRING(0 0,4 4,6 3)", - expected("tii")("txu")("tiu")("mxi")); + expected("tii++")("txu==")("tiu==")("mxi=+")); // spike - eq neq test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(0 0,4 4,6 3)", - expected("tii")("mxu")("miu")("mxi")); + expected("tii++")("mxu==")("miu==")("mxi=+")); // spike - neq neq test_geometry("LINESTRING(1 1,3 3,2 2)", "LINESTRING(0 0,4 4,6 3)", - expected("mii")("mxu")("miu")("mxi")); + expected("mii++")("mxu==")("miu==")("mxi=+")); // spike - out neq test_geometry("LINESTRING(0 0,3 3,2 2)", "LINESTRING(1 1,4 4,6 3)", - expected("mii")("mxu")("miu")("mxi")); + expected("mii++")("mxu==")("miu==")("mxi=+")); // spike - out eq test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(1 1,4 4,6 3)", - expected("mii")("txu")("tiu")("mxi")); + expected("mii++")("txu==")("tiu==")("mxi=+")); // spike - out out/eq test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(1 0,4 4,6 3)", - expected("tuu")); + expected("tuu++")); test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(0 1,4 4,6 3)", - expected("tuu")); + expected("tuu++")); // spike - out out/neq test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(4 0,4 5,6 3)", - expected("muu")); + expected("muu++")); test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(0 4,5 4,6 3)", - expected("muu")); + expected("muu++")); // SPIKE - NON-ENDPOINT - OPPOSITE // opposite - eq eq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "LINESTRING(-1 -1,0 0,4 4,6 3)", - expected("tiu")("txi")("tii")("mxu")); + expected("tiu+=")("txi=+")("tii=+")("mxu==")); test_geometry("LINESTRING(-1 -1,0 0,4 4,6 3)", "LINESTRING(6 6,4 4,0 0,2 2)", - expected("tui")("tix")("tii")("mux")); + expected("tui=+")("tix+=")("tii+=")("mux==")); // opposite - neq eq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "LINESTRING(-1 -1,0 0,5 5,6 3)", - expected("miu")("txi")("tii")("mxu")); + expected("miu+=")("txi=+")("tii=+")("mxu==")); // opposite - eq neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "LINESTRING(-2 -2,-1 -1,4 4,6 3)", - expected("tiu")("mxi")("mii")("mxu")); + expected("tiu+=")("mxi=+")("mii=+")("mxu==")); // opposite - neq neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "LINESTRING(-2 -2,-1 -1,3 3,6 3)", - expected("miu")("mxi")("mii")("mxu")); + expected("miu+=")("mxi=+")("mii=+")("mxu==")); // opposite - neq neq test_geometry("LINESTRING(6 6,4 4,0 0,2 2)", "LINESTRING(-2 -2,-1 -1,3 3,5 5,6 3)", - expected("miu")("mxi")("mii")("mxu")); + expected("miu+=")("mxi=+")("mii=+")("mxu==")); // opposite - neq eq test_geometry("LINESTRING(6 3,3 3,0 0)", "LINESTRING(0 0,2 2,3 3,1 1)", - expected("txi")("tix")("tii")("mux")); + expected("txi=+")("tix+=")("tii+=")("mux==")); // SPIKE - ENDPOINT - NON-OPPOSITE // spike - neq eq test_geometry("LINESTRING(2 2,4 4,1 1)", "LINESTRING(0 0,4 4)", - expected("mii")("txx")("tix")("mxi")); + expected("mii++")("txx==")("tix==")("mxi=+")); test_geometry("LINESTRING(2 2,4 4,1 1)", "LINESTRING(4 4,0 0)", - expected("miu")("txi")("tii")("mxu")); + expected("miu+=")("txi=+")("tii=+")("mxu==")); // spike - eq eq test_geometry("LINESTRING(0 0,4 4,1 1)", "LINESTRING(0 0,4 4)", - expected("tii")("txx")("tix")("mxi")); + expected("tii++")("txx==")("tix==")("mxi=+")); test_geometry("LINESTRING(0 0,4 4,1 1)", "LINESTRING(4 4,0 0)", - expected("tix")("txi")("tii")("mxu")); + expected("tix+=")("txi=+")("tii=+")("mxu==")); // spike - eq neq test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(0 0,4 4)", - expected("tii")("mxu")("miu")("mxi")); + expected("tii++")("mxu==")("miu==")("mxi=+")); test_geometry("LINESTRING(0 0,3 3,1 1)", "LINESTRING(4 4,0 0)", - expected("tix")("mxi")("mii")("mxu")); + expected("tix+=")("mxi=+")("mii=+")("mxu==")); // spike - neq neq test_geometry("LINESTRING(1 1,3 3,2 2)", "LINESTRING(0 0,4 4)", - expected("mii")("mxu")("miu")("mxi")); + expected("mii++")("mxu==")("miu==")("mxi=+")); test_geometry("LINESTRING(1 1,3 3,2 2)", "LINESTRING(4 4,0 0)", - expected("miu")("mxi")("mii")("mxu")); + expected("miu+=")("mxi=+")("mii=+")("mxu==")); // spike - out neq test_geometry("LINESTRING(0 0,3 3,2 2)", "LINESTRING(1 1,4 4)", - expected("mii")("mxu")("miu")("mxi")); + expected("mii++")("mxu==")("miu==")("mxi=+")); test_geometry("LINESTRING(0 0,3 3,2 2)", "LINESTRING(4 4,1 1)", - expected("mix")("mxi")("mii")("mxu")); + expected("mix+=")("mxi=+")("mii=+")("mxu==")); // spike - out eq test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(1 1,4 4)", - expected("mii")("txx")("tix")("mxi")); + expected("mii++")("txx==")("tix==")("mxi=+")); test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(4 4,1 1)", - expected("mix")("txi")("tii")("mxu")); + expected("mix+=")("txi=+")("tii=+")("mxu==")); // spike - out out/eq test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(1 0,4 4)", - expected("tux")); + expected("tux++")); test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(0 1,4 4)", - expected("tux")); + expected("tux++")); // spike - out out/neq test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(4 0,4 5)", - expected("muu")); + expected("muu++")); test_geometry("LINESTRING(0 0,4 4,2 2)", "LINESTRING(0 4,5 4)", - expected("muu")); + expected("muu++")); // TODO: //test_geometry("LINESTRING(0 0,2 0,1 0)", "LINESTRING(0 1,0 0,2 0)", "1FF00F102"); @@ -233,16 +243,16 @@ void test_all() // duplicated test_geometry("MULTILINESTRING((0 0,10 0,30 0))", "MULTILINESTRING((0 10,5 0,20 0,20 0,30 0),(2 0,2 0),(3 0,3 0,3 0))", - expected("mii")("ccc")("ccc")("txx")); + expected("mii++")("ccc==")("ccc==")("txx==")); // spike test_geometry("LINESTRING(2 2,5 -1,15 2,18 0,20 0)", "LINESTRING(30 0,19 0,18 0,0 0)", - expected("iuu")("iuu")("tiu")("mxi")); + expected("iuu++")("iuu++")("tiu+=")("mxi=+")); // spike test_geometry("MULTILINESTRING((0 0,10 0,5 0))", "MULTILINESTRING((1 0,8 0,4 0))", - expected("mii")("mix")("mux")("mui")("mix")("mii")("mxu")("mxi")); + expected("mii++")("mix+=")("mux==")("mui==")("mix+=")("mii+=")("mxu==")("mxi=+")); /*test_geometry("MULTILINESTRING((0 0,10 0,5 0))", "MULTILINESTRING((1 0,8 0,4 0),(2 0,9 0,5 0))", @@ -251,98 +261,120 @@ void test_all() // spike vs endpoint test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,-2 0),(11 0,10 0,12 0))", - expected("tuu")("txu")); + expected("tuu++")("txu++")); // internal turning R vs spike test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((0 1,1 1,0 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((1 2,1 1,1 2))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((0 0,1 0,0 0))", "MULTILINESTRING((2 0,1 0,2 0))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((0 2,1 1,0 2))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((2 0,1 1,2 0))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,2 1))", "MULTILINESTRING((2 1,1 1,2 1))", - expected("txi")("tix")("tii")("txx")); + expected("txi=+")("tix+=")("tii+=")("txx==")); // internal turning L vs spike test_geometry("MULTILINESTRING((1 0,1 1,0 1))", "MULTILINESTRING((2 1,1 1,2 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,0 1))", "MULTILINESTRING((1 2,1 1,1 2))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,0 1))", "MULTILINESTRING((2 2,1 1,2 2))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,0 1))", "MULTILINESTRING((0 0,1 1,0 0))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 0,1 1,0 1))", "MULTILINESTRING((0 1,1 1,0 1))", - expected("txi")("tix")("tii")("txx")); + expected("txi=+")("tix+=")("tii+=")("txx==")); // spike vs internal turning R test_geometry("MULTILINESTRING((0 1,1 1,0 1))", "MULTILINESTRING((1 0,1 1,2 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 2,1 1,1 2))", "MULTILINESTRING((1 0,1 1,2 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((2 0,1 0,2 0))", "MULTILINESTRING((0 0,1 0,0 0))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((0 2,1 1,0 2))", "MULTILINESTRING((1 0,1 1,2 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((2 0,1 1,2 0))", "MULTILINESTRING((1 0,1 1,2 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((2 1,1 1,2 1))", "MULTILINESTRING((1 0,1 1,2 1))", - expected("tix")("txi")("tii")("txx")); + expected("tix+=")("txi=+")("tii=+")("txx==")); // spike vs internal turning L test_geometry("MULTILINESTRING((2 1,1 1,2 1))", "MULTILINESTRING((1 0,1 1,0 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((1 2,1 1,1 2))", "MULTILINESTRING((1 0,1 1,0 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((2 2,1 1,2 2))", "MULTILINESTRING((1 0,1 1,0 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((0 0,1 1,0 0))", "MULTILINESTRING((1 0,1 1,0 1))", - expected("tuu")); + expected("tuu++")); test_geometry("MULTILINESTRING((0 1,1 1,0 1))", "MULTILINESTRING((1 0,1 1,0 1))", - expected("tix")("txi")("tii")("txx")); + expected("tix+=")("txi=+")("tii=+")("txx==")); // spike vs internal collinear test_geometry("MULTILINESTRING((0 1,1 1,0 1))", "MULTILINESTRING((2 1,1 1,0 1))", - expected("tix")("txi")("tii")("txx")); + expected("tix+=")("txi=+")("tii=+")("txx==")); // internal collinear vs spike test_geometry("MULTILINESTRING((2 1,1 1,0 1))", "MULTILINESTRING((0 1,1 1,0 1))", - expected("txi")("tix")("tii")("txx")); + expected("txi=+")("tix+=")("tii+=")("txx==")); + // spike vs spike + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((2 2,1 1,2 2))", + expected("tuu++")); + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((2 0,1 1,2 0))", + expected("tuu++")); + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((2 1,1 1,2 1))", + expected("tuu++")); + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((0 1,1 1,0 1))", + expected("tuu++")); + // collinear spikes + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((0 0,1 1,0 0))", + expected("tii++")("tix+=")("txi=+")("txx==") + ("ecc==")); + test_geometry("MULTILINESTRING((0 0,1 1,0 0))", + "MULTILINESTRING((1 1,0 0,1 1))", + expected("tix+=")("tii+=")("txx==")("txi==") + ("txi=+")("tii=+")("txx==")("tix==")); // non-spike similar test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 0,0 0,2 0))", - expected("tii")("mux")); + expected("tii++")("mux==")); test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((-1 -1,0 0,2 0))", - expected("tii")("mux")); + expected("tii++")("mux==")); test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((2 0,0 0,-1 0))", - expected("tiu")("mui")); + expected("tiu+=")("mui=+")); test_geometry("MULTILINESTRING((0 0,10 0))", "MULTILINESTRING((2 0,0 0,-1 -1))", - expected("tiu")("mui")); + expected("tiu+=")("mui=+")); } int test_main(int, char* []) diff --git a/test/algorithms/overlay/test_get_turns.hpp b/test/algorithms/overlay/test_get_turns.hpp index f5e72797d..0950ebfe1 100644 --- a/test/algorithms/overlay/test_get_turns.hpp +++ b/test/algorithms/overlay/test_get_turns.hpp @@ -33,6 +33,36 @@ #include #include +template +struct expected_pusher +{ + static const int version = Version; + + void push_back(std::string const& ex) { vec.push_back(ex); } + expected_pusher & operator()(std::string const& ex) + { + push_back(ex); + return *this; + } + + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + iterator begin() { return vec.begin(); } + iterator end() { return vec.end(); } + const_iterator begin() const { return vec.begin(); } + const_iterator end() const { return vec.end(); } + + std::vector vec; +}; + +expected_pusher<1> expected(std::string const& ex) +{ + expected_pusher<1> res; + return res(ex); +} + +template struct equal_turn { equal_turn(std::string const& s) : turn_ptr(&s) {} @@ -49,13 +79,46 @@ struct equal_turn const std::string * turn_ptr; }; -template +template <> +struct equal_turn<1> +{ + equal_turn(std::string const& s) : turn_ptr(&s) {} + + template + bool operator()(T const& t) const + { + unsigned count = turn_ptr->size(); + BOOST_ASSERT(turn_ptr && count >= 1); + return bg::method_char(t.method) == (*turn_ptr)[0] + && ( count > 1 + ? bg::operation_char(t.operations[0].operation) == (*turn_ptr)[1] + : true ) + && ( count > 2 + ? bg::operation_char(t.operations[1].operation) == (*turn_ptr)[2] + : true ) + && ( count > 3 + ? is_colinear_char(t.operations[0].is_collinear) == (*turn_ptr)[3] + : true ) + && ( count > 4 + ? is_colinear_char(t.operations[1].is_collinear) == (*turn_ptr)[4] + : true ); + } + + static inline char is_colinear_char(bool is_collinear) + { + return is_collinear ? '=' : '+'; + } + + const std::string * turn_ptr; +}; + +template void check_geometry_range( Geometry1 const& g1, Geometry2 const& g2, std::string const& wkt1, std::string const& wkt2, - Range const& expected) + Expected const& expected) { typedef bg::detail::no_rescale_policy robust_policy_type; typedef typename bg::point_type::type point_type; @@ -95,11 +158,11 @@ void check_geometry_range( "get_turns: " << wkt1 << " and " << wkt2 << " -> Expected turns #: " << boost::size(expected) << " detected turns #: " << turns.size()); - for ( typename boost::range_iterator::type sit = boost::begin(expected) ; + for ( typename boost::range_iterator::type sit = boost::begin(expected) ; sit != boost::end(expected) ; ++sit) { typename std::vector::iterator - it = std::find_if(turns.begin(), turns.end(), equal_turn(*sit)); + it = std::find_if(turns.begin(), turns.end(), equal_turn(*sit)); if ( it != turns.end() ) turns.erase(it); @@ -112,9 +175,9 @@ void check_geometry_range( } } -template +template void test_geometry_range(std::string const& wkt1, std::string const& wkt2, - Range const& expected) + Expected const& expected) { Geometry1 geometry1; Geometry2 geometry2; @@ -127,67 +190,26 @@ template void test_geometry(std::string const& wkt1, std::string const& wkt2, std::string const& ex0) { - std::vector expected; - expected.push_back(ex0); - test_geometry_range(wkt1, wkt2, expected); + test_geometry_range(wkt1, wkt2, expected(ex0)); } template void test_geometry(std::string const& wkt1, std::string const& wkt2, std::string const& ex0, std::string const& ex1) { - std::vector expected; - expected.push_back(ex0); - expected.push_back(ex1); - test_geometry_range(wkt1, wkt2, expected); + test_geometry_range(wkt1, wkt2, expected(ex0)(ex1)); } template void test_geometry(std::string const& wkt1, std::string const& wkt2, std::string const& ex0, std::string const& ex1, std::string const& ex2) { - std::vector expected; - expected.push_back(ex0); - expected.push_back(ex1); - expected.push_back(ex2); - test_geometry_range(wkt1, wkt2, expected); + test_geometry_range(wkt1, wkt2, expected(ex0)(ex1)(ex2)); } -struct expected_pusher -{ - expected_pusher & operator()(std::string const& ex) - { - vec.push_back(ex); - return *this; - } - - typedef std::vector::iterator iterator; - typedef std::vector::const_iterator const_iterator; - - iterator begin() { return vec.begin(); } - iterator end() { return vec.end(); } - const_iterator begin() const { return vec.begin(); } - const_iterator end() const { return vec.end(); } - - std::vector vec; -}; - -expected_pusher expected(std::string const& ex) -{ - expected_pusher res; - return res(ex); -} - -template +template void test_geometry(std::string const& wkt1, std::string const& wkt2, - std::vector const& expected) -{ - test_geometry_range(wkt1, wkt2, expected); -} - -template -void test_geometry(std::string const& wkt1, std::string const& wkt2, - expected_pusher const& expected) + expected_pusher const& expected) { test_geometry_range(wkt1, wkt2, expected); }