Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
403 changes: 54 additions & 349 deletions extensions/test/algorithms/dissolve.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -202,70 +202,6 @@ inline void enrich_assign(Operations& operations, Turns& turns)
#endif
}

template <typename Operations, typename Turns>
inline void enrich_adapt(Operations& operations, Turns& turns)
{
// Operations is a vector of indexed_turn_operation<>
// If it is empty, or contains one or two items, it makes no sense
if (operations.size() < 3)
{
return;
}

bool next_phase = false;
std::size_t previous_index = operations.size() - 1;

for (auto const& item : util::enumerate(operations))
{
auto const& index = item.index;
auto const& indexed = item.value;
auto& turn = turns[indexed.turn_index];
auto& op = turn.operations[indexed.operation_index];

std::size_t const next_index = (index + 1) % operations.size();
auto const& next_turn = turns[operations[next_index].turn_index];
auto const& next_op = next_turn.operations[operations[next_index].operation_index];

if (op.seg_id.segment_index == next_op.seg_id.segment_index)
{
auto const& prev_turn = turns[operations[previous_index].turn_index];
auto const& prev_op = prev_turn.operations[operations[previous_index].operation_index];
if (op.seg_id.segment_index == prev_op.seg_id.segment_index)
{
op.enriched.startable = false;
next_phase = true;
}
}
previous_index = index;
}

if (! next_phase)
{
return;
}

// Discard turns which are both non-startable
next_phase = false;
for (auto& turn : turns)
{
if (! turn.operations[0].enriched.startable
&& ! turn.operations[1].enriched.startable)
{
turn.discarded = true;
next_phase = true;
}
}

if (! next_phase)
{
return;
}

// Remove discarded turns from operations to avoid having them as next turn
discarded_indexed_turn<Turns> const predicate(turns);
operations.erase(std::remove_if(std::begin(operations),
std::end(operations), predicate), std::end(operations));
}

struct enriched_map_default_include_policy
{
Expand Down Expand Up @@ -420,11 +356,6 @@ inline void enrich_turns(Turns& turns,
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
std::cout << "ENRICH-assign Ring " << pair.first << std::endl;
#endif
if BOOST_GEOMETRY_CONSTEXPR (OverlayType == overlay_dissolve)
{
enrich_adapt(pair.second, turns);
}

enrich_assign(pair.second, turns);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void add_tois(Turns const& turns, Clusters const& clusters,
turn_operation_id const toi{source_index, i};
if (is_target_operation<TargetOperation>(turns, toi))
{
result.insert(std::move(toi));
result.insert(toi);
}
}
}
Expand Down Expand Up @@ -111,9 +111,9 @@ set_of_tois get_tois(Turns const& turns, Clusters const& clusters,
}

// Variant with multiple target nodes
template <operation_type TargetOperation, typename Turns, typename Clusters>
template <operation_type TargetOperation, typename Turns, typename Clusters, typename TargetNodeIds>
set_of_tois get_tois(Turns const& turns, Clusters const& clusters,
signed_size_type source_node_id, std::set<signed_size_type> const& target_node_ids)
signed_size_type source_node_id, TargetNodeIds const& target_node_ids)
{
set_of_tois result;
for (auto const& target : target_node_ids)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_operation_id.hpp>

#include <algorithm>
#include <set>
#include <vector>
#include <tuple>

namespace boost { namespace geometry
Expand Down Expand Up @@ -121,14 +123,15 @@ void get_target_operations(Turns const& turns,


// Get the target nodes of a specific component_id only.
// Target nodes are sorted on preference index.
template <operation_type TargetOperation, typename Turns, typename Clusters, typename Set>
auto get_target_nodes(Turns const& turns, Clusters const& clusters,
Set const& turn_indices,
signed_size_type component_id)
{
using is_included = is_operation_included<TargetOperation>;

std::set<signed_size_type> result;
std::vector<std::pair<signed_size_type, std::size_t>> selected;
for (auto turn_index : turn_indices)
{
auto const& turn = turns[turn_index];
Expand All @@ -144,10 +147,19 @@ auto get_target_nodes(Turns const& turns, Clusters const& clusters,
&& is_included::apply(op)
&& is_target_operation<TargetOperation>(turns, {turn_index, j}))
{
result.insert(get_node_id(turns, op.enriched.travels_to_ip_index));
selected.push_back({get_node_id(turns, op.enriched.travels_to_ip_index), op.preference_index});
}
}
}

std::sort(selected.begin(), selected.end(),
[](auto const& a, auto const& b) { return a.second < b.second; });

std::vector<signed_size_type> result;
for (auto const& item : selected)
{
result.push_back(item.first);
}
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ struct edge_and_side
int side{0};
};

// Sort by side and always take the right (smaller) side (for intersection, union, buffer).
template <overlay_type OverlayType>
struct compare_edges_by_side
{
template <typename Edge1, typename Edge2>
bool operator()(Edge1 const& a, Edge2 const& b) const
{
return a.side < b.side;
}
};

// For dissolve, the left (larger) side is taken.
template <>
struct compare_edges_by_side<overlay_dissolve>
{
template <typename Edge1, typename Edge2>
bool operator()(Edge1 const& a, Edge2 const& b) const
{
return a.side > b.side;
}
};

template
<
bool Reverse1,
Expand Down Expand Up @@ -154,14 +176,8 @@ struct edge_selector
edge.side = side_strategy.apply(p1, p2, edge.point);
}

// Sort by side (with respect to segment [p1..p2]) (TEMPORARY: and then by toi)
// Right = -1 will come first. Left = 1 will come last.
// This works for both union and intersection operations, because it should always
// take the right turn (even in uu in buffer/union).
std::sort(edges.begin(), edges.end(), [](auto const& a, auto const& b)
{
return std::tie(a.side, a.toi) < std::tie(b.side, b.toi);
});
// Sort by side, with respect to segment [p1..p2]
std::sort(edges.begin(), edges.end(), compare_edges_by_side<OverlayType>());

report("by side", edges, p1, p2);

Expand Down Expand Up @@ -274,6 +290,16 @@ struct edge_selector
return edges.front().toi;
}

if (target_operation == operation_union
&& op0.operation == operation_union
&& op1.operation == operation_union
&& op0.preference_index != op1.preference_index)
{
return op0.preference_index < op1.preference_index
? edges[0].toi
: edges[1].toi;
}

if (target_operation == operation_union
&& turn0.is_clustered()
&& op0.operation == operation_union
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,17 @@ struct traverse_graph
// more than times there are turns (this should not happen).
while (iteration_count < m_turns.size())
{
auto const current_turn_indices = get_turn_indices_by_node_id(m_turns, m_clusters,
current_node_id, allow_closed);
auto const current_turn_indices = get_turn_indices_by_node_id(m_turns, m_clusters,
current_node_id, allow_closed);

// Any valid node should always deliver at least one turn
BOOST_ASSERT(! current_turn_indices.empty());
// Any valid node should always deliver at least one turn
BOOST_ASSERT(! current_turn_indices.empty());

auto const next_target_nodes = get_target_nodes<target_operation>(m_turns, m_clusters,
current_turn_indices, component_id);
auto const next_target_nodes = get_target_nodes<target_operation>(m_turns, m_clusters,
current_turn_indices, component_id);

if (next_target_nodes.empty())
{
if (next_target_nodes.empty())
{
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
std::cout << "Stuck, start: " << start_node_id
<< " stuck: " << current_node_id
Expand All @@ -209,8 +209,8 @@ struct traverse_graph
return false;
}

auto const tois = get_tois<target_operation>(m_turns, m_clusters,
current_node_id, next_target_nodes);
auto const tois = get_tois<target_operation>(m_turns, m_clusters,
current_node_id, next_target_nodes);

if (tois.empty())
{
Expand All @@ -221,12 +221,12 @@ struct traverse_graph

auto toi = *tois.begin();

if (tois.size() > 1)
{
// Select the best target edge, using the last point of the ring and the turn point
// for side calculations (if any).
if (tois.size() > 1)
{
// Select the best target edge, using the last point of the ring and the turn point
// for side calculations (if any).
toi = m_edge_selector.select_target_edge(tois, range::back(ring), turn_point);
}
}

if (m_visited_tois.count(toi) > 0 || m_finished_tois.count(toi) > 0)
{
Expand Down Expand Up @@ -272,7 +272,7 @@ struct traverse_graph
// Select the first toi which is not yet visited and has the requested component.
// If all tois are visited, not having the same component, it is not possible to continue,
// and it returns an invalid toi.
auto select_first_toi = [&](auto const& tois)
auto select_first_toi_other = [&](auto const& tois)
{
for (auto const& toi : tois)
{
Expand All @@ -292,6 +292,40 @@ struct traverse_graph
return turn_operation_id{0, -1};
};

auto select_first_toi_of_two_in_union = [&](auto const& tois)
{
const auto& toi0 = *tois.begin();
const auto& toi1 = *(++tois.begin());

if (m_finished_tois.count(toi0) == 0
&& m_finished_tois.count(toi1) == 0)
{
auto const& turn0 = m_turns[toi0.turn_index];
auto const& turn1 = m_turns[toi1.turn_index];
auto const& op0 = turn0.operations[toi0.operation_index];
auto const& op1 = turn1.operations[toi1.operation_index];

if (op0.preference_index != op1.preference_index)
{
return op0.preference_index < op1.preference_index
? toi0
: toi1;
}
}
return select_first_toi_other(tois);
};

auto select_first_toi = [&](auto const& tois)
{
if (tois.size() == 2
&& target_operation == operation_union)
{
return select_first_toi_of_two_in_union(tois);
}

return select_first_toi_other(tois);
};

auto const toi = select_first_toi(get_tois<target_operation>(m_turns, m_clusters,
start_node_id, target_node_id));
if (toi.operation_index < 0)
Expand Down Expand Up @@ -365,25 +399,44 @@ struct traverse_graph
{
return;
}
auto const source_node_id = get_node_id(m_turns, turn_index);
auto const turn_indices = get_turn_indices_by_node_id(m_turns, m_clusters,
source_node_id, allow_closed);

// Iterate through the turns operations which are sorted by preference.
std::vector<turn_operation_id> start_operations;
for (int j = 0; j < 2; j++)
{
auto const& op = turn.operations[j];
if (! op.enriched.startable || ! is_included::apply(op))
{
continue;
}

turn_operation_id const toi{turn_index, j};
if (m_finished_tois.count(toi) > 0
|| ! is_target_operation<target_operation>(m_turns, toi))
if (op.enriched.startable
&& m_finished_tois.count(toi) == 0
&& is_target_operation<target_operation>(m_turns, toi)
&& is_included::apply(op))
{
continue;
start_operations.push_back(toi);
}
}

if (start_operations.empty())
{
return;
}

std::sort(start_operations.begin(), start_operations.end(),
[&](auto const& a, auto const& b)
{
auto const& op_a = m_turns[a.turn_index].operations[a.operation_index];
auto const& op_b = m_turns[b.turn_index].operations[b.operation_index];
// Sort by preference index, then by operation index
return std::tie(op_a.preference_index, a.operation_index)
< std::tie(op_b.preference_index, b.operation_index);
});

auto const source_node_id = get_node_id(m_turns, turn_index);
auto const turn_indices = get_turn_indices_by_node_id(m_turns, m_clusters,
source_node_id, allow_closed);

for (const auto& toi : start_operations)
{
auto const& op = turn.operations[toi.operation_index];
auto const component_id = op.enriched.component_id;
auto const target_nodes = get_target_nodes<target_operation>(m_turns, m_clusters,
turn_indices, component_id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ struct turn_operation
operation_type operation{operation_none};
segment_identifier seg_id;
segment_ratio_type fraction;

// Preference index can be used to sort operations which are otherwise equivalent.
// It is used by dissolve overlays, where operation is often set to operation_union
// but the preference index is used to determine which operation to use first.
std::size_t preference_index{0};
};


Expand Down
Loading
Loading