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..df62a1f2f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -0,0 +1,100 @@ +// 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; + bool only_turn_on_ring; + + + 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) + , only_turn_on_ring(false) + {} +}; + +struct rank_with_rings +{ + std::size_t rank; + std::set 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; + } +}; + +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; + rwd.only_turn_on_ring = ranked_point.only_turn_on_ring; + + + 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 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/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 5f3857dcd..400ed3b88 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -687,6 +687,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++) @@ -713,7 +714,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..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 @@ -181,11 +185,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; @@ -202,8 +231,8 @@ struct side_sorter 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) { @@ -292,8 +321,6 @@ struct side_sorter &segment_identifier::source_index >(handled); } - - assign_zones(); } void reverse() @@ -334,8 +361,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 +381,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 +390,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 +475,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 +491,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 +527,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 +541,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/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index ae292f205..381ba6052 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,30 +526,76 @@ struct traversal sbs.apply(turn.point); -#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) - is_touching = is_union && cinfo.open_count > 1; - if (is_touching) + bool result = false; + + if (is_union) { - if (cinfo.switch_source) + #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) + is_touching = cinfo.open_count > 1; + if (is_touching) { - is_touching = false; - std::cout << "CLUSTER: SWITCH SOURCES at " << turn_index << std::endl; + 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 + #else + is_touching = cinfo.open_count > 1 && ! cinfo.switch_source; + #endif + + if (is_touching) { - std::cout << "CLUSTER: CONTINUE at " << turn_index << std::endl; + sbs.reverse(); } + + result = select_from_cluster_union(turn_index, op_index, start_turn_index, sbs, + is_touching); } -#else - is_touching = is_union && cinfo.open_count > 1 && ! cinfo.switch_source; -#endif - if (is_touching) + else { - sbs.reverse(); + 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 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); } - return select_from_cluster(turn_index, op_index, start_turn_index, sbs, - is_touching); + 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, @@ -594,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, @@ -603,10 +700,37 @@ 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) + { + bool const back_at_start_cluster + = current_turn.cluster_id >= 0 + && m_turns[start_turn_index].cluster_id == current_turn.cluster_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) { 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; } @@ -618,8 +742,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 bdea318d1..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)) @@ -187,6 +188,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/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 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 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/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 4909d93e6..b7f250794 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)))" }; @@ -498,6 +502,27 @@ 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_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)))", @@ -796,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/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index f45b211e4..372e6a7b8 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) + double expected_area, + std::size_t expected_clip_count, + std::size_t expected_hole_count) { Geometry g1; bg::read_wkt(wkt1, g1); @@ -386,6 +388,14 @@ 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), + 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);" @@ -394,6 +404,20 @@ void test_overlay(std::string const& caseid, #endif } +#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, 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() { @@ -401,121 +425,37 @@ 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, 1, 0); + TEST_INTERSECTION(case_multi_simplex, 6.42, 2, 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_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, 2, 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, 17, 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, 2, 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, 1, 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_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_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_INTERSECTION(case_recursive_boxes_11, 1.0, 2, 0); + TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 4, 0); - 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_UNION(case_recursive_boxes_4, 96.75, 1, 2); + 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); // std::cout 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/overlay/sort_by_side.cpp b/test/algorithms/overlay/sort_by_side.cpp new file mode 100644 index 000000000..81c29436d --- /dev/null +++ b/test/algorithms/overlay/sort_by_side.cpp @@ -0,0 +1,261 @@ +// 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_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); + 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_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); + 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; + } diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 8cd7b3d8c..fb7c57853 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -216,8 +216,17 @@ 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], + 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], @@ -406,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", @@ -516,26 +525,25 @@ 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); + 1, 2, -1, 92.0 + 14.0); 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 e84930ad8..1644de9cf 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; @@ -153,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], @@ -160,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.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index b1de3445c..ebd07d5f0 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -332,6 +332,13 @@ 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); + 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/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 782d2100b..2ffa77e2b 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() { @@ -102,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); @@ -133,9 +141,16 @@ void test_areal() 5, 33, 7.5, ignore_validity); #endif + + TEST_INTERSECTION(case_123_multi, 3, 13, 1.875); + 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 @@ -149,6 +164,7 @@ 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], 6, 47, 19.0); @@ -238,8 +254,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, @@ -251,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 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], 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);