Add data structure to allow identification of via-way turns during creation of edge-based-graph

initial version of handling via-way turn restrictions (this is dirty)

 - requires update of data structures
 - requires clean-up
 - requires optimisation
This commit is contained in:
Moritz Kobitzsch
2017-07-11 14:22:28 +02:00
parent b1809d1667
commit 8d0202d240
17 changed files with 833 additions and 353 deletions
+332 -120
View File
@@ -46,15 +46,13 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
CompressedEdgeContainer &compressed_edge_container,
const std::unordered_set<NodeID> &barrier_nodes,
const std::unordered_set<NodeID> &traffic_lights,
std::shared_ptr<const RestrictionMap> restriction_map,
const std::vector<util::Coordinate> &coordinates,
const extractor::PackedOSMIDs &osm_node_ids,
ProfileProperties profile_properties,
const util::NameTable &name_table,
guidance::LaneDescriptionMap &lane_description_map)
: m_max_edge_id(0), m_coordinates(coordinates), m_osm_node_ids(osm_node_ids),
m_node_based_graph(std::move(node_based_graph)),
m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes),
: m_number_of_edge_based_nodes(0), m_coordinates(coordinates), m_osm_node_ids(osm_node_ids),
m_node_based_graph(std::move(node_based_graph)), m_barrier_nodes(barrier_nodes),
m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
profile_properties(std::move(profile_properties)), name_table(name_table),
lane_description_map(lane_description_map)
@@ -93,7 +91,10 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &out
swap(m_edge_based_node_weights, output_node_weights);
}
EdgeID EdgeBasedGraphFactory::GetHighestEdgeID() { return m_max_edge_id; }
std::uint64_t EdgeBasedGraphFactory::GetNumberOfEdgeBasedNodes() const
{
return m_number_of_edge_based_nodes;
}
NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeID node_v)
{
@@ -176,8 +177,8 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
current_edge_target_coordinate_id,
i);
m_edge_based_node_is_startpoint.push_back(forward_data.startpoint ||
reverse_data.startpoint);
m_edge_based_node_is_startpoint.push_back(
(forward_data.startpoint || reverse_data.startpoint));
current_edge_source_coordinate_id = current_edge_target_coordinate_id;
}
@@ -192,15 +193,17 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
const std::string &turn_weight_penalties_filename,
const std::string &turn_duration_penalties_filename,
const std::string &turn_penalties_index_filename,
const std::string &cnbg_ebg_mapping_path)
const std::string &cnbg_ebg_mapping_path,
const RestrictionMap &restriction_map,
const WayRestrictionMap &way_restriction_map)
{
TIMER_START(renumber);
m_max_edge_id = RenumberEdges() - 1;
m_number_of_edge_based_nodes = RenumberEdges() + way_restriction_map.NumberOfDuplicatedNodes();
TIMER_STOP(renumber);
TIMER_START(generate_nodes);
{
auto mapping = GenerateEdgeExpandedNodes();
auto mapping = GenerateEdgeExpandedNodes(way_restriction_map);
files::writeNBGMapping(cnbg_ebg_mapping_path, mapping);
}
TIMER_STOP(generate_nodes);
@@ -211,7 +214,9 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
turn_lane_data_filename,
turn_weight_penalties_filename,
turn_duration_penalties_filename,
turn_penalties_index_filename);
turn_penalties_index_filename,
restriction_map,
way_restriction_map);
TIMER_STOP(generate_edges);
@@ -257,14 +262,19 @@ unsigned EdgeBasedGraphFactory::RenumberEdges()
}
/// Creates the nodes in the edge expanded graph from edges in the node-based graph.
std::vector<NBGToEBG> EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
std::vector<NBGToEBG>
EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_restriction_map)
{
std::vector<NBGToEBG> mapping;
// Allocate memory for edge-based nodes
m_edge_based_node_container = EdgeBasedNodeDataContainer(m_max_edge_id + 1);
// In addition to the normal edges, allocate enough space for copied edges from
// via-way-restrictions
m_edge_based_node_container = EdgeBasedNodeDataContainer(m_number_of_edge_based_nodes);
util::Log() << "Generating edge expanded nodes ... ";
// indicating a normal node within the edge-based graph. This node represents an edge in the
// node-based graph
{
util::UnbufferedLog log;
util::Percent progress(log, m_node_based_graph->GetNumberOfNodes());
@@ -305,10 +315,55 @@ std::vector<NBGToEBG> EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
}
}
BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size());
BOOST_ASSERT(m_max_edge_id + 1 == m_edge_based_node_weights.size());
util::Log() << "Expanding via-way turn restrictions ... ";
// Add copies of the nodes
{
util::UnbufferedLog log;
const auto via_ways = way_restriction_map.DuplicatedNodeRepresentatives();
util::Percent progress(log, via_ways.size());
util::Log() << "Generated " << (m_max_edge_id + 1) << " nodes and "
NodeID edge_based_node_id =
NodeID(m_number_of_edge_based_nodes - way_restriction_map.NumberOfDuplicatedNodes());
std::size_t progress_counter = 0;
// allocate enough space for the mapping
for (const auto way : via_ways)
{
const auto node_u = way.from;
const auto node_v = way.to;
// we know that the edge exists as non-reversed edge
const auto eid = m_node_based_graph->FindEdge(node_u, node_v);
BOOST_ASSERT(m_node_based_graph->GetEdgeData(eid).edge_id != SPECIAL_NODEID);
// merge edges together into one EdgeBasedNode
BOOST_ASSERT(node_u != SPECIAL_NODEID);
BOOST_ASSERT(node_v != SPECIAL_NODEID);
// find node in the edge based graph, we only require one id:
const EdgeData &edge_data = m_node_based_graph->GetEdgeData(eid);
// what is this ID all about? :(
BOOST_ASSERT(edge_data.edge_id != SPECIAL_NODEID);
BOOST_ASSERT(edge_data.edge_id < m_edge_based_node_container.Size());
m_edge_based_node_container.SetData(
edge_based_node_id,
// fetch the known geometry ID
m_edge_based_node_container.GetGeometryID(static_cast<NodeID>(edge_data.edge_id)),
edge_data.name_id,
edge_data.travel_mode,
edge_data.classes);
m_edge_based_node_weights.push_back(m_edge_based_node_weights[eid]);
edge_based_node_id++;
progress.PrintStatus(progress_counter++);
}
}
BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size());
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_weights.size());
util::Log() << "Generated " << m_number_of_edge_based_nodes << " nodes and "
<< m_edge_based_node_segments.size() << " segments in edge-expanded graph";
return mapping;
@@ -321,7 +376,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const std::string &turn_lane_data_filename,
const std::string &turn_weight_penalties_filename,
const std::string &turn_duration_penalties_filename,
const std::string &turn_penalties_index_filename)
const std::string &turn_penalties_index_filename,
const RestrictionMap &restriction_map,
const WayRestrictionMap &way_restriction_map)
{
util::Log() << "Generating edge-expanded edges ";
@@ -342,7 +399,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
SuffixTable street_name_suffix_table(scripting_environment);
guidance::TurnAnalysis turn_analysis(*m_node_based_graph,
m_coordinates,
*m_restriction_map,
restriction_map,
m_barrier_nodes,
m_compressed_edge_container,
name_table,
@@ -410,7 +467,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// appended to the various output arrays/files by the `output_stage`.
struct IntersectionData
{
std::size_t nodes_processed = 0;
std::vector<lookup::TurnIndexBlock> turn_indexes;
std::vector<EdgeBasedEdge> edges_list;
std::vector<TurnPenalty> turn_weight_penalties;
@@ -418,13 +474,138 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
std::vector<TurnData> turn_data_container;
};
// same as IntersectionData, but grouped with edge to allow sorting after creating. Edges
// are 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;
};
// add into delayed data
const auto delayed_inserter = [](const auto &edge_with_data, auto &buffer) {
buffer.delayed_data.push_back(edge_with_data);
};
// add into main data
const auto continuous_inserter = [](const auto &edge_with_data, auto &buffer) {
buffer.continuous_data.edges_list.push_back(edge_with_data.edge);
buffer.continuous_data.turn_indexes.push_back(edge_with_data.turn_index);
buffer.continuous_data.turn_weight_penalties.push_back(
edge_with_data.turn_weight_penalty);
buffer.continuous_data.turn_duration_penalties.push_back(
edge_with_data.turn_duration_penalty);
buffer.continuous_data.turn_data_container.push_back(edge_with_data.turn_data);
};
// Generate edges for either artificial nodes or the main graph
const auto generate_edges = [this, &scripting_environment, weight_multiplier](
// what nodes will be used? In most cases this will be the id stored in the edge_data.
// In case of duplicated nodes (e.g. due to via-way restrictions), one/both of these
// might refer to a newly added edge based node
const auto edge_based_node_from,
const auto edge_based_node_to,
// the situation of the turn
const auto node_along_road_entering,
const auto node_based_edge_from,
const auto node_at_center_of_intersection,
const auto node_based_edge_to,
const auto &intersection,
const auto &turn,
const auto entry_class_id,
// we require a sorted output, additional nodes are collected and added after the
// sorting is done Here we can specify how/where to add the data
auto inserter,
auto &output_buffer) {
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(node_based_edge_from);
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(node_based_edge_to);
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
BOOST_ASSERT(!edge_data1.reversed);
BOOST_ASSERT(!edge_data2.reversed);
// the following is the core of the loop.
TurnData turn_data = {turn.instruction,
turn.lane_data_id,
entry_class_id,
util::guidance::TurnBearing(intersection[0].bearing),
util::guidance::TurnBearing(turn.bearing)};
// compute weight and duration penalties
auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection);
ExtractionTurn extracted_turn(turn, is_traffic_light);
extracted_turn.source_restricted = edge_data1.restricted;
extracted_turn.target_restricted = edge_data2.restricted;
scripting_environment.ProcessTurn(extracted_turn);
// turn penalties are limited to [-2^15, 2^15) which roughly
// translates to 54 minutes and fits signed 16bit deci-seconds
auto weight_penalty =
boost::numeric_cast<TurnPenalty>(extracted_turn.weight * weight_multiplier);
auto duration_penalty = boost::numeric_cast<TurnPenalty>(extracted_turn.duration * 10.);
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id);
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
// auto turn_id = m_edge_based_edge_list.size();
auto weight = boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
auto duration = boost::numeric_cast<EdgeWeight>(edge_data1.duration + duration_penalty);
EdgeBasedEdge edge_based_edge = {
edge_based_node_from,
edge_based_node_to,
SPECIAL_NODEID, // This will be updated once the main loop
// completes!
weight,
duration,
true,
false};
// We write out the mapping between the edge-expanded edges and
// the original nodes. Since each edge represents a possible
// maneuver, external programs can use this to quickly perform updates to edge
// weights in order to penalize certain turns.
// If this edge is 'trivial' -- where the compressed edge
// corresponds exactly to an original OSM segment -- we can pull the turn's
// preceding node ID directly with `node_along_road_entering`;
// otherwise, we need to look up the node immediately preceding the turn
// from the compressed edge container.
const bool isTrivial = m_compressed_edge_container.IsTrivial(node_based_edge_from);
const auto &from_node =
isTrivial ? node_along_road_entering
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
const auto &via_node =
m_compressed_edge_container.GetLastEdgeTargetID(node_based_edge_from);
const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
lookup::TurnIndexBlock turn_index_block = {from_node, via_node, to_node};
// insert data into the designated buffer
inserter(
EdgeWithData{
edge_based_edge, turn_index_block, weight_penalty, duration_penalty, turn_data},
output_buffer);
};
// Second part of the pipeline is where the intersection analysis is done for
// each intersection
tbb::filter_t<tbb::blocked_range<NodeID>, std::shared_ptr<IntersectionData>>
processor_stage(
tbb::filter_t<tbb::blocked_range<NodeID>, std::shared_ptr<PipelineBuffer>> processor_stage(
tbb::filter::parallel, [&](const tbb::blocked_range<NodeID> &intersection_node_range) {
auto buffer = std::make_shared<IntersectionData>();
auto buffer = std::make_shared<PipelineBuffer>();
buffer->nodes_processed =
intersection_node_range.end() - intersection_node_range.begin();
@@ -514,92 +695,105 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
bearing_class_by_node_based_node[node_at_center_of_intersection] =
bearing_class_id;
// check if we are turning off a via way
const auto turning_off_via_way = way_restriction_map.IsViaWay(
node_along_road_entering, node_at_center_of_intersection);
for (const auto &turn : intersection)
{
// only keep valid turns
if (!turn.entry_allowed)
continue;
// only add an edge if turn is not prohibited
const EdgeData &edge_data1 =
m_node_based_graph->GetEdgeData(incoming_edge);
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
BOOST_ASSERT(!edge_data1.reversed);
BOOST_ASSERT(!edge_data2.reversed);
// the following is the core of the loop.
buffer->turn_data_container.push_back(
{turn.instruction,
turn.lane_data_id,
entry_class_id,
util::guidance::TurnBearing(intersection[0].bearing),
util::guidance::TurnBearing(turn.bearing)});
// compute weight and duration penalties
auto is_traffic_light =
m_traffic_lights.count(node_at_center_of_intersection);
ExtractionTurn extracted_turn(turn, is_traffic_light);
extracted_turn.source_restricted = edge_data1.restricted;
extracted_turn.target_restricted = edge_data2.restricted;
scripting_environment.ProcessTurn(extracted_turn);
// turn penalties are limited to [-2^15, 2^15) which roughly
// translates to 54 minutes and fits signed 16bit deci-seconds
auto weight_penalty = boost::numeric_cast<TurnPenalty>(
extracted_turn.weight * weight_multiplier);
auto duration_penalty =
boost::numeric_cast<TurnPenalty>(extracted_turn.duration * 10.);
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id);
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
// auto turn_id = m_edge_based_edge_list.size();
auto weight =
boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
auto duration = boost::numeric_cast<EdgeWeight>(edge_data1.duration +
duration_penalty);
buffer->edges_list.emplace_back(
edge_data1.edge_id,
// 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
// edge_data2.edge_id 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(
edge_data2.edge_id,
SPECIAL_NODEID, // This will be updated once the main loop
// completes!
weight,
duration,
true,
false);
node_along_road_entering,
node_at_center_of_intersection,
m_node_based_graph->GetTarget(turn.eid),
m_number_of_edge_based_nodes);
BOOST_ASSERT(buffer->turn_weight_penalties.size() ==
buffer->edges_list.size() - 1);
buffer->turn_weight_penalties.push_back(weight_penalty);
BOOST_ASSERT(buffer->turn_duration_penalties.size() ==
buffer->edges_list.size() - 1);
buffer->turn_duration_penalties.push_back(duration_penalty);
generate_edges(edge_data1.edge_id,
target_id,
node_along_road_entering,
incoming_edge,
node_at_center_of_intersection,
turn.eid,
intersection,
turn,
entry_class_id,
continuous_inserter,
*buffer);
// We write out the mapping between the edge-expanded edges and the
// original nodes. Since each edge represents a possible maneuver,
// external programs can use this to quickly perform updates to edge
// weights in order to penalize certain turns.
// 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(
node_along_road_entering, node_at_center_of_intersection);
// If this edge is 'trivial' -- where the compressed edge corresponds
// exactly to an original OSM segment -- we can pull the turn's
// preceding node ID directly with `node_along_road_entering`;
// otherwise, we need to look up the node immediately preceding the turn
// from the compressed edge container.
const bool isTrivial =
m_compressed_edge_container.IsTrivial(incoming_edge);
// 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
const auto add_unrestricted_turns =
[&](const auto duplicated_node_id) {
const auto from_id =
m_number_of_edge_based_nodes -
way_restriction_map.NumberOfDuplicatedNodes() +
duplicated_node_id;
const auto &from_node =
isTrivial ? node_along_road_entering
: m_compressed_edge_container.GetLastEdgeSourceID(
incoming_edge);
const auto &via_node =
m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge);
const auto &to_node =
m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
auto const node_at_end_of_turn =
m_node_based_graph->GetTarget(turn.eid);
buffer->turn_indexes.push_back({from_node, via_node, to_node});
const auto is_restricted = way_restriction_map.IsRestricted(
duplicated_node_id, node_at_end_of_turn);
if (is_restricted)
return;
generate_edges(
NodeID(from_id),
m_node_based_graph->GetEdgeData(turn.eid).edge_id,
node_along_road_entering,
incoming_edge,
node_at_center_of_intersection,
turn.eid,
intersection,
turn,
entry_class_id,
delayed_inserter,
*buffer);
};
std::for_each(duplicated_nodes.begin(),
duplicated_nodes.end(),
add_unrestricted_turns);
}
}
}
}
@@ -616,36 +810,42 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
std::vector<lookup::TurnIndexBlock> turn_indexes_write_buffer;
turn_indexes_write_buffer.reserve(TURN_INDEX_WRITE_BUFFER_SIZE);
// Last part of the pipeline puts all the calculated data into the serial buffers
tbb::filter_t<std::shared_ptr<IntersectionData>, void> output_stage(
tbb::filter::serial_in_order, [&](const std::shared_ptr<IntersectionData> buffer) {
std::vector<EdgeWithData> delayed_data;
auto const append_data_to_output = [&](IntersectionData const &data) {
// 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());
// Buffer writes to reduce syscall count
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
{
turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(),
turn_indexes_write_buffer.size());
turn_indexes_write_buffer.clear();
}
};
// Last part of the pipeline puts all the calculated data into the serial buffers
tbb::filter_t<std::shared_ptr<PipelineBuffer>, void> output_stage(
tbb::filter::serial_in_order, [&](const std::shared_ptr<PipelineBuffer> buffer) {
nodes_completed += buffer->nodes_processed;
progress.PrintStatus(nodes_completed);
// NOTE: potential overflow here if we hit 2^32 routable edges
m_edge_based_edge_list.append(buffer->edges_list.begin(), buffer->edges_list.end());
BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
turn_weight_penalties.insert(turn_weight_penalties.end(),
buffer->turn_weight_penalties.begin(),
buffer->turn_weight_penalties.end());
turn_duration_penalties.insert(turn_duration_penalties.end(),
buffer->turn_duration_penalties.begin(),
buffer->turn_duration_penalties.end());
turn_data_container.append(buffer->turn_data_container);
turn_indexes_write_buffer.insert(turn_indexes_write_buffer.end(),
buffer->turn_indexes.begin(),
buffer->turn_indexes.end());
// Buffer writes to reduce syscall count
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
{
turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(),
turn_indexes_write_buffer.size());
turn_indexes_write_buffer.clear();
}
append_data_to_output(buffer->continuous_data);
delayed_data.insert(
delayed_data.end(), buffer->delayed_data.begin(), buffer->delayed_data.end());
});
// Now, execute the pipeline. The value of "5" here was chosen by experimentation
@@ -656,6 +856,18 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5,
generator_stage & processor_stage & output_stage);
std::sort(delayed_data.begin(), delayed_data.end(), [](auto const &lhs, auto const &rhs) {
return lhs.edge.source < rhs.edge.source;
});
auto const transfer_data = [&](auto const &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_data_container.push_back(edge_with_data.turn_data);
turn_indexes_write_buffer.push_back(edge_with_data.turn_index);
};
std::for_each(delayed_data.begin(), delayed_data.end(), transfer_data);
// Flush the turn_indexes_write_buffer if it's not empty
if (!turn_indexes_write_buffer.empty())
{
@@ -715,7 +927,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
util::Log() << " contains " << m_edge_based_edge_list.size() << " edges";
util::Log() << " skips " << restricted_turns_counter << " turns, "
"defined by "
<< m_restriction_map->size() << " restrictions";
<< restriction_map.size() << " restrictions";
util::Log() << " skips " << skipped_uturns_counter << " U turns";
util::Log() << " skips " << skipped_barrier_turns_counter << " turns over barriers";
}
+33 -33
View File
@@ -668,16 +668,16 @@ void ExtractionContainers::PrepareRestrictions()
if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION)
{
const auto &way = turn_restriction.AsWayRestriction();
referenced_ways[OSMWayID{way.from}] = dummy_segment;
referenced_ways[OSMWayID{way.to}] = dummy_segment;
referenced_ways[OSMWayID{way.via}] = dummy_segment;
referenced_ways[way.from] = dummy_segment;
referenced_ways[way.to] = dummy_segment;
referenced_ways[way.via] = dummy_segment;
}
else
{
BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION);
const auto &node = turn_restriction.AsNodeRestriction();
referenced_ways[OSMWayID{node.from}] = dummy_segment;
referenced_ways[OSMWayID{node.to}] = dummy_segment;
referenced_ways[node.from] = dummy_segment;
referenced_ways[node.to] = dummy_segment;
}
};
@@ -702,7 +702,7 @@ void ExtractionContainers::PrepareRestrictions()
auto const to_internal = [&](auto const osm_node) {
auto internal = mapExternalToInternalNodeID(
used_node_id_list.begin(), used_node_id_list.end(), OSMNodeID{osm_node});
used_node_id_list.begin(), used_node_id_list.end(), osm_node);
if (internal == SPECIAL_NODEID)
{
util::Log(logDEBUG) << "Restriction references invalid node: " << osm_node;
@@ -763,15 +763,15 @@ void ExtractionContainers::PrepareRestrictions()
// be connected at a single location)
auto const get_node_restriction_from_OSM_ids = [&](
auto const from_id, auto const to_id, const OSMNodeID via_node = MAX_OSM_NODEID) {
auto const from_segment_itr = referenced_ways.find(OSMWayID{from_id});
if (from_segment_itr->second.way_id != OSMWayID{from_id})
auto const from_segment_itr = referenced_ways.find(from_id);
if (from_segment_itr->second.way_id != from_id)
{
util::Log(logDEBUG) << "Restriction references invalid way: " << from_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
auto const to_segment_itr = referenced_ways.find(OSMWayID{to_id});
if (to_segment_itr->second.way_id != OSMWayID{to_id})
auto const to_segment_itr = referenced_ways.find(to_id);
if (to_segment_itr->second.way_id != to_id)
{
util::Log(logDEBUG) << "Restriction references invalid way: " << to_id;
return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
@@ -781,7 +781,7 @@ void ExtractionContainers::PrepareRestrictions()
// transform an OSMRestriction (based on WayIDs) into an OSRM restriction (base on NodeIDs)
// returns true on successful transformation, false in case of invalid references
const auto transform = [&](auto const &external_type, auto &internal_type) {
const auto transform = [&](const auto &external_type, auto &internal_type) {
if (external_type.Type() == RestrictionType::WAY_RESTRICTION)
{
auto const &external = external_type.AsWayRestriction();
@@ -808,12 +808,11 @@ void ExtractionContainers::PrepareRestrictions()
auto const via_node = to_internal(external.via);
// check if we were able to resolve all the involved ways
auto restriction = get_node_restriction_from_OSM_ids(
external.from, external.to, OSMNodeID{external.via});
auto restriction =
get_node_restriction_from_OSM_ids(external.from, external.to, external.via);
if (!restriction.Valid())
{
std::cout << " >>> Invalid" << std::endl;
return false;
}
@@ -830,25 +829,26 @@ void ExtractionContainers::PrepareRestrictions()
// wrapper function to handle distinction between conditional and unconditional turn
// restrictions
const auto transform_into_internal_types = [&](auto &external_restriction) {
// unconditional restriction
if (external_restriction.condition.empty())
{
TurnRestriction restriction;
restriction.flags = external_restriction.flags;
if (transform(external_restriction, restriction))
unconditional_turn_restrictions.push_back(restriction);
}
// conditional turn restriction
else
{
ConditionalTurnRestriction restriction;
restriction.flags = external_restriction.flags;
restriction.condition = std::move(external_restriction.condition);
if (transform(external_restriction, restriction))
conditional_turn_restrictions.push_back(restriction);
}
};
const auto transform_into_internal_types =
[&](const InputConditionalTurnRestriction &external_restriction) {
// unconditional restriction
if (external_restriction.condition.empty())
{
TurnRestriction restriction;
restriction.is_only = external_restriction.is_only;
if (transform(external_restriction, restriction))
unconditional_turn_restrictions.push_back(restriction);
}
// conditional turn restriction
else
{
ConditionalTurnRestriction restriction;
restriction.is_only = external_restriction.is_only;
restriction.condition = std::move(external_restriction.condition);
if (transform(external_restriction, restriction))
conditional_turn_restrictions.push_back(restriction);
}
};
// Transforming the restrictions into the dedicated internal types
{
+32 -22
View File
@@ -24,6 +24,7 @@
#include "extractor/compressed_edge_container.hpp"
#include "extractor/restriction_map.hpp"
#include "extractor/way_restriction_map.hpp"
#include "util/static_graph.hpp"
#include "util/static_rtree.hpp"
@@ -140,7 +141,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
turn_lane_map);
auto number_of_node_based_nodes = graph_size.first;
auto max_edge_id = graph_size.second;
auto max_edge_id = graph_size.second - 1;
TIMER_STOP(expansion);
@@ -470,26 +471,36 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
util::NameTable name_table(config.GetPath(".osrm.names").string());
auto restriction_map = std::make_shared<RestrictionMap>(turn_restrictions);
EdgeBasedGraphFactory edge_based_graph_factory(
node_based_graph,
compressed_edge_container,
barrier_nodes,
traffic_lights,
std::const_pointer_cast<RestrictionMap const>(restriction_map),
coordinates,
osm_node_ids,
scripting_environment.GetProfileProperties(),
name_table,
turn_lane_map);
EdgeBasedGraphFactory edge_based_graph_factory(node_based_graph,
compressed_edge_container,
barrier_nodes,
traffic_lights,
coordinates,
osm_node_ids,
scripting_environment.GetProfileProperties(),
name_table,
turn_lane_map);
edge_based_graph_factory.Run(scripting_environment,
config.GetPath(".osrm.edges").string(),
config.GetPath(".osrm.tld").string(),
config.GetPath(".osrm.turn_weight_penalties").string(),
config.GetPath(".osrm.turn_duration_penalties").string(),
config.GetPath(".osrm.turn_penalties_index").string(),
config.GetPath(".osrm.cnbg_to_ebg").string());
const auto create_edge_based_edges = [&]() {
// scoped to relase intermediate datastructures right after the call
RestrictionMap via_node_restriction_map(turn_restrictions);
WayRestrictionMap via_way_restriction_map(turn_restrictions);
turn_restrictions.clear();
turn_restrictions.shrink_to_fit();
edge_based_graph_factory.Run(scripting_environment,
config.GetPath(".osrm.edges").string(),
config.GetPath(".osrm.tld").string(),
config.GetPath(".osrm.turn_weight_penalties").string(),
config.GetPath(".osrm.turn_duration_penalties").string(),
config.GetPath(".osrm.turn_penalties_index").string(),
config.GetPath(".osrm.cnbg_to_ebg").string(),
via_node_restriction_map,
via_way_restriction_map);
return edge_based_graph_factory.GetNumberOfEdgeBasedNodes();
};
const auto number_of_edge_based_nodes = create_edge_based_edges();
compressed_edge_container.PrintStatistics();
@@ -531,7 +542,6 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments);
edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint);
edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights);
auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID();
const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes();
@@ -545,7 +555,7 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
TIMER_STOP(write_intersections);
util::Log() << "ok, after " << TIMER_SEC(write_intersections) << "s";
return std::make_pair(number_of_node_based_nodes, max_edge_id);
return std::make_pair(number_of_node_based_nodes, number_of_edge_based_nodes);
}
/**
+2 -30
View File
@@ -47,7 +47,7 @@ RestrictionMap::RestrictionMap(const std::vector<TurnRestriction> &restriction_l
{
continue;
}
else if (restriction.flags.is_only)
else if (restriction.is_only)
{
// We are going to insert an is_only_*-restriction. There can be only one.
m_count -= m_restriction_bucket_list.at(index).size();
@@ -55,8 +55,7 @@ RestrictionMap::RestrictionMap(const std::vector<TurnRestriction> &restriction_l
}
}
++m_count;
m_restriction_bucket_list.at(index).emplace_back(node_restriction.to,
restriction.flags.is_only);
m_restriction_bucket_list.at(index).emplace_back(node_restriction.to, restriction.is_only);
}
}
@@ -65,33 +64,6 @@ bool RestrictionMap::IsViaNode(const NodeID node) const
return m_no_turn_via_node_set.find(node) != m_no_turn_via_node_set.end();
}
// Replaces start edge (v, w) with (u, w). Only start node changes.
void RestrictionMap::FixupStartingTurnRestriction(const NodeID node_u,
const NodeID node_v,
const NodeID node_w)
{
BOOST_ASSERT(node_u != SPECIAL_NODEID);
BOOST_ASSERT(node_v != SPECIAL_NODEID);
BOOST_ASSERT(node_w != SPECIAL_NODEID);
if (!IsSourceNode(node_v))
{
return;
}
const auto restriction_iterator = m_restriction_map.find({node_v, node_w});
if (restriction_iterator != m_restriction_map.end())
{
const unsigned index = restriction_iterator->second;
// remove old restriction start (v,w)
m_restriction_map.erase(restriction_iterator);
m_restriction_start_nodes.emplace(node_u);
// insert new restriction start (u,w) (pointing to index)
RestrictionSource new_source = {node_u, node_w};
m_restriction_map.emplace(new_source, index);
}
}
// Check if edge (u, v) is the start of any turn restriction.
// If so returns id of first target node.
NodeID RestrictionMap::CheckForEmanatingIsOnlyTurn(const NodeID node_u, const NodeID node_v) const
+5 -3
View File
@@ -125,7 +125,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
// we pretend every restriction is a conditional restriction. If we do not find any restriction,
// we can trim away the vector after parsing
InputConditionalTurnRestriction restriction_container;
restriction_container.flags.is_only = is_only_restriction;
restriction_container.is_only = is_only_restriction;
boost::optional<std::uint64_t> from = boost::none, via = boost::none, to = boost::none;
bool is_node_restriction;
@@ -212,11 +212,13 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
{
if (is_node_restriction)
{
restriction_container.node_or_way = InputNodeRestriction{*from, *via, *to};
// template struct requires bracket for ID initialisation :(
restriction_container.node_or_way = InputNodeRestriction{{*from}, {*via}, {*to}};
}
else
{
restriction_container.node_or_way = InputWayRestriction{*from, *via, *to};
// template struct requires bracket for ID initialisation :(
restriction_container.node_or_way = InputWayRestriction{{*from}, {*via}, {*to}};
}
return restriction_container;
}
+199
View File
@@ -0,0 +1,199 @@
#include "extractor/way_restriction_map.hpp"
#include <iterator>
#include <tuple>
#include <utility>
namespace osrm
{
namespace extractor
{
namespace
{
struct FindViaWay
{
bool operator()(const std::tuple<NodeID, NodeID> value,
const TurnRestriction &restriction) const
{
const auto &way = restriction.AsWayRestriction();
return value < std::tie(way.in_restriction.via, way.out_restriction.via);
}
bool operator()(const TurnRestriction &restriction,
const std::tuple<NodeID, NodeID> value) const
{
const auto &way = restriction.AsWayRestriction();
return std::tie(way.in_restriction.via, way.out_restriction.via) < value;
}
};
} // namespace
WayRestrictionMap::WayRestrictionMap(const std::vector<TurnRestriction> &turn_restrictions)
{
// get all way restrictions
const auto extract_restrictions = [this](const auto &turn_restriction) {
if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION)
{
const auto &way = turn_restriction.AsWayRestriction();
// so far we can only handle restrictions that are not interrupted
if (way.in_restriction.via == way.out_restriction.from &&
way.in_restriction.to == way.out_restriction.via)
restriction_data.push_back(turn_restriction);
}
};
std::for_each(turn_restrictions.begin(), turn_restrictions.end(), extract_restrictions);
const auto as_duplicated_node =
[](auto const &restriction) -> std::tuple<NodeID, NodeID, NodeID> {
auto &way = restriction.AsWayRestriction();
// group restrictions by the via-way. On same via-ways group by from
return std::make_tuple(
way.in_restriction.via, way.out_restriction.via, way.in_restriction.from);
};
const auto by_duplicated_node = [&](auto const &lhs, auto const &rhs) {
return as_duplicated_node(lhs) < as_duplicated_node(rhs);
};
std::sort(restriction_data.begin(), restriction_data.end(), by_duplicated_node);
std::size_t index = 0, duplication_id = 0;
// map all way restrictions into access containers
const auto prepare_way_restriction = [this, &index, &duplication_id, as_duplicated_node](
const auto &restriction) {
const auto &way = restriction.AsWayRestriction();
restriction_starts.insert(
std::make_pair(std::make_pair(way.in_restriction.from, way.in_restriction.via), index));
++index;
};
std::for_each(restriction_data.begin(), restriction_data.end(), prepare_way_restriction);
std::size_t offset = 1;
// the first group starts at 0
if (!restriction_data.empty())
duplicated_node_groups.push_back(0);
auto const add_offset_on_new_groups = [&](auto const &lhs, auto const &rhs) {
BOOST_ASSERT(rhs == restriction_data[offset]);
// add a new lower bound for rhs
if (as_duplicated_node(lhs) != as_duplicated_node(rhs))
duplicated_node_groups.push_back(offset);
++offset;
return false; // continue until the end
};
std::adjacent_find(restriction_data.begin(), restriction_data.end(), add_offset_on_new_groups);
duplicated_node_groups.push_back(restriction_data.size());
}
std::size_t WayRestrictionMap::NumberOfDuplicatedNodes() const
{
return duplicated_node_groups.size() - 1;
}
bool WayRestrictionMap::IsViaWay(const NodeID from, const NodeID to) const
{
// safe-guards
if (restriction_data.empty())
return false;
const auto itr = std::lower_bound(
restriction_data.begin(), restriction_data.end(), std::make_tuple(from, to), FindViaWay());
// no fitting restriction
if (itr == restriction_data.end())
return false;
const auto &way = itr->AsWayRestriction();
return way.out_restriction.from == from && way.out_restriction.via == to;
}
std::size_t WayRestrictionMap::AsDuplicatedNodeID(const std::size_t restriction_id) const
{
return std::distance(duplicated_node_groups.begin(),
std::upper_bound(duplicated_node_groups.begin(),
duplicated_node_groups.end(),
restriction_id)) -
1;
}
util::range<std::size_t> WayRestrictionMap::DuplicatedNodeIDs(const NodeID from,
const NodeID to) const
{
const auto duplicated_node_range_itr = std::equal_range(
restriction_data.begin(), restriction_data.end(), std::make_tuple(from, to), FindViaWay());
const auto as_restriction_id = [this](const auto itr) {
return std::distance(restriction_data.begin(), itr);
};
return util::irange<std::size_t>(
AsDuplicatedNodeID(as_restriction_id(duplicated_node_range_itr.first)),
AsDuplicatedNodeID(as_restriction_id(duplicated_node_range_itr.second)));
}
bool WayRestrictionMap::IsRestricted(std::size_t duplicated_node, const NodeID to) const
{
// loop over all restrictions associated with the node. Mark as restricted based on
// is_only/restricted targets
for (std::size_t restriction_index = duplicated_node_groups[duplicated_node];
restriction_index != duplicated_node_groups[duplicated_node + 1];
++restriction_index)
{
const auto &restriction = restriction_data[restriction_index];
const auto &way = restriction.AsWayRestriction();
if (restriction.is_only)
return way.out_restriction.to != to;
else if (to == way.out_restriction.to)
return true;
}
return false;
}
TurnRestriction const &WayRestrictionMap::GetRestriction(const std::size_t id) const
{
return restriction_data[id];
}
std::vector<WayRestrictionMap::ViaWay> WayRestrictionMap::DuplicatedNodeRepresentatives() const
{
std::vector<ViaWay> result;
result.reserve(NumberOfDuplicatedNodes());
std::transform(duplicated_node_groups.begin(),
duplicated_node_groups.end() - 1,
std::back_inserter(result),
[&](auto const representative_id) -> ViaWay {
auto &way = restriction_data[representative_id].AsWayRestriction();
return {representative_id, way.in_restriction.via, way.out_restriction.via};
});
return result;
}
NodeID WayRestrictionMap::RemapIfRestricted(const NodeID edge_based_node,
const NodeID node_based_from,
const NodeID node_based_via,
const NodeID node_based_to,
const NodeID number_of_edge_based_nodes) const
{
auto range = restriction_starts.equal_range(std::make_pair(node_based_from, node_based_via));
// returns true if the ID saved in an iterator belongs to a turn restriction that references
// node_based_to as destination of the `in_restriction`
const auto restriction_targets_to = [node_based_to, this](const auto &pair) {
return restriction_data[pair.second].AsWayRestriction().in_restriction.to == node_based_to;
};
const auto itr = std::find_if(range.first, range.second, restriction_targets_to);
// in case we found a matching restriction, we can remap the edge_based_node
if (itr != range.second)
return number_of_edge_based_nodes - NumberOfDuplicatedNodes() +
AsDuplicatedNodeID(itr->second);
else
return edge_based_node;
}
} // namespace extractor
} // namespace osrm
+1 -1
View File
@@ -511,7 +511,7 @@ updateConditionalTurns(const UpdaterConfig &config,
// only add restrictions to the lookups if the restriction is valid now
if (node_or_way.flags.is_only)
if (node_or_way.is_only)
{
is_only_lookup.lookup.push_back({std::make_tuple(c.from, c.via), c.to});
}