[turns] have all discarding of start_turns in one place and clean up clusters before assigning

This commit is contained in:
Barend Gehrels 2021-04-28 11:54:22 +02:00
parent 1a3f425583
commit 10142a6703
6 changed files with 88 additions and 95 deletions

View File

@ -20,6 +20,13 @@ namespace detail { namespace overlay
{
inline bool same_multi_and_ring_id(segment_identifier const& first,
segment_identifier const& second)
{
return first.ring_index == second.ring_index
&& first.multi_index == second.multi_index;
}
template <typename Geometry0, typename Geometry1>
inline bool is_consecutive(segment_identifier const& first,
segment_identifier const& second,
@ -28,7 +35,6 @@ inline bool is_consecutive(segment_identifier const& first,
{
if (first.source_index == second.source_index
&& first.ring_index == second.ring_index
&& first.piece_index == second.piece_index
&& first.multi_index == second.multi_index)
{
// If the segment distance is 1, there are no segments in between
@ -36,12 +42,11 @@ inline bool is_consecutive(segment_identifier const& first,
signed_size_type const sd = first.source_index == 0
? segment_distance(geometry0, first, second)
: segment_distance(geometry1, first, second);
return sd == 1;
return sd <= 1;
}
return false;
}
// Returns true if two turns correspond to each other in the sense that one has
// "method_start" and the other has "method_touch" or "method_interior" at that
// same point (but with next segment)
@ -50,39 +55,54 @@ inline bool corresponding_turn(Turn const& turn, Turn const& start_turn,
Geometry0 const& geometry0,
Geometry1 const& geometry1)
{
std::size_t count = 0;
for (std::size_t i = 0; i < 2; i++)
{
for (std::size_t j = 0; j < 2; j++)
{
if (turn.operations[i].seg_id == start_turn.operations[j].seg_id
&& (turn.method == method_touch
|| turn.method == method_touch_interior))
if (same_multi_and_ring_id(turn.operations[i].seg_id,
start_turn.operations[j].seg_id))
{
// Verify if the other operation is consecutive
return is_consecutive(turn.operations[1 - i].seg_id,
// Verify if all operations are consecutive
if (is_consecutive(turn.operations[i].seg_id,
start_turn.operations[j].seg_id,
geometry0, geometry1)
&& is_consecutive(turn.operations[1 - i].seg_id,
start_turn.operations[1 - j].seg_id,
geometry0, geometry1);
geometry0, geometry1))
{
count++;
}
}
}
}
return false;
// An intersection is located on exactly two rings
// The corresponding turn, if any, should be located on the same two rings.
return count == 2;
}
template <typename Turns, typename Geometry0, typename Geometry1>
inline void discard_duplicate_start_turns(Turns& turns,
Geometry0 const& geometry0,
Geometry1 const& geometry1)
{
// Start turns are generated, in case the previous turn is missed
// But often it is not missed, and then it should still be deleted
// Start turns are generated, in case the previous turn is missed.
// But often it is not missed, and then it should be deleted.
// This is how it can be
// (in float, collinear, points far apart due to floating point precision)
// [m, i s:0, v:6 1/1 (1) // u s:1, v:5 pnt (2.54044, 3.12623)]
// [s, i s:0, v:7 0/1 (0) // u s:1, v:5 pnt (2.70711, 3.29289)]
// 1 Build map (segment->turnids) of start turns
std::map<segment_identifier, std::vector<std::size_t>> start_turns_per_segment;
using multi_and_ring_id_type = std::pair<signed_size_type, signed_size_type>;
auto adapt_id = [](segment_identifier const& seg_id)
{
return multi_and_ring_id_type{seg_id.multi_index, seg_id.ring_index};
};
// 1 Build map of start turns (multi/ring-id -> turn indices)
std::map<multi_and_ring_id_type, std::vector<std::size_t>> start_turns_per_segment;
std::size_t index = 0;
for (auto const& turn : turns)
{
@ -90,7 +110,7 @@ inline void discard_duplicate_start_turns(Turns& turns,
{
for (const auto& op : turn.operations)
{
start_turns_per_segment[op.seg_id].push_back(index);
start_turns_per_segment[adapt_id(op.seg_id)].push_back(index);
}
}
index++;
@ -100,11 +120,13 @@ inline void discard_duplicate_start_turns(Turns& turns,
// if they have the other turns as corresponding
for (auto const& turn : turns)
{
if (turn.method != method_start)
// Any turn which "crosses" does not have a corresponding turn.
// Also avoid comparing "start" with itself.
if (turn.method != method_crosses && turn.method != method_start)
{
for (const auto& op : turn.operations)
{
auto it = start_turns_per_segment.find(op.seg_id);
auto it = start_turns_per_segment.find(adapt_id(op.seg_id));
if (it != start_turns_per_segment.end())
{
for (std::size_t const& i : it->second)

View File

@ -35,6 +35,7 @@
#include <boost/range/value_type.hpp>
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/discard_duplicate_turns.hpp>
#include <boost/geometry/algorithms/detail/overlay/handle_colocations.hpp>
#include <boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
@ -444,11 +445,14 @@ inline void enrich_intersection_points(Turns& turns,
// From here on, turn indexes are used (in clusters, next_index, etc)
// and may not be DELETED - they may only be flagged as discarded
discard_duplicate_start_turns(turns, geometry1, geometry2);
bool has_cc = false;
bool const has_colocations
= detail::overlay::handle_colocations<Reverse1, Reverse2, OverlayType>(turns,
clusters, geometry1, geometry2, robust_policy);
= detail::overlay::handle_colocations
<
Reverse1, Reverse2, OverlayType, Geometry1, Geometry2
>(turns, clusters, robust_policy);
// Discard turns not part of target overlay
for (typename boost::range_iterator<Turns>::type
@ -530,6 +534,23 @@ inline void enrich_intersection_points(Turns& turns,
#endif
}
if (has_colocations)
{
// First gather cluster properties (using even clusters with
// discarded turns - for open turns), then clean up clusters
detail::overlay::gather_cluster_properties
<
Reverse1,
Reverse2,
OverlayType
>(clusters, turns, target_operation,
geometry1, geometry2, strategy.side()); // TODO: pass strategy
detail::overlay::cleanup_clusters(turns, clusters);
}
// After cleaning up clusters assign the next turns
for (typename mapped_vector_type::iterator mit
= mapped_vector.begin();
mit != mapped_vector.end();
@ -546,21 +567,6 @@ inline void enrich_intersection_points(Turns& turns,
detail::overlay::enrich_assign(mit->second, turns, ! is_dissolve);
}
if (has_colocations)
{
// First gather cluster properties (using even clusters with
// discarded turns - for open turns), then clean up clusters
detail::overlay::gather_cluster_properties
<
Reverse1,
Reverse2,
OverlayType
>(clusters, turns, target_operation,
geometry1, geometry2, strategy.side()); // TODO: pass strategy
detail::overlay::cleanup_clusters(turns, clusters);
}
if (has_cc)
{
detail::overlay::calculate_remaining_distance(turns);

View File

@ -30,7 +30,6 @@
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_clusters.hpp>
#include <boost/geometry/algorithms/detail/overlay/discard_duplicate_turns.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
@ -226,46 +225,6 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
}
}
template<typename Turns, typename Clusters>
inline void discard_start_turns(Turns& turns, Clusters& clusters)
{
for (auto& nv : clusters)
{
cluster_info& cinfo = nv.second;
auto& indices = cinfo.turn_indices;
std::size_t start_count{0};
for (signed_size_type index : indices)
{
auto const& turn = turns[index];
if (turn.method == method_start)
{
start_count++;
}
}
if (start_count == 0 && start_count == indices.size())
{
// There are no start turns, or all turns in the cluster are start turns.
continue;
}
// Discard the start turns and simultaneously erase them from the indices
for (auto it = indices.begin(); it != indices.end();)
{
auto& turn = turns[*it];
if (turn.method == method_start)
{
turn.discarded = true;
turn.cluster_id = -1;
it = indices.erase(it);
}
else
{
++it;
}
}
}
}
template
<
overlay_type OverlayType,
@ -368,18 +327,15 @@ template
<
bool Reverse1, bool Reverse2,
overlay_type OverlayType,
typename Turns,
typename Clusters,
typename Geometry0,
typename Geometry1,
typename Turns,
typename Clusters,
typename RobustPolicy
>
inline bool handle_colocations(Turns& turns, Clusters& clusters,
Geometry0 const& geometry0, Geometry1 const& geometry1,
RobustPolicy const& robust_policy)
{
discard_duplicate_start_turns(turns, geometry0, geometry1);
static const detail::overlay::operation_type target_operation
= detail::overlay::operation_from_overlay<OverlayType>::value;
@ -392,14 +348,10 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters,
assign_cluster_ids(turns, clusters);
// Get colocated information here and not later, to keep information
// Get colocated information here, and not later, to keep information
// on turns which are discarded afterwards
set_colocation<OverlayType>(turns, clusters);
// Discard start turns (which are extra and usually redundant, only added in case the
// companion turn is missing) from clusters
discard_start_turns(turns, clusters);
if (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
{
discard_interior_exterior_turns
@ -409,8 +361,8 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters,
>(turns, clusters);
}
// Remove clusters which has only one turn
//TODO
// There might be clusters having only one turn, if the rest is discarded
// This is cleaned up later, after gathering the properties.
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
std::cout << "*** Colocations " << map.size() << std::endl;
@ -423,7 +375,7 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters,
std::cout << std::endl;
}
}
#endif // DEBUG
#endif
return true;
}

View File

@ -353,6 +353,10 @@ static std::string const nores_b8e6
= "MULTIPOLYGON(((4 4,5 5,5 4,4 4)),((4 2,5 2,5 1,3 1,4 2)),((3 1,4 0,3 0,3 1)))";
static std::string const nores_2881
= "MULTIPOLYGON(((2 5,2 6,3 5,2 5)),((1 7,0 7,0 8,1 8,1 7)),((1 7,1 6,0 6,1 7)))";
// The same but with an extra unrelated polygon, still influencing order of turns and behavior
static std::string const nores_2881b
= "MULTIPOLYGON(((5 7,5 8,6 8,6 7,5 7)),((2 5,2 6,3 5,2 5)),((1 7,0 7,0 8,1 8,1 7)),((1 7,1 6,0 6,1 7)))";
static std::string const nores_3af0
= "MULTIPOLYGON(((1 8,0 8,0 9,1 9,1 8)),((1 8,1 7,0 7,1 8)),((2 4,3 5,3 4,2 4)),((2 6,2 7,3 6,2 6)))";
static std::string const nores_6061
@ -360,8 +364,15 @@ static std::string const nores_6061
static std::string const nores_1ea1
= "MULTIPOLYGON(((2 0,2 1,3 0,2 0)),((7 5,6 4,5 3,5 4,5 5,7 5)),((2 3,1 3,1 4,2 3)),((2 3,2 4,3 3,2 3)))";
// Related to discarding start turns
static std::string const nores_804e
= "MULTIPOLYGON(((4 8,4 9,5 8,4 8)),((3 9,3 10,4 10,3 9)),((0 7,0 8,1 7,0 7)),((4 6,3 6,3 7,4 6)),((4 6,4 7,5 7,4 6)))";
static std::string const nores_51c6
= "MULTIPOLYGON(((3 6,4 7,4 6,3 6)),((5 7,5 8,6 8,5 7)),((6 7,7 6,7 5,6 5,6 7)))";
static std::string const nores_e5f3
= "MULTIPOLYGON(((5 1,6 2,6 1,5 1)),((4 2,3 2,4 3,4 2)),((4 2,5 3,5 2,4 2)))";
static std::string const nores_37f6
= "MULTIPOLYGON(((4 1,5 2,5 1,4 1)),((1 0,1 1,2 1,2 0,1 0)),((0 3,0 4,1 4,1 3,0 3)),((2 2,2 3,3 2,2 2)))";
@ -606,15 +617,18 @@ void test_all()
test_one<multi_polygon_type, polygon_type>("nores_wt_2", nores_wt_2, join_round32, end_flat, 22.1102, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_b8e6", nores_b8e6, join_round32, end_flat, 19.8528, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_2881", nores_2881, join_round32, end_flat, 16.5517, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_2881", nores_2881, join_round32, end_flat, 16.5510, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_6061", nores_6061, join_round32, end_flat, 39.7371, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_37f6", nores_37f6, join_round32, end_flat, 26.5339, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_1ea1", nores_1ea1, join_round32, end_flat, 28.9755, 1.0);
#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Cases not yet solved without rescaling (out of 241)
test_one<multi_polygon_type, polygon_type>("nores_804e", nores_804e, join_round32, end_flat, 26.4503, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_51c6", nores_51c6, join_round32, end_flat, 20.2419, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_e5f3", nores_e5f3, join_round32, end_flat, 14.5512, 1.0);
#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Cases not yet solved without rescaling
test_one<multi_polygon_type, polygon_type>("nores_3af0", nores_3af0, join_round32, end_flat, 22.1008, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_2881b", nores_2881b, join_round32, end_flat, 24.6731, 1.0);
#endif
test_one<multi_polygon_type, polygon_type>("nores_495d", nores_495d, join_round32, end_flat, 23.4376, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_e402", nores_e402, join_round32, end_flat, 9.9888, 1.0);
@ -669,7 +683,7 @@ int test_main(int, char* [])
#endif
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
BoostGeometryWriteExpectedFailures(5, 4, 5, 8);
BoostGeometryWriteExpectedFailures(5, 4, 4, 9);
#endif
return 0;

View File

@ -866,7 +866,7 @@ int test_main(int, char* [])
#endif
#if defined(BOOST_GEOMETRY_TEST_FAILURES)
BoostGeometryWriteExpectedFailures(2, 1, 9, 2);
BoostGeometryWriteExpectedFailures(2, 1, 9, 1);
#endif
return 0;

View File

@ -66,7 +66,6 @@ void do_test(std::string const& case_id,
template <typename Point, typename T>
void test_get_clusters(T eps)
{
using coor_type = typename bg::coordinate_type<Point>::type;
do_test<Point>("no", {{1.0, 1.0}, {1.0, 2.0}}, 0);
do_test<Point>("simplex", {{1.0, 1.0}, {1.0, 1.0}}, 1);