From aa2162f56b2dd5cc51a2c49eac6dfeee3bc6e2ed Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Nov 2024 16:48:25 +0100 Subject: [PATCH 1/4] fix: add condition to handle_imperfect_touch Fixes #1345 --- .../detail/overlay/get_turn_info.hpp | 11 +++- .../detail/overlay/get_turn_info_helpers.hpp | 13 ++--- test/algorithms/overlay/overlay_cases.hpp | 53 +++++++++++++++++++ .../set_operations/difference/difference.cpp | 3 ++ .../intersection/intersection.cpp | 3 ++ .../algorithms/set_operations/union/union.cpp | 6 +++ 6 files changed, 82 insertions(+), 7 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 8a85d37302..bc63357b3b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -569,7 +569,7 @@ struct touch : public base_turn_handler // || // |^---- // >----->P - // * * they touch here (P/Q are (nearly) on top) + // * * they touch here (P/Q are (nearly) on top of each other) // // Q continues from where P comes. // P continues from where Q comes @@ -586,6 +586,14 @@ struct touch : public base_turn_handler // >----->P qj is LEFT of P1 and pi is LEFT of Q2 // (the other way round is also possible) + // There are also cases like this: + // P + // ^ + // || + // || + // P----^----- bool { auto const d1 = get_distance_measure(r1.at(0), r1.at(1), r2.at(1), umbrella_strategy); @@ -674,6 +682,7 @@ struct touch : public base_turn_handler { if (side_qk_p1 == 0 && side_pk_q1 == 0 && has_pk && has_qk + && opposite(side.pi_wrt_q1(), side.qk_wrt_p2()) && handle_imperfect_touch(range_p, range_q, side_pk_q2, umbrella_strategy, ti)) { // If q continues collinearly (opposite) with p, it should be blocked 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 index 97be7a1a23..3b3c03ae67 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -58,6 +58,13 @@ struct side_calculator , m_range_q(range_q) {} + inline int pi_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pi()); } + + inline int pj_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pj()); } + inline int pj_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pj()); } + inline int qj_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qj()); } + inline int qj_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qj()); } + inline int pk_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_pk()); } inline int pk_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pk()); } inline int qk_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qk()); } @@ -66,12 +73,6 @@ struct side_calculator inline int pk_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pk()); } inline int qk_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qk()); } - // Necessary when rescaling turns off: - inline int qj_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qj()); } - inline int qj_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qj()); } - inline int pj_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pj()); } - inline int pj_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pj()); } - inline auto const& get_pi() const { return m_range_p.at(0); } inline auto const& get_pj() const { return m_range_p.at(1); } inline auto const& get_pk() const { return m_range_p.at(2); } diff --git a/test/algorithms/overlay/overlay_cases.hpp b/test/algorithms/overlay/overlay_cases.hpp index 3fcabd6c03..6b54807313 100644 --- a/test/algorithms/overlay/overlay_cases.hpp +++ b/test/algorithms/overlay/overlay_cases.hpp @@ -1231,6 +1231,59 @@ static std::string issue_1342_b[2] = "POLYGON((2.000000861538488 2.944711629068049e-16,2.000000861538488 2.0000008,23.52786548447633 2.0000008,23.52786548447633 2.944711629068049e-16,2.000000861538488 2.944711629068049e-16))" }; +static std::string issue_1345_a[2] = +{ + // Needs check for opposite in handle_imperfect_touch + R""""( + POLYGON(( + -2.0785311235613415 -0.6304193410175202, + -2.0534946127981359 -0.6304193410175202, + -2.0534946127981359 -0.8867932112327471, + -2.3098684830133629 -0.8867932112327471, + -2.3098684830133629 -0.6554558517807265, + -2.2848319722501573 -0.6554558517807265, + -2.0785311235613415 -0.6554558517807265, + -2.0785311235613415 -0.6304193410175202 + )) + )"""", + R""""( + POLYGON(( + -2.0785311235613420 -0.6304193410175202, + -2.0534946127981359 -0.6304193410175202, + -2.0534946127981359 -0.6554558517807265, + -2.0785311235613420 -0.6554558517807265, + -2.0785311235613420 -0.6304193410175202 + )) + )"""" +}; + +static std::string issue_1345_b[2] = +{ + // Needs check for opposite in handle_imperfect_touch + R""""( + POLYGON(( + -6.1723606999999996 3.2834021000000000, + -6.1723606999999996 2.8006724999999992, + -5.7133718999999994 2.8006724999999992, + -5.7133718999999994 3.2834021000000000, + -5.6896310999999997 3.2834021000000000, + -5.6896310999999997 2.7769316999999996, + -6.1961014999999993 2.7769316999999996, + -6.1961014999999993 3.2834021000000000, + -6.1723606999999996 3.2834021000000000 + )) + )"""", + R""""( + POLYGON(( + -6.1723606999999996 2.8006724999999997, + -5.7133718999999994 2.8006724999999997, + -5.7133718999999994 2.7769316999999996, + -6.1723606999999996 2.7769316999999996, + -6.1723606999999996 2.8006724999999997 + )) + )"""" +}; + static std::string ggl_list_20120229_volker[3] = { "POLYGON((1716 1554,2076 2250,2436 2352,2796 1248,3156 2484,3516 2688,3516 2688,3156 2484,2796 1248,2436 2352,2076 2250, 1716 1554))", diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index b2c2dc6ea7..d6112f9118 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -647,6 +647,9 @@ void test_all() TEST_DIFFERENCE(issue_1342_a, 2, 5.762381026454777, 0, 0.0, 2); TEST_DIFFERENCE(issue_1342_b, 2, 5.762381026454777, 1, 2.55e-14, 3); + TEST_DIFFERENCE(issue_1345_a, 1, 0.059308854, 0, 0.0, 1); + TEST_DIFFERENCE(issue_1345_b, 2, 0.024048025, 0, 0.0, 2); + TEST_DIFFERENCE(mysql_21977775, 2, 160.856568913, 2, 92.3565689126, 4); TEST_DIFFERENCE(mysql_21965285, 1, 92.0, 1, 14.0, 1); TEST_DIFFERENCE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2); diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index fe926a6462..2ce4956a86 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -315,6 +315,9 @@ void test_areal() TEST_INTERSECTION(issue_1342_a, 1, -1, 43.05575); TEST_INTERSECTION(issue_1342_b, 1, -1, 43.05575); + TEST_INTERSECTION(issue_1345_a, 1, -1, 0.00062682687); + TEST_INTERSECTION(issue_1345_b, 1, -1, 0.010896761); + test_one("buffer_mp1", buffer_mp1[0], buffer_mp1[1], 1, 31, 2.271707796); test_one("buffer_mp2", buffer_mp2[0], buffer_mp2[1], diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index 0473c5cfa2..2824016d2b 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -493,6 +493,12 @@ void test_areal() TEST_UNION(issue_1342_b, 1, 0, -1, 48.81812749462214); TEST_UNION_REV(issue_1342_b, 1, 0, -1, 48.81812749462214); + TEST_UNION(issue_1345_a, 1, 0, -1, 0.059935681); + TEST_UNION_REV(issue_1345_a, 1, 0, -1, 0.059935681); + + TEST_UNION(issue_1345_b, 1, 0, -1, 0.034944786); + TEST_UNION_REV(issue_1345_b, 1, 0, -1, 0.034944786); + TEST_UNION(geos_1, 1, 0, -1, expectation_limits(3458.0, 3461.3203125)); TEST_UNION(geos_2, 1, 0, -1, expectation_limits(349.0625, 350.55102539)); TEST_UNION(geos_3, 1, 0, -1, 29391548.4998779); From cd4deda8e826b2f0b863bdfa9e38c783a7cd0f75 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Nov 2024 16:55:19 +0100 Subject: [PATCH 2/4] chore: update release notes for issue 1345 --- doc/release_notes.qbk | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 1271b6c8dd..90064f5146 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -43,6 +43,7 @@ * [@https://github.com/boostorg/geometry/issues/1226 1226] [@https://github.com/boostorg/geometry/issues/1326 1326] Fix extra round on ring * [@https://github.com/boostorg/geometry/issues/1293 1293] [@https://github.com/boostorg/geometry/issues/1294 1294] [@https://github.com/boostorg/geometry/issues/1295 1295] Fix traversal through non clustered rings * [@https://github.com/boostorg/geometry/issues/1342 1342] Fix discarding a start turn for combination with touch +* [@https://github.com/boostorg/geometry/issues/1345 1345] Fix in handling imperfect touch [/=================] [heading Boost 1.85] From 0edb67322dc050decf1037a94542a26e588e6429 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 24 Nov 2024 10:32:55 +0100 Subject: [PATCH 3/4] fix: add condition to handle_as_touch Fixes #1288 --- .../detail/overlay/get_turn_info.hpp | 26 ++++++++++++++++--- .../overlay/multi_overlay_cases.hpp | 14 ++++++++++ .../difference/difference_multi.cpp | 10 +++++++ .../intersection/intersection_multi.cpp | 3 +++ .../set_operations/union/union_multi.cpp | 3 +++ 5 files changed, 52 insertions(+), 4 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 bc63357b3b..7795f41e45 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -343,10 +343,14 @@ struct touch_interior : public base_turn_handler template < typename IntersectionInfo, - typename UniqueSubRange + typename SideCalculator, + typename UniqueSubRange1, + typename UniqueSubRange2 > static bool handle_as_touch(IntersectionInfo const& info, - UniqueSubRange const& non_touching_range) + SideCalculator const& side, + UniqueSubRange1 const& non_touching_range, + UniqueSubRange2 const& other_range) { if BOOST_GEOMETRY_CONSTEXPR (! VerifyPolicy::use_handle_as_touch) { @@ -354,6 +358,20 @@ struct touch_interior : public base_turn_handler } else // else prevents unreachable code warning { + bool const has_k = ! non_touching_range.is_last_segment() + && ! other_range.is_last_segment(); + if (has_k + && (same(side.pj_wrt_q1(), side.qj_wrt_p2()) + || same(side.pj_wrt_q2(), side.qj_wrt_p1()))) + { + // At a touch, the touching points (pj and qj) should be collinear + // with both other segments. + // If that is not the case (both left or both right), it should not be handled as a touch, + // (though the intersection point might be close to the end), + // because segments might cross each other or touch the other in the middle. + return false; + } + // // // ^ Q(i) ^ P(i) @@ -1461,7 +1479,7 @@ struct get_turn_info if ( inters.d_info().arrival[1] == 1 ) { // Q arrives - if (handler::handle_as_touch(inters.i_info(), range_p)) + if (handler::handle_as_touch(inters.i_info(), inters.sides(), range_p, range_q)) { handle_as_touch = true; } @@ -1475,7 +1493,7 @@ struct get_turn_info else { // P arrives, swap p/q - if (handler::handle_as_touch(inters.i_info(), range_q)) + if (handler::handle_as_touch(inters.i_info(), inters.swapped_sides(), range_q, range_p)) { handle_as_touch = true; } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index d0a53ab9cd..8d391aa105 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1580,6 +1580,20 @@ static std::string issue_1109[2] = "MULTIPOLYGON(((0 -88,0 -115.40000152587890625,-10 -88,0 -88)))" }; +static std::string issue_1222[2] = +{ + "MULTIPOLYGON(((2 4,2 2,0 2,0 4,2 4)),((6 4,4 4,2 4,2 6,0 6,0 10,6 10,6 4)))", + "MULTIPOLYGON(((4 4,4 2,2 2,2 4,4 4)),((4 8,4 6,2 6,2 8,4 8)))" +}; + +static std::string issue_1288[3] = +{ + // Issue with differences in behaviour for multi polygon vs polygon + "MULTIPOLYGON(((-2.0 -1.5, 2.0 -1.5, 2.0 1.5, -2.0 1.5)))", + "MULTIPOLYGON(((-0.5 -1.49999999, -2.0 -0.1, -1.99999999 -1.5)))", + "POLYGON((-0.5 -1.49999999, -2.0 -0.1, -1.99999999 -1.5))" +}; + static std::string bug_21155501[2] = { "MULTIPOLYGON(((-8.3935546875 27.449790329784214,4.9658203125 18.729501999072138,11.8212890625 23.563987128451217,9.7119140625 25.48295117535531,9.8876953125 31.728167146023935,8.3056640625 32.99023555965106,8.5693359375 37.16031654673677,-1.8896484375 35.60371874069731,-0.5712890625 32.02670629333614,-8.9208984375 29.458731185355344,-8.3935546875 27.449790329784214)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 8810f61b4e..cc9db5707c 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -211,6 +211,16 @@ void test_areal() TEST_DIFFERENCE(issue_900, 0, 0.0, 2, 35, 2); + TEST_DIFFERENCE(issue_1222, 2, 32.0, 1, 4.0, 1); + { + // "method: t; operations: c/c;" still happening in the result + // for multi/multi + ut_settings settings; + settings.set_test_validity(BG_IF_TEST_FAILURES); + settings.validity_of_sym = BG_IF_TEST_FAILURES; + TEST_DIFFERENCE_WITH(0, 1, issue_1288, 2, 10.95, 0, 0.0, 2); + } + // Areas and #clips correspond with POSTGIS (except sym case) test_one("case_101_multi", case_101_multi[0], case_101_multi[1], diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 37ef6c4a20..733a9d9e7e 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -362,6 +362,9 @@ void test_areal() TEST_INTERSECTION(issue_888_34, 7, -1, 0.0256838); TEST_INTERSECTION(issue_888_37, 13, -1, 0.0567043); + TEST_INTERSECTION(issue_1222, 1, -1, 4.0); + TEST_INTERSECTION(issue_1288, 1, -1, 1.05); + TEST_INTERSECTION(mysql_23023665_7, 2, 11, 9.80505786783); TEST_INTERSECTION(mysql_23023665_12, 2, 0, 11.812440191387557); TEST_INTERSECTION(mysql_regression_1_65_2017_08_31, 2, -1, 29.9022122); diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index d325a87401..792aa4e417 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -437,6 +437,9 @@ void test_areal() TEST_UNION(issue_1109, 2, 0, -1, 3946.5); + TEST_UNION(issue_1222, 1, 0, -1, 40.0); + TEST_UNION(issue_1288, 1, 0, -1, 12.0); + // One or two polygons, the ideal case is 1 TEST_UNION(mail_2019_01_21_johan, count_set(1, 2), 0, -1, 0.00058896); From 68d41297fdb737befb810a0b3ed85d218c049ff9 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 24 Nov 2024 11:10:58 +0100 Subject: [PATCH 4/4] chore: update release notes for issue 1288 --- doc/release_notes.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 90064f5146..75b3cd4425 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -43,7 +43,7 @@ * [@https://github.com/boostorg/geometry/issues/1226 1226] [@https://github.com/boostorg/geometry/issues/1326 1326] Fix extra round on ring * [@https://github.com/boostorg/geometry/issues/1293 1293] [@https://github.com/boostorg/geometry/issues/1294 1294] [@https://github.com/boostorg/geometry/issues/1295 1295] Fix traversal through non clustered rings * [@https://github.com/boostorg/geometry/issues/1342 1342] Fix discarding a start turn for combination with touch -* [@https://github.com/boostorg/geometry/issues/1345 1345] Fix in handling imperfect touch +* [@https://github.com/boostorg/geometry/issues/1288 1288] [@https://github.com/boostorg/geometry/issues/1345 1345] Fixes in handling (imperfect) touch [/=================] [heading Boost 1.85]