mirror of
https://github.com/boostorg/geometry.git
synced 2025-05-11 21:44:04 +00:00
[turns] have all discarding of start_turns in one place and clean up clusters before assigning
This commit is contained in:
parent
1a3f425583
commit
10142a6703
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user