From 92010649906a3817fddaa62639dffb84390e7652 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 28 Sep 2016 12:22:52 +0200 Subject: [PATCH 01/18] [validity] fix zone count for intersection operations --- .../detail/overlay/handle_colocations.hpp | 3 +- .../detail/overlay/sort_by_side.hpp | 76 ++++++++++++++----- .../intersection/intersection_multi.cpp | 5 +- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index cee709a86..2e9977043 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -684,6 +684,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, sbs.apply(turn_point); sbs.find_open(); + sbs.assign_zones(for_operation); // Unset the startable flag for all 'closed' zones for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) @@ -710,7 +711,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, } } - cinfo.open_count = sbs.open_count(); + cinfo.open_count = sbs.open_count(for_operation); } } diff --git a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index a8080ccf5..8059c656d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -181,11 +181,36 @@ private : Point m_p1, m_p2; }; +// Sorts vectors in counter clockwise order (by default) template struct side_sorter { typedef ranked_point rp; +private : + struct include_union + { + inline bool operator()(rp const& ranked_point) const + { + // New candidate if there are no polygons on left side, + // but there are on right side + return ranked_point.count_left == 0 + && ranked_point.count_right > 0; + } + }; + + struct include_intersection + { + inline bool operator()(rp const& ranked_point) const + { + // New candidate if there are two polygons on right side, + // and less on the left side + return ranked_point.count_left < 2 + && ranked_point.count_right >= 2; + } + }; + +public : inline void set_origin(Point const& origin) { m_origin = origin; @@ -292,8 +317,6 @@ struct side_sorter &segment_identifier::source_index >(handled); } - - assign_zones(); } void reverse() @@ -334,8 +357,17 @@ struct side_sorter } } +//private : + + typedef std::vector container_type; + container_type m_ranked_points; + Point m_origin; + +private : + //! Check how many open spaces there are - inline std::size_t open_count() const + template + inline std::size_t open_count(Include const& include_functor) const { std::size_t result = 0; std::size_t last_rank = 0; @@ -345,8 +377,7 @@ struct side_sorter if (ranked_point.rank > last_rank && ranked_point.direction == sort_by_side::dir_to - && ranked_point.count_left == 0 - && ranked_point.count_right > 0) + && include_functor(ranked_point)) { result++; last_rank = ranked_point.rank; @@ -355,15 +386,6 @@ struct side_sorter return result; } -//protected : - - typedef std::vector container_type; - container_type m_ranked_points; - Point m_origin; - -private : - - std::size_t move(std::size_t index) const { std::size_t const result = index + 1; @@ -449,7 +471,8 @@ private : } //! Find closed zones and assign it - void assign_zones() + template + std::size_t assign_zones(Include const& include_functor) { // Find a starting point (the first rank after an outgoing rank // with no polygons on the left side) @@ -464,8 +487,7 @@ private : max_rank = ranked_point.rank; } if (ranked_point.direction == sort_by_side::dir_to - && ranked_point.count_left == 0 - && ranked_point.count_right > 0) + && include_functor(ranked_point)) { start_rank = ranked_point.rank + 1; } @@ -501,8 +523,7 @@ private : } if (ranked_point.direction == sort_by_side::dir_to - && ranked_point.count_left == 0 - && ranked_point.count_right > 0) + && include_functor(ranked_point)) { rank_at_next_zone = ranked_point.rank + 1; if (rank_at_next_zone > max_rank) @@ -516,8 +537,25 @@ private : ranked_point.zone = zone_id; } + return zone_id; } +public : + inline std::size_t open_count(operation_type for_operation) const + { + return for_operation == operation_union + ? open_count(include_union()) + : open_count(include_intersection()) + ; + } + + inline std::size_t assign_zones(operation_type for_operation) + { + return for_operation == operation_union + ? assign_zones(include_union()) + : assign_zones(include_intersection()) + ; + } }; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 782d2100b..7691bf2a0 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -145,7 +145,7 @@ void test_areal() test_one("case_recursive_boxes_4", case_recursive_boxes_4[0], case_recursive_boxes_4[1], - 8, 179, 67.0, // Area from SQL Server + 7, 179, 67.0, // Area from SQL Server ignore_validity); // Fixed by replacing handle_tangencies in less_by_segment_ratio sort order @@ -226,7 +226,8 @@ void test_areal() 5, 0, 3.75); test_one("case_recursive_boxes_30", case_recursive_boxes_30[0], case_recursive_boxes_30[1], - 4, 0, 6.0); + 3, 0, 6.0, + ignore_validity); test_one("case_recursive_boxes_31", case_recursive_boxes_31[0], case_recursive_boxes_31[1], 2, 0, 2.5); From 30ad9da654508ad6ba915a7970fae419da1e7a6d Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 28 Sep 2016 12:43:22 +0200 Subject: [PATCH 02/18] [validity] add cases with single cluster --- test/algorithms/overlay/overlay_cases.hpp | 14 ++++++++++++++ .../set_operations/difference/difference.cpp | 10 ++++++++++ .../set_operations/intersection/intersection.cpp | 8 ++++++++ test/algorithms/set_operations/union/union.cpp | 6 ++++++ 4 files changed, 38 insertions(+) diff --git a/test/algorithms/overlay/overlay_cases.hpp b/test/algorithms/overlay/overlay_cases.hpp index 5e07a2f65..474a40e3c 100644 --- a/test/algorithms/overlay/overlay_cases.hpp +++ b/test/algorithms/overlay/overlay_cases.hpp @@ -514,6 +514,20 @@ static std::string case_100[2] = "POLYGON((4 0,8 0,8 4,4 4,4 0))" }; +static std::string case_101[2] = +{ + // Smaller adapted version of case_recursive_boxes_34, with only one cluster + "POLYGON((0 1,0 5,5 5,5 1,0 1))", + "POLYGON((3 6,4 5,5 4,3 2,1 4,3 6),(4 5,3 4,3.5 3.5,4 4,4 5))" +}; + +static std::string case_102[2] = +{ + // Smaller adapted version of case_recursive_boxes_34, with only one cluster + "POLYGON((1.25 3.75,1.25 5,5 5,5 3.75,1.25 3.75))", + "POLYGON((3 6,4 5,5 4,3 2,1 4,3 6),(4 5,3 4,3.5 3.5,4 4,4 5))" +}; + static std::string case_many_situations[2] = { "POLYGON((2 6,2 14,10 18,18 14,18 6,16 5,14 4,12 3,10 2,8 3,6 4,4 5,2 6))", "POLYGON((2 6,2 7,2 8,2 9,2 10,2 11,2 12,1 14" diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 8cd7b3d8c..37f41f945 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -219,6 +219,16 @@ void test_all() 1, 13, 16.0 + 3.125, ignore_validity); + test_one("case_101", + case_101[0], case_101[1], + 3, 17, 13.75, + 1, 4, 1.0); + + test_one("case_102", + case_102[0], case_102[1], + 4, 18, 1.5, + 3, 15, 4.0625); + test_one("winded", winded[0], winded[1], 3, 37, 61, diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index b1de3445c..c150f5ca6 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -332,6 +332,14 @@ void test_areal() case_81[0], case_81[1], 0, -1, 0.0); + test_one("case_101", + case_101[0], case_101[1], + 0, -1, 6.25, + ignore_validity); + test_one("case_102", + case_102[0], case_102[1], + 0, -1, 3.1875); + test_one("mysql_21964049", mysql_21964049[0], mysql_21964049[1], 0, -1, 0.0); diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index f2b7cf0ee..aa0155604 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -231,6 +231,12 @@ void test_areal() test_one("100", case_100[0], case_100[1], 1, 1, 13, 19.125); + test_one("101", + case_101[0], case_101[1], 1, 0, 9, 21.0); + + test_one("102", + case_102[0], case_102[1], 1, 1, 17, 8.75); + /* test_one(102, simplex_normal[0], simplex_reversed[1], From 2a343f072f70362bd0683ca3ab6f2ed733206134 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 28 Sep 2016 13:21:22 +0200 Subject: [PATCH 03/18] [validity][test] add unit test for sort_by_side, testing open_count of the one and only cluster found in the test cases --- test/algorithms/overlay/Jamfile.v2 | 1 + test/algorithms/overlay/sort_by_side.cpp | 257 +++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 test/algorithms/overlay/sort_by_side.cpp diff --git a/test/algorithms/overlay/Jamfile.v2 b/test/algorithms/overlay/Jamfile.v2 index 51ba13fde..9fb83345c 100644 --- a/test/algorithms/overlay/Jamfile.v2 +++ b/test/algorithms/overlay/Jamfile.v2 @@ -25,6 +25,7 @@ test-suite boost-geometry-algorithms-overlay [ run get_turns_linear_linear.cpp : : : : algorithms_get_turns_linear_linear ] [ run get_turns_linear_linear_sph.cpp : : : : algorithms_get_turns_linear_linear_sph ] [ run overlay.cpp : : : : algorithms_overlay ] + [ run sort_by_side.cpp : : : : algorithms_sort_by_side ] #[ run handle_touch.cpp : : : : algorithms_handle_touch ] [ run relative_order.cpp : : : : algorithms_relative_order ] [ run select_rings.cpp : : : : algorithms_select_rings ] diff --git a/test/algorithms/overlay/sort_by_side.cpp b/test/algorithms/overlay/sort_by_side.cpp new file mode 100644 index 000000000..06c6c1895 --- /dev/null +++ b/test/algorithms/overlay/sort_by_side.cpp @@ -0,0 +1,257 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2016 Barend Gehrels, Amsterdam, the Netherlands. + +// 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) + +#include + +#include +#include +#include + +#include "multi_overlay_cases.hpp" + +static const std::size_t default_result = 9999; + +// Adapted copy of handle_colocations::gather_cluster_properties +template +< + bool Reverse1, bool Reverse2, + typename Turns, + typename Clusters, + typename Geometry1, + typename Geometry2 +> +std::size_t test_gather_cluster_properties(std::string const& case_id, + Clusters& clusters, Turns& turns, + bg::detail::overlay::operation_type for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + using namespace boost::geometry; + using namespace boost::geometry::detail::overlay; + + std::size_t result = default_result; + + typedef typename boost::range_value::type turn_type; + typedef typename turn_type::point_type point_type; + typedef typename turn_type::turn_operation_type turn_operation_type; + + // Define sorter, sorting counter-clockwise such that polygons are on the + // right side + typedef sort_by_side::side_sorter + < + Reverse1, Reverse2, point_type, std::less + > sbs_type; + + // Only test the FIRST cluster (for now) + typename Clusters::iterator mit = clusters.begin(); + if (mit == clusters.end()) + { + return result; + } + + cluster_info& cinfo = mit->second; + std::set const& ids = cinfo.turn_indices; + if (ids.empty()) + { + return result; + } + + sbs_type sbs; + point_type turn_point; // should be all the same for all turns in cluster + + bool first = true; + for (typename std::set::const_iterator sit = ids.begin(); + sit != ids.end(); ++sit) + { + signed_size_type turn_index = *sit; + turn_type const& turn = turns[turn_index]; + if (first) + { + turn_point = turn.point; + } + for (int i = 0; i < 2; i++) + { + turn_operation_type const& op = turn.operations[i]; + sbs.add(op, turn_index, i, geometry1, geometry2, first); + first = false; + } + } + sbs.apply(turn_point); + + sbs.find_open(); + sbs.assign_zones(for_operation); + + cinfo.open_count = sbs.open_count(for_operation); + + return cinfo.open_count; +} + + +// Adapted copy of overlay::apply +template +< + bg::overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename GeometryOut, + typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy +> +std::size_t apply_overlay(std::string const& case_id, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Strategy const& ) +{ + using namespace boost::geometry; + + typedef typename bg::point_type::type point_type; + typedef bg::detail::overlay::traversal_turn_info + < + point_type, + typename bg::segment_ratio_type::type + > turn_info; + typedef std::deque turn_container_type; + + typedef std::deque + < + typename bg::ring_type::type + > ring_container_type; + + // Define the clusters, mapping cluster_id -> turns + typedef std::map + < + signed_size_type, + bg::detail::overlay::cluster_info + > cluster_type; + + turn_container_type turns; + + detail::get_turns::no_interrupt_policy policy; + bg::get_turns + < + Reverse1, Reverse2, + detail::overlay::assign_null_policy + >(geometry1, geometry2, robust_policy, turns, policy); + + typename Strategy::side_strategy_type side_strategy; + cluster_type clusters; + + bg::enrich_intersection_points(turns, + clusters, geometry1, geometry2, + robust_policy, + side_strategy); + + if (clusters.size() == 1) + { + // Now gather cluster properties again, with test option + return test_gather_cluster_properties(case_id, + clusters, turns, bg::detail::overlay::operation_from_overlay::value, + geometry1, geometry2); + } + + return default_result; +} + + +template +void test_sort_by_side(std::string const& case_id, + std::string const& wkt1, std::string const& wkt2, + std::size_t expected_open_count) +{ +// std::cout << case_id << std::endl; + + Geometry g1; + bg::read_wkt(wkt1, g1); + + Geometry g2; + bg::read_wkt(wkt2, g2); + + // Reverse if necessary + bg::correct(g1); + bg::correct(g2); + + typedef typename boost::range_value::type geometry_out; + + typedef typename bg::rescale_overlay_policy_type + < + Geometry, + Geometry + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = bg::get_rescale_policy(g1, g2); + + typedef bg::intersection_strategies + < + typename bg::cs_tag::type, + Geometry, + Geometry, + typename bg::point_type::type, + rescale_policy_type + > strategy; + + std::size_t open_count = apply_overlay(case_id, g1, g2, + robust_policy, + strategy()); + + BOOST_CHECK_MESSAGE(open_count == expected_open_count, + " caseid=" << case_id + << " open count: expected=" << expected_open_count + << " detected=" << open_count); +} + + +// Define two small macro's to avoid repetitions of testcases/names etc +#define TEST_INT(caseid, exp) { (test_sort_by_side) \ + ( #caseid "_int", caseid[0], caseid[1], exp); } + +#define TEST_UNION(caseid, exp) { (test_sort_by_side) \ + ( #caseid "_union", caseid[0], caseid[1], exp); } + +template +void test_all() +{ + typedef bg::model::point point_type; + typedef bg::model::polygon polygon; + typedef bg::model::multi_polygon multi_polygon; + + // Selection of test cases having only one cluster + + TEST_INT(case_62_multi, 1); + TEST_INT(case_63_multi, 1); + TEST_INT(case_64_multi, 1); + TEST_INT(case_72_multi, 3); + TEST_INT(case_107_multi, 2); + TEST_INT(case_recursive_boxes_1, 2); + TEST_INT(case_recursive_boxes_10, 2); + TEST_INT(case_recursive_boxes_18, 0); + TEST_INT(case_recursive_boxes_19, 0); + TEST_INT(case_recursive_boxes_20, 2); + TEST_INT(case_recursive_boxes_21, 1); + TEST_INT(case_recursive_boxes_22, 0); + TEST_INT(case_recursive_boxes_23, 1); + + TEST_UNION(case_62_multi, 2); + TEST_UNION(case_63_multi, 2); + TEST_UNION(case_64_multi, 1); + TEST_UNION(case_72_multi, 0); + TEST_UNION(case_107_multi, 1); + TEST_UNION(case_recursive_boxes_1, 1); + TEST_UNION(case_recursive_boxes_10, 1); + TEST_UNION(case_recursive_boxes_18, 3); + TEST_UNION(case_recursive_boxes_19, 3); + TEST_UNION(case_recursive_boxes_20, 2); + TEST_UNION(case_recursive_boxes_21, 1); + TEST_UNION(case_recursive_boxes_22, 1); + TEST_UNION(case_recursive_boxes_23, 3); +} + +int test_main(int, char* []) +{ + test_all(); + return 0; + } From 26ae7f5bfa946e8ce8a6abb2c4bf523426853f4e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 28 Sep 2016 15:41:01 +0200 Subject: [PATCH 04/18] [validity] set is_touching for intersection too. Reverse should be done for intersection too, but that does not work yet. --- .../geometry/algorithms/detail/overlay/traversal.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 78612bca1..770fd1353 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -476,7 +476,7 @@ struct traversal sbs.apply(turn.point); #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) - is_touching = is_union && cinfo.open_count > 1; + is_touching = cinfo.open_count > 1; if (is_touching) { if (cinfo.switch_source) @@ -490,10 +490,14 @@ struct traversal } } #else - is_touching = is_union && cinfo.open_count > 1 && ! cinfo.switch_source; + is_touching = cinfo.open_count > 1 && ! cinfo.switch_source; #endif - if (is_touching) + + if (is_touching && is_union) { +#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) + std::cout << "CLUSTER: REVERSE" << std::endl; +#endif sbs.reverse(); } From a057b49ec02b06d6d2204a337948fb5a33e35277 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Oct 2016 12:55:39 +0200 Subject: [PATCH 05/18] [test] add testcases with one cluster and multiple outputs (one is invalid) Add macros to specify testcases more conveniently --- test/algorithms/overlay/multi_overlay_cases.hpp | 14 ++++++++++++++ test/algorithms/overlay/sort_by_side.cpp | 4 ++++ .../set_operations/difference/difference_multi.cpp | 8 ++++++++ .../intersection/intersection_multi.cpp | 13 +++++++++++++ .../set_operations/union/union_multi.cpp | 7 +++++++ 5 files changed, 46 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 4909d93e6..a3a78c29d 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -498,6 +498,20 @@ static std::string case_122_multi[2] = "MULTIPOLYGON(((7 3,7 6,9 6,9 5,11 5,11 3,7 3)),((10 6,10 9,12 9,12 6,10 6)),((7 7,7 10,10 10,9 9,9 7,7 7)))" }; +static std::string case_123_multi[2] = +{ + // Intersection: one cluster with 3 zones, intersection -> no holes + "MULTIPOLYGON(((1 0,1 1,1.5 0.5,2 0.5,2 0,1 0)),((0 1,1 2,2 2,2 1,0 1)))", + "MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))" +}; + +static std::string case_124_multi[2] = +{ + // Intersection: one cluster with 3 zones, intersection -> one hole + "MULTIPOLYGON(((1 0,1 1,0 1,1 2,2 2,2 0,1 0),(1.5 0.5,1.75 1,1 1,1.5 0.5)))", + "MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))" +}; + static std::string case_recursive_boxes_1[2] = { "MULTIPOLYGON(((1 0,0 0,0 1,1 1,1 2,0 2,0 4,2 4,2 5,3 5,3 6,1 6,1 5,0 5,0 10,9 10,9 9,7 9,7 8,6 8,6 7,8 7,8 6,9 6,9 4,8 4,8 3,10 3,10 0,6 0,6 1,5 1,5 0,1 0),(8 4,8 5,7 5,7 6,6 6,6 5,5 5,5 4,4 4,4 3,5 3,5 2,7 2,7 3,6 3,6 4,8 4),(8 1,9 1,9 2,8 2,8 1),(4 7,4 9,3 9,3 7,4 7)),((9 9,10 9,10 7,10 6,9 6,9 7,8 7,8 8,9 8,9 9)))", diff --git a/test/algorithms/overlay/sort_by_side.cpp b/test/algorithms/overlay/sort_by_side.cpp index 06c6c1895..81c29436d 100644 --- a/test/algorithms/overlay/sort_by_side.cpp +++ b/test/algorithms/overlay/sort_by_side.cpp @@ -226,6 +226,8 @@ void test_all() TEST_INT(case_64_multi, 1); TEST_INT(case_72_multi, 3); TEST_INT(case_107_multi, 2); + TEST_INT(case_123_multi, 3); + TEST_INT(case_124_multi, 3); TEST_INT(case_recursive_boxes_1, 2); TEST_INT(case_recursive_boxes_10, 2); TEST_INT(case_recursive_boxes_18, 0); @@ -240,6 +242,8 @@ void test_all() TEST_UNION(case_64_multi, 1); TEST_UNION(case_72_multi, 0); TEST_UNION(case_107_multi, 1); + TEST_UNION(case_123_multi, 1); + TEST_UNION(case_124_multi, 1); TEST_UNION(case_recursive_boxes_1, 1); TEST_UNION(case_recursive_boxes_10, 1); TEST_UNION(case_recursive_boxes_18, 3); diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index e84930ad8..0df2fe34e 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -23,6 +23,11 @@ #include +#define TEST_DIFFERENCE(caseid, clips1, points1, area1, clips2, points2, area2) \ + (test_one) \ + ( #caseid, caseid[0], caseid[1], clips1, points1, area1, clips2, points2, area2) + + template void test_areal() { @@ -86,6 +91,9 @@ void test_areal() case_78_multi[0], case_78_multi[1], 1, 5, 1.0, 1, 5, 1.0); + TEST_DIFFERENCE(case_123_multi, 1, 4, 0.25, 2, 9, 0.625); + TEST_DIFFERENCE(case_124_multi, 1, 4, 0.25, 2, 9, 0.4375); + { ut_settings settings; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 7691bf2a0..9d7de9147 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -28,6 +28,15 @@ #include +#define TEST_INTERSECTION(caseid, clips, points, area) \ + (test_one) \ + ( #caseid, caseid[0], caseid[1], clips, points, area) + +#define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \ + (test_one) \ + ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity) + + template void test_areal() { @@ -133,6 +142,10 @@ void test_areal() 5, 33, 7.5, ignore_validity); #endif + + TEST_INTERSECTION(case_123_multi, 3, 13, 1.875); + TEST_INTERSECTION_IGNORE(case_124_multi, 2, 13, 2.0625); + test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], 8, 97, 47.0, ignore_validity); diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 419ed8377..88289e8cc 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -29,6 +29,10 @@ #include +#define TEST_UNION(caseid, clips, holes, points, area) \ + (test_one) \ + ( #caseid, caseid[0], caseid[1], clips, holes, points, area) + template void test_areal() { @@ -177,6 +181,9 @@ void test_areal() case_122_multi[0], case_122_multi[1], 1, 1, 28, 29.5); + TEST_UNION(case_123_multi, 1, 0, 11, 2.75); + TEST_UNION(case_124_multi, 1, 0, 9, 2.75); + test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], 1, 1, 36, 97.0); From 11a249d5eb4a6546d56e212e18e6302e5c8cb462 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 12 Oct 2016 17:57:34 +0200 Subject: [PATCH 06/18] [test] add one testcase, used for pending work (make intersections valid) --- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/intersection/intersection_multi.cpp | 1 + 2 files changed, 8 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index a3a78c29d..2399d294b 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -512,6 +512,13 @@ static std::string case_124_multi[2] = "MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))" }; +static std::string case_125_multi[2] = +{ + // Intersection: one cluster with 3 zones, intersection -> one hole (filled with other polygon) + "MULTIPOLYGON(((1 0,1 1,0 1,1 2,2 2,2 0,1 0),(1.5 0.5,1.75 1,1 1,1.5 0.5)),((1 1,1.5 0.9,1.25 0.8,1 1)))", + "MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))" +}; + static std::string case_recursive_boxes_1[2] = { "MULTIPOLYGON(((1 0,0 0,0 1,1 1,1 2,0 2,0 4,2 4,2 5,3 5,3 6,1 6,1 5,0 5,0 10,9 10,9 9,7 9,7 8,6 8,6 7,8 7,8 6,9 6,9 4,8 4,8 3,10 3,10 0,6 0,6 1,5 1,5 0,1 0),(8 4,8 5,7 5,7 6,6 6,6 5,5 5,5 4,4 4,4 3,5 3,5 2,7 2,7 3,6 3,6 4,8 4),(8 1,9 1,9 2,8 2,8 1),(4 7,4 9,3 9,3 7,4 7)),((9 9,10 9,10 7,10 6,9 6,9 7,8 7,8 8,9 8,9 9)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 9d7de9147..756ab37f5 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -145,6 +145,7 @@ void test_areal() TEST_INTERSECTION(case_123_multi, 3, 13, 1.875); TEST_INTERSECTION_IGNORE(case_124_multi, 2, 13, 2.0625); + TEST_INTERSECTION_IGNORE(case_125_multi, 3, 17, 2.1); test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], From 121db33f47b02d8a82a1605f610a8ec357963541 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 12 Oct 2016 17:58:26 +0200 Subject: [PATCH 07/18] [overlay] add new algorithm to aggregate sort_by_side output per rank, using incoming/outgoing rings. Usage of this algorithm not yet committed. --- .../detail/overlay/aggregate_operations.hpp | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp new file mode 100644 index 000000000..3b63fccf3 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -0,0 +1,83 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2016 Barend Gehrels, Amsterdam, the Netherlands. + +// 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP + +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { namespace sort_by_side +{ + +struct ring_with_direction +{ + ring_identifier ring_id; + direction_type direction; + + inline bool operator<(ring_with_direction const& other) const + { + return this->ring_id != other.ring_id + ? this->ring_id < other.ring_id + : this->direction < other.direction; + } + + ring_with_direction() + : direction(dir_unknown) + {} +}; + +struct rank_with_rings +{ + std::size_t rank; + std::set rings; + + rank_with_rings() + : rank(0) + { + + } +}; + +template +inline void aggregate_operations(Sbs const& sbs, std::vector& aggregation) +{ + aggregation.clear(); + for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) + { + typename Sbs::rp const& ranked_point = sbs.m_ranked_points[i]; + + if (aggregation.empty() || aggregation.back().rank != ranked_point.rank) + { + rank_with_rings current; + current.rank = ranked_point.rank; + aggregation.push_back(current); + } + + ring_with_direction rwd; + segment_identifier const& sid = ranked_point.seg_id; + rwd.ring_id = ring_identifier(sid.source_index, sid.multi_index, sid.ring_index); + rwd.direction = ranked_point.direction; + + aggregation.back().rings.insert(rwd); + } +} + + +}}} // namespace detail::overlay::sort_by_side +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP From 843ddb79bf383e3d2f8bec1930c58676cf8a7579 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 19 Oct 2016 17:12:51 +0200 Subject: [PATCH 08/18] [overlay] add all_to to ragne aggregation --- .../detail/overlay/aggregate_operations.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index 3b63fccf3..39440b855 100644 --- a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -45,7 +45,19 @@ struct rank_with_rings rank_with_rings() : rank(0) { + } + inline bool all_to() const + { + for (std::set::const_iterator it = rings.begin(); + it != rings.end(); ++it) + { + if (it->direction == sort_by_side::dir_from) + { + return false; + } + } + return true; } }; From 794b16881797201bd2c0b7c47f9f5f765049b564 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Nov 2016 12:52:46 +0100 Subject: [PATCH 09/18] [overlay] add information about if it is the only turn on a ring, to each op (enriched) --- .../detail/overlay/aggregate_operations.hpp | 7 ++- .../detail/overlay/enrichment_info.hpp | 2 + .../detail/overlay/sort_by_side.hpp | 14 +++-- .../overlay/traversal_switch_detector.hpp | 51 +++++++++++++++++++ 4 files changed, 68 insertions(+), 6 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index 39440b855..df62a1f2f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -24,6 +24,8 @@ struct ring_with_direction { ring_identifier ring_id; direction_type direction; + bool only_turn_on_ring; + inline bool operator<(ring_with_direction const& other) const { @@ -33,7 +35,8 @@ struct ring_with_direction } ring_with_direction() - : direction(dir_unknown) + : direction(dir_unknown) + , only_turn_on_ring(false) {} }; @@ -80,6 +83,8 @@ inline void aggregate_operations(Sbs const& sbs, std::vector& a segment_identifier const& sid = ranked_point.seg_id; rwd.ring_id = ring_identifier(sid.source_index, sid.multi_index, sid.ring_index); rwd.direction = ranked_point.direction; + rwd.only_turn_on_ring = ranked_point.only_turn_on_ring; + aggregation.back().rings.insert(rwd); } diff --git a/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp index cc5541487..264341534 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -38,6 +38,7 @@ struct enrichment_info , count_left(0) , count_right(0) , zone(-1) + , only_turn_on_ring(false) {} // vertex to which is free travel after this IP, @@ -57,6 +58,7 @@ struct enrichment_info std::size_t count_left; std::size_t count_right; signed_size_type zone; // open zone, in cluster + bool only_turn_on_ring; // True if it is the only turn on a ring (for clusters) }; diff --git a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index 8059c656d..bbba623ee 100644 --- a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -37,10 +37,12 @@ struct ranked_point , count_left(0) , count_right(0) , operation(operation_none) + , only_turn_on_ring(false) {} + template ranked_point(const Point& p, signed_size_type ti, int oi, - direction_type d, operation_type op, segment_identifier sid) + direction_type d, Op op) : point(p) , rank(0) , zone(-1) @@ -49,8 +51,9 @@ struct ranked_point , direction(d) , count_left(0) , count_right(0) - , operation(op) - , seg_id(sid) + , operation(op.operation) + , seg_id(op.seg_id) + , only_turn_on_ring(op.enriched.only_turn_on_ring) {} Point point; @@ -63,6 +66,7 @@ struct ranked_point std::size_t count_right; operation_type operation; segment_identifier seg_id; + bool only_turn_on_ring; }; struct less_by_turn_index @@ -227,8 +231,8 @@ public : op.seg_id, point1, point2, point3); Point const& point_to = op.fraction.is_one() ? point3 : point2; - m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op.operation, op.seg_id)); - m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op.operation, op.seg_id)); + m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op)); + m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op)); if (is_origin) { diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 2815ee984..183131c74 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -132,6 +132,56 @@ struct traversal_switch_detector } } + void check_turns_per_ring(ring_identifier const& ring_id, + std::set const& ring_turn_indices) + { + bool only_turn_on_ring = true; + if (ring_turn_indices.size() > 1) + { + // More turns on this ring. Only leave only_turn_on_ring true + // if they are all of the same cluster + int cluster_id = -1; + for (set_iterator sit = ring_turn_indices.begin(); + sit != ring_turn_indices.end(); ++sit) + { + turn_type const& turn = m_turns[*sit]; + if (turn.cluster_id == -1) + { + // Unclustered turn - and there are 2 or more turns + // so the ring has different turns + only_turn_on_ring = false; + break; + } + + // Clustered turn, check if it is the first or same as previous + if (cluster_id == -1) + { + cluster_id = turn.cluster_id; + } + else if (turn.cluster_id != cluster_id) + { + only_turn_on_ring = false; + break; + } + } + } + + // Assign result to matching operation (a turn is always on two rings) + for (set_iterator sit = ring_turn_indices.begin(); + sit != ring_turn_indices.end(); ++sit) + { + turn_type& turn = m_turns[*sit]; + for (int i = 0; i < 2; i++) + { + turn_operation_type& op = turn.operations[i]; + if (ring_id_by_seg_id(op.seg_id) == ring_id) + { + op.enriched.only_turn_on_ring = only_turn_on_ring; + } + } + } + } + void propagate_region(ring_identifier const& ring_id, int region_id) { std::map >::const_iterator it = m_turns_per_ring.find(ring_id); @@ -168,6 +218,7 @@ struct traversal_switch_detector = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it) { create_region(it->first, it->second); + check_turns_per_ring(it->first, it->second); } // Now that all regions are filled, assign switch_source property From 70e20ddd273f2e77eac8f98cd2c3b69ca62d1820 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Nov 2016 15:55:30 +0100 Subject: [PATCH 10/18] [overlay] fix some validity cases for intersection/difference --- .../algorithms/detail/overlay/traversal.hpp | 157 +++++++++++++----- .../detail/overlay/traversal_ring_creator.hpp | 12 ++ .../set_operations/difference/difference.cpp | 11 +- .../difference/difference_multi.cpp | 4 + .../intersection/intersection_multi.cpp | 10 +- 5 files changed, 137 insertions(+), 57 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 770fd1353..ae69c592e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -334,13 +335,10 @@ struct traversal return true; } - inline bool select_from_cluster(signed_size_type& turn_index, + inline bool select_from_cluster_union(signed_size_type& turn_index, int& op_index, signed_size_type start_turn_index, sbs_type const& sbs, bool is_touching) const { - bool const is_union = target_operation == operation_union; - bool const is_intersection = target_operation == operation_intersection; - std::size_t selected_rank = 0; std::size_t min_rank = 0; bool result = false; @@ -373,11 +371,8 @@ struct traversal && (ranked_point.rank > min_rank || ranked_turn.both(operation_continue))) { - if ((is_union - && ranked_op.enriched.count_left == 0 + if (ranked_op.enriched.count_left == 0 && ranked_op.enriched.count_right > 0) - || (is_intersection - && ranked_op.enriched.count_right == 2)) { if (result && ranked_point.turn_index != start_turn_index) { @@ -388,16 +383,6 @@ struct traversal turn_index = ranked_point.turn_index; op_index = ranked_point.operation_index; - if (is_intersection - && ranked_turn.both(operation_intersection) - && ranked_op.visited.finalized()) - { - // Override: - // For a ii turn, even though one operation might be selected, - // it should take the other one if the first one is used in a completed ring - op_index = 1 - ranked_point.operation_index; - } - result = true; selected_rank = ranked_point.rank; } @@ -410,10 +395,76 @@ struct traversal return result; } + inline bool analyze_cluster_intersection(signed_size_type& turn_index, + int& op_index, + sbs_type const& sbs) const + { + std::vector aggregation; + sort_by_side::aggregate_operations(sbs, aggregation); + + std::size_t selected_rank = 0; + + for (std::size_t i = 0; i < aggregation.size(); i++) + { + sort_by_side::rank_with_rings const& rwr = aggregation[i]; + + if (i > 1 + && i - 1 == selected_rank + && rwr.rings.size() == 1) + { + sort_by_side::ring_with_direction const& rwd = *rwr.rings.begin(); + + if (rwd.only_turn_on_ring) + { + // Find if this arriving ring was leaving previously + sort_by_side::ring_with_direction leaving = rwd; + leaving.direction = sort_by_side::dir_to; + + sort_by_side::rank_with_rings const& previous = aggregation[i - 1]; + + if (previous.rings.size() == 1 + && previous.rings.count(leaving) == 1) + { + // It arrives back - if this is one of the selected, unselect it + selected_rank = 0; + } + } + } + + if (rwr.all_to()) + { + if (selected_rank == 0) + { + // Take the first (= right) where segments leave, + // having the polygon on the right side + selected_rank = rwr.rank; + } + } + } + + if (selected_rank > 0) + { + for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) + { + typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[i]; + if (ranked_point.rank == selected_rank) + { + // Take the first turn from this rank + turn_index = ranked_point.turn_index; + op_index = ranked_point.operation_index; + return true; + } + } + } + + return false; + } + inline bool select_turn_from_cluster(signed_size_type& turn_index, int& op_index, bool& is_touching, signed_size_type start_turn_index, - segment_identifier const& previous_seg_id) const + segment_identifier const& previous_seg_id, + bool is_start) const { bool const is_union = target_operation == operation_union; @@ -475,34 +526,50 @@ struct traversal sbs.apply(turn.point); -#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) - is_touching = cinfo.open_count > 1; - if (is_touching) - { - if (cinfo.switch_source) - { - is_touching = false; - std::cout << "CLUSTER: SWITCH SOURCES at " << turn_index << std::endl; - } - else - { - std::cout << "CLUSTER: CONTINUE at " << turn_index << std::endl; - } - } -#else - is_touching = cinfo.open_count > 1 && ! cinfo.switch_source; -#endif + bool result = false; - if (is_touching && is_union) + if (is_union) { -#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) - std::cout << "CLUSTER: REVERSE" << std::endl; -#endif - sbs.reverse(); - } + #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) + is_touching = cinfo.open_count > 1; + if (is_touching) + { + if (cinfo.switch_source) + { + is_touching = false; + std::cout << "CLUSTER: SWITCH SOURCES at " << turn_index << std::endl; + } + else + { + std::cout << "CLUSTER: CONTINUE at " << turn_index << std::endl; + } + } + #else + is_touching = cinfo.open_count > 1 && ! cinfo.switch_source; + #endif - return select_from_cluster(turn_index, op_index, start_turn_index, sbs, - is_touching); + if (is_touching) + { + sbs.reverse(); + } + + result = select_from_cluster_union(turn_index, op_index, start_turn_index, sbs, + is_touching); + } + else + { + if (is_start + && turn.both(operation_intersection) + && turn.operations[op_index].enriched.only_turn_on_ring) + { + // For an ii (usually interior ring), only turn on ring, + // reverse to take first exit + sbs.reverse(); + } + + result = analyze_cluster_intersection(turn_index, op_index, sbs); + } + return result; } inline void change_index_for_self_turn(signed_size_type& to_vertex_index, @@ -610,7 +677,7 @@ struct traversal if (m_turns[turn_index].cluster_id >= 0) { if (! select_turn_from_cluster(turn_index, op_index, is_touching, - start_turn_index, previous_seg_id)) + start_turn_index, previous_seg_id, is_start)) { return false; } diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index bdea318d1..41e510f2e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -187,6 +187,18 @@ struct traversal_ring_creator return traverse_error_none; } + if (start_turn.cluster_id >= 0) + { + turn_type const& turn = m_turns[current_turn_index]; + if (turn.cluster_id == start_turn.cluster_id) + { + turn_operation_type& op = m_turns[start_turn_index].operations[current_op_index]; + op.visited.set_finished(); + m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish (cluster)"); + return traverse_error_none; + } + } + std::size_t const max_iterations = 2 + 2 * m_turns.size(); for (std::size_t i = 0; i <= max_iterations; i++) { diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 37f41f945..9442139af 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -216,8 +216,7 @@ void test_all() case_100[0], case_100[1], 1, 7, 3.125, 1, 7, 16.0, - 1, 13, 16.0 + 3.125, - ignore_validity); + 1, 13, 16.0 + 3.125); test_one("case_101", case_101[0], case_101[1], @@ -526,26 +525,26 @@ void test_all() // also mysql_23023665 test_one("mysql_21965285", mysql_21965285[0], mysql_21965285[1], - 1, 2 - correction_for_invalidity, -1, 92.0, + 1, 2, -1, 92.0, 1, 1, -1, 14.0, 1, 2, -1, 92.0 + 14.0, ignore_validity); test_one("mysql_23023665_1", mysql_23023665_1[0], mysql_23023665_1[1], - 1, 2 - correction_for_invalidity, -1, 92.0, + 1, 2, -1, 92.0, 1, 1, -1, 142.5, ignore_validity); test_one("mysql_23023665_2", mysql_23023665_2[0], mysql_23023665_2[1], - 1, 2 - correction_for_invalidity, -1, 96.0, + 1, 2, -1, 96.0, 1, 1, -1, 16.0, ignore_validity); test_one("mysql_23023665_3", mysql_23023665_3[0], mysql_23023665_3[1], - 1, 2 - correction_for_invalidity, -1, 225.0, + 1, 2, -1, 225.0, 1, 1, -1, 66.0, ignore_validity); diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 0df2fe34e..1644de9cf 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -161,6 +161,8 @@ void test_areal() ignore_validity); #endif +#if 0 + // Regression (intersections valid): fails to output two triangles in A // Areas and #clips correspond with POSTGIS (except sym case) test_one("case_101_multi", case_101_multi[0], case_101_multi[1], @@ -168,12 +170,14 @@ void test_areal() 5, 40, 12.75, 5, 48, 4.75 + 12.75); + // Regression (intersections valid): fails to output one triangle in A // Areas and #clips correspond with POSTGIS test_one("case_102_multi", case_102_multi[0], case_102_multi[1], 2, 8, 0.75, 6, 25, 3.75, 6, 27, 0.75 + 3.75); +#endif // Areas and #clips correspond with POSTGIS test_one("case_107_multi", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 756ab37f5..82bb112ab 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -111,8 +111,7 @@ void test_areal() 3, 14, 2.85); test_one("case_72_multi_inv_b", case_72_multi[1], case_72_multi[2], - 3, 16, 6.15, - ignore_validity); + 3, 16, 6.15); test_one("case_77_multi", case_77_multi[0], case_77_multi[1], 5, 33, 9.0); @@ -144,7 +143,7 @@ void test_areal() #endif TEST_INTERSECTION(case_123_multi, 3, 13, 1.875); - TEST_INTERSECTION_IGNORE(case_124_multi, 2, 13, 2.0625); + TEST_INTERSECTION(case_124_multi, 2, 13, 2.0625); TEST_INTERSECTION_IGNORE(case_125_multi, 3, 17, 2.1); test_one("case_recursive_boxes_1", @@ -165,7 +164,7 @@ void test_areal() // Fixed by replacing handle_tangencies in less_by_segment_ratio sort order test_one("case_recursive_boxes_6", case_recursive_boxes_6[0], case_recursive_boxes_6[1], - 6, 47, 19.0); + 4, 47, 19.0); test_one("case_recursive_boxes_7", case_recursive_boxes_7[0], case_recursive_boxes_7[1], @@ -253,8 +252,7 @@ void test_areal() 3, 0, 2.0); test_one("case_recursive_boxes_34", case_recursive_boxes_34[0], case_recursive_boxes_34[1], - 2, 0, 17.25, - ignore_validity); + 2, 0, 17.25); test_one("case_recursive_boxes_35", case_recursive_boxes_35[0], case_recursive_boxes_35[1], 1, 0, 20.0, From 71d1d7583548075cb4504ca86bc27aa58794dda4 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Nov 2016 15:55:54 +0100 Subject: [PATCH 11/18] add comment --- include/boost/geometry/algorithms/detail/overlay/turn_info.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index d9f5a7d13..e09af126c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -89,7 +89,7 @@ struct turn_info Point point; method_type method; - signed_size_type cluster_id; + signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1 bool discarded; bool colocated; bool switch_source; // For u/u turns which can either switch or not From 3d2ff82fd7e86340356318daab1954cc4cf88f05 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Nov 2016 16:26:29 +0100 Subject: [PATCH 12/18] [test] add inverse version for failing case for difference --- test/algorithms/overlay/multi_overlay_cases.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 2399d294b..ec4f60f43 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -328,11 +328,15 @@ static std::string case_100_multi[2] = "MULTIPOLYGON(((1 1,1 2,2 2,2 1,1 1)),((1 2,0 1,0 3,1 4,1 2)))" }; -static std::string case_101_multi[2] = +static std::string case_101_multi[4] = { // interior ring / union "MULTIPOLYGON(((7 2,7 3,8 2,7 2)),((9 3,9 4,10 3,9 3)),((10 1,10 0,8 0,8 1,9 2,10 2,10 1)),((9 3,9 2,8 2,8 3,7 3,7 4,8 4,9 3)),((8 4,8 7,9 6,9 4,8 4)))", - "MULTIPOLYGON(((5 1,5 2,6 3,6 4,7 5,6 5,7 6,8 6,8 5,9 5,10 5,10 1,8 1,7 0,5 0,5 1),(9 5,8 4,9 4,9 5),(8 1,8 3,7 3,7 2,6 2,7 1,8 1),(5 1,5.5 0.5,6 1,5 1),(8.5 2.5,9 2,9 3,8.5 2.5)))" + "MULTIPOLYGON(((5 1,5 2,6 3,6 4,7 5,6 5,7 6,8 6,8 5,9 5,10 5,10 1,8 1,7 0,5 0,5 1),(9 5,8 4,9 4,9 5),(8 1,8 3,7 3,7 2,6 2,7 1,8 1),(5 1,5.5 0.5,6 1,5 1),(8.5 2.5,9 2,9 3,8.5 2.5)))", + + // inverse versions + "MULTIPOLYGON(((4 -1,4 8,11 8,11 -1,4 -1),(7 2,8 2,7 3,7 2),(9 3,10 3,9 4,9 3),(10 1,10 2,9 2,8 1,8 0,10 0,10 1),(9 3,8 4,7 4,7 3,8 3,8 2,9 2,9 3),(8 4,9 4,9 6,8 7,8 4)))", + "MULTIPOLYGON(((4 -1,4 8,11 8,11 -1,4 -1),(5 1,5 0,7 0,8 1,10 1,10 5,9 5,8 5,8 6,7 6,6 5,7 5,6 4,6 3,5 2,5 1)),((9 5,9 4,8 4,9 5)),((8 1,7 1,6 2,7 2,7 3,8 3,8 1)),((5 1,6 1,5.5 0.5,5 1)),((8.5 2.5,9 3,9 2,8.5 2.5)))" }; static std::string case_102_multi[4] = @@ -341,7 +345,7 @@ static std::string case_102_multi[4] = "MULTIPOLYGON(((0 2,0 7,5 7,5 2,0 2),(4 3,4 6,1 6,2 5,1 5,1 4,3 4,4 3)),((3 4,3 5,4 5,3 4)),((2 5,3 6,3 5,2 4,2 5)))", "MULTIPOLYGON(((0 2,0 7,5 7,5 2,0 2),(2 4,3 5,2 5,2 4),(4 4,3 4,3 3,4 4),(4 5,4 6,3 6,4 5)))", - /* inverse versions (first was already having an interior, so outer ring is just removed */ + // inverse versions (first was already having an interior, so outer ring is just removed "MULTIPOLYGON(((4 3,3 4,1 4,1 5,2 5,1 6,4 6,4 3),(3 4,4 5,3 5,3 4),(2 5,2 4,3 5,3 6,2 5)))", "MULTIPOLYGON(((-1 1,-1 8,6 8,6 1,-1 1),(0 2,5 2,5 7,0 7,0 2)),((2 4,2 5,3 5,2 4)),((4 4,3 3,3 4,4 4)),((4 5,3 6,4 6,4 5)))" }; @@ -381,7 +385,7 @@ static std::string case_107_multi[4] = "MULTIPOLYGON(((6 8,7 9,7 7,8 7,7 6,6 6,6 8)),((6.5 9.5,7 10,7 9,6 9,6 10,6.5 9.5)))", "MULTIPOLYGON(((5 7,6 8,6 10,7 9,8 10,8 8,7 8,6 7,6 6,5 7)))", - /* inverse versions */ + // inverse versions "MULTIPOLYGON(((5 5,5 11,9 11,9 5,5 5),(6 8,6 6,7 6,8 7,7 7,7 9,6 8),(6.5 9.5,6 10,6 9,7 9,7 10,6.5 9.5)))", "MULTIPOLYGON(((4 5,4 11,9 11,9 5,4 5),(5 7,6 6,6 7,7 8,8 8,8 10,7 9,6 10,6 8,5 7)))" }; From 44ddcf1833ac25b100e205d45e8bb03c89fe1cc5 Mon Sep 17 00:00:00 2001 From: barendgehrels Date: Thu, 17 Nov 2016 13:18:17 +0100 Subject: [PATCH 13/18] [test] use macros to get conveniently add/manage the testcases --- test/algorithms/overlay/overlay.cpp | 144 +++++++--------------------- 1 file changed, 37 insertions(+), 107 deletions(-) diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index f45b211e4..38d9c6045 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -314,7 +314,7 @@ struct map_visitor template void test_overlay(std::string const& caseid, std::string const& wkt1, std::string const& wkt2, - double expected_area) + double expected_area, std::size_t expected_hole_count = 0) { Geometry g1; bg::read_wkt(wkt1, g1); @@ -386,6 +386,9 @@ void test_overlay(std::string const& caseid, strategy(), visitor); BOOST_CHECK_CLOSE(bg::area(result), expected_area, 0.001); + BOOST_CHECK_MESSAGE((bg::num_interior_rings(result) == expected_hole_count), + " hole count: detected: " << bg::num_interior_rings(result) + << " expected: " << expected_hole_count); #if defined(TEST_WITH_SVG) mapper.map(result, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);" @@ -394,6 +397,20 @@ void test_overlay(std::string const& caseid, #endif } +#define TEST_INTERSECTION(caseid, area, holes) (test_overlay) \ + ( #caseid "_int", caseid[0], caseid[1], area, holes) +#define TEST_UNION(caseid, area, holes) (test_overlay) \ + ( #caseid "_union", caseid[0], caseid[1], area, holes) +#define TEST_DIFFERENCE_A(caseid, area, holes) (test_overlay) \ + ( #caseid "_diff_a", caseid[0], caseid[1], area, holes) +#define TEST_DIFFERENCE_B(caseid, area, holes) (test_overlay) \ + ( #caseid "_diff_b", caseid[1], caseid[0], area, holes) + +#define TEST_INTERSECTION_WITH(caseid, index1, index2, area, holes) (test_overlay) \ + ( #caseid "_int_" #index1 "_" #index2, caseid[index1], caseid[index2], area, holes) +#define TEST_UNION_WITH(caseid, index1, index2, area, holes) (test_overlay) \ + ( #caseid "_union" #index1 "_" #index2, caseid[index1], caseid[index2], area, holes) + template void test_all() { @@ -401,121 +418,34 @@ void test_all() typedef bg::model::polygon polygon; typedef bg::model::multi_polygon multi_polygon; - test_overlay - ( - "case_multi_simplex_union", - case_multi_simplex[0], case_multi_simplex[1], - 14.58 - ); - test_overlay - ( - "case_multi_simplex_intersection", - case_multi_simplex[0], case_multi_simplex[1], - 6.42 - ); - test_overlay - ( - "case_multi_simplex_diff_a", - case_multi_simplex[0], case_multi_simplex[1], - 5.58 - ); - test_overlay - ( - "case_multi_simplex_diff_b", - case_multi_simplex[1], case_multi_simplex[0], - 2.58 - ); + TEST_UNION(case_multi_simplex, 14.58, 0); + TEST_INTERSECTION(case_multi_simplex, 6.42, 0); + + TEST_DIFFERENCE_A(case_multi_simplex, 5.58, 0); + TEST_DIFFERENCE_B(case_multi_simplex, 2.58, 0); // Contains 5 clusters, needing immediate selection of next turn - test_overlay - ( - "case_58_multi_0_3_union", - case_58_multi[0], case_58_multi[3], - 19.8333333 - ); + TEST_UNION_WITH(case_58_multi, 0, 3, 19.8333333, 0); // Contains many clusters, needing to exclude u/u turns - test_overlay - ( - "case_recursive_boxes_3_union", - case_recursive_boxes_3[0], case_recursive_boxes_3[1], - 56.5 - ); + TEST_UNION(case_recursive_boxes_3, 56.5, 6); + // Contains 4 clusters, one of which having 4 turns - test_overlay - ( - "case_recursive_boxes_7_union", - case_recursive_boxes_7[0], case_recursive_boxes_7[1], - 7.0 - ); + TEST_UNION(case_recursive_boxes_7, 7.0, 0); // Contains 5 clusters, needing immediate selection of next turn - test_overlay - ( - "case_89_multi_union", - case_89_multi[0], case_89_multi[1], - 6.0 - ); + TEST_UNION(case_89_multi, 6.0, 0); // Needs ux/next_turn_index==-1 to be filtered out - test_overlay - ( - "case_77_multi_intersection", - case_77_multi[0], case_77_multi[1], - 9.0 - ); - - test_overlay - ( - "case_101_multi_union", - case_101_multi[0], case_101_multi[1], - 22.25 - ); - test_overlay - ( - "case_101_multi_intersection", - case_101_multi[0], case_101_multi[1], - 4.75 - ); - - test_overlay - ( - "case_recursive_boxes_11_intersection", - case_recursive_boxes_11[0], case_recursive_boxes_11[1], - 1.0 - ); - - test_overlay - ( - "case_recursive_boxes_4_union", - case_recursive_boxes_4[0], case_recursive_boxes_4[1], - 96.75 - ); - - test_overlay - ( - "case_58_multi_b6_intersection", - case_58_multi[6], case_58_multi[2], - 13.25 - ); - test_overlay - ( - "case_72_multi_intersection_inv_b", - case_72_multi[2], case_72_multi[1], - 6.15 - ); - test_overlay - ( - "case_recursive_boxes_12_union", - case_recursive_boxes_12[0], case_recursive_boxes_12[1], - 6.0 - ); - test_overlay - ( - "case_recursive_boxes_13_union", - case_recursive_boxes_13[0], case_recursive_boxes_13[1], - 10.25 - ); + TEST_INTERSECTION(case_77_multi, 9.0, 0); + TEST_UNION(case_101_multi, 22.25, 3); + TEST_INTERSECTION(case_101_multi, 4.75, 0); + TEST_INTERSECTION(case_recursive_boxes_11, 1.0, 0); + TEST_UNION(case_recursive_boxes_4, 96.75, 2); + TEST_INTERSECTION_WITH(case_58_multi, 6, 2, 13.25, 1); + TEST_INTERSECTION_WITH(case_72_multi, 2, 1, 6.15, 1); + TEST_UNION(case_recursive_boxes_12, 6.0, 0); + TEST_UNION(case_recursive_boxes_13, 10.25, 0); // std::cout From d687a55bbe00c805ede39466e6cb87275eabbb90 Mon Sep 17 00:00:00 2001 From: barendgehrels Date: Thu, 17 Nov 2016 13:37:51 +0100 Subject: [PATCH 14/18] [test] correct testcase --- .../set_operations/intersection/intersection_multi.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 82bb112ab..7bc04c944 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -162,9 +162,10 @@ void test_areal() ignore_validity); // Fixed by replacing handle_tangencies in less_by_segment_ratio sort order + // Should contain 6 output polygons test_one("case_recursive_boxes_6", case_recursive_boxes_6[0], case_recursive_boxes_6[1], - 4, 47, 19.0); + 6, 47, 19.0); test_one("case_recursive_boxes_7", case_recursive_boxes_7[0], case_recursive_boxes_7[1], From a801bf621ebf7c0e77d472b71c3517af960982a1 Mon Sep 17 00:00:00 2001 From: barendgehrels Date: Thu, 17 Nov 2016 13:56:42 +0100 Subject: [PATCH 15/18] [test] overlay: test number of clips, and add (still failing) testcase --- test/algorithms/overlay/overlay.cpp | 74 ++++++++++++++++------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index 38d9c6045..3302f2f4f 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -314,7 +314,9 @@ struct map_visitor template void test_overlay(std::string const& caseid, std::string const& wkt1, std::string const& wkt2, - double expected_area, std::size_t expected_hole_count = 0) + double expected_area, + std::size_t expected_clip_count, + std::size_t expected_hole_count) { Geometry g1; bg::read_wkt(wkt1, g1); @@ -387,8 +389,13 @@ void test_overlay(std::string const& caseid, BOOST_CHECK_CLOSE(bg::area(result), expected_area, 0.001); BOOST_CHECK_MESSAGE((bg::num_interior_rings(result) == expected_hole_count), - " hole count: detected: " << bg::num_interior_rings(result) - << " expected: " << expected_hole_count); + caseid + << " hole count: detected: " << bg::num_interior_rings(result) + << " expected: " << expected_hole_count); + BOOST_CHECK_MESSAGE((result.size() == expected_clip_count), + caseid + << " clip count: detected: " << result.size() + << " expected: " << expected_clip_count); #if defined(TEST_WITH_SVG) mapper.map(result, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);" @@ -397,19 +404,19 @@ void test_overlay(std::string const& caseid, #endif } -#define TEST_INTERSECTION(caseid, area, holes) (test_overlay) \ - ( #caseid "_int", caseid[0], caseid[1], area, holes) -#define TEST_UNION(caseid, area, holes) (test_overlay) \ - ( #caseid "_union", caseid[0], caseid[1], area, holes) -#define TEST_DIFFERENCE_A(caseid, area, holes) (test_overlay) \ - ( #caseid "_diff_a", caseid[0], caseid[1], area, holes) -#define TEST_DIFFERENCE_B(caseid, area, holes) (test_overlay) \ - ( #caseid "_diff_b", caseid[1], caseid[0], area, holes) +#define TEST_INTERSECTION(caseid, area, clips, holes) (test_overlay) \ + ( #caseid "_int", caseid[0], caseid[1], area, clips, holes) +#define TEST_UNION(caseid, area, clips, holes) (test_overlay) \ + ( #caseid "_union", caseid[0], caseid[1], area, clips, holes) +#define TEST_DIFFERENCE_A(caseid, area, clips, holes) (test_overlay) \ + ( #caseid "_diff_a", caseid[0], caseid[1], area, clips, holes) +#define TEST_DIFFERENCE_B(caseid, area, clips, holes) (test_overlay) \ + ( #caseid "_diff_b", caseid[1], caseid[0], area, clips, holes) -#define TEST_INTERSECTION_WITH(caseid, index1, index2, area, holes) (test_overlay) \ - ( #caseid "_int_" #index1 "_" #index2, caseid[index1], caseid[index2], area, holes) -#define TEST_UNION_WITH(caseid, index1, index2, area, holes) (test_overlay) \ - ( #caseid "_union" #index1 "_" #index2, caseid[index1], caseid[index2], area, holes) +#define TEST_INTERSECTION_WITH(caseid, index1, index2, area, clips, holes) (test_overlay) \ + ( #caseid "_int_" #index1 "_" #index2, caseid[index1], caseid[index2], area, clips, holes) +#define TEST_UNION_WITH(caseid, index1, index2, area, clips, holes) (test_overlay) \ + ( #caseid "_union" #index1 "_" #index2, caseid[index1], caseid[index2], area, clips, holes) template void test_all() @@ -418,34 +425,37 @@ void test_all() typedef bg::model::polygon polygon; typedef bg::model::multi_polygon multi_polygon; - TEST_UNION(case_multi_simplex, 14.58, 0); - TEST_INTERSECTION(case_multi_simplex, 6.42, 0); + TEST_UNION(case_multi_simplex, 14.58, 1, 0); + TEST_INTERSECTION(case_multi_simplex, 6.42, 2, 0); - TEST_DIFFERENCE_A(case_multi_simplex, 5.58, 0); - TEST_DIFFERENCE_B(case_multi_simplex, 2.58, 0); + TEST_DIFFERENCE_A(case_multi_simplex, 5.58, 5, 0); + TEST_DIFFERENCE_B(case_multi_simplex, 2.58, 4, 0); // Contains 5 clusters, needing immediate selection of next turn - TEST_UNION_WITH(case_58_multi, 0, 3, 19.8333333, 0); + TEST_UNION_WITH(case_58_multi, 0, 3, 19.8333333, 2, 0); // Contains many clusters, needing to exclude u/u turns - TEST_UNION(case_recursive_boxes_3, 56.5, 6); + TEST_UNION(case_recursive_boxes_3, 56.5, 17, 6); // Contains 4 clusters, one of which having 4 turns - TEST_UNION(case_recursive_boxes_7, 7.0, 0); + TEST_UNION(case_recursive_boxes_7, 7.0, 2, 0); // Contains 5 clusters, needing immediate selection of next turn - TEST_UNION(case_89_multi, 6.0, 0); + TEST_UNION(case_89_multi, 6.0, 1, 0); // Needs ux/next_turn_index==-1 to be filtered out - TEST_INTERSECTION(case_77_multi, 9.0, 0); - TEST_UNION(case_101_multi, 22.25, 3); - TEST_INTERSECTION(case_101_multi, 4.75, 0); - TEST_INTERSECTION(case_recursive_boxes_11, 1.0, 0); - TEST_UNION(case_recursive_boxes_4, 96.75, 2); - TEST_INTERSECTION_WITH(case_58_multi, 6, 2, 13.25, 1); - TEST_INTERSECTION_WITH(case_72_multi, 2, 1, 6.15, 1); - TEST_UNION(case_recursive_boxes_12, 6.0, 0); - TEST_UNION(case_recursive_boxes_13, 10.25, 0); + TEST_INTERSECTION(case_77_multi, 9.0, 5, 0); + TEST_UNION(case_101_multi, 22.25, 1, 3); + TEST_INTERSECTION(case_101_multi, 4.75, 4, 0); + + TEST_INTERSECTION(case_recursive_boxes_11, 1.0, 2, 0); + TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 3, 0); + + TEST_UNION(case_recursive_boxes_4, 96.75, 1, 2); + TEST_INTERSECTION_WITH(case_58_multi, 6, 2, 13.25, 1, 1); + TEST_INTERSECTION_WITH(case_72_multi, 2, 1, 6.15, 3, 1); + TEST_UNION(case_recursive_boxes_12, 6.0, 6, 0); + TEST_UNION(case_recursive_boxes_13, 10.25, 3, 0); // std::cout From 3c58f15105a1fc8c278fed6f1cbfffd08acfcf77 Mon Sep 17 00:00:00 2001 From: barendgehrels Date: Wed, 23 Nov 2016 17:35:29 +0100 Subject: [PATCH 16/18] [traversal] handle non clustered ii turns using sort-by-side --- .../algorithms/detail/overlay/traversal.hpp | 54 +++++++++++++++++-- .../detail/overlay/traversal_ring_creator.hpp | 3 +- test/algorithms/overlay/overlay.cpp | 2 +- .../set_operations/difference/difference.cpp | 5 +- .../intersection/intersection.cpp | 3 +- .../intersection/intersection_multi.cpp | 5 +- 6 files changed, 58 insertions(+), 14 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index ae69c592e..c1782db70 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -572,6 +572,32 @@ struct traversal return result; } + inline bool analyze_ii_intersection(signed_size_type& turn_index, int& op_index, + turn_type const& current_turn, + segment_identifier const& previous_seg_id) + { + sbs_type sbs; + + // Add this turn to the sort-by-side sorter + bool has_origin = false; + for (int i = 0; i < 2; i++) + { + turn_operation_type const& op = current_turn.operations[i]; + bool const is_origin = op.seg_id.source_index + == previous_seg_id.source_index; + has_origin = has_origin || is_origin; + sbs.add(op, turn_index, i, m_geometry1, m_geometry2, is_origin); + } + + if (! has_origin) + { + return false; + } + + sbs.apply(current_turn.point); + return analyze_cluster_intersection(turn_index, op_index, sbs); + } + inline void change_index_for_self_turn(signed_size_type& to_vertex_index, turn_type const& start_turn, turn_operation_type const& start_op, @@ -665,7 +691,7 @@ struct traversal return true; } - bool select_turn(signed_size_type start_turn_index, + bool select_turn(signed_size_type start_turn_index, int start_op_index, signed_size_type& turn_index, int& op_index, bool& is_touching, @@ -674,7 +700,29 @@ struct traversal segment_identifier const& previous_seg_id, bool is_start) { - if (m_turns[turn_index].cluster_id >= 0) + turn_type const& current_turn = m_turns[turn_index]; + + if (target_operation == operation_intersection + && current_turn.both(operation_intersection) + && turn_index == start_turn_index) + { + // Intersection can always be finished if returning at ii + op_index = start_op_index; + return true; + } + + if (current_turn.cluster_id < 0 + && target_operation == operation_intersection + && current_turn.both(operation_intersection)) + { + if (analyze_ii_intersection(turn_index, op_index, current_turn, + previous_seg_id)) + { + return true; + } + } + + if (current_turn.cluster_id >= 0) { if (! select_turn_from_cluster(turn_index, op_index, is_touching, start_turn_index, previous_seg_id, is_start)) @@ -689,8 +737,6 @@ struct traversal } else { - turn_type const& current_turn = m_turns[turn_index]; - op_index = starting_operation_index(current_turn); if (op_index == -1) { diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index 41e510f2e..e0dfee19a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -121,7 +121,8 @@ struct traversal_ring_creator } bool is_touching = false; - if (! m_trav.select_turn(start_turn_index, turn_index, op_index, + if (! m_trav.select_turn(start_turn_index, start_op_index, + turn_index, op_index, is_touching, previous_op_index, previous_turn_index, previous_seg_id, is_start)) diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index 3302f2f4f..18b2da198 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -449,7 +449,7 @@ void test_all() TEST_INTERSECTION(case_101_multi, 4.75, 4, 0); TEST_INTERSECTION(case_recursive_boxes_11, 1.0, 2, 0); - TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 3, 0); + TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 4, 0); TEST_UNION(case_recursive_boxes_4, 96.75, 1, 2); TEST_INTERSECTION_WITH(case_58_multi, 6, 2, 13.25, 1, 1); diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 9442139af..fb7c57853 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -415,7 +415,7 @@ void test_all() test_one("ticket_9563", ticket_9563[0], ticket_9563[1], 0, 0, 0, - 1, 20, 20.096189, ignore_validity); + 6, 24, 20.096189); #endif test_one("ticket_10108_a", @@ -527,8 +527,7 @@ void test_all() mysql_21965285[0], mysql_21965285[1], 1, 2, -1, 92.0, 1, 1, -1, 14.0, - 1, 2, -1, 92.0 + 14.0, - ignore_validity); + 1, 2, -1, 92.0 + 14.0); test_one("mysql_23023665_1", mysql_23023665_1[0], mysql_23023665_1[1], diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index c150f5ca6..ebd07d5f0 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -334,8 +334,7 @@ void test_areal() test_one("case_101", case_101[0], case_101[1], - 0, -1, 6.25, - ignore_validity); + 0, -1, 6.25); test_one("case_102", case_102[0], case_102[1], 0, -1, 3.1875); diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 7bc04c944..60113be8f 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -158,7 +158,7 @@ void test_areal() test_one("case_recursive_boxes_4", case_recursive_boxes_4[0], case_recursive_boxes_4[1], - 7, 179, 67.0, // Area from SQL Server + 8, 179, 67.0, // Area from SQL Server ignore_validity); // Fixed by replacing handle_tangencies in less_by_segment_ratio sort order @@ -240,8 +240,7 @@ void test_areal() 5, 0, 3.75); test_one("case_recursive_boxes_30", case_recursive_boxes_30[0], case_recursive_boxes_30[1], - 3, 0, 6.0, - ignore_validity); + 4, 0, 6.0); test_one("case_recursive_boxes_31", case_recursive_boxes_31[0], case_recursive_boxes_31[1], 2, 0, 2.5); From c4ab3653eb497c5fdeb467a8f6d680984f618e10 Mon Sep 17 00:00:00 2001 From: barendgehrels Date: Wed, 23 Nov 2016 18:01:45 +0100 Subject: [PATCH 17/18] [test] minor order in testcase indexes --- test/algorithms/overlay/overlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index 18b2da198..372e6a7b8 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -452,8 +452,8 @@ void test_all() TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 4, 0); TEST_UNION(case_recursive_boxes_4, 96.75, 1, 2); - TEST_INTERSECTION_WITH(case_58_multi, 6, 2, 13.25, 1, 1); - TEST_INTERSECTION_WITH(case_72_multi, 2, 1, 6.15, 3, 1); + TEST_INTERSECTION_WITH(case_58_multi, 2, 6, 13.25, 1, 1); + TEST_INTERSECTION_WITH(case_72_multi, 1, 2, 6.15, 3, 1); TEST_UNION(case_recursive_boxes_12, 6.0, 6, 0); TEST_UNION(case_recursive_boxes_13, 10.25, 3, 0); From efecc02866782fdc489c49bcd88b78adf6a278ac Mon Sep 17 00:00:00 2001 From: barendgehrels Date: Wed, 30 Nov 2016 12:56:48 +0100 Subject: [PATCH 18/18] [traverse] fix certain cases to recognize starting point during intersection in a broader context --- .../algorithms/detail/overlay/traversal.hpp | 31 +++++++++++-------- .../overlay/multi_overlay_cases.hpp | 7 +++++ .../intersection/intersection_multi.cpp | 4 +++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index c1782db70..cc1dde679 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -702,24 +702,29 @@ struct traversal { turn_type const& current_turn = m_turns[turn_index]; - if (target_operation == operation_intersection - && current_turn.both(operation_intersection) - && turn_index == start_turn_index) + if (target_operation == operation_intersection) { - // Intersection can always be finished if returning at ii - op_index = start_op_index; - return true; - } + bool const back_at_start_cluster + = current_turn.cluster_id >= 0 + && m_turns[start_turn_index].cluster_id == current_turn.cluster_id; - if (current_turn.cluster_id < 0 - && target_operation == operation_intersection - && current_turn.both(operation_intersection)) - { - if (analyze_ii_intersection(turn_index, op_index, current_turn, - previous_seg_id)) + if (turn_index == start_turn_index || back_at_start_cluster) { + // Intersection can always be finished if returning + turn_index = start_turn_index; + op_index = start_op_index; return true; } + + if (current_turn.cluster_id < 0 + && current_turn.both(operation_intersection)) + { + if (analyze_ii_intersection(turn_index, op_index, current_turn, + previous_seg_id)) + { + return true; + } + } } if (current_turn.cluster_id >= 0) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index ec4f60f43..b7f250794 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -821,6 +821,13 @@ static std::string case_recursive_boxes_38[2] = "MULTIPOLYGON(((2 2,4 2,4 1,2 1,2 2)),((0 4,0 5,1 4,0 4)),((2 3,2 4,4 4,4 3,2 3)),((2 2,1 2,1 3,2 3,2 2)),((1 2,0 2,0 3,1 3,0.5 2.5,1 2)))" }; +static std::string case_recursive_boxes_39[2] = +{ + // Needs check for back at start during traversal + "MULTIPOLYGON(((3 8,2 8,2 9,3 9,3 8)),((4 8,4 9,5 9,5 8,4 8)),((6 9,6 10,7 10,7 9,6 9)),((5 6,4 6,4 7,6 7,6 5,5 5,5 6)),((4 7,3 7,3 8,4 8,4 7)),((7 8,8 8,8 7,6 7,6 8,7 8)))", + "MULTIPOLYGON(((3 7,3 6,2 6,2 7,3 7)),((4 8,4 7,3 7,3 8,4 8)),((6 10,7 10,7 9,5 9,5 10,6 10)),((5 8,4 8,4 9,5 9,5 8)),((5 8,6 8,6 7,5 7,5 8)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 60113be8f..2ffa77e2b 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -146,9 +146,11 @@ void test_areal() TEST_INTERSECTION(case_124_multi, 2, 13, 2.0625); TEST_INTERSECTION_IGNORE(case_125_multi, 3, 17, 2.1); + // #1 needs self-turns to make valid test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], 8, 97, 47.0, ignore_validity); + test_one("case_recursive_boxes_2", case_recursive_boxes_2[0], case_recursive_boxes_2[1], 1, 50, 90.0); // Area from SQL Server @@ -264,6 +266,8 @@ void test_areal() case_recursive_boxes_37[0], case_recursive_boxes_37[1], 2, 0, 1.0); + TEST_INTERSECTION(case_recursive_boxes_39, 3, 0, 3.00); + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server