Duplicate edges and turns generation code
This commit is contained in:
parent
1cbfbd34cf
commit
90ff725125
@ -55,6 +55,9 @@ template <> struct hash<std::pair<NodeID, NodeID>>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buffer size of turn_indexes_write_buffer to reduce number of write(v) syscals
|
||||||
|
const constexpr int TURN_INDEX_WRITE_BUFFER_SIZE = 1000;
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
namespace extractor
|
namespace extractor
|
||||||
@ -483,8 +486,53 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
|
|
||||||
const NodeID node_count = m_node_based_graph.GetNumberOfNodes();
|
const NodeID node_count = m_node_based_graph.GetNumberOfNodes();
|
||||||
util::Percent progress(log, node_count);
|
util::Percent progress(log, node_count);
|
||||||
// This counter is used to keep track of how far along we've made it
|
|
||||||
std::uint64_t nodes_completed = 0;
|
// Because we write TurnIndexBlock data as we go, we'll
|
||||||
|
// buffer them into groups of 1000 to reduce the syscall
|
||||||
|
// count by 1000x. This doesn't need much memory, but
|
||||||
|
// greatly reduces the syscall overhead of writing lots
|
||||||
|
// of small objects
|
||||||
|
std::vector<lookup::TurnIndexBlock> turn_indexes_write_buffer;
|
||||||
|
turn_indexes_write_buffer.reserve(TURN_INDEX_WRITE_BUFFER_SIZE);
|
||||||
|
|
||||||
|
// TODO: update comments block
|
||||||
|
// This struct is the buffered output of the `processor_stage`. This data is
|
||||||
|
// appended to the various output arrays/files by the `output_stage`.
|
||||||
|
// same as IntersectionData, but grouped with edge to allow sorting after creating. Edges
|
||||||
|
// can be out of order
|
||||||
|
struct EdgeWithData
|
||||||
|
{
|
||||||
|
EdgeBasedEdge edge;
|
||||||
|
lookup::TurnIndexBlock turn_index;
|
||||||
|
TurnPenalty turn_weight_penalty;
|
||||||
|
TurnPenalty turn_duration_penalty;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto const transfer_data = [&](const EdgeWithData &edge_with_data) {
|
||||||
|
m_edge_based_edge_list.push_back(edge_with_data.edge);
|
||||||
|
turn_weight_penalties.push_back(edge_with_data.turn_weight_penalty);
|
||||||
|
turn_duration_penalties.push_back(edge_with_data.turn_duration_penalty);
|
||||||
|
turn_indexes_write_buffer.push_back(edge_with_data.turn_index);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EdgesPipelineBuffer
|
||||||
|
{
|
||||||
|
std::size_t nodes_processed = 0;
|
||||||
|
|
||||||
|
std::vector<EdgeWithData> continuous_data;
|
||||||
|
std::vector<EdgeWithData> delayed_data;
|
||||||
|
std::vector<Conditional> conditionals;
|
||||||
|
};
|
||||||
|
using EdgesPipelineBufferPtr = std::shared_ptr<EdgesPipelineBuffer>;
|
||||||
|
|
||||||
|
struct TurnsPipelineBuffer
|
||||||
|
{
|
||||||
|
std::size_t nodes_processed = 0;
|
||||||
|
|
||||||
|
std::vector<TurnData> continuous_turn_data;
|
||||||
|
std::vector<TurnData> delayed_turn_data;
|
||||||
|
};
|
||||||
|
using TurnsPipelineBufferPtr = std::shared_ptr<TurnsPipelineBuffer>;
|
||||||
|
|
||||||
// going over all nodes (which form the center of an intersection), we compute all possible
|
// going over all nodes (which form the center of an intersection), we compute all possible
|
||||||
// turns along these intersections.
|
// turns along these intersections.
|
||||||
@ -499,7 +547,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
// First part of the pipeline generates iterator ranges of IDs in sets of
|
// First part of the pipeline generates iterator ranges of IDs in sets of
|
||||||
// GRAINSIZE
|
// GRAINSIZE
|
||||||
tbb::filter_t<void, tbb::blocked_range<NodeID>> generator_stage(
|
tbb::filter_t<void, tbb::blocked_range<NodeID>> generator_stage(
|
||||||
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) -> tbb::blocked_range<NodeID> {
|
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) {
|
||||||
if (current_node < node_count)
|
if (current_node < node_count)
|
||||||
{
|
{
|
||||||
auto next_node = std::min(current_node + GRAINSIZE, node_count);
|
auto next_node = std::min(current_node + GRAINSIZE, node_count);
|
||||||
@ -514,36 +562,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// This struct is the buffered output of the `processor_stage`. This data is appended to
|
|
||||||
// the various output arrays/files by the `output_stage`.
|
|
||||||
struct IntersectionData
|
|
||||||
{
|
|
||||||
std::vector<lookup::TurnIndexBlock> turn_indexes;
|
|
||||||
std::vector<EdgeBasedEdge> edges_list;
|
|
||||||
std::vector<TurnPenalty> turn_weight_penalties;
|
|
||||||
std::vector<TurnPenalty> turn_duration_penalties;
|
|
||||||
std::vector<TurnData> turn_data_container;
|
|
||||||
};
|
|
||||||
|
|
||||||
// same as IntersectionData, but grouped with edge to allow sorting after creating. Edges
|
|
||||||
// can be out of order
|
|
||||||
struct EdgeWithData
|
|
||||||
{
|
|
||||||
EdgeBasedEdge edge;
|
|
||||||
lookup::TurnIndexBlock turn_index;
|
|
||||||
TurnPenalty turn_weight_penalty;
|
|
||||||
TurnPenalty turn_duration_penalty;
|
|
||||||
TurnData turn_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PipelineBuffer
|
|
||||||
{
|
|
||||||
std::size_t nodes_processed = 0;
|
|
||||||
IntersectionData continuous_data;
|
|
||||||
std::vector<EdgeWithData> delayed_data;
|
|
||||||
std::vector<Conditional> conditionals;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate edges for either artificial nodes or the main graph
|
// Generate edges for either artificial nodes or the main graph
|
||||||
const auto generate_edge = [this,
|
const auto generate_edge = [this,
|
||||||
&scripting_environment,
|
&scripting_environment,
|
||||||
@ -680,24 +698,20 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node};
|
lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node};
|
||||||
|
|
||||||
// insert data into the designated buffer
|
// insert data into the designated buffer
|
||||||
return std::make_pair(
|
return std::make_tuple(
|
||||||
EdgeWithData{
|
EdgeWithData{edge_based_edge, turn_index_block, weight_penalty, duration_penalty},
|
||||||
edge_based_edge, turn_index_block, weight_penalty, duration_penalty, turn_data},
|
turn_data,
|
||||||
conditional);
|
conditional);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Second part of the pipeline is where the intersection analysis is done for each
|
//
|
||||||
// intersection
|
// Edge-based-graph stage
|
||||||
tbb::filter_t<tbb::blocked_range<NodeID>, std::shared_ptr<PipelineBuffer>> processor_stage(
|
//
|
||||||
|
tbb::filter_t<tbb::blocked_range<NodeID>, EdgesPipelineBufferPtr> processor_stage(
|
||||||
tbb::filter::parallel, [&](const tbb::blocked_range<NodeID> &intersection_node_range) {
|
tbb::filter::parallel, [&](const tbb::blocked_range<NodeID> &intersection_node_range) {
|
||||||
|
|
||||||
auto buffer = std::make_shared<PipelineBuffer>();
|
auto buffer = std::make_shared<EdgesPipelineBuffer>();
|
||||||
buffer->nodes_processed =
|
buffer->nodes_processed = intersection_node_range.size();
|
||||||
intersection_node_range.end() - intersection_node_range.begin();
|
|
||||||
|
|
||||||
// If we get fed a 0-length range for some reason, we can just return right away
|
|
||||||
if (buffer->nodes_processed == 0)
|
|
||||||
return buffer;
|
|
||||||
|
|
||||||
for (auto intersection_node = intersection_node_range.begin(),
|
for (auto intersection_node = intersection_node_range.begin(),
|
||||||
end = intersection_node_range.end();
|
end = intersection_node_range.end();
|
||||||
@ -916,20 +930,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
entry_class_id,
|
entry_class_id,
|
||||||
edge_geometries);
|
edge_geometries);
|
||||||
|
|
||||||
buffer->continuous_data.edges_list.push_back(
|
buffer->continuous_data.push_back(
|
||||||
edge_with_data_and_condition.first.edge);
|
std::get<0>(edge_with_data_and_condition));
|
||||||
buffer->continuous_data.turn_indexes.push_back(
|
if (std::get<2>(edge_with_data_and_condition))
|
||||||
edge_with_data_and_condition.first.turn_index);
|
|
||||||
buffer->continuous_data.turn_weight_penalties.push_back(
|
|
||||||
edge_with_data_and_condition.first.turn_weight_penalty);
|
|
||||||
buffer->continuous_data.turn_duration_penalties.push_back(
|
|
||||||
edge_with_data_and_condition.first.turn_duration_penalty);
|
|
||||||
buffer->continuous_data.turn_data_container.push_back(
|
|
||||||
edge_with_data_and_condition.first.turn_data);
|
|
||||||
if (edge_with_data_and_condition.second)
|
|
||||||
{
|
{
|
||||||
buffer->conditionals.push_back(
|
buffer->conditionals.push_back(
|
||||||
*edge_with_data_and_condition.second);
|
*std::get<2>(edge_with_data_and_condition));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -982,12 +988,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
edge_geometries);
|
edge_geometries);
|
||||||
|
|
||||||
buffer->delayed_data.push_back(
|
buffer->delayed_data.push_back(
|
||||||
std::move(edge_with_data_and_condition.first));
|
std::get<0>(edge_with_data_and_condition));
|
||||||
|
if (std::get<2>(edge_with_data_and_condition))
|
||||||
if (edge_with_data_and_condition.second)
|
|
||||||
{
|
{
|
||||||
buffer->conditionals.push_back(
|
buffer->conditionals.push_back(
|
||||||
*edge_with_data_and_condition.second);
|
*std::get<2>(edge_with_data_and_condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
// also add the conditions for the way
|
// also add the conditions for the way
|
||||||
@ -1019,12 +1024,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
edge_geometries);
|
edge_geometries);
|
||||||
|
|
||||||
buffer->delayed_data.push_back(
|
buffer->delayed_data.push_back(
|
||||||
std::move(edge_with_data_and_condition.first));
|
std::get<0>(edge_with_data_and_condition));
|
||||||
|
if (std::get<2>(edge_with_data_and_condition))
|
||||||
if (edge_with_data_and_condition.second)
|
|
||||||
{
|
{
|
||||||
buffer->conditionals.push_back(
|
buffer->conditionals.push_back(
|
||||||
*edge_with_data_and_condition.second);
|
*std::get<2>(edge_with_data_and_condition));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1036,42 +1040,334 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
return buffer;
|
return buffer;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Because we write TurnIndexBlock data as we go, we'll buffer them into groups of 1000 to
|
//
|
||||||
// reduce the syscall count by 1000x. This doesn't need much memory, but greatly reduces
|
// Guidance stage
|
||||||
// the syscall overhead of writing lots of small objects
|
//
|
||||||
const constexpr int TURN_INDEX_WRITE_BUFFER_SIZE = 1000;
|
tbb::filter_t<tbb::blocked_range<NodeID>, TurnsPipelineBufferPtr> guidance_stage(
|
||||||
std::vector<lookup::TurnIndexBlock> turn_indexes_write_buffer;
|
tbb::filter::parallel, [&](const tbb::blocked_range<NodeID> &intersection_node_range) {
|
||||||
turn_indexes_write_buffer.reserve(TURN_INDEX_WRITE_BUFFER_SIZE);
|
|
||||||
|
|
||||||
std::vector<EdgeWithData> delayed_data;
|
auto buffer = std::make_shared<TurnsPipelineBuffer>();
|
||||||
|
buffer->nodes_processed = intersection_node_range.size();
|
||||||
|
|
||||||
|
for (auto intersection_node = intersection_node_range.begin(),
|
||||||
|
end = intersection_node_range.end();
|
||||||
|
intersection_node < end;
|
||||||
|
++intersection_node)
|
||||||
|
{
|
||||||
|
// We capture the thread-local work in these objects, then flush
|
||||||
|
// them in a controlled manner at the end of the parallel range
|
||||||
|
const auto &incoming_edges =
|
||||||
|
intersection::getIncomingEdges(m_node_based_graph, intersection_node);
|
||||||
|
const auto &outgoing_edges =
|
||||||
|
intersection::getOutgoingEdges(m_node_based_graph, intersection_node);
|
||||||
|
const auto &edge_geometries_and_merged_edges =
|
||||||
|
intersection::getIntersectionGeometries(m_node_based_graph,
|
||||||
|
m_compressed_edge_container,
|
||||||
|
m_coordinates,
|
||||||
|
mergable_road_detector,
|
||||||
|
intersection_node);
|
||||||
|
const auto &edge_geometries = edge_geometries_and_merged_edges.first;
|
||||||
|
const auto &merged_edge_ids = edge_geometries_and_merged_edges.second;
|
||||||
|
|
||||||
|
// all nodes in the graph are connected in both directions. We check all
|
||||||
|
// outgoing nodes to find the incoming edge. This is a larger search overhead,
|
||||||
|
// but the cost we need to pay to generate edges here is worth the additional
|
||||||
|
// search overhead.
|
||||||
|
//
|
||||||
|
// a -> b <-> c
|
||||||
|
// |
|
||||||
|
// v
|
||||||
|
// d
|
||||||
|
//
|
||||||
|
// will have:
|
||||||
|
// a: b,rev=0
|
||||||
|
// b: a,rev=1 c,rev=0 d,rev=0
|
||||||
|
// c: b,rev=0
|
||||||
|
//
|
||||||
|
// From the flags alone, we cannot determine which nodes are connected to
|
||||||
|
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
|
||||||
|
// edges entering `b`
|
||||||
|
|
||||||
|
for (const auto &incoming_edge : incoming_edges)
|
||||||
|
{
|
||||||
|
++node_based_edge_counter;
|
||||||
|
|
||||||
|
const auto intersection_view =
|
||||||
|
convertToIntersectionView(m_node_based_graph,
|
||||||
|
m_edge_based_node_container,
|
||||||
|
node_restriction_map,
|
||||||
|
m_barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edges,
|
||||||
|
merged_edge_ids);
|
||||||
|
|
||||||
|
auto intersection = turn_analysis.AssignTurnTypes(
|
||||||
|
incoming_edge.node, incoming_edge.edge, intersection_view);
|
||||||
|
|
||||||
|
OSRM_ASSERT(intersection.valid(), m_coordinates[intersection_node]);
|
||||||
|
intersection = turn_lane_handler.assignTurnLanes(
|
||||||
|
incoming_edge.node, incoming_edge.edge, std::move(intersection));
|
||||||
|
|
||||||
|
// the entry class depends on the turn, so we have to classify the
|
||||||
|
// interesction for every edge
|
||||||
|
const auto turn_classification =
|
||||||
|
classifyIntersection(intersection, m_coordinates[intersection_node]);
|
||||||
|
|
||||||
|
const auto entry_class_id =
|
||||||
|
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
|
||||||
|
|
||||||
|
const auto bearing_class_id =
|
||||||
|
bearing_class_hash.ConcurrentFindOrAdd(turn_classification.second);
|
||||||
|
|
||||||
|
// Note - this is strictly speaking not thread safe, but we know we
|
||||||
|
// should never be touching the same element twice, so we should
|
||||||
|
// be fine.
|
||||||
|
bearing_class_by_node_based_node[intersection_node] = bearing_class_id;
|
||||||
|
|
||||||
|
// check if we are turning off a via way
|
||||||
|
const auto turning_off_via_way =
|
||||||
|
way_restriction_map.IsViaWay(incoming_edge.node, intersection_node);
|
||||||
|
|
||||||
|
// Save reversed incoming bearing to compute turn angles
|
||||||
|
const auto reversed_incoming_bearing = util::bearing::reverse(
|
||||||
|
findEdgeBearing(edge_geometries, incoming_edge.edge));
|
||||||
|
|
||||||
|
for (const auto &outgoing_edge : outgoing_edges)
|
||||||
|
{
|
||||||
|
if (!intersection::isTurnAllowed(m_node_based_graph,
|
||||||
|
m_edge_based_node_container,
|
||||||
|
node_restriction_map,
|
||||||
|
m_barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edge))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto turn =
|
||||||
|
std::find_if(intersection.begin(),
|
||||||
|
intersection.end(),
|
||||||
|
[edge = outgoing_edge.edge](const auto &road) {
|
||||||
|
return road.eid == edge;
|
||||||
|
});
|
||||||
|
BOOST_ASSERT(turn != intersection.end());
|
||||||
|
|
||||||
|
std::vector<ExtractionTurnLeg> road_legs_on_the_right;
|
||||||
|
std::vector<ExtractionTurnLeg> road_legs_on_the_left;
|
||||||
|
|
||||||
|
auto get_connected_road_info = [&](const auto &connected_edge) {
|
||||||
|
const auto &edge_data =
|
||||||
|
m_node_based_graph.GetEdgeData(connected_edge.eid);
|
||||||
|
return ExtractionTurnLeg(
|
||||||
|
edge_data.flags.restricted,
|
||||||
|
edge_data.flags.road_classification.IsMotorwayClass(),
|
||||||
|
edge_data.flags.road_classification.IsLinkClass(),
|
||||||
|
edge_data.flags.road_classification.GetNumberOfLanes(),
|
||||||
|
edge_data.flags.highway_turn_classification,
|
||||||
|
edge_data.flags.access_turn_classification,
|
||||||
|
((double)intersection::findEdgeLength(edge_geometries,
|
||||||
|
connected_edge.eid) /
|
||||||
|
edge_data.duration) *
|
||||||
|
36,
|
||||||
|
!connected_edge.entry_allowed ||
|
||||||
|
(edge_data.flags.forward &&
|
||||||
|
edge_data.flags.backward), // is incoming
|
||||||
|
connected_edge.entry_allowed);
|
||||||
|
};
|
||||||
|
|
||||||
|
// all connected roads on the right of a u turn
|
||||||
|
if (turn->instruction.IsUTurn())
|
||||||
|
{
|
||||||
|
if (turn != intersection.begin())
|
||||||
|
{
|
||||||
|
std::transform(intersection.begin() + 1,
|
||||||
|
turn,
|
||||||
|
std::back_inserter(road_legs_on_the_right),
|
||||||
|
get_connected_road_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::transform(turn + 1,
|
||||||
|
intersection.end(),
|
||||||
|
std::back_inserter(road_legs_on_the_right),
|
||||||
|
get_connected_road_info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (intersection.begin() != turn)
|
||||||
|
{
|
||||||
|
std::transform(intersection.begin() + 1,
|
||||||
|
turn,
|
||||||
|
std::back_inserter(road_legs_on_the_right),
|
||||||
|
get_connected_road_info);
|
||||||
|
}
|
||||||
|
std::transform(turn + 1,
|
||||||
|
intersection.end(),
|
||||||
|
std::back_inserter(road_legs_on_the_left),
|
||||||
|
get_connected_road_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turn->instruction.IsUTurn() && turn != intersection.begin())
|
||||||
|
{
|
||||||
|
util::Log(logWARNING)
|
||||||
|
<< "Turn is a u turn but not turning to the first connected "
|
||||||
|
"edge of the intersection. Node ID: "
|
||||||
|
<< intersection_node << ", OSM link: "
|
||||||
|
<< m_coordinates[intersection_node].toOSMLink();
|
||||||
|
}
|
||||||
|
else if (turn == intersection.begin() && !turn->instruction.IsUTurn())
|
||||||
|
{
|
||||||
|
util::Log(logWARNING)
|
||||||
|
<< "Turn is a u turn but not classified as a u turn. Node ID: "
|
||||||
|
<< intersection_node << ", OSM link: "
|
||||||
|
<< m_coordinates[intersection_node].toOSMLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case a way restriction starts at a given location, add a turn onto
|
||||||
|
// every artificial node eminating here.
|
||||||
|
//
|
||||||
|
// e - f
|
||||||
|
// |
|
||||||
|
// a - b
|
||||||
|
// |
|
||||||
|
// c - d
|
||||||
|
//
|
||||||
|
// ab via bc to cd
|
||||||
|
// ab via be to ef
|
||||||
|
//
|
||||||
|
// has two artifical nodes (be/bc) with restrictions starting at `ab`.
|
||||||
|
// Since every restriction group (abc | abe) refers to the same
|
||||||
|
// artificial node, we simply have to find a single representative for
|
||||||
|
// the turn. Here we check whether the turn in question is the start of
|
||||||
|
// a via way restriction. If that should be the case, we switch
|
||||||
|
// the id of the edge-based-node for the target to the ID of the
|
||||||
|
// duplicated node associated with the turn. (e.g. ab via bc switches bc
|
||||||
|
// to bc_dup)
|
||||||
|
auto const target_id = way_restriction_map.RemapIfRestricted(
|
||||||
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
|
incoming_edge.node,
|
||||||
|
outgoing_edge.node,
|
||||||
|
m_node_based_graph.GetTarget(outgoing_edge.edge),
|
||||||
|
m_number_of_edge_based_nodes);
|
||||||
|
|
||||||
|
{ // scope to forget edge_with_data after
|
||||||
|
const auto edge_with_data_and_condition =
|
||||||
|
generate_edge(nbe_to_ebn_mapping[incoming_edge.edge],
|
||||||
|
target_id,
|
||||||
|
incoming_edge.node,
|
||||||
|
incoming_edge.edge,
|
||||||
|
outgoing_edge.node,
|
||||||
|
outgoing_edge.edge,
|
||||||
|
reversed_incoming_bearing,
|
||||||
|
*turn,
|
||||||
|
road_legs_on_the_right,
|
||||||
|
road_legs_on_the_left,
|
||||||
|
entry_class_id,
|
||||||
|
edge_geometries);
|
||||||
|
|
||||||
|
buffer->continuous_turn_data.push_back(
|
||||||
|
std::get<1>(edge_with_data_and_condition));
|
||||||
|
}
|
||||||
|
|
||||||
|
// when turning off a a via-way turn restriction, we need to not only
|
||||||
|
// handle the normal edges for the way, but also add turns for every
|
||||||
|
// duplicated node. This process is integrated here to avoid doing the
|
||||||
|
// turn analysis multiple times.
|
||||||
|
if (turning_off_via_way)
|
||||||
|
{
|
||||||
|
const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs(
|
||||||
|
incoming_edge.node, intersection_node);
|
||||||
|
|
||||||
|
// next to the normal restrictions tracked in `entry_allowed`, via
|
||||||
|
// ways might introduce additional restrictions. These are handled
|
||||||
|
// here when turning off a via-way
|
||||||
|
for (auto duplicated_node_id : duplicated_nodes)
|
||||||
|
{
|
||||||
|
const auto from_id =
|
||||||
|
NodeID(m_number_of_edge_based_nodes -
|
||||||
|
way_restriction_map.NumberOfDuplicatedNodes() +
|
||||||
|
duplicated_node_id);
|
||||||
|
|
||||||
|
auto const node_at_end_of_turn =
|
||||||
|
m_node_based_graph.GetTarget(outgoing_edge.edge);
|
||||||
|
|
||||||
|
const auto is_way_restricted = way_restriction_map.IsRestricted(
|
||||||
|
duplicated_node_id, node_at_end_of_turn);
|
||||||
|
|
||||||
|
if (is_way_restricted)
|
||||||
|
{
|
||||||
|
auto const restriction = way_restriction_map.GetRestriction(
|
||||||
|
duplicated_node_id, node_at_end_of_turn);
|
||||||
|
|
||||||
|
if (restriction.condition.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// add into delayed data
|
||||||
|
auto edge_with_data_and_condition =
|
||||||
|
generate_edge(from_id,
|
||||||
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
|
incoming_edge.node,
|
||||||
|
incoming_edge.edge,
|
||||||
|
outgoing_edge.node,
|
||||||
|
outgoing_edge.edge,
|
||||||
|
reversed_incoming_bearing,
|
||||||
|
*turn,
|
||||||
|
road_legs_on_the_right,
|
||||||
|
road_legs_on_the_left,
|
||||||
|
entry_class_id,
|
||||||
|
edge_geometries);
|
||||||
|
|
||||||
|
buffer->delayed_turn_data.push_back(
|
||||||
|
std::get<1>(edge_with_data_and_condition));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto edge_with_data_and_condition =
|
||||||
|
generate_edge(from_id,
|
||||||
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
|
incoming_edge.node,
|
||||||
|
incoming_edge.edge,
|
||||||
|
outgoing_edge.node,
|
||||||
|
outgoing_edge.edge,
|
||||||
|
reversed_incoming_bearing,
|
||||||
|
*turn,
|
||||||
|
road_legs_on_the_right,
|
||||||
|
road_legs_on_the_left,
|
||||||
|
entry_class_id,
|
||||||
|
edge_geometries);
|
||||||
|
|
||||||
|
buffer->delayed_turn_data.push_back(
|
||||||
|
std::get<1>(edge_with_data_and_condition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
});
|
||||||
|
|
||||||
// Last part of the pipeline puts all the calculated data into the serial buffers
|
// Last part of the pipeline puts all the calculated data into the serial buffers
|
||||||
tbb::filter_t<std::shared_ptr<PipelineBuffer>, void> output_stage(
|
std::uint64_t routing_nodes_completed = 0;
|
||||||
tbb::filter::serial_in_order, [&](const std::shared_ptr<PipelineBuffer> buffer) {
|
std::vector<EdgeWithData> delayed_data;
|
||||||
nodes_completed += buffer->nodes_processed;
|
tbb::filter_t<EdgesPipelineBufferPtr, void> output_stage(
|
||||||
progress.PrintStatus(nodes_completed);
|
tbb::filter::serial_in_order, [&](auto buffer) {
|
||||||
|
|
||||||
// for readability
|
routing_nodes_completed += buffer->nodes_processed;
|
||||||
const auto &data = buffer->continuous_data;
|
progress.PrintStatus(routing_nodes_completed);
|
||||||
// NOTE: potential overflow here if we hit 2^32 routable edges
|
|
||||||
m_edge_based_edge_list.append(data.edges_list.begin(), data.edges_list.end());
|
|
||||||
|
|
||||||
BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
|
|
||||||
|
|
||||||
turn_weight_penalties.insert(turn_weight_penalties.end(),
|
|
||||||
data.turn_weight_penalties.begin(),
|
|
||||||
data.turn_weight_penalties.end());
|
|
||||||
turn_duration_penalties.insert(turn_duration_penalties.end(),
|
|
||||||
data.turn_duration_penalties.begin(),
|
|
||||||
data.turn_duration_penalties.end());
|
|
||||||
turn_data_container.append(data.turn_data_container);
|
|
||||||
turn_indexes_write_buffer.insert(turn_indexes_write_buffer.end(),
|
|
||||||
data.turn_indexes.begin(),
|
|
||||||
data.turn_indexes.end());
|
|
||||||
|
|
||||||
|
// Copy data from local buffers into global
|
||||||
|
// EBG data
|
||||||
|
std::for_each(
|
||||||
|
buffer->continuous_data.begin(), buffer->continuous_data.end(), transfer_data);
|
||||||
conditionals.insert(
|
conditionals.insert(
|
||||||
conditionals.end(), buffer->conditionals.begin(), buffer->conditionals.end());
|
conditionals.end(), buffer->conditionals.begin(), buffer->conditionals.end());
|
||||||
|
|
||||||
|
// NOTE: potential overflow here if we hit 2^32 routable edges
|
||||||
|
BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
|
||||||
|
|
||||||
// Buffer writes to reduce syscall count
|
// Buffer writes to reduce syscall count
|
||||||
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
|
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
|
||||||
{
|
{
|
||||||
@ -1080,29 +1376,62 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
turn_indexes_write_buffer.clear();
|
turn_indexes_write_buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy via-way restrictions delayed data
|
||||||
delayed_data.insert(
|
delayed_data.insert(
|
||||||
delayed_data.end(), buffer->delayed_data.begin(), buffer->delayed_data.end());
|
delayed_data.end(), buffer->delayed_data.begin(), buffer->delayed_data.end());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Now, execute the pipeline. The value of "5" here was chosen by experimentation on a
|
// Last part of the pipeline puts all the calculated data into the serial buffers
|
||||||
// 16-CPU machine and seemed to give the best performance. This value needs to be balanced
|
std::uint64_t guidance_nodes_completed = 0;
|
||||||
// with the GRAINSIZE above - ideally, the pipeline puts as much work as possible in the
|
std::vector<TurnData> delayed_turn_data;
|
||||||
// `intersection_handler` step so that those parallel workers don't get blocked too much by
|
|
||||||
// the slower (io-performing) `buffer_storage`
|
tbb::filter_t<TurnsPipelineBufferPtr, void> guidance_output_stage(
|
||||||
|
tbb::filter::serial_in_order, [&](auto buffer) {
|
||||||
|
|
||||||
|
guidance_nodes_completed += buffer->nodes_processed;
|
||||||
|
progress.PrintStatus(guidance_nodes_completed);
|
||||||
|
|
||||||
|
// Guidance data
|
||||||
|
std::for_each(buffer->continuous_turn_data.begin(),
|
||||||
|
buffer->continuous_turn_data.end(),
|
||||||
|
[&turn_data_container](const auto &turn_data) {
|
||||||
|
turn_data_container.push_back(turn_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy via-way restrictions delayed data
|
||||||
|
delayed_turn_data.insert(delayed_turn_data.end(),
|
||||||
|
buffer->delayed_turn_data.begin(),
|
||||||
|
buffer->delayed_turn_data.end());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now, execute the pipeline. The value of "5" here was chosen by experimentation
|
||||||
|
// on a 16-CPU machine and seemed to give the best performance. This value needs
|
||||||
|
// to be balanced with the GRAINSIZE above - ideally, the pipeline puts as much work
|
||||||
|
// as possible in the `intersection_handler` step so that those parallel workers
|
||||||
|
// don't
|
||||||
|
// get blocked too much by the slower (io-performing) `buffer_storage`
|
||||||
|
current_node = 0;
|
||||||
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5,
|
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5,
|
||||||
generator_stage & processor_stage & output_stage);
|
generator_stage & processor_stage & output_stage);
|
||||||
|
|
||||||
std::sort(delayed_data.begin(), delayed_data.end(), [](auto const &lhs, auto const &rhs) {
|
current_node = 0;
|
||||||
return lhs.edge.source < rhs.edge.source;
|
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5,
|
||||||
});
|
generator_stage & guidance_stage & guidance_output_stage);
|
||||||
auto const transfer_data = [&](auto const &edge_with_data) {
|
|
||||||
m_edge_based_edge_list.push_back(edge_with_data.edge);
|
// TODO: remove sorting below
|
||||||
turn_weight_penalties.push_back(edge_with_data.turn_weight_penalty);
|
// std::sort(delayed_data.begin(), delayed_data.end(), [](auto const &lhs, auto
|
||||||
turn_duration_penalties.push_back(edge_with_data.turn_duration_penalty);
|
// const &rhs)
|
||||||
turn_data_container.push_back(edge_with_data.turn_data);
|
// {
|
||||||
turn_indexes_write_buffer.push_back(edge_with_data.turn_index);
|
// return lhs.edge.source < rhs.edge.source;
|
||||||
};
|
// });
|
||||||
|
|
||||||
|
// NOTE: buffer.delayed_data and buffer.delayed_turn_data have the same index
|
||||||
std::for_each(delayed_data.begin(), delayed_data.end(), transfer_data);
|
std::for_each(delayed_data.begin(), delayed_data.end(), transfer_data);
|
||||||
|
std::for_each(delayed_turn_data.begin(),
|
||||||
|
delayed_turn_data.end(),
|
||||||
|
[&turn_data_container](const auto &turn_data) {
|
||||||
|
turn_data_container.push_back(turn_data);
|
||||||
|
});
|
||||||
|
|
||||||
// Flush the turn_indexes_write_buffer if it's not empty
|
// Flush the turn_indexes_write_buffer if it's not empty
|
||||||
if (!turn_indexes_write_buffer.empty())
|
if (!turn_indexes_write_buffer.empty())
|
||||||
|
Loading…
Reference in New Issue
Block a user