simplify passing annotation data through OSRM pipeline using the node-based datastore
- separates node-based graph creation and compression from edge-based graph creation - moves usage of edge-based node data-container to pre-processing as well, unifying access to node-based data - single struct instead of separate vectors for annotation data in engine (single place of modification)
This commit is contained in:
committed by
Michael Krasnyk
parent
9b044aaa42
commit
2ddd98ee6d
@@ -264,6 +264,9 @@ void CompressedEdgeContainer::InitializeBothwayVector()
|
||||
|
||||
unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID r_edge_id)
|
||||
{
|
||||
if (!segment_data)
|
||||
InitializeBothwayVector();
|
||||
|
||||
const auto &forward_bucket = GetBucketReference(f_edge_id);
|
||||
const auto &reverse_bucket = GetBucketReference(r_edge_id);
|
||||
|
||||
|
||||
@@ -60,19 +60,18 @@ namespace extractor
|
||||
|
||||
// Configuration to find representative candidate for turn angle calculations
|
||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
std::shared_ptr<util::NodeBasedDynamicGraph> node_based_graph,
|
||||
CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
EdgeBasedNodeDataContainer &node_data_container,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
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_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),
|
||||
: m_edge_based_node_container(node_data_container), m_number_of_edge_based_nodes(0),
|
||||
m_coordinates(coordinates), 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), name_table(name_table),
|
||||
lane_description_map(lane_description_map)
|
||||
{
|
||||
}
|
||||
@@ -85,12 +84,6 @@ void EdgeBasedGraphFactory::GetEdgeBasedEdges(
|
||||
swap(m_edge_based_edge_list, output_edge_list);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodes(EdgeBasedNodeDataContainer &data_container)
|
||||
{
|
||||
using std::swap; // Koenig swap
|
||||
swap(data_container, m_edge_based_node_container);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodeSegments(std::vector<EdgeBasedNodeSegment> &nodes)
|
||||
{
|
||||
using std::swap; // Koenig swap
|
||||
@@ -121,21 +114,23 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
|
||||
BOOST_ASSERT(node_v != SPECIAL_NODEID);
|
||||
|
||||
// find forward edge id and
|
||||
const EdgeID edge_id_1 = m_node_based_graph->FindEdge(node_u, node_v);
|
||||
const EdgeID edge_id_1 = m_node_based_graph.FindEdge(node_u, node_v);
|
||||
BOOST_ASSERT(edge_id_1 != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData &forward_data = m_node_based_graph->GetEdgeData(edge_id_1);
|
||||
const EdgeData &forward_data = m_node_based_graph.GetEdgeData(edge_id_1);
|
||||
|
||||
// find reverse edge id and
|
||||
const EdgeID edge_id_2 = m_node_based_graph->FindEdge(node_v, node_u);
|
||||
const EdgeID edge_id_2 = m_node_based_graph.FindEdge(node_v, node_u);
|
||||
BOOST_ASSERT(edge_id_2 != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData &reverse_data = m_node_based_graph->GetEdgeData(edge_id_2);
|
||||
const EdgeData &reverse_data = m_node_based_graph.GetEdgeData(edge_id_2);
|
||||
|
||||
BOOST_ASSERT(forward_data.edge_id != SPECIAL_NODEID || reverse_data.edge_id != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID ||
|
||||
nbe_to_ebn_mapping[edge_id_2] != SPECIAL_NODEID);
|
||||
|
||||
if (forward_data.edge_id != SPECIAL_NODEID && reverse_data.edge_id == SPECIAL_NODEID)
|
||||
m_edge_based_node_weights[forward_data.edge_id] = INVALID_EDGE_WEIGHT;
|
||||
if (nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID &&
|
||||
nbe_to_ebn_mapping[edge_id_2] == SPECIAL_NODEID)
|
||||
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] = INVALID_EDGE_WEIGHT;
|
||||
|
||||
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) ==
|
||||
m_compressed_edge_container.HasEntryForID(edge_id_2));
|
||||
@@ -149,7 +144,8 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
|
||||
// There should always be some geometry
|
||||
BOOST_ASSERT(0 != segment_count);
|
||||
|
||||
const unsigned packed_geometry_id = m_compressed_edge_container.ZipEdges(edge_id_1, edge_id_2);
|
||||
// const unsigned packed_geometry_id = m_compressed_edge_container.ZipEdges(edge_id_1,
|
||||
// edge_id_2);
|
||||
|
||||
NodeID current_edge_source_coordinate_id = node_u;
|
||||
|
||||
@@ -163,23 +159,18 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
|
||||
};
|
||||
|
||||
// Add edge-based node data for forward and reverse nodes indexed by edge_id
|
||||
BOOST_ASSERT(forward_data.edge_id != SPECIAL_EDGEID);
|
||||
m_edge_based_node_container.SetData(forward_data.edge_id,
|
||||
GeometryID{packed_geometry_id, true},
|
||||
forward_data.name_id,
|
||||
forward_data.travel_mode,
|
||||
forward_data.classes,
|
||||
forward_data.is_left_hand_driving);
|
||||
if (reverse_data.edge_id != SPECIAL_EDGEID)
|
||||
BOOST_ASSERT(nbe_to_ebn_mapping[edge_id_1] != SPECIAL_EDGEID);
|
||||
m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_1]].geometry_id =
|
||||
forward_data.geometry_id;
|
||||
m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_1]].annotation_id =
|
||||
forward_data.annotation_data;
|
||||
if (nbe_to_ebn_mapping[edge_id_2] != SPECIAL_EDGEID)
|
||||
{
|
||||
m_edge_based_node_container.SetData(reverse_data.edge_id,
|
||||
GeometryID{packed_geometry_id, false},
|
||||
reverse_data.name_id,
|
||||
reverse_data.travel_mode,
|
||||
reverse_data.classes,
|
||||
reverse_data.is_left_hand_driving);
|
||||
m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_2]].geometry_id =
|
||||
reverse_data.geometry_id;
|
||||
m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_2]].annotation_id =
|
||||
reverse_data.annotation_data;
|
||||
}
|
||||
|
||||
// Add segments of edge-based nodes
|
||||
for (const auto i : util::irange(std::size_t{0}, segment_count))
|
||||
{
|
||||
@@ -196,20 +187,21 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
|
||||
BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id);
|
||||
|
||||
// build edges
|
||||
m_edge_based_node_segments.emplace_back(edge_id_to_segment_id(forward_data.edge_id),
|
||||
edge_id_to_segment_id(reverse_data.edge_id),
|
||||
current_edge_source_coordinate_id,
|
||||
current_edge_target_coordinate_id,
|
||||
i);
|
||||
m_edge_based_node_segments.emplace_back(
|
||||
edge_id_to_segment_id(nbe_to_ebn_mapping[edge_id_1]),
|
||||
edge_id_to_segment_id(nbe_to_ebn_mapping[edge_id_2]),
|
||||
current_edge_source_coordinate_id,
|
||||
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.flags.startpoint ||
|
||||
reverse_data.flags.startpoint);
|
||||
current_edge_source_coordinate_id = current_edge_target_coordinate_id;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(current_edge_source_coordinate_id == node_v);
|
||||
|
||||
return NBGToEBG{node_u, node_v, forward_data.edge_id, reverse_data.edge_id};
|
||||
return NBGToEBG{node_u, node_v, nbe_to_ebn_mapping[edge_id_1], nbe_to_ebn_mapping[edge_id_2]};
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
|
||||
@@ -225,9 +217,15 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
|
||||
const WayRestrictionMap &way_restriction_map)
|
||||
{
|
||||
TIMER_START(renumber);
|
||||
m_number_of_edge_based_nodes = RenumberEdges() + way_restriction_map.NumberOfDuplicatedNodes();
|
||||
m_number_of_edge_based_nodes =
|
||||
LabelEdgeBasedNodes() + way_restriction_map.NumberOfDuplicatedNodes();
|
||||
TIMER_STOP(renumber);
|
||||
|
||||
// Allocate memory for edge-based nodes
|
||||
// In addition to the normal edges, allocate enough space for copied edges from
|
||||
// via-way-restrictions, see calculation above
|
||||
m_edge_based_node_container.nodes.resize(m_number_of_edge_based_nodes);
|
||||
|
||||
TIMER_START(generate_nodes);
|
||||
{
|
||||
auto mapping = GenerateEdgeExpandedNodes(way_restriction_map);
|
||||
@@ -258,19 +256,19 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
|
||||
/// Renumbers all _forward_ edges and sets the edge_id.
|
||||
/// A specific numbering is not important. Any unique ID will do.
|
||||
/// Returns the number of edge-based nodes.
|
||||
unsigned EdgeBasedGraphFactory::RenumberEdges()
|
||||
unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
|
||||
{
|
||||
// heuristic: node-based graph node is a simple intersection with four edges (edge-based nodes)
|
||||
m_edge_based_node_weights.reserve(4 * m_node_based_graph->GetNumberOfNodes());
|
||||
m_edge_based_node_weights.reserve(4 * m_node_based_graph.GetNumberOfNodes());
|
||||
nbe_to_ebn_mapping.resize(m_node_based_graph.GetEdgeCapacity(), SPECIAL_NODEID);
|
||||
|
||||
// renumber edge based node of outgoing edges
|
||||
unsigned numbered_edges_count = 0;
|
||||
for (const auto current_node : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
for (const auto current_node : util::irange(0u, m_node_based_graph.GetNumberOfNodes()))
|
||||
{
|
||||
for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node))
|
||||
for (const auto current_edge : m_node_based_graph.GetAdjacentEdgeRange(current_node))
|
||||
{
|
||||
EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge);
|
||||
|
||||
const EdgeData &edge_data = m_node_based_graph.GetEdgeData(current_edge);
|
||||
// only number incoming edges
|
||||
if (edge_data.reversed)
|
||||
{
|
||||
@@ -279,11 +277,9 @@ unsigned EdgeBasedGraphFactory::RenumberEdges()
|
||||
|
||||
m_edge_based_node_weights.push_back(edge_data.weight);
|
||||
|
||||
BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges());
|
||||
edge_data.edge_id = numbered_edges_count;
|
||||
BOOST_ASSERT(numbered_edges_count < m_node_based_graph.GetNumberOfEdges());
|
||||
nbe_to_ebn_mapping[current_edge] = numbered_edges_count;
|
||||
++numbered_edges_count;
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data.edge_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,31 +292,25 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
|
||||
{
|
||||
std::vector<NBGToEBG> mapping;
|
||||
|
||||
// Allocate memory for edge-based nodes
|
||||
// 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());
|
||||
util::Percent progress(log, m_node_based_graph.GetNumberOfNodes());
|
||||
|
||||
m_compressed_edge_container.InitializeBothwayVector();
|
||||
// m_compressed_edge_container.InitializeBothwayVector();
|
||||
|
||||
// loop over all edges and generate new set of nodes
|
||||
for (const auto nbg_node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
for (const auto nbg_node_u : util::irange(0u, m_node_based_graph.GetNumberOfNodes()))
|
||||
{
|
||||
BOOST_ASSERT(nbg_node_u != SPECIAL_NODEID);
|
||||
progress.PrintStatus(nbg_node_u);
|
||||
for (EdgeID nbg_edge_id : m_node_based_graph->GetAdjacentEdgeRange(nbg_node_u))
|
||||
for (EdgeID nbg_edge_id : m_node_based_graph.GetAdjacentEdgeRange(nbg_node_u))
|
||||
{
|
||||
BOOST_ASSERT(nbg_edge_id != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData &nbg_edge_data = m_node_based_graph->GetEdgeData(nbg_edge_id);
|
||||
const NodeID nbg_node_v = m_node_based_graph->GetTarget(nbg_edge_id);
|
||||
const NodeID nbg_node_v = m_node_based_graph.GetTarget(nbg_edge_id);
|
||||
BOOST_ASSERT(nbg_node_v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(nbg_node_u != nbg_node_v);
|
||||
|
||||
@@ -332,7 +322,7 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
|
||||
}
|
||||
|
||||
// if we found a non-forward edge reverse and try again
|
||||
if (nbg_edge_data.edge_id == SPECIAL_NODEID)
|
||||
if (nbe_to_ebn_mapping[nbg_edge_id] == SPECIAL_NODEID)
|
||||
{
|
||||
mapping.push_back(InsertEdgeBasedNode(nbg_node_v, nbg_node_u));
|
||||
}
|
||||
@@ -360,28 +350,21 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
|
||||
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);
|
||||
const auto eid = m_node_based_graph.FindEdge(node_u, node_v);
|
||||
|
||||
BOOST_ASSERT(m_node_based_graph->GetEdgeData(eid).edge_id != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(nbe_to_ebn_mapping[eid] != 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,
|
||||
edge_data.is_left_hand_driving);
|
||||
const EdgeData &edge_data = m_node_based_graph.GetEdgeData(eid);
|
||||
// BOOST_ASSERT(edge_data.edge_id < m_edge_based_node_container.Size());
|
||||
m_edge_based_node_container.nodes[edge_based_node_id].geometry_id =
|
||||
edge_data.geometry_id;
|
||||
m_edge_based_node_container.nodes[edge_based_node_id].annotation_id =
|
||||
edge_data.annotation_data;
|
||||
|
||||
m_edge_based_node_weights.push_back(m_edge_based_node_weights[eid]);
|
||||
|
||||
@@ -431,20 +414,23 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
||||
// linear number of turns only.
|
||||
SuffixTable street_name_suffix_table(scripting_environment);
|
||||
guidance::TurnAnalysis turn_analysis(*m_node_based_graph,
|
||||
guidance::TurnAnalysis turn_analysis(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
m_coordinates,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
m_compressed_edge_container,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
profile_properties);
|
||||
street_name_suffix_table);
|
||||
|
||||
util::guidance::LaneDataIdMap lane_data_map;
|
||||
guidance::lanes::TurnLaneHandler turn_lane_handler(
|
||||
*m_node_based_graph, lane_description_map, turn_analysis, lane_data_map);
|
||||
guidance::lanes::TurnLaneHandler turn_lane_handler(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
lane_description_map,
|
||||
turn_analysis,
|
||||
lane_data_map);
|
||||
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph.GetNumberOfNodes(),
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
|
||||
// FIXME these need to be tuned in pre-allocated size
|
||||
@@ -467,7 +453,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
{
|
||||
util::UnbufferedLog log;
|
||||
|
||||
const NodeID node_count = m_node_based_graph->GetNumberOfNodes();
|
||||
const NodeID node_count = m_node_based_graph.GetNumberOfNodes();
|
||||
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;
|
||||
@@ -550,7 +536,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
const auto node_restricted = isRestricted(node_along_road_entering,
|
||||
node_at_center_of_intersection,
|
||||
m_node_based_graph->GetTarget(turn.eid),
|
||||
m_node_based_graph.GetTarget(turn.eid),
|
||||
conditional_restriction_map);
|
||||
|
||||
boost::optional<Conditional> conditional = boost::none;
|
||||
@@ -565,10 +551,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
conditions}}};
|
||||
}
|
||||
|
||||
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);
|
||||
const auto &edge_data1 = m_node_based_graph.GetEdgeData(node_based_edge_from);
|
||||
const auto &edge_data2 = m_node_based_graph.GetEdgeData(node_based_edge_to);
|
||||
|
||||
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
|
||||
BOOST_ASSERT(nbe_to_ebn_mapping[node_based_edge_from] !=
|
||||
nbe_to_ebn_mapping[node_based_edge_to]);
|
||||
BOOST_ASSERT(!edge_data1.reversed);
|
||||
BOOST_ASSERT(!edge_data2.reversed);
|
||||
|
||||
@@ -581,11 +568,13 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
// 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,
|
||||
edge_data1.restricted,
|
||||
edge_data2.restricted,
|
||||
edge_data1.is_left_hand_driving);
|
||||
ExtractionTurn extracted_turn(
|
||||
turn,
|
||||
is_traffic_light,
|
||||
edge_data1.flags.restricted,
|
||||
edge_data2.flags.restricted,
|
||||
m_edge_based_node_container.GetAnnotation(edge_data1.annotation_data)
|
||||
.is_left_hand_driving);
|
||||
scripting_environment.ProcessTurn(extracted_turn);
|
||||
|
||||
// turn penalties are limited to [-2^15, 2^15) which roughly
|
||||
@@ -594,8 +583,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
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);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != nbe_to_ebn_mapping[node_based_edge_from]);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != nbe_to_ebn_mapping[node_based_edge_to]);
|
||||
|
||||
// auto turn_id = m_edge_based_edge_list.size();
|
||||
auto weight = boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
|
||||
@@ -682,15 +671,15 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
|
||||
// edges entering `b`
|
||||
for (const EdgeID outgoing_edge :
|
||||
m_node_based_graph->GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
m_node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
const NodeID node_along_road_entering =
|
||||
m_node_based_graph->GetTarget(outgoing_edge);
|
||||
m_node_based_graph.GetTarget(outgoing_edge);
|
||||
|
||||
const auto incoming_edge = m_node_based_graph->FindEdge(
|
||||
const auto incoming_edge = m_node_based_graph.FindEdge(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
if (m_node_based_graph->GetEdgeData(incoming_edge).reversed)
|
||||
if (m_node_based_graph.GetEdgeData(incoming_edge).reversed)
|
||||
continue;
|
||||
|
||||
++node_based_edge_counter;
|
||||
@@ -743,10 +732,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
if (!turn.entry_allowed)
|
||||
continue;
|
||||
|
||||
const EdgeData &edge_data1 =
|
||||
m_node_based_graph->GetEdgeData(incoming_edge);
|
||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
|
||||
|
||||
// In case a way restriction starts at a given location, add a turn onto
|
||||
// every artificial node eminating here.
|
||||
//
|
||||
@@ -764,18 +749,19 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// 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)
|
||||
// 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(
|
||||
edge_data2.edge_id,
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
node_at_center_of_intersection,
|
||||
m_node_based_graph->GetTarget(turn.eid),
|
||||
m_node_based_graph.GetTarget(turn.eid),
|
||||
m_number_of_edge_based_nodes);
|
||||
|
||||
{ // scope to forget edge_with_data after
|
||||
const auto edge_with_data_and_condition =
|
||||
generate_edge(edge_data1.edge_id,
|
||||
generate_edge(nbe_to_ebn_mapping[incoming_edge],
|
||||
target_id,
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
@@ -822,14 +808,13 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
duplicated_node_id;
|
||||
|
||||
auto const node_at_end_of_turn =
|
||||
m_node_based_graph->GetTarget(turn.eid);
|
||||
m_node_based_graph.GetTarget(turn.eid);
|
||||
|
||||
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);
|
||||
|
||||
@@ -837,16 +822,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
continue;
|
||||
|
||||
// add into delayed data
|
||||
auto edge_with_data_and_condition = generate_edge(
|
||||
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);
|
||||
auto edge_with_data_and_condition =
|
||||
generate_edge(NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->delayed_data.push_back(
|
||||
std::move(edge_with_data_and_condition.first));
|
||||
@@ -863,7 +848,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// add a new conditional for the edge we just created
|
||||
buffer->conditionals.push_back(
|
||||
{NodeID(from_id),
|
||||
m_node_based_graph->GetEdgeData(turn.eid).edge_id,
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
{static_cast<std::uint64_t>(-1),
|
||||
m_coordinates[node_at_center_of_intersection],
|
||||
restriction.condition}});
|
||||
@@ -871,16 +856,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
}
|
||||
else
|
||||
{
|
||||
auto edge_with_data_and_condition = generate_edge(
|
||||
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);
|
||||
auto edge_with_data_and_condition =
|
||||
generate_edge(NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->delayed_data.push_back(
|
||||
std::move(edge_with_data_and_condition.first));
|
||||
|
||||
@@ -67,23 +67,26 @@ struct CmpEdgeByInternalSourceTargetAndName
|
||||
if (lhs.result.target == SPECIAL_NODEID)
|
||||
return false;
|
||||
|
||||
if (lhs.result.name_id == rhs.result.name_id)
|
||||
auto const lhs_name_id = edge_annotation_data[lhs.result.annotation_data].name_id;
|
||||
auto const rhs_name_id = edge_annotation_data[rhs.result.annotation_data].name_id;
|
||||
if (lhs_name_id == rhs_name_id)
|
||||
return false;
|
||||
|
||||
if (lhs.result.name_id == EMPTY_NAMEID)
|
||||
if (lhs_name_id == EMPTY_NAMEID)
|
||||
return false;
|
||||
|
||||
if (rhs.result.name_id == EMPTY_NAMEID)
|
||||
if (rhs_name_id == EMPTY_NAMEID)
|
||||
return true;
|
||||
|
||||
BOOST_ASSERT(!name_offsets.empty() && name_offsets.back() == name_data.size());
|
||||
const oe::ExtractionContainers::NameCharData::const_iterator data = name_data.begin();
|
||||
return std::lexicographical_compare(data + name_offsets[lhs.result.name_id],
|
||||
data + name_offsets[lhs.result.name_id + 1],
|
||||
data + name_offsets[rhs.result.name_id],
|
||||
data + name_offsets[rhs.result.name_id + 1]);
|
||||
return std::lexicographical_compare(data + name_offsets[lhs_name_id],
|
||||
data + name_offsets[lhs_name_id + 1],
|
||||
data + name_offsets[rhs_name_id],
|
||||
data + name_offsets[rhs_name_id + 1]);
|
||||
}
|
||||
|
||||
const oe::ExtractionContainers::AnnotationDataVector &edge_annotation_data;
|
||||
const oe::ExtractionContainers::NameCharData &name_data;
|
||||
const oe::ExtractionContainers::NameOffsets &name_offsets;
|
||||
};
|
||||
@@ -136,6 +139,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme
|
||||
all_nodes_list.clear(); // free all_nodes_list before allocation of normal_edges
|
||||
all_nodes_list.shrink_to_fit();
|
||||
WriteEdges(file_out);
|
||||
WriteMetadata(file_out);
|
||||
|
||||
PrepareRestrictions();
|
||||
WriteCharData(name_file_name);
|
||||
@@ -361,7 +365,7 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
util::Coordinate target_coord{node_iterator->lon, node_iterator->lat};
|
||||
|
||||
// flip source and target coordinates if segment is in backward direction only
|
||||
if (!edge_iterator->result.forward && edge_iterator->result.backward)
|
||||
if (!edge_iterator->result.flags.forward && edge_iterator->result.flags.backward)
|
||||
std::swap(source_coord, target_coord);
|
||||
|
||||
const auto distance =
|
||||
@@ -389,9 +393,9 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
std::swap(edge.source, edge.target);
|
||||
|
||||
// std::swap does not work with bit-fields
|
||||
bool temp = edge.forward;
|
||||
edge.forward = edge.backward;
|
||||
edge.backward = temp;
|
||||
bool temp = edge.flags.forward;
|
||||
edge.flags.forward = edge.flags.backward;
|
||||
edge.flags.backward = temp;
|
||||
}
|
||||
++edge_iterator;
|
||||
}
|
||||
@@ -415,7 +419,8 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
std::mutex name_data_mutex;
|
||||
tbb::parallel_sort(all_edges_list.begin(),
|
||||
all_edges_list.end(),
|
||||
CmpEdgeByInternalSourceTargetAndName{name_char_data, name_offsets});
|
||||
CmpEdgeByInternalSourceTargetAndName{
|
||||
all_edges_annotation_data_list, name_char_data, name_offsets});
|
||||
TIMER_STOP(sort_edges_by_renumbered_start);
|
||||
log << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s";
|
||||
}
|
||||
@@ -452,12 +457,12 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
{
|
||||
const auto &result = all_edges_list[i].result;
|
||||
const auto value = std::make_pair(result.weight, result.duration);
|
||||
if (result.forward && value < min_forward)
|
||||
if (result.flags.forward && value < min_forward)
|
||||
{
|
||||
min_forward_idx = i;
|
||||
min_forward = value;
|
||||
}
|
||||
if (result.backward && value < min_backward)
|
||||
if (result.flags.backward && value < min_backward)
|
||||
{
|
||||
min_backward_idx = i;
|
||||
min_backward = value;
|
||||
@@ -476,9 +481,9 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
|
||||
if (min_backward_idx == min_forward_idx)
|
||||
{
|
||||
all_edges_list[min_forward_idx].result.is_split = false;
|
||||
all_edges_list[min_forward_idx].result.forward = true;
|
||||
all_edges_list[min_forward_idx].result.backward = true;
|
||||
all_edges_list[min_forward_idx].result.flags.is_split = false;
|
||||
all_edges_list[min_forward_idx].result.flags.forward = true;
|
||||
all_edges_list[min_forward_idx].result.flags.backward = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -486,17 +491,17 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
bool has_backward = min_backward_idx != std::numeric_limits<std::size_t>::max();
|
||||
if (has_forward)
|
||||
{
|
||||
all_edges_list[min_forward_idx].result.forward = true;
|
||||
all_edges_list[min_forward_idx].result.backward = false;
|
||||
all_edges_list[min_forward_idx].result.is_split = has_backward;
|
||||
all_edges_list[min_forward_idx].result.flags.forward = true;
|
||||
all_edges_list[min_forward_idx].result.flags.backward = false;
|
||||
all_edges_list[min_forward_idx].result.flags.is_split = has_backward;
|
||||
}
|
||||
if (has_backward)
|
||||
{
|
||||
std::swap(all_edges_list[min_backward_idx].result.source,
|
||||
all_edges_list[min_backward_idx].result.target);
|
||||
all_edges_list[min_backward_idx].result.forward = true;
|
||||
all_edges_list[min_backward_idx].result.backward = false;
|
||||
all_edges_list[min_backward_idx].result.is_split = has_forward;
|
||||
all_edges_list[min_backward_idx].result.flags.forward = true;
|
||||
all_edges_list[min_backward_idx].result.flags.backward = false;
|
||||
all_edges_list[min_backward_idx].result.flags.is_split = has_forward;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,10 +550,25 @@ void ExtractionContainers::WriteEdges(storage::io::FileWriter &file_out) const
|
||||
|
||||
TIMER_STOP(write_edges);
|
||||
log << "ok, after " << TIMER_SEC(write_edges) << "s";
|
||||
log << "Processed " << normal_edges.size() << " edges";
|
||||
log << " -- Processed " << normal_edges.size() << " edges";
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteMetadata(storage::io::FileWriter &file_out) const
|
||||
{
|
||||
util::UnbufferedLog log;
|
||||
log << "Writing way meta-data ... " << std::flush;
|
||||
TIMER_START(write_meta_data);
|
||||
|
||||
file_out.WriteElementCount64(all_edges_annotation_data_list.size());
|
||||
file_out.WriteFrom(all_edges_annotation_data_list.data(),
|
||||
all_edges_annotation_data_list.size());
|
||||
|
||||
TIMER_STOP(write_meta_data);
|
||||
log << "ok, after " << TIMER_SEC(write_meta_data) << "s";
|
||||
log << " -- Metadata contains << " << all_edges_annotation_data_list.size() << " entries.";
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteNodes(storage::io::FileWriter &file_out) const
|
||||
{
|
||||
{
|
||||
|
||||
+103
-122
@@ -7,6 +7,7 @@
|
||||
#include "extractor/extraction_way.hpp"
|
||||
#include "extractor/extractor_callbacks.hpp"
|
||||
#include "extractor/files.hpp"
|
||||
#include "extractor/node_based_graph_factory.hpp"
|
||||
#include "extractor/raster_source.hpp"
|
||||
#include "extractor/restriction_filter.hpp"
|
||||
#include "extractor/restriction_parser.hpp"
|
||||
@@ -209,27 +210,80 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
util::DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
|
||||
std::vector<bool> node_is_startpoint;
|
||||
std::vector<EdgeWeight> edge_based_node_weights;
|
||||
std::vector<util::Coordinate> coordinates;
|
||||
extractor::PackedOSMIDs osm_node_ids;
|
||||
|
||||
auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
|
||||
coordinates,
|
||||
osm_node_ids,
|
||||
edge_based_nodes_container,
|
||||
edge_based_node_segments,
|
||||
node_is_startpoint,
|
||||
edge_based_node_weights,
|
||||
edge_based_edge_list,
|
||||
config.GetPath(".osrm.icd").string(),
|
||||
turn_restrictions,
|
||||
conditional_turn_restrictions,
|
||||
turn_lane_map);
|
||||
// Create a node-based graph from the OSRM file
|
||||
NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"),
|
||||
scripting_environment,
|
||||
turn_restrictions,
|
||||
conditional_turn_restrictions);
|
||||
|
||||
auto number_of_node_based_nodes = graph_size.first;
|
||||
auto max_edge_id = graph_size.second - 1;
|
||||
util::Log() << "Writing nodes for nodes-based and edges-based graphs ...";
|
||||
auto const &coordinates = node_based_graph_factory.GetCoordinates();
|
||||
files::writeNodes(
|
||||
config.GetPath(".osrm.nbg_nodes"), coordinates, node_based_graph_factory.GetOsmNodes());
|
||||
node_based_graph_factory.ReleaseOsmNodes();
|
||||
|
||||
auto const &node_based_graph = node_based_graph_factory.GetGraph();
|
||||
|
||||
// The osrm-partition tool requires the compressed node based graph with an embedding.
|
||||
//
|
||||
// The `Run` function above re-numbers non-reverse compressed node based graph edges
|
||||
// to a continuous range so that the nodes in the edge based graph are continuous.
|
||||
//
|
||||
// Luckily node based node ids still coincide with the coordinate array.
|
||||
// That's the reason we can only here write out the final compressed node based graph.
|
||||
|
||||
// Dumps to file asynchronously and makes sure we wait for its completion.
|
||||
std::future<void> compressed_node_based_graph_writing;
|
||||
|
||||
BOOST_SCOPE_EXIT_ALL(&)
|
||||
{
|
||||
if (compressed_node_based_graph_writing.valid())
|
||||
compressed_node_based_graph_writing.wait();
|
||||
};
|
||||
|
||||
compressed_node_based_graph_writing = std::async(std::launch::async, [&] {
|
||||
WriteCompressedNodeBasedGraph(
|
||||
config.GetPath(".osrm.cnbg").string(), node_based_graph, coordinates);
|
||||
});
|
||||
|
||||
node_based_graph_factory.GetCompressedEdges().PrintStatistics();
|
||||
|
||||
const auto &barrier_nodes = node_based_graph_factory.GetBarriers();
|
||||
const auto &traffic_signals = node_based_graph_factory.GetTrafficSignals();
|
||||
// stealing the annotation data from the node-based graph
|
||||
edge_based_nodes_container =
|
||||
EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData()));
|
||||
|
||||
conditional_turn_restrictions =
|
||||
removeInvalidRestrictions(std::move(conditional_turn_restrictions), node_based_graph);
|
||||
|
||||
const auto number_of_node_based_nodes = node_based_graph.GetNumberOfNodes();
|
||||
|
||||
const auto number_of_edge_based_nodes =
|
||||
BuildEdgeExpandedGraph(node_based_graph,
|
||||
coordinates,
|
||||
node_based_graph_factory.GetCompressedEdges(),
|
||||
barrier_nodes,
|
||||
traffic_signals,
|
||||
turn_restrictions,
|
||||
conditional_turn_restrictions,
|
||||
turn_lane_map,
|
||||
scripting_environment,
|
||||
edge_based_nodes_container,
|
||||
edge_based_node_segments,
|
||||
node_is_startpoint,
|
||||
edge_based_node_weights,
|
||||
edge_based_edge_list,
|
||||
config.GetPath(".osrm.icd").string());
|
||||
|
||||
TIMER_STOP(expansion);
|
||||
|
||||
// output the geometry of the node-based graph, needs to be done after the last usage, since it
|
||||
// destroys internal containers
|
||||
files::writeSegmentData(config.GetPath(".osrm.geometry"),
|
||||
*node_based_graph_factory.GetCompressedEdges().ToSegmentData());
|
||||
|
||||
util::Log() << "Saving edge-based node weights to file.";
|
||||
TIMER_START(timer_write_node_weights);
|
||||
{
|
||||
@@ -241,8 +295,10 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
|
||||
|
||||
util::Log() << "Computing strictly connected components ...";
|
||||
FindComponents(
|
||||
max_edge_id, edge_based_edge_list, edge_based_node_segments, edge_based_nodes_container);
|
||||
FindComponents(number_of_edge_based_nodes,
|
||||
edge_based_edge_list,
|
||||
edge_based_node_segments,
|
||||
edge_based_nodes_container);
|
||||
|
||||
util::Log() << "Building r-tree ...";
|
||||
TIMER_START(rtree);
|
||||
@@ -250,13 +306,12 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
|
||||
TIMER_STOP(rtree);
|
||||
|
||||
util::Log() << "Writing nodes for nodes-based and edges-based graphs ...";
|
||||
files::writeNodes(config.GetPath(".osrm.nbg_nodes"), coordinates, osm_node_ids);
|
||||
files::writeNodeData(config.GetPath(".osrm.ebg_nodes"), edge_based_nodes_container);
|
||||
|
||||
util::Log() << "Writing edge-based-graph edges ... " << std::flush;
|
||||
TIMER_START(write_edges);
|
||||
files::writeEdgeBasedGraph(config.GetPath(".osrm.ebg"), max_edge_id, edge_based_edge_list);
|
||||
files::writeEdgeBasedGraph(
|
||||
config.GetPath(".osrm.ebg"), number_of_edge_based_nodes, edge_based_edge_list);
|
||||
TIMER_STOP(write_edges);
|
||||
util::Log() << "ok, after " << TIMER_SEC(write_edges) << "s";
|
||||
|
||||
@@ -265,7 +320,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
const auto nodes_per_second =
|
||||
static_cast<std::uint64_t>(number_of_node_based_nodes / TIMER_SEC(expansion));
|
||||
const auto edges_per_second =
|
||||
static_cast<std::uint64_t>((max_edge_id + 1) / TIMER_SEC(expansion));
|
||||
static_cast<std::uint64_t>((number_of_edge_based_nodes) / TIMER_SEC(expansion));
|
||||
|
||||
util::Log() << "Expansion: " << nodes_per_second << " nodes/sec and " << edges_per_second
|
||||
<< " edges/sec";
|
||||
@@ -492,7 +547,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
|
||||
std::move(extraction_containers.conditional_turn_restrictions));
|
||||
}
|
||||
|
||||
void Extractor::FindComponents(unsigned max_edge_id,
|
||||
void Extractor::FindComponents(unsigned number_of_edge_based_nodes,
|
||||
const util::DeallocatingVector<EdgeBasedEdge> &input_edge_list,
|
||||
const std::vector<EdgeBasedNodeSegment> &input_node_segments,
|
||||
EdgeBasedNodeDataContainer &nodes_container) const
|
||||
@@ -506,8 +561,8 @@ void Extractor::FindComponents(unsigned max_edge_id,
|
||||
{
|
||||
BOOST_ASSERT_MSG(static_cast<unsigned int>(std::max(edge.data.weight, 1)) > 0,
|
||||
"edge distance < 1");
|
||||
BOOST_ASSERT(edge.source <= max_edge_id);
|
||||
BOOST_ASSERT(edge.target <= max_edge_id);
|
||||
BOOST_ASSERT(edge.source < number_of_edge_based_nodes);
|
||||
BOOST_ASSERT(edge.target < number_of_edge_based_nodes);
|
||||
if (edge.data.forward)
|
||||
{
|
||||
edges.push_back({edge.source, edge.target});
|
||||
@@ -525,8 +580,8 @@ void Extractor::FindComponents(unsigned max_edge_id,
|
||||
{
|
||||
if (segment.reverse_segment_id.enabled)
|
||||
{
|
||||
BOOST_ASSERT(segment.forward_segment_id.id <= max_edge_id);
|
||||
BOOST_ASSERT(segment.reverse_segment_id.id <= max_edge_id);
|
||||
BOOST_ASSERT(segment.forward_segment_id.id < number_of_edge_based_nodes);
|
||||
BOOST_ASSERT(segment.reverse_segment_id.id < number_of_edge_based_nodes);
|
||||
edges.push_back({segment.forward_segment_id.id, segment.reverse_segment_id.id});
|
||||
edges.push_back({segment.reverse_segment_id.id, segment.forward_segment_id.id});
|
||||
}
|
||||
@@ -535,98 +590,54 @@ void Extractor::FindComponents(unsigned max_edge_id,
|
||||
tbb::parallel_sort(edges.begin(), edges.end());
|
||||
edges.erase(std::unique(edges.begin(), edges.end()), edges.end());
|
||||
|
||||
auto uncontracted_graph = UncontractedGraph(max_edge_id + 1, edges);
|
||||
auto uncontracted_graph = UncontractedGraph(number_of_edge_based_nodes, edges);
|
||||
|
||||
TarjanSCC<UncontractedGraph> component_search(uncontracted_graph);
|
||||
component_search.Run();
|
||||
|
||||
for (NodeID node_id = 0; node_id <= max_edge_id; ++node_id)
|
||||
for (NodeID node_id = 0; node_id < number_of_edge_based_nodes; ++node_id)
|
||||
{
|
||||
const auto forward_component = component_search.GetComponentID(node_id);
|
||||
const auto component_size = component_search.GetComponentSize(forward_component);
|
||||
const auto is_tiny = component_size < config.small_component_size;
|
||||
nodes_container.SetComponentID(node_id, {1 + forward_component, is_tiny});
|
||||
BOOST_ASSERT(node_id < nodes_container.NumberOfNodes());
|
||||
nodes_container.nodes[node_id].component_id = {1 + forward_component, is_tiny};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Load node based graph from .osrm file
|
||||
*/
|
||||
std::shared_ptr<util::NodeBasedDynamicGraph>
|
||||
Extractor::LoadNodeBasedGraph(std::unordered_set<NodeID> &barriers,
|
||||
std::unordered_set<NodeID> &traffic_signals,
|
||||
std::vector<util::Coordinate> &coordiantes,
|
||||
extractor::PackedOSMIDs &osm_node_ids)
|
||||
{
|
||||
storage::io::FileReader file_reader(config.GetPath(".osrm"),
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
auto barriers_iter = inserter(barriers, end(barriers));
|
||||
auto traffic_signals_iter = inserter(traffic_signals, end(traffic_signals));
|
||||
|
||||
NodeID number_of_node_based_nodes = util::loadNodesFromFile(
|
||||
file_reader, barriers_iter, traffic_signals_iter, coordiantes, osm_node_ids);
|
||||
|
||||
util::Log() << " - " << barriers.size() << " bollard nodes, " << traffic_signals.size()
|
||||
<< " traffic lights";
|
||||
|
||||
std::vector<NodeBasedEdge> edge_list;
|
||||
util::loadEdgesFromFile(file_reader, edge_list);
|
||||
|
||||
if (edge_list.empty())
|
||||
{
|
||||
throw util::exception("Node-based-graph (" + config.GetPath(".osrm").string() +
|
||||
") contains no edges." + SOURCE_REF);
|
||||
}
|
||||
|
||||
return util::NodeBasedDynamicGraphFromEdges(number_of_node_based_nodes, edge_list);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Building an edge-expanded graph from node-based input and turn restrictions
|
||||
*/
|
||||
std::pair<std::size_t, EdgeID> Extractor::BuildEdgeExpandedGraph(
|
||||
|
||||
EdgeID Extractor::BuildEdgeExpandedGraph(
|
||||
// input data
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_signals,
|
||||
const std::vector<TurnRestriction> &turn_restrictions,
|
||||
const std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions,
|
||||
// might have to be updated to add new lane combinations
|
||||
guidance::LaneDescriptionMap &turn_lane_map,
|
||||
// for calculating turn penalties
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<util::Coordinate> &coordinates,
|
||||
extractor::PackedOSMIDs &osm_node_ids,
|
||||
// output data
|
||||
EdgeBasedNodeDataContainer &edge_based_nodes_container,
|
||||
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||
const std::string &intersection_class_output_file,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions,
|
||||
guidance::LaneDescriptionMap &turn_lane_map)
|
||||
const std::string &intersection_class_output_file)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes;
|
||||
std::unordered_set<NodeID> traffic_signals;
|
||||
|
||||
auto node_based_graph =
|
||||
LoadNodeBasedGraph(barrier_nodes, traffic_signals, coordinates, osm_node_ids);
|
||||
|
||||
CompressedEdgeContainer compressed_edge_container;
|
||||
GraphCompressor graph_compressor;
|
||||
graph_compressor.Compress(barrier_nodes,
|
||||
traffic_signals,
|
||||
scripting_environment,
|
||||
turn_restrictions,
|
||||
conditional_turn_restrictions,
|
||||
*node_based_graph,
|
||||
compressed_edge_container);
|
||||
|
||||
conditional_turn_restrictions =
|
||||
removeInvalidRestrictions(std::move(conditional_turn_restrictions), *node_based_graph);
|
||||
|
||||
util::NameTable name_table(config.GetPath(".osrm.names").string());
|
||||
|
||||
EdgeBasedGraphFactory edge_based_graph_factory(node_based_graph,
|
||||
edge_based_nodes_container,
|
||||
compressed_edge_container,
|
||||
barrier_nodes,
|
||||
traffic_signals,
|
||||
coordinates,
|
||||
osm_node_ids,
|
||||
scripting_environment.GetProfileProperties(),
|
||||
name_table,
|
||||
turn_lane_map);
|
||||
|
||||
@@ -646,8 +657,6 @@ std::pair<std::size_t, EdgeID> Extractor::BuildEdgeExpandedGraph(
|
||||
WayRestrictionMap via_way_restriction_map(conditional_turn_restrictions);
|
||||
ConditionalRestrictionMap conditional_node_restriction_map(conditional_node_restrictions,
|
||||
IndexNodeByFromAndVia());
|
||||
turn_restrictions.clear();
|
||||
turn_restrictions.shrink_to_fit();
|
||||
|
||||
edge_based_graph_factory.Run(scripting_environment,
|
||||
config.GetPath(".osrm.edges").string(),
|
||||
@@ -664,29 +673,6 @@ std::pair<std::size_t, EdgeID> Extractor::BuildEdgeExpandedGraph(
|
||||
};
|
||||
|
||||
const auto number_of_edge_based_nodes = create_edge_based_edges();
|
||||
compressed_edge_container.PrintStatistics();
|
||||
|
||||
// The osrm-partition tool requires the compressed node based graph with an embedding.
|
||||
//
|
||||
// The `Run` function above re-numbers non-reverse compressed node based graph edges
|
||||
// to a continuous range so that the nodes in the edge based graph are continuous.
|
||||
//
|
||||
// Luckily node based node ids still coincide with the coordinate array.
|
||||
// That's the reason we can only here write out the final compressed node based graph.
|
||||
|
||||
// Dumps to file asynchronously and makes sure we wait for its completion.
|
||||
std::future<void> compressed_node_based_graph_writing;
|
||||
|
||||
BOOST_SCOPE_EXIT_ALL(&)
|
||||
{
|
||||
if (compressed_node_based_graph_writing.valid())
|
||||
compressed_node_based_graph_writing.wait();
|
||||
};
|
||||
|
||||
compressed_node_based_graph_writing = std::async(std::launch::async, [&] {
|
||||
WriteCompressedNodeBasedGraph(
|
||||
config.GetPath(".osrm.cnbg").string(), *node_based_graph, coordinates);
|
||||
});
|
||||
|
||||
{
|
||||
std::vector<std::uint32_t> turn_lane_offsets;
|
||||
@@ -696,17 +682,12 @@ std::pair<std::size_t, EdgeID> Extractor::BuildEdgeExpandedGraph(
|
||||
files::writeTurnLaneDescriptions(
|
||||
config.GetPath(".osrm.tls"), turn_lane_offsets, turn_lane_masks);
|
||||
}
|
||||
files::writeSegmentData(config.GetPath(".osrm.geometry"),
|
||||
*compressed_edge_container.ToSegmentData());
|
||||
|
||||
edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list);
|
||||
edge_based_graph_factory.GetEdgeBasedNodes(edge_based_nodes_container);
|
||||
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);
|
||||
|
||||
const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes();
|
||||
|
||||
util::Log() << "Writing Intersection Classification Data";
|
||||
TIMER_START(write_intersections);
|
||||
files::writeIntersections(
|
||||
@@ -717,7 +698,7 @@ std::pair<std::size_t, EdgeID> Extractor::BuildEdgeExpandedGraph(
|
||||
TIMER_STOP(write_intersections);
|
||||
util::Log() << "ok, after " << TIMER_SEC(write_intersections) << "s";
|
||||
|
||||
return std::make_pair(number_of_node_based_nodes, number_of_edge_based_nodes);
|
||||
return number_of_edge_based_nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -390,57 +390,67 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
|
||||
if (in_forward_direction)
|
||||
{ // add (forward) segments or (forward,backward) for non-split edges in backward direction
|
||||
const auto annotation_data_id = external_memory.all_edges_annotation_data_list.size();
|
||||
external_memory.all_edges_annotation_data_list.push_back({name_id,
|
||||
turn_lane_id_forward,
|
||||
forward_classes,
|
||||
parsed_way.forward_travel_mode,
|
||||
parsed_way.is_left_hand_driving});
|
||||
util::for_each_pair(
|
||||
nodes.cbegin(),
|
||||
nodes.cend(),
|
||||
[&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
|
||||
external_memory.all_edges_list.push_back(
|
||||
InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
|
||||
OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
|
||||
name_id,
|
||||
forward_weight_data,
|
||||
forward_duration_data,
|
||||
true,
|
||||
in_backward_direction && !split_edge,
|
||||
parsed_way.roundabout,
|
||||
parsed_way.circular,
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.forward_restricted,
|
||||
parsed_way.is_left_hand_driving,
|
||||
split_edge,
|
||||
parsed_way.forward_travel_mode,
|
||||
forward_classes,
|
||||
turn_lane_id_forward,
|
||||
road_classification,
|
||||
{}));
|
||||
NodeBasedEdgeWithOSM edge = {
|
||||
OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
|
||||
OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
|
||||
0, // weight
|
||||
0, // duration
|
||||
{}, // geometry id
|
||||
static_cast<AnnotationID>(annotation_data_id),
|
||||
{true,
|
||||
in_backward_direction && !split_edge,
|
||||
split_edge,
|
||||
parsed_way.roundabout,
|
||||
parsed_way.circular,
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.forward_restricted,
|
||||
road_classification}};
|
||||
|
||||
external_memory.all_edges_list.push_back(InternalExtractorEdge(
|
||||
std::move(edge), forward_weight_data, forward_duration_data, {}));
|
||||
});
|
||||
}
|
||||
|
||||
if (in_backward_direction && (!in_forward_direction || split_edge))
|
||||
{ // add (backward) segments for split edges or not in forward direction
|
||||
const auto annotation_data_id = external_memory.all_edges_annotation_data_list.size();
|
||||
external_memory.all_edges_annotation_data_list.push_back({name_id,
|
||||
turn_lane_id_backward,
|
||||
backward_classes,
|
||||
parsed_way.backward_travel_mode,
|
||||
parsed_way.is_left_hand_driving});
|
||||
util::for_each_pair(
|
||||
nodes.cbegin(),
|
||||
nodes.cend(),
|
||||
[&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
|
||||
external_memory.all_edges_list.push_back(
|
||||
InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
|
||||
OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
|
||||
name_id,
|
||||
backward_weight_data,
|
||||
backward_duration_data,
|
||||
false,
|
||||
true,
|
||||
parsed_way.roundabout,
|
||||
parsed_way.circular,
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.backward_restricted,
|
||||
parsed_way.is_left_hand_driving,
|
||||
split_edge,
|
||||
parsed_way.backward_travel_mode,
|
||||
backward_classes,
|
||||
turn_lane_id_backward,
|
||||
road_classification,
|
||||
{}));
|
||||
NodeBasedEdgeWithOSM edge = {
|
||||
OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
|
||||
OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
|
||||
0, // weight
|
||||
0, // duration
|
||||
{}, // geometry id
|
||||
static_cast<AnnotationID>(annotation_data_id),
|
||||
{false,
|
||||
true,
|
||||
split_edge,
|
||||
parsed_way.roundabout,
|
||||
parsed_way.circular,
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.backward_restricted,
|
||||
road_classification}};
|
||||
|
||||
external_memory.all_edges_list.push_back(InternalExtractorEdge(
|
||||
std::move(edge), backward_weight_data, backward_duration_data, {}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ void GraphCompressor::Compress(
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions,
|
||||
util::NodeBasedDynamicGraph &graph,
|
||||
const std::vector<NodeBasedEdgeAnnotation> &node_data_container,
|
||||
CompressedEdgeContainer &geometry_compressor)
|
||||
{
|
||||
const unsigned original_number_of_nodes = graph.GetNumberOfNodes();
|
||||
@@ -104,6 +105,7 @@ void GraphCompressor::Compress(
|
||||
BOOST_ASSERT(forward_e2 >= graph.BeginEdges(node_v) &&
|
||||
forward_e2 < graph.EndEdges(node_v));
|
||||
const EdgeID reverse_e2 = graph.BeginEdges(node_v) + 1 - reverse_edge_order;
|
||||
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2);
|
||||
BOOST_ASSERT(reverse_e2 >= graph.BeginEdges(node_v) &&
|
||||
reverse_e2 < graph.EndEdges(node_v));
|
||||
@@ -127,6 +129,10 @@ void GraphCompressor::Compress(
|
||||
|
||||
const EdgeData &fwd_edge_data1 = graph.GetEdgeData(forward_e1);
|
||||
const EdgeData &rev_edge_data1 = graph.GetEdgeData(reverse_e1);
|
||||
const auto fwd_annotation_data1 = node_data_container[fwd_edge_data1.annotation_data];
|
||||
const auto fwd_annotation_data2 = node_data_container[fwd_edge_data2.annotation_data];
|
||||
const auto rev_annotation_data1 = node_data_container[rev_edge_data1.annotation_data];
|
||||
const auto rev_annotation_data2 = node_data_container[rev_edge_data2.annotation_data];
|
||||
|
||||
if (graph.FindEdgeInEitherDirection(node_u, node_w) != SPECIAL_EDGEID)
|
||||
{
|
||||
@@ -134,20 +140,22 @@ void GraphCompressor::Compress(
|
||||
}
|
||||
|
||||
// this case can happen if two ways with different names overlap
|
||||
if (fwd_edge_data1.name_id != rev_edge_data1.name_id ||
|
||||
fwd_edge_data2.name_id != rev_edge_data2.name_id)
|
||||
if ((fwd_annotation_data1.name_id != rev_annotation_data1.name_id) ||
|
||||
(fwd_annotation_data2.name_id != rev_annotation_data2.name_id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fwd_edge_data1.CanCombineWith(fwd_edge_data2) &&
|
||||
rev_edge_data1.CanCombineWith(rev_edge_data2))
|
||||
if ((fwd_edge_data1.flags == fwd_edge_data2.flags) &&
|
||||
(rev_edge_data1.flags == rev_edge_data2.flags) &&
|
||||
(fwd_edge_data1.reversed == fwd_edge_data2.reversed) &&
|
||||
(rev_edge_data1.reversed == rev_edge_data2.reversed) &&
|
||||
// annotations need to match, except for the lane-id which can differ
|
||||
fwd_annotation_data1.CanCombineWith(fwd_annotation_data2) &&
|
||||
rev_annotation_data1.CanCombineWith(rev_annotation_data2))
|
||||
{
|
||||
BOOST_ASSERT(graph.GetEdgeData(forward_e1).name_id ==
|
||||
graph.GetEdgeData(reverse_e1).name_id);
|
||||
BOOST_ASSERT(graph.GetEdgeData(forward_e2).name_id ==
|
||||
graph.GetEdgeData(reverse_e2).name_id);
|
||||
|
||||
BOOST_ASSERT(!(graph.GetEdgeData(forward_e1).reversed &&
|
||||
graph.GetEdgeData(reverse_e1).reversed));
|
||||
/*
|
||||
* Remember Lane Data for compressed parts. This handles scenarios where lane-data
|
||||
* is
|
||||
@@ -175,24 +183,26 @@ void GraphCompressor::Compress(
|
||||
* just
|
||||
* like a barrier.
|
||||
*/
|
||||
const auto selectLaneID = [](const LaneDescriptionID front,
|
||||
const LaneDescriptionID back) {
|
||||
const auto selectAnnotation = [&node_data_container](
|
||||
const AnnotationID front_annotation, const AnnotationID back_annotation) {
|
||||
// A lane has tags: u - (front) - v - (back) - w
|
||||
// During contraction, we keep only one of the tags. Usually the one closer to
|
||||
// the
|
||||
// intersection is preferred. If its empty, however, we keep the non-empty one
|
||||
if (back == INVALID_LANE_DESCRIPTIONID)
|
||||
return front;
|
||||
return back;
|
||||
// the intersection is preferred. If its empty, however, we keep the non-empty
|
||||
// one
|
||||
if (node_data_container[back_annotation].lane_description_id ==
|
||||
INVALID_LANE_DESCRIPTIONID)
|
||||
return front_annotation;
|
||||
return back_annotation;
|
||||
};
|
||||
graph.GetEdgeData(forward_e1).lane_description_id = selectLaneID(
|
||||
fwd_edge_data1.lane_description_id, fwd_edge_data2.lane_description_id);
|
||||
graph.GetEdgeData(reverse_e1).lane_description_id = selectLaneID(
|
||||
rev_edge_data1.lane_description_id, rev_edge_data2.lane_description_id);
|
||||
graph.GetEdgeData(forward_e2).lane_description_id = selectLaneID(
|
||||
fwd_edge_data2.lane_description_id, fwd_edge_data1.lane_description_id);
|
||||
graph.GetEdgeData(reverse_e2).lane_description_id = selectLaneID(
|
||||
rev_edge_data2.lane_description_id, rev_edge_data1.lane_description_id);
|
||||
|
||||
graph.GetEdgeData(forward_e1).annotation_data = selectAnnotation(
|
||||
fwd_edge_data1.annotation_data, fwd_edge_data2.annotation_data);
|
||||
graph.GetEdgeData(reverse_e1).annotation_data = selectAnnotation(
|
||||
rev_edge_data1.annotation_data, rev_edge_data2.annotation_data);
|
||||
graph.GetEdgeData(forward_e2).annotation_data = selectAnnotation(
|
||||
fwd_edge_data2.annotation_data, fwd_edge_data1.annotation_data);
|
||||
graph.GetEdgeData(reverse_e2).annotation_data = selectAnnotation(
|
||||
rev_edge_data2.annotation_data, rev_edge_data1.annotation_data);
|
||||
|
||||
/*
|
||||
// Do not compress edge if it crosses a traffic signal.
|
||||
@@ -206,14 +216,15 @@ void GraphCompressor::Compress(
|
||||
if (has_node_penalty)
|
||||
{
|
||||
// we cannot handle this as node penalty, if it depends on turn direction
|
||||
if (fwd_edge_data1.restricted != fwd_edge_data2.restricted)
|
||||
if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted)
|
||||
continue;
|
||||
|
||||
// generate an artifical turn for the turn penalty generation
|
||||
ExtractionTurn extraction_turn(true,
|
||||
fwd_edge_data1.restricted,
|
||||
fwd_edge_data2.restricted,
|
||||
fwd_edge_data1.is_left_hand_driving);
|
||||
ExtractionTurn extraction_turn(
|
||||
true,
|
||||
fwd_edge_data1.flags.restricted,
|
||||
fwd_edge_data2.flags.restricted,
|
||||
node_data_container[fwd_edge_data1.annotation_data].is_left_hand_driving);
|
||||
|
||||
scripting_environment.ProcessTurn(extraction_turn);
|
||||
node_duration_penalty = extraction_turn.duration * 10;
|
||||
|
||||
@@ -122,7 +122,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
// coordinate set to add a small level of fault tolerance
|
||||
const constexpr double skipping_inaccuracies_distance = 2;
|
||||
|
||||
const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge);
|
||||
const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge).flags;
|
||||
|
||||
// roundabouts, check early to avoid other costly checks
|
||||
if (turn_edge_data.roundabout || turn_edge_data.circular)
|
||||
@@ -175,7 +175,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
// the lane count might not always be set. We need to assume a positive number, though. Here we
|
||||
// select the number of lanes to operate on
|
||||
const auto considered_lanes =
|
||||
GetOffsetCorrectionFactor(node_based_graph.GetEdgeData(turn_edge).road_classification) *
|
||||
GetOffsetCorrectionFactor(turn_edge_data.road_classification) *
|
||||
((intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes);
|
||||
|
||||
/* if the very first coordinate along the road is reasonably far away from the road, we assume
|
||||
@@ -644,7 +644,7 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
|
||||
const std::vector<double> &segment_distances,
|
||||
const double segment_length,
|
||||
const double considered_lane_width,
|
||||
const util::NodeBasedEdgeData &edge_data) const
|
||||
const extractor::NodeBasedEdgeClassification &edge_data) const
|
||||
{
|
||||
BOOST_ASSERT(coordinates.size() > 2);
|
||||
|
||||
|
||||
@@ -14,10 +14,12 @@ namespace guidance
|
||||
|
||||
DrivewayHandler::DrivewayHandler(const IntersectionGenerator &intersection_generator,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
@@ -38,13 +40,13 @@ bool DrivewayHandler::canProcess(const NodeID /*nid*/,
|
||||
const auto from_eid = intersection.getUTurnRoad().eid;
|
||||
|
||||
if (intersection.size() <= 2 ||
|
||||
node_based_graph.GetEdgeData(from_eid).road_classification.IsLowPriorityRoadClass())
|
||||
node_based_graph.GetEdgeData(from_eid).flags.road_classification.IsLowPriorityRoadClass())
|
||||
return false;
|
||||
|
||||
auto low_priority_count =
|
||||
std::count_if(intersection.begin(), intersection.end(), [this](const auto &road) {
|
||||
return node_based_graph.GetEdgeData(road.eid)
|
||||
.road_classification.IsLowPriorityRoadClass();
|
||||
.flags.road_classification.IsLowPriorityRoadClass();
|
||||
});
|
||||
|
||||
// Process intersection if it has two edges with normal priority and one is the entry edge,
|
||||
@@ -58,7 +60,7 @@ operator()(const NodeID nid, const EdgeID source_edge_id, Intersection intersect
|
||||
auto road =
|
||||
std::find_if(intersection.begin() + 1, intersection.end(), [this](const auto &road) {
|
||||
return !node_based_graph.GetEdgeData(road.eid)
|
||||
.road_classification.IsLowPriorityRoadClass();
|
||||
.flags.road_classification.IsLowPriorityRoadClass();
|
||||
});
|
||||
|
||||
(void)nid;
|
||||
|
||||
@@ -33,12 +33,13 @@ const constexpr bool USE_HIGH_PRECISION_MODE = !USE_LOW_PRECISION_MODE;
|
||||
|
||||
IntersectionGenerator::IntersectionGenerator(
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container)
|
||||
: node_based_graph(node_based_graph), restriction_map(restriction_map),
|
||||
barrier_nodes(barrier_nodes), coordinates(coordinates),
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
restriction_map(restriction_map), barrier_nodes(barrier_nodes), coordinates(coordinates),
|
||||
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
|
||||
{
|
||||
}
|
||||
@@ -69,15 +70,15 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
|
||||
|
||||
// number of lanes at the intersection changes how far we look down the road
|
||||
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection);
|
||||
const auto max_lanes_intersection = std::accumulate(
|
||||
edge_range.begin(),
|
||||
edge_range.end(),
|
||||
std::uint8_t{0},
|
||||
[this](const auto current_max, const auto current_eid) {
|
||||
return std::max(
|
||||
current_max,
|
||||
node_based_graph.GetEdgeData(current_eid).road_classification.GetNumberOfLanes());
|
||||
});
|
||||
const auto max_lanes_intersection =
|
||||
std::accumulate(edge_range.begin(),
|
||||
edge_range.end(),
|
||||
std::uint8_t{0},
|
||||
[this](const auto current_max, const auto current_eid) {
|
||||
return std::max(current_max,
|
||||
node_based_graph.GetEdgeData(current_eid)
|
||||
.flags.road_classification.GetNumberOfLanes());
|
||||
});
|
||||
|
||||
for (const EdgeID edge_connected_to_intersection :
|
||||
node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
@@ -265,9 +266,11 @@ IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const Edge
|
||||
query_node = next_node;
|
||||
query_edge = next_edge;
|
||||
|
||||
if (!node_based_graph.GetEdgeData(query_edge)
|
||||
.IsCompatibleTo(node_based_graph.GetEdgeData(next_edge)) ||
|
||||
node_based_graph.GetTarget(next_edge) == starting_node)
|
||||
// check if there is a relevant change in the graph
|
||||
if (!CanBeCompressed(node_based_graph.GetEdgeData(query_edge),
|
||||
node_based_graph.GetEdgeData(next_edge),
|
||||
node_data_container) ||
|
||||
(node_based_graph.GetTarget(next_edge) == starting_node))
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,21 +24,38 @@ namespace guidance
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
|
||||
// TODO check flags!
|
||||
inline bool requiresAnnouncement(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const EdgeID from,
|
||||
const EdgeID to)
|
||||
{
|
||||
return !from.CanCombineWith(to);
|
||||
}
|
||||
const auto &from_edge = node_based_graph.GetEdgeData(from);
|
||||
const auto &to_edge = node_based_graph.GetEdgeData(to);
|
||||
|
||||
if (from_edge.reversed != to_edge.reversed)
|
||||
return true;
|
||||
|
||||
if (!(from_edge.flags == to_edge.flags))
|
||||
return true;
|
||||
|
||||
const auto &annotation_from = node_data_container.GetAnnotation(from_edge.annotation_data);
|
||||
const auto &annotation_to = node_data_container.GetAnnotation(to_edge.annotation_data);
|
||||
return !annotation_from.CanCombineWith(annotation_to);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), coordinates(coordinates), name_table(name_table),
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
coordinates(coordinates), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table),
|
||||
intersection_generator(intersection_generator),
|
||||
graph_walker(node_based_graph, intersection_generator)
|
||||
graph_walker(node_based_graph, node_data_container, intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -48,20 +65,23 @@ TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
|
||||
const ConnectedRoad &road) const
|
||||
{
|
||||
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.eid);
|
||||
|
||||
bool on_ramp = in_data.road_classification.IsRampClass();
|
||||
|
||||
bool onto_ramp = out_data.road_classification.IsRampClass();
|
||||
bool on_ramp = node_based_graph.GetEdgeData(via_edge).flags.road_classification.IsRampClass();
|
||||
bool onto_ramp = node_based_graph.GetEdgeData(road.eid).flags.road_classification.IsRampClass();
|
||||
|
||||
if (!on_ramp && onto_ramp)
|
||||
return TurnType::OnRamp;
|
||||
|
||||
const auto same_name = !util::guidance::requiresNameAnnounced(
|
||||
in_data.name_id, out_data.name_id, name_table, street_name_suffix_table);
|
||||
const auto &in_name =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data)
|
||||
.name_id;
|
||||
const auto &out_name =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.name_id;
|
||||
|
||||
if (in_data.name_id != EMPTY_NAMEID && out_data.name_id != EMPTY_NAMEID && same_name)
|
||||
const auto same_name = !util::guidance::requiresNameAnnounced(
|
||||
in_name, out_name, name_table, street_name_suffix_table);
|
||||
|
||||
if (in_name != EMPTY_NAMEID && out_name != EMPTY_NAMEID && same_name)
|
||||
{
|
||||
return TurnType::Continue;
|
||||
}
|
||||
@@ -86,14 +106,22 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
|
||||
}
|
||||
|
||||
// handle travel modes:
|
||||
const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode;
|
||||
const auto out_mode = node_based_graph.GetEdgeData(road.eid).travel_mode;
|
||||
const auto in_mode =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data)
|
||||
.travel_mode;
|
||||
const auto out_mode =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.travel_mode;
|
||||
const auto needs_notification = in_mode != out_mode;
|
||||
|
||||
if (type == TurnType::Turn)
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &in_classification = node_based_graph.GetEdgeData(via_edge).flags;
|
||||
const auto &in_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(via_edge).annotation_data);
|
||||
const auto &out_classification = node_based_graph.GetEdgeData(road.eid).flags;
|
||||
const auto &out_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(road.eid).annotation_data);
|
||||
|
||||
if (util::guidance::requiresNameAnnounced(
|
||||
in_data.name_id, out_data.name_id, name_table, street_name_suffix_table))
|
||||
@@ -104,12 +132,12 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
|
||||
// We reserve merges for motorway types. All others are considered for simply going
|
||||
// straight onto a road. This avoids confusion about merge directions on streets
|
||||
// that could potentially also offer different choices
|
||||
if (out_data.road_classification.IsMotorwayClass())
|
||||
if (out_classification.road_classification.IsMotorwayClass())
|
||||
return {TurnType::Merge,
|
||||
road.angle > STRAIGHT_ANGLE ? DirectionModifier::SlightRight
|
||||
: DirectionModifier::SlightLeft};
|
||||
else if (in_data.road_classification.IsRampClass() &&
|
||||
out_data.road_classification.IsRampClass())
|
||||
else if (in_classification.road_classification.IsRampClass() &&
|
||||
out_classification.road_classification.IsRampClass())
|
||||
{
|
||||
// This check is more a precaution than anything else. Our current travel modes
|
||||
// cannot reach this, since all ramps are exposing the same travel type. But we
|
||||
@@ -177,15 +205,20 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
ConnectedRoad &left,
|
||||
ConnectedRoad &right) const
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const bool low_priority_left =
|
||||
node_based_graph.GetEdgeData(left.eid).road_classification.IsLowPriorityRoadClass();
|
||||
const bool low_priority_right =
|
||||
node_based_graph.GetEdgeData(right.eid).road_classification.IsLowPriorityRoadClass();
|
||||
const auto same_mode_left =
|
||||
in_data.travel_mode == node_based_graph.GetEdgeData(left.eid).travel_mode;
|
||||
const auto same_mode_right =
|
||||
in_data.travel_mode == node_based_graph.GetEdgeData(right.eid).travel_mode;
|
||||
const auto &in_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data);
|
||||
const auto &lhs_classification =
|
||||
node_based_graph.GetEdgeData(left.eid).flags.road_classification;
|
||||
const auto &lhs_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(left.eid).annotation_data);
|
||||
const auto &rhs_classification =
|
||||
node_based_graph.GetEdgeData(right.eid).flags.road_classification;
|
||||
const auto &rhs_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(right.eid).annotation_data);
|
||||
const bool low_priority_left = lhs_classification.IsLowPriorityRoadClass();
|
||||
const bool low_priority_right = rhs_classification.IsLowPriorityRoadClass();
|
||||
const auto same_mode_left = in_data.travel_mode == lhs_data.travel_mode;
|
||||
const auto same_mode_right = in_data.travel_mode == rhs_data.travel_mode;
|
||||
const auto suppressed_left_type =
|
||||
same_mode_left ? TurnType::Suppressed : TurnType::Notification;
|
||||
const auto suppressed_right_type =
|
||||
@@ -194,8 +227,7 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
angularDeviation(right.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE))
|
||||
{
|
||||
// left side is actually straight
|
||||
const auto &out_data = node_based_graph.GetEdgeData(left.eid);
|
||||
if (detail::requiresAnnouncement(in_data, out_data))
|
||||
if (detail::requiresAnnouncement(node_based_graph, node_data_container, via_edge, left.eid))
|
||||
{
|
||||
if (low_priority_right && !low_priority_left)
|
||||
{
|
||||
@@ -230,11 +262,11 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
angularDeviation(left.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
|
||||
{
|
||||
// right side is actually straight
|
||||
const auto &out_data = node_based_graph.GetEdgeData(right.eid);
|
||||
if (angularDeviation(right.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
angularDeviation(left.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
|
||||
{
|
||||
if (detail::requiresAnnouncement(in_data, out_data))
|
||||
if (detail::requiresAnnouncement(
|
||||
node_based_graph, node_data_container, via_edge, right.eid))
|
||||
{
|
||||
if (low_priority_left && !low_priority_right)
|
||||
{
|
||||
@@ -304,8 +336,14 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
{
|
||||
// TODO handle low priority road classes in a reasonable way
|
||||
const auto suppressed_type = [&](const ConnectedRoad &road) {
|
||||
const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode;
|
||||
const auto out_mode = node_based_graph.GetEdgeData(road.eid).travel_mode;
|
||||
const auto in_mode =
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data)
|
||||
.travel_mode;
|
||||
const auto out_mode =
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.travel_mode;
|
||||
return in_mode == out_mode ? TurnType::Suppressed : TurnType::Notification;
|
||||
};
|
||||
|
||||
@@ -314,9 +352,8 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
left.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
|
||||
if (angularDeviation(center.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(center.eid);
|
||||
if (detail::requiresAnnouncement(in_data, out_data))
|
||||
if (detail::requiresAnnouncement(
|
||||
node_based_graph, node_data_container, via_edge, center.eid))
|
||||
{
|
||||
center.instruction = {TurnType::Fork, DirectionModifier::Straight};
|
||||
}
|
||||
@@ -371,7 +408,8 @@ void IntersectionHandler::assignTrivialTurns(const EdgeID via_eid,
|
||||
bool IntersectionHandler::isThroughStreet(const std::size_t index,
|
||||
const Intersection &intersection) const
|
||||
{
|
||||
const auto &data_at_index = node_based_graph.GetEdgeData(intersection[index].eid);
|
||||
const auto &data_at_index = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(intersection[index].eid).annotation_data);
|
||||
|
||||
if (data_at_index.name_id == EMPTY_NAMEID)
|
||||
return false;
|
||||
@@ -383,7 +421,8 @@ bool IntersectionHandler::isThroughStreet(const std::size_t index,
|
||||
continue;
|
||||
|
||||
const auto &road = intersection[road_index];
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &road_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(road.eid).annotation_data);
|
||||
|
||||
// roads have a near straight angle (180 degree)
|
||||
const bool is_nearly_straight = angularDeviation(road.angle, intersection[index].angle) >
|
||||
@@ -395,7 +434,8 @@ bool IntersectionHandler::isThroughStreet(const std::size_t index,
|
||||
data_at_index.name_id, road_data.name_id, name_table, street_name_suffix_table);
|
||||
|
||||
const bool have_same_category =
|
||||
data_at_index.road_classification == road_data.road_classification;
|
||||
node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification ==
|
||||
node_based_graph.GetEdgeData(road.eid).flags.road_classification;
|
||||
|
||||
if (is_nearly_straight && have_same_name && have_same_category)
|
||||
return true;
|
||||
@@ -443,8 +483,10 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
|
||||
|
||||
bool IntersectionHandler::isSameName(const EdgeID source_edge_id, const EdgeID target_edge_id) const
|
||||
{
|
||||
const auto &source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
|
||||
const auto &target_edge_data = node_based_graph.GetEdgeData(target_edge_id);
|
||||
const auto &source_edge_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(source_edge_id).annotation_data);
|
||||
const auto &target_edge_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(target_edge_id).annotation_data);
|
||||
|
||||
return source_edge_data.name_id != EMPTY_NAMEID && //
|
||||
target_edge_data.name_id != EMPTY_NAMEID && //
|
||||
|
||||
@@ -14,13 +14,16 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
IntersectionNormalizer::IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
IntersectionNormalizer::IntersectionNormalizer(
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
|
||||
mergable_road_detector(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
intersection_generator,
|
||||
intersection_generator.GetCoordinateExtractor(),
|
||||
|
||||
@@ -25,13 +25,17 @@ namespace
|
||||
// check a connected road for equality of a name
|
||||
inline auto makeCheckRoadForName(const NameID name_id,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &suffix_table)
|
||||
{
|
||||
return [name_id, &node_based_graph, &name_table, &suffix_table](
|
||||
return [name_id, &node_based_graph, &node_data_container, &name_table, &suffix_table](
|
||||
const MergableRoadDetector::MergableRoadData &road) {
|
||||
// since we filter here, we don't want any other name than the one we are looking for
|
||||
const auto road_name = node_based_graph.GetEdgeData(road.eid).name_id;
|
||||
const auto road_name =
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.name_id;
|
||||
if (name_id == EMPTY_NAMEID || road_name == EMPTY_NAMEID)
|
||||
return true;
|
||||
const auto requires_announcement =
|
||||
@@ -44,14 +48,16 @@ inline auto makeCheckRoadForName(const NameID name_id,
|
||||
}
|
||||
|
||||
MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const IntersectionGenerator &intersection_generator,
|
||||
const CoordinateExtractor &coordinate_extractor,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
|
||||
intersection_generator(intersection_generator), coordinate_extractor(coordinate_extractor),
|
||||
name_table(name_table), street_name_suffix_table(street_name_suffix_table)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
node_coordinates(node_coordinates), intersection_generator(intersection_generator),
|
||||
coordinate_extractor(coordinate_extractor), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -63,11 +69,14 @@ bool MergableRoadDetector::CanMergeRoad(const NodeID intersection_node,
|
||||
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
||||
return false;
|
||||
|
||||
const auto &lhs_edge_data = node_based_graph.GetEdgeData(lhs.eid);
|
||||
const auto &rhs_edge_data = node_based_graph.GetEdgeData(rhs.eid);
|
||||
const auto &lhs_edge = node_based_graph.GetEdgeData(lhs.eid);
|
||||
const auto &rhs_edge = node_based_graph.GetEdgeData(rhs.eid);
|
||||
const auto &lhs_edge_data = node_data_container.GetAnnotation(lhs_edge.annotation_data);
|
||||
const auto &rhs_edge_data = node_data_container.GetAnnotation(rhs_edge.annotation_data);
|
||||
|
||||
// and they need to describe the same road
|
||||
if (!EdgeDataSupportsMerge(lhs_edge_data, rhs_edge_data))
|
||||
if ((lhs_edge.reversed == rhs_edge.reversed) ||
|
||||
!EdgeDataSupportsMerge(lhs_edge.flags, rhs_edge.flags, lhs_edge_data, rhs_edge_data))
|
||||
return false;
|
||||
|
||||
/* don't use any circular links, since they mess up detection we jump out early.
|
||||
@@ -120,36 +129,36 @@ bool MergableRoadDetector::IsDistinctFrom(const MergableRoadData &lhs,
|
||||
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
||||
return true;
|
||||
else // or it cannot have the same name
|
||||
return !HaveIdenticalNames(node_based_graph.GetEdgeData(lhs.eid).name_id,
|
||||
node_based_graph.GetEdgeData(rhs.eid).name_id);
|
||||
return !HaveIdenticalNames(
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(lhs.eid).annotation_data)
|
||||
.name_id,
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(rhs.eid).annotation_data)
|
||||
.name_id);
|
||||
}
|
||||
|
||||
bool MergableRoadDetector::EdgeDataSupportsMerge(const util::NodeBasedEdgeData &lhs_edge_data,
|
||||
const util::NodeBasedEdgeData &rhs_edge_data) const
|
||||
bool MergableRoadDetector::EdgeDataSupportsMerge(
|
||||
const NodeBasedEdgeClassification &lhs_flags,
|
||||
const NodeBasedEdgeClassification &rhs_flags,
|
||||
const NodeBasedEdgeAnnotation &lhs_annotation,
|
||||
const NodeBasedEdgeAnnotation &rhs_annotation) const
|
||||
{
|
||||
// roundabouts are special, simply don't hurt them. We might not want to bear the
|
||||
// consequences
|
||||
if (lhs_edge_data.roundabout || rhs_edge_data.roundabout)
|
||||
return false;
|
||||
|
||||
/* to describe the same road, but in opposite directions (which is what we require for a
|
||||
* merge), the roads have to feature one reversed and one non-reversed edge
|
||||
*/
|
||||
if (lhs_edge_data.reversed == rhs_edge_data.reversed)
|
||||
if (lhs_flags.roundabout || rhs_flags.roundabout)
|
||||
return false;
|
||||
|
||||
/* The travel mode should be the same for both roads. If we were to merge different travel
|
||||
* modes, we would hide information/run the risk of loosing valid choices (e.g. short period
|
||||
* of pushing)
|
||||
*/
|
||||
if (lhs_edge_data.travel_mode != rhs_edge_data.travel_mode)
|
||||
if (lhs_annotation.travel_mode != rhs_annotation.travel_mode)
|
||||
return false;
|
||||
|
||||
// we require valid names
|
||||
if (!HaveIdenticalNames(lhs_edge_data.name_id, rhs_edge_data.name_id))
|
||||
if (!HaveIdenticalNames(lhs_annotation.name_id, rhs_annotation.name_id))
|
||||
return false;
|
||||
|
||||
return lhs_edge_data.road_classification == rhs_edge_data.road_classification;
|
||||
return lhs_flags.road_classification == rhs_flags.road_classification;
|
||||
}
|
||||
|
||||
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
|
||||
@@ -172,9 +181,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||
* Since both items have the same id, we can `select` based on any setup
|
||||
*/
|
||||
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||
node_based_graph.GetEdgeData(lhs.eid).name_id, lhs.bearing, /*requires entry=*/false);
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(lhs.eid).annotation_data)
|
||||
.name_id,
|
||||
lhs.bearing,
|
||||
/*requires entry=*/false);
|
||||
|
||||
NodeBasedGraphWalker graph_walker(node_based_graph, intersection_generator);
|
||||
NodeBasedGraphWalker graph_walker(
|
||||
node_based_graph, node_data_container, intersection_generator);
|
||||
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
|
||||
/* if the intersection does not have a right turn, we continue onto the next one once
|
||||
* (skipping over a single small side street)
|
||||
@@ -231,7 +244,7 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||
|
||||
const auto num_lanes = [this](const MergableRoadData &road) {
|
||||
return std::max<std::uint8_t>(
|
||||
node_based_graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes(), 1);
|
||||
node_based_graph.GetEdgeData(road.eid).flags.road_classification.GetNumberOfLanes(), 1);
|
||||
};
|
||||
|
||||
// the width we can bridge at the intersection
|
||||
@@ -265,11 +278,15 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
||||
return false;
|
||||
|
||||
// Find a coordinate following a road that is far away
|
||||
NodeBasedGraphWalker graph_walker(node_based_graph, intersection_generator);
|
||||
NodeBasedGraphWalker graph_walker(
|
||||
node_based_graph, node_data_container, intersection_generator);
|
||||
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||
node_based_graph.GetEdgeData(edge_id).name_id, lhs.bearing, /*requires_entry=*/false);
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(edge_id).annotation_data)
|
||||
.name_id,
|
||||
lhs.bearing,
|
||||
/*requires_entry=*/false);
|
||||
graph_walker.TraverseRoad(intersection_node, edge_id, accumulator, selector);
|
||||
|
||||
return std::make_pair(accumulator.accumulated_length, accumulator.coordinates);
|
||||
@@ -342,9 +359,9 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
||||
coordinates_to_the_right.end());
|
||||
|
||||
const auto lane_count_lhs = std::max<int>(
|
||||
1, node_based_graph.GetEdgeData(lhs.eid).road_classification.GetNumberOfLanes());
|
||||
1, node_based_graph.GetEdgeData(lhs.eid).flags.road_classification.GetNumberOfLanes());
|
||||
const auto lane_count_rhs = std::max<int>(
|
||||
1, node_based_graph.GetEdgeData(rhs.eid).road_classification.GetNumberOfLanes());
|
||||
1, node_based_graph.GetEdgeData(rhs.eid).flags.road_classification.GetNumberOfLanes());
|
||||
|
||||
const auto combined_road_width = 0.5 * (lane_count_lhs + lane_count_rhs) * ASSUMED_LANE_WIDTH;
|
||||
const auto constexpr MAXIMAL_ALLOWED_SEPARATION_WIDTH = 8;
|
||||
@@ -380,10 +397,16 @@ bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node,
|
||||
|
||||
// check if all items share a name
|
||||
const auto range = node_based_graph.GetAdjacentEdgeRange(nid);
|
||||
const auto required_name_id = node_based_graph.GetEdgeData(range.front()).name_id;
|
||||
const auto required_name_id =
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(range.front()).annotation_data)
|
||||
.name_id;
|
||||
|
||||
const auto has_required_name = [this, required_name_id](const auto edge_id) {
|
||||
const auto road_name = node_based_graph.GetEdgeData(edge_id).name_id;
|
||||
const auto road_name =
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(edge_id).annotation_data)
|
||||
.name_id;
|
||||
if (required_name_id == EMPTY_NAMEID || road_name == EMPTY_NAMEID)
|
||||
return false;
|
||||
return !util::guidance::requiresNameAnnounced(
|
||||
@@ -428,14 +451,19 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
||||
const auto next_intersection_along_road = intersection_generator.GetConnectedRoads(
|
||||
next_intersection_parameters.nid, next_intersection_parameters.via_eid);
|
||||
const auto extract_name_id = [this](const MergableRoadData &road) {
|
||||
return node_based_graph.GetEdgeData(road.eid).name_id;
|
||||
return node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.name_id;
|
||||
};
|
||||
|
||||
const auto requested_name_id = extract_name_id(road);
|
||||
const auto next_road_along_path = next_intersection_along_road.findClosestTurn(
|
||||
STRAIGHT_ANGLE,
|
||||
makeCheckRoadForName(
|
||||
requested_name_id, node_based_graph, name_table, street_name_suffix_table));
|
||||
makeCheckRoadForName(requested_name_id,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
name_table,
|
||||
street_name_suffix_table));
|
||||
|
||||
// we need to have a continuing road to successfully detect a link road
|
||||
if (next_road_along_path == next_intersection_along_road.end())
|
||||
@@ -460,9 +488,16 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
||||
// near straight road that continues
|
||||
return angularDeviation(opposite_of_next_road_along_path->angle, next_road_along_path->angle) >=
|
||||
(STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) &&
|
||||
(node_based_graph.GetEdgeData(next_road_along_path->eid).reversed ==
|
||||
node_based_graph.GetEdgeData(opposite_of_next_road_along_path->eid).reversed) &&
|
||||
EdgeDataSupportsMerge(
|
||||
node_based_graph.GetEdgeData(next_road_along_path->eid),
|
||||
node_based_graph.GetEdgeData(opposite_of_next_road_along_path->eid));
|
||||
node_based_graph.GetEdgeData(next_road_along_path->eid).flags,
|
||||
node_based_graph.GetEdgeData(opposite_of_next_road_along_path->eid).flags,
|
||||
node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(next_road_along_path->eid).annotation_data),
|
||||
node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(opposite_of_next_road_along_path->eid)
|
||||
.annotation_data));
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -24,27 +24,29 @@ namespace
|
||||
|
||||
inline bool isMotorwayClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
|
||||
{
|
||||
return node_based_graph.GetEdgeData(eid).road_classification.IsMotorwayClass();
|
||||
return node_based_graph.GetEdgeData(eid).flags.road_classification.IsMotorwayClass();
|
||||
}
|
||||
inline RoadClassification roadClass(const ConnectedRoad &road,
|
||||
const util::NodeBasedDynamicGraph &graph)
|
||||
{
|
||||
return graph.GetEdgeData(road.eid).road_classification;
|
||||
return graph.GetEdgeData(road.eid).flags.road_classification;
|
||||
}
|
||||
|
||||
inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
|
||||
{
|
||||
return node_based_graph.GetEdgeData(eid).road_classification.IsRampClass();
|
||||
return node_based_graph.GetEdgeData(eid).flags.road_classification.IsRampClass();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
@@ -101,7 +103,8 @@ operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
|
||||
|
||||
Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_eid);
|
||||
const auto &in_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_eid).annotation_data);
|
||||
BOOST_ASSERT(isMotorwayClass(via_eid, node_based_graph));
|
||||
|
||||
const auto countExitingMotorways = [this](const Intersection &intersection) {
|
||||
@@ -121,7 +124,8 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
|
||||
if (!road.entry_allowed)
|
||||
continue;
|
||||
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &out_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(road.eid).annotation_data);
|
||||
|
||||
const auto same_name = !util::guidance::requiresNameAnnounced(
|
||||
in_data.name_id, out_data.name_id, name_table, street_name_suffix_table);
|
||||
@@ -353,8 +357,10 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
|
||||
}
|
||||
else if (intersection.size() == 3)
|
||||
{
|
||||
const auto &second_intersection_data = node_based_graph.GetEdgeData(intersection[2].eid);
|
||||
const auto &first_intersection_data = node_based_graph.GetEdgeData(intersection[1].eid);
|
||||
const auto &second_intersection_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(intersection[2].eid).annotation_data);
|
||||
const auto &first_intersection_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(intersection[1].eid).annotation_data);
|
||||
const auto first_second_same_name =
|
||||
!util::guidance::requiresNameAnnounced(second_intersection_data.name_id,
|
||||
first_intersection_data.name_id,
|
||||
|
||||
@@ -15,8 +15,10 @@ namespace guidance
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), intersection_generator(intersection_generator)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
intersection_generator(intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,21 +66,24 @@ boost::optional<EdgeID> SelectRoadByNameOnlyChoiceAndStraightness::
|
||||
operator()(const NodeID /*nid*/,
|
||||
const EdgeID /*via_edge_id*/,
|
||||
const IntersectionView &intersection,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph) const
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container) const
|
||||
{
|
||||
BOOST_ASSERT(!intersection.empty());
|
||||
const auto comparator = [this, &node_based_graph](const IntersectionViewData &lhs,
|
||||
const IntersectionViewData &rhs) {
|
||||
const auto comparator = [&](const IntersectionViewData &lhs, const IntersectionViewData &rhs) {
|
||||
// the score of an elemnt results in an ranking preferring valid entries, if required over
|
||||
// invalid requested name_ids over non-requested narrow deviations over non-narrow
|
||||
const auto score = [this, &node_based_graph](const IntersectionViewData &road) {
|
||||
const auto score = [&](const IntersectionViewData &road) {
|
||||
double result_score = 0;
|
||||
// since angular deviation is limited by 0-180, we add 360 for invalid
|
||||
if (requires_entry && !road.entry_allowed)
|
||||
result_score += 360.;
|
||||
|
||||
// 180 for undesired name-ids
|
||||
if (desired_name_id != node_based_graph.GetEdgeData(road.eid).name_id)
|
||||
if (desired_name_id !=
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.name_id)
|
||||
result_score += 180;
|
||||
|
||||
return result_score + angularDeviation(road.angle, STRAIGHT_ANGLE);
|
||||
@@ -108,24 +113,27 @@ boost::optional<EdgeID> SelectStraightmostRoadByNameAndOnlyChoice::
|
||||
operator()(const NodeID /*nid*/,
|
||||
const EdgeID /*via_edge_id*/,
|
||||
const IntersectionView &intersection,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph) const
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container) const
|
||||
{
|
||||
BOOST_ASSERT(!intersection.empty());
|
||||
if (intersection.size() == 1)
|
||||
return {};
|
||||
|
||||
const auto comparator = [this, &node_based_graph](const IntersectionViewData &lhs,
|
||||
const IntersectionViewData &rhs) {
|
||||
const auto comparator = [&](const IntersectionViewData &lhs, const IntersectionViewData &rhs) {
|
||||
// the score of an elemnt results in an ranking preferring valid entries, if required over
|
||||
// invalid requested name_ids over non-requested narrow deviations over non-narrow
|
||||
const auto score = [this, &node_based_graph](const IntersectionViewData &road) {
|
||||
const auto score = [&](const IntersectionViewData &road) {
|
||||
double result_score = 0;
|
||||
// since angular deviation is limited by 0-180, we add 360 for invalid
|
||||
if (requires_entry && !road.entry_allowed)
|
||||
result_score += 360.;
|
||||
|
||||
// 180 for undesired name-ids
|
||||
if (desired_name_id != node_based_graph.GetEdgeData(road.eid).name_id)
|
||||
if (desired_name_id !=
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.name_id)
|
||||
result_score += 180;
|
||||
|
||||
return result_score + angularDeviation(road.angle, STRAIGHT_ANGLE);
|
||||
@@ -135,11 +143,11 @@ operator()(const NodeID /*nid*/,
|
||||
};
|
||||
|
||||
const auto count_desired_name =
|
||||
std::count_if(std::begin(intersection),
|
||||
std::end(intersection),
|
||||
[this, &node_based_graph](const auto &road) {
|
||||
return node_based_graph.GetEdgeData(road.eid).name_id == desired_name_id;
|
||||
});
|
||||
std::count_if(std::begin(intersection), std::end(intersection), [&](const auto &road) {
|
||||
return node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.name_id == desired_name_id;
|
||||
});
|
||||
if (count_desired_name > 2)
|
||||
return {};
|
||||
|
||||
@@ -149,7 +157,9 @@ operator()(const NodeID /*nid*/,
|
||||
const auto is_valid_choice = !requires_entry || min_element->entry_allowed;
|
||||
const auto is_only_choice_with_same_name =
|
||||
count_desired_name <= 2 && // <= in case we come from a bridge
|
||||
node_based_graph.GetEdgeData(min_element->eid).name_id == desired_name_id &&
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(min_element->eid).annotation_data)
|
||||
.name_id == desired_name_id &&
|
||||
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < 100; // don't do crazy turns
|
||||
const auto has_valid_angle =
|
||||
((intersection.size() == 2 ||
|
||||
|
||||
@@ -24,18 +24,19 @@ namespace guidance
|
||||
{
|
||||
|
||||
RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const ProfileProperties &profile_properties,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
compressed_edge_container(compressed_edge_container), profile_properties(profile_properties),
|
||||
compressed_edge_container(compressed_edge_container),
|
||||
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
|
||||
{
|
||||
}
|
||||
@@ -70,22 +71,24 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
|
||||
const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const
|
||||
{
|
||||
const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
|
||||
bool on_roundabout = in_edge_data.roundabout || in_edge_data.circular;
|
||||
const auto &in_edge_class = in_edge_data.flags;
|
||||
bool on_roundabout = in_edge_class.roundabout || in_edge_class.circular;
|
||||
bool can_enter_roundabout = false;
|
||||
bool can_exit_roundabout_separately = false;
|
||||
|
||||
const bool lhs = in_edge_data.is_left_hand_driving;
|
||||
const bool lhs =
|
||||
node_data_container.GetAnnotation(in_edge_data.annotation_data).is_left_hand_driving;
|
||||
const int step = lhs ? -1 : 1;
|
||||
for (std::size_t cnt = 0, idx = lhs ? intersection.size() - 1 : 0; cnt < intersection.size();
|
||||
++cnt, idx += step)
|
||||
{
|
||||
const auto &road = intersection[idx];
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &edge = node_based_graph.GetEdgeData(road.eid);
|
||||
// only check actual outgoing edges
|
||||
if (edge_data.reversed || !road.entry_allowed)
|
||||
if (edge.reversed || !road.entry_allowed)
|
||||
continue;
|
||||
|
||||
if (edge_data.roundabout || edge_data.circular)
|
||||
if (edge.flags.roundabout || edge.flags.circular)
|
||||
{
|
||||
can_enter_roundabout = true;
|
||||
}
|
||||
@@ -108,8 +111,8 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection &intersection) const
|
||||
{
|
||||
const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
|
||||
if (in_edge_data.roundabout || in_edge_data.circular)
|
||||
const auto &in_edge_class = node_based_graph.GetEdgeData(via_eid).flags;
|
||||
if (in_edge_class.roundabout || in_edge_class.circular)
|
||||
return;
|
||||
|
||||
// Find range in which exits that must be invalidated (shaded areas):
|
||||
@@ -136,10 +139,10 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
auto invalidate_from = intersection.end(), invalidate_to = intersection.end();
|
||||
for (auto road = intersection.begin(); road != intersection.end(); ++road)
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(road->eid);
|
||||
if (edge_data.roundabout || edge_data.circular)
|
||||
const auto &edge = node_based_graph.GetEdgeData(road->eid);
|
||||
if (edge.flags.roundabout || edge.flags.circular)
|
||||
{
|
||||
if (edge_data.reversed)
|
||||
if (edge.reversed)
|
||||
{
|
||||
if (roundabout_entry_first)
|
||||
{ // invalidate turns in range exit..end
|
||||
@@ -166,8 +169,8 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
// u-turn against the roundabout direction is invalidated.
|
||||
for (; invalidate_from != invalidate_to; ++invalidate_from)
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(invalidate_from->eid);
|
||||
if (!edge_data.roundabout && !edge_data.circular &&
|
||||
const auto &edge = node_based_graph.GetEdgeData(invalidate_from->eid);
|
||||
if (!edge.flags.roundabout && !edge.flags.circular &&
|
||||
node_based_graph.GetTarget(invalidate_from->eid) != from_nid)
|
||||
{
|
||||
invalidate_from->entry_allowed = false;
|
||||
@@ -207,8 +210,8 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
|
||||
// can only contain a single further road
|
||||
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto edge_data = node_based_graph.GetEdgeData(edge);
|
||||
if (edge_data.roundabout || edge_data.circular)
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(edge);
|
||||
if (edge_data.flags.roundabout || edge_data.flags.circular)
|
||||
continue;
|
||||
|
||||
// there is a single non-roundabout edge
|
||||
@@ -222,7 +225,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
|
||||
[this](const auto current_max, const auto current_eid) {
|
||||
return std::max(current_max,
|
||||
node_based_graph.GetEdgeData(current_eid)
|
||||
.road_classification.GetNumberOfLanes());
|
||||
.flags.road_classification.GetNumberOfLanes());
|
||||
});
|
||||
|
||||
const auto next_coordinate =
|
||||
@@ -279,11 +282,12 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
||||
const NodeID node, const bool roundabout, const bool circular) {
|
||||
BOOST_ASSERT(roundabout != circular);
|
||||
EdgeID continue_edge = SPECIAL_EDGEID;
|
||||
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
|
||||
for (const auto edge_id : node_based_graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(edge);
|
||||
if (!edge_data.reversed && (edge_data.circular == circular) &&
|
||||
(edge_data.roundabout == roundabout))
|
||||
const auto &edge = node_based_graph.GetEdgeData(edge_id);
|
||||
const auto &edge_data = node_data_container.GetAnnotation(edge.annotation_data);
|
||||
if (!edge.reversed && (edge.flags.circular == circular) &&
|
||||
(edge.flags.roundabout == roundabout))
|
||||
{
|
||||
if (SPECIAL_EDGEID != continue_edge)
|
||||
{
|
||||
@@ -303,9 +307,9 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
||||
roundabout_name_ids.insert(edge_data.name_id);
|
||||
}
|
||||
|
||||
continue_edge = edge;
|
||||
continue_edge = edge_id;
|
||||
}
|
||||
else if (!edge_data.roundabout && !edge_data.circular)
|
||||
else if (!edge.flags.roundabout && !edge.flags.circular)
|
||||
{
|
||||
// remember all connected road names
|
||||
connected_names.insert(edge_data.name_id);
|
||||
@@ -322,7 +326,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
||||
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(at_node))
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(edge);
|
||||
if (edge_data.roundabout || edge_data.circular)
|
||||
if (edge_data.flags.roundabout || edge_data.flags.circular)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
@@ -353,7 +357,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
||||
bool roundabout = false, circular = false;
|
||||
for (const auto eid : node_based_graph.GetAdjacentEdgeRange(nid))
|
||||
{
|
||||
const auto data = node_based_graph.GetEdgeData(eid);
|
||||
const auto data = node_based_graph.GetEdgeData(eid).flags;
|
||||
roundabout |= data.roundabout;
|
||||
circular |= data.circular;
|
||||
}
|
||||
@@ -435,7 +439,8 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
|
||||
NodeID node_at_center_of_intersection = node_based_graph.GetTarget(via_eid);
|
||||
const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
|
||||
|
||||
const bool lhs = in_edge_data.is_left_hand_driving;
|
||||
const bool lhs =
|
||||
node_data_container.GetAnnotation(in_edge_data.annotation_data).is_left_hand_driving;
|
||||
const int step = lhs ? -1 : 1;
|
||||
|
||||
if (on_roundabout)
|
||||
@@ -448,7 +453,8 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
|
||||
{
|
||||
auto &road = intersection[idx];
|
||||
auto &turn = road;
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.eid).flags;
|
||||
;
|
||||
if (out_data.roundabout || out_data.circular)
|
||||
{
|
||||
// TODO can forks happen in roundabouts? E.g. required lane changes
|
||||
@@ -471,11 +477,10 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
|
||||
for (const auto eid :
|
||||
node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
const auto &data_of_leaving_edge = node_based_graph.GetEdgeData(eid);
|
||||
if (!data_of_leaving_edge.reversed &&
|
||||
!data_of_leaving_edge.roundabout &&
|
||||
!data_of_leaving_edge.circular &&
|
||||
!data_of_leaving_edge.road_classification.IsLowPriorityRoadClass())
|
||||
const auto &leaving_edge = node_based_graph.GetEdgeData(eid);
|
||||
if (!leaving_edge.reversed && !leaving_edge.flags.roundabout &&
|
||||
!leaving_edge.flags.circular &&
|
||||
!leaving_edge.flags.road_classification.IsLowPriorityRoadClass())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -511,7 +516,7 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
|
||||
++cnt, idx += step)
|
||||
{
|
||||
auto &turn = intersection[idx];
|
||||
const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(turn.eid).flags;
|
||||
|
||||
// A roundabout consists of exactly two roads at an intersection. by toggeling this
|
||||
// flag, we can switch between roads crossing the roundabout and roads that are on the
|
||||
|
||||
@@ -23,10 +23,12 @@ namespace guidance
|
||||
|
||||
SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_generator,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
@@ -109,7 +111,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid).flags;
|
||||
|
||||
auto is_roundabout = road_data.roundabout;
|
||||
|
||||
@@ -156,7 +158,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
|
||||
for (const auto &road : main_road_intersection->intersection)
|
||||
{
|
||||
const auto &target_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto target_annotation_id = node_based_graph.GetEdgeData(road.eid).annotation_data;
|
||||
const auto &target_data = node_data_container.GetAnnotation(target_annotation_id);
|
||||
target_road_name_ids.push_back(target_data.name_id);
|
||||
}
|
||||
|
||||
@@ -173,7 +176,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
continue;
|
||||
|
||||
auto &sliproad = intersection[road_index]; // this is what we're checking and assigning to
|
||||
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid);
|
||||
const auto &sliproad_edge_data = node_based_graph.GetEdgeData(sliproad.eid);
|
||||
|
||||
// Intersection is orderd: 0 is UTurn, then from sharp right to sharp left.
|
||||
// We already have an obvious index (bc) for going straight-ish.
|
||||
@@ -199,13 +202,13 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
}
|
||||
|
||||
// Discard service and other low priority roads - never Sliproad candidate
|
||||
if (sliproad_data.road_classification.IsLowPriorityRoadClass())
|
||||
if (sliproad_edge_data.flags.road_classification.IsLowPriorityRoadClass())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Incoming-only can never be a Sliproad
|
||||
if (sliproad_data.reversed)
|
||||
if (sliproad_edge_data.reversed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -350,7 +353,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &onto_data = node_based_graph.GetEdgeData(onto.eid);
|
||||
const auto &onto_data = node_based_graph.GetEdgeData(onto.eid).flags;
|
||||
|
||||
if (onto_data.roundabout)
|
||||
{
|
||||
@@ -434,7 +437,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
|
||||
// Check sliproads with skew main intersections
|
||||
if (deviation_from_straight > 180. - minimal_crossroad_angle_of_intersection &&
|
||||
!node_based_graph.GetEdgeData(sliproad.eid).road_classification.IsLinkClass())
|
||||
!node_based_graph.GetEdgeData(sliproad.eid)
|
||||
.flags.road_classification.IsLinkClass())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -449,9 +453,10 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
// ` .
|
||||
// d
|
||||
//
|
||||
const auto area_threshold = std::pow(
|
||||
scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, sliproad_data.road_classification),
|
||||
2.);
|
||||
const auto area_threshold =
|
||||
std::pow(scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD,
|
||||
sliproad_edge_data.flags.road_classification),
|
||||
2.);
|
||||
|
||||
if (!isValidSliproadArea(area_threshold,
|
||||
intersection_node_id,
|
||||
@@ -464,7 +469,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
// Check all roads at `d` if one is connected to `c`, is so `bd` is Sliproad.
|
||||
for (const auto &candidate_road : target_intersection)
|
||||
{
|
||||
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.eid);
|
||||
const auto &candidate_data = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(candidate_road.eid).annotation_data);
|
||||
|
||||
// Name mismatch: check roads at `c` and `d` for same name
|
||||
const auto name_mismatch = [&](const NameID road_name_id) {
|
||||
@@ -489,15 +495,20 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
|
||||
// Check if main road -> sliproad (non-link) -> candidate road requires two name
|
||||
// announcements then don't suppress one announcement via sliproad handler
|
||||
const auto main_road_name_id = node_based_graph.GetEdgeData(main_road.eid).name_id;
|
||||
if (!sliproad_data.road_classification.IsLinkClass() &&
|
||||
sliproad_data.name_id != EMPTY_NAMEID && main_road_name_id != EMPTY_NAMEID &&
|
||||
const auto main_road_name_id =
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(main_road.eid).annotation_data)
|
||||
.name_id;
|
||||
const auto &sliproad_annotation =
|
||||
node_data_container.GetAnnotation(sliproad_edge_data.annotation_data);
|
||||
if (!sliproad_edge_data.flags.road_classification.IsLinkClass() &&
|
||||
sliproad_annotation.name_id != EMPTY_NAMEID && main_road_name_id != EMPTY_NAMEID &&
|
||||
candidate_data.name_id != EMPTY_NAMEID &&
|
||||
util::guidance::requiresNameAnnounced(main_road_name_id,
|
||||
sliproad_data.name_id,
|
||||
sliproad_annotation.name_id,
|
||||
name_table,
|
||||
street_name_suffix_table) &&
|
||||
util::guidance::requiresNameAnnounced(sliproad_data.name_id,
|
||||
util::guidance::requiresNameAnnounced(sliproad_annotation.name_id,
|
||||
candidate_data.name_id,
|
||||
name_table,
|
||||
street_name_suffix_table))
|
||||
@@ -553,6 +564,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
// In those cases the obvious non-Sliproad is now obvious and we discard the Fork turn type.
|
||||
if (sliproad_found && main_road.instruction.type == TurnType::Fork)
|
||||
{
|
||||
const auto &main_data = node_based_graph.GetEdgeData(main_road.eid);
|
||||
const auto &main_annotation = node_data_container.GetAnnotation(main_data.annotation_data);
|
||||
if (isSameName(source_edge_id, main_road.eid))
|
||||
{
|
||||
if (angularDeviation(main_road.angle, STRAIGHT_ANGLE) < 5)
|
||||
@@ -562,7 +575,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
intersection[*obvious].instruction.direction_modifier =
|
||||
getTurnDirection(intersection[*obvious].angle);
|
||||
}
|
||||
else if (node_based_graph.GetEdgeData(main_road.eid).name_id != EMPTY_NAMEID)
|
||||
else if (main_annotation.name_id != EMPTY_NAMEID)
|
||||
{
|
||||
intersection[*obvious].instruction.type = TurnType::NewName;
|
||||
intersection[*obvious].instruction.direction_modifier =
|
||||
@@ -644,7 +657,7 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
|
||||
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
||||
|
||||
// Base max distance threshold on the current road class we're on
|
||||
const auto &data = node_based_graph.GetEdgeData(onto);
|
||||
const auto &data = node_based_graph.GetEdgeData(onto).flags;
|
||||
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
|
||||
data.road_classification);
|
||||
|
||||
@@ -662,13 +675,15 @@ bool SliproadHandler::isThroughStreet(const EdgeID from, const IntersectionView
|
||||
BOOST_ASSERT(from != SPECIAL_EDGEID);
|
||||
BOOST_ASSERT(!intersection.empty());
|
||||
|
||||
const auto &edge_name_id = node_based_graph.GetEdgeData(from).name_id;
|
||||
const auto from_annotation_id = node_based_graph.GetEdgeData(from).annotation_data;
|
||||
const auto &edge_name_id = node_data_container.GetAnnotation(from_annotation_id).name_id;
|
||||
|
||||
auto first = begin(intersection) + 1; // Skip UTurn road
|
||||
auto last = end(intersection);
|
||||
|
||||
auto same_name = [&](const auto &road) {
|
||||
const auto &road_name_id = node_based_graph.GetEdgeData(road.eid).name_id;
|
||||
const auto annotation_id = node_based_graph.GetEdgeData(road.eid).annotation_data;
|
||||
const auto &road_name_id = node_data_container.GetAnnotation(annotation_id).name_id;
|
||||
|
||||
return edge_name_id != EMPTY_NAMEID && //
|
||||
road_name_id != EMPTY_NAMEID && //
|
||||
@@ -683,10 +698,13 @@ bool SliproadHandler::isThroughStreet(const EdgeID from, const IntersectionView
|
||||
|
||||
bool SliproadHandler::roadContinues(const EdgeID current, const EdgeID next) const
|
||||
{
|
||||
const auto ¤t_data = node_based_graph.GetEdgeData(current);
|
||||
const auto &next_data = node_based_graph.GetEdgeData(next);
|
||||
const auto ¤t_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(current).annotation_data);
|
||||
const auto &next_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(next).annotation_data);
|
||||
|
||||
auto same_road_category = current_data.road_classification == next_data.road_classification;
|
||||
auto same_road_category = node_based_graph.GetEdgeData(current).flags.road_classification ==
|
||||
node_based_graph.GetEdgeData(next).flags.road_classification;
|
||||
auto same_travel_mode = current_data.travel_mode == next_data.travel_mode;
|
||||
|
||||
auto same_name = current_data.name_id != EMPTY_NAMEID && //
|
||||
@@ -730,22 +748,18 @@ bool SliproadHandler::isValidSliproadArea(const double max_area,
|
||||
}
|
||||
|
||||
bool SliproadHandler::isValidSliproadLink(const IntersectionViewData &sliproad,
|
||||
const IntersectionViewData &first,
|
||||
const IntersectionViewData & /*first*/,
|
||||
const IntersectionViewData &second) const
|
||||
{
|
||||
// If the sliproad is not a link we don't care
|
||||
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid);
|
||||
// If the Sliproad is not a link we don't care
|
||||
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid).flags;
|
||||
if (!sliproad_data.road_classification.IsLinkClass())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise the first road leading to the intersection we shortcut
|
||||
// can be a link or a usual road (ignore the check at this place)
|
||||
(void)first;
|
||||
|
||||
// and the second road coming from the intersection we shortcut must be a non-link
|
||||
const auto &second_road_data = node_based_graph.GetEdgeData(second.eid);
|
||||
const auto &second_road_data = node_based_graph.GetEdgeData(second.eid).flags;
|
||||
if (second_road_data.road_classification.IsLinkClass())
|
||||
{
|
||||
return false;
|
||||
@@ -758,10 +772,15 @@ bool SliproadHandler::allSameMode(const EdgeID from,
|
||||
const EdgeID sliproad_candidate,
|
||||
const EdgeID target_road) const
|
||||
{
|
||||
return node_based_graph.GetEdgeData(from).travel_mode ==
|
||||
node_based_graph.GetEdgeData(sliproad_candidate).travel_mode &&
|
||||
node_based_graph.GetEdgeData(sliproad_candidate).travel_mode ==
|
||||
node_based_graph.GetEdgeData(target_road).travel_mode;
|
||||
const auto &from_annotation =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(from).annotation_data);
|
||||
const auto &sliproad_annotation = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(sliproad_candidate).annotation_data);
|
||||
const auto &target_annotation = node_data_container.GetAnnotation(
|
||||
node_based_graph.GetEdgeData(target_road).annotation_data);
|
||||
|
||||
return (from_annotation.travel_mode == sliproad_annotation.travel_mode) &&
|
||||
(target_annotation.travel_mode == sliproad_annotation.travel_mode);
|
||||
}
|
||||
|
||||
bool SliproadHandler::canBeTargetOfSliproad(const IntersectionView &intersection)
|
||||
|
||||
@@ -13,10 +13,12 @@ namespace guidance
|
||||
|
||||
SuppressModeHandler::SuppressModeHandler(const IntersectionGenerator &intersection_generator,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
@@ -36,14 +38,18 @@ bool SuppressModeHandler::canProcess(const NodeID,
|
||||
|
||||
// If the approach way is not on the suppression blacklist, and not all the exit ways share that
|
||||
// mode, there are no ways to suppress by this criteria.
|
||||
const auto in_mode = node_based_graph.GetEdgeData(via_eid).travel_mode;
|
||||
const auto in_mode =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_eid).annotation_data)
|
||||
.travel_mode;
|
||||
const auto suppress_in_mode = std::find(begin(suppressed), end(suppressed), in_mode);
|
||||
|
||||
const auto first = begin(intersection);
|
||||
const auto last = end(intersection);
|
||||
|
||||
const auto all_share_mode = std::all_of(first, last, [this, &in_mode](const auto &road) {
|
||||
return node_based_graph.GetEdgeData(road.eid).travel_mode == in_mode;
|
||||
return node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.travel_mode == in_mode;
|
||||
});
|
||||
|
||||
return (suppress_in_mode != end(suppressed)) && all_share_mode;
|
||||
|
||||
@@ -21,56 +21,61 @@ namespace guidance
|
||||
|
||||
using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
|
||||
|
||||
bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
|
||||
{
|
||||
return !from.CanCombineWith(to);
|
||||
}
|
||||
|
||||
TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const ProfileProperties &profile_properties)
|
||||
: node_based_graph(node_based_graph),
|
||||
intersection_generator(
|
||||
node_based_graph, restriction_map, barrier_nodes, coordinates, compressed_edge_container),
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: node_based_graph(node_based_graph), intersection_generator(node_based_graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
coordinates,
|
||||
compressed_edge_container),
|
||||
intersection_normalizer(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
roundabout_handler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
compressed_edge_container,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
profile_properties,
|
||||
intersection_generator),
|
||||
motorway_handler(node_based_graph,
|
||||
node_data_container,
|
||||
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
turn_handler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
sliproad_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
suppress_mode_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
driveway_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
@@ -163,7 +168,7 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti
|
||||
node_prior_to_intersection, entering_via_edge, std::move(intersection));
|
||||
|
||||
// Turn On Ramps Into Off Ramps, if we come from a motorway-like road
|
||||
if (node_based_graph.GetEdgeData(entering_via_edge).road_classification.IsMotorwayClass())
|
||||
if (node_based_graph.GetEdgeData(entering_via_edge).flags.road_classification.IsMotorwayClass())
|
||||
{
|
||||
std::for_each(intersection.begin(), intersection.end(), [](ConnectedRoad &road) {
|
||||
if (road.instruction.type == TurnType::OnRamp)
|
||||
|
||||
@@ -111,11 +111,13 @@ std::size_t TurnHandler::Fork::getLeftIndex() const
|
||||
}
|
||||
|
||||
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
@@ -174,12 +176,16 @@ bool TurnHandler::isObviousOfTwo(const EdgeID via_edge,
|
||||
const ConnectedRoad &road,
|
||||
const ConnectedRoad &other) const
|
||||
{
|
||||
const auto &via_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const auto &other_data = node_based_graph.GetEdgeData(other.eid);
|
||||
const auto &via_classification = via_data.road_classification;
|
||||
const auto &road_classification = road_data.road_classification;
|
||||
const auto &other_classification = other_data.road_classification;
|
||||
const auto &via_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data);
|
||||
const auto &road_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data);
|
||||
const auto &via_classification =
|
||||
node_based_graph.GetEdgeData(via_edge).flags.road_classification;
|
||||
const auto &road_classification =
|
||||
node_based_graph.GetEdgeData(road.eid).flags.road_classification;
|
||||
const auto &other_classification =
|
||||
node_based_graph.GetEdgeData(other.eid).flags.road_classification;
|
||||
|
||||
// if one of the given roads is obvious by class, obviousness is trivial
|
||||
if (obviousByRoadClass(via_classification, road_classification, other_classification))
|
||||
@@ -335,9 +341,9 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
|
||||
if (fork->size == 2)
|
||||
{
|
||||
const auto left_classification =
|
||||
node_based_graph.GetEdgeData(fork->getLeft().eid).road_classification;
|
||||
node_based_graph.GetEdgeData(fork->getLeft().eid).flags.road_classification;
|
||||
const auto right_classification =
|
||||
node_based_graph.GetEdgeData(fork->getRight().eid).road_classification;
|
||||
node_based_graph.GetEdgeData(fork->getRight().eid).flags.road_classification;
|
||||
if (canBeSeenAsFork(left_classification, right_classification))
|
||||
{
|
||||
assignFork(via_edge, fork->getLeft(), fork->getRight());
|
||||
@@ -615,26 +621,27 @@ TurnHandler::findForkCandidatesByGeometry(Intersection &intersection) const
|
||||
// incoming edge are compatible by class
|
||||
bool TurnHandler::isCompatibleByRoadClass(const Intersection &intersection, const Fork fork) const
|
||||
{
|
||||
const auto via_class = node_based_graph.GetEdgeData(intersection[0].eid).road_classification;
|
||||
const auto via_class =
|
||||
node_based_graph.GetEdgeData(intersection[0].eid).flags.road_classification;
|
||||
|
||||
// if any of the considered roads is a link road, it cannot be a fork
|
||||
// except if rightmost fork candidate is also a link road
|
||||
const auto is_right_link_class =
|
||||
node_based_graph.GetEdgeData(fork.getRight().eid).road_classification.IsLinkClass();
|
||||
node_based_graph.GetEdgeData(fork.getRight().eid).flags.road_classification.IsLinkClass();
|
||||
if (!std::all_of(fork.begin + 1, fork.end, [&](ConnectedRoad &road) {
|
||||
return is_right_link_class ==
|
||||
node_based_graph.GetEdgeData(road.eid).road_classification.IsLinkClass();
|
||||
node_based_graph.GetEdgeData(road.eid).flags.road_classification.IsLinkClass();
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::all_of(fork.begin, fork.end, [&](ConnectedRoad &base) {
|
||||
const auto base_class = node_based_graph.GetEdgeData(base.eid).road_classification;
|
||||
const auto base_class = node_based_graph.GetEdgeData(base.eid).flags.road_classification;
|
||||
// check that there is no turn obvious == check that all turns are non-onvious
|
||||
return std::all_of(fork.begin, fork.end, [&](ConnectedRoad &compare) {
|
||||
const auto compare_class =
|
||||
node_based_graph.GetEdgeData(compare.eid).road_classification;
|
||||
node_based_graph.GetEdgeData(compare.eid).flags.road_classification;
|
||||
return compare.eid == base.eid ||
|
||||
!(obviousByRoadClass(via_class, base_class, compare_class));
|
||||
});
|
||||
@@ -670,8 +677,12 @@ boost::optional<TurnHandler::Fork> TurnHandler::findFork(const EdgeID via_edge,
|
||||
|
||||
const auto has_compatible_modes =
|
||||
std::all_of(fork->begin, fork->end, [&](const auto &road) {
|
||||
return node_based_graph.GetEdgeData(road.eid).travel_mode ==
|
||||
node_based_graph.GetEdgeData(via_edge).travel_mode;
|
||||
return node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
.travel_mode ==
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data)
|
||||
.travel_mode;
|
||||
});
|
||||
|
||||
if (separated_at_left_side && separated_at_right_side && !has_obvious &&
|
||||
@@ -703,9 +714,10 @@ void TurnHandler::handleDistinctConflict(const EdgeID via_edge,
|
||||
getTurnDirection(left.angle) == DirectionModifier::SlightLeft ||
|
||||
getTurnDirection(right.angle) == DirectionModifier::SlightRight)
|
||||
{
|
||||
const auto left_classification = node_based_graph.GetEdgeData(left.eid).road_classification;
|
||||
const auto left_classification =
|
||||
node_based_graph.GetEdgeData(left.eid).flags.road_classification;
|
||||
const auto right_classification =
|
||||
node_based_graph.GetEdgeData(right.eid).road_classification;
|
||||
node_based_graph.GetEdgeData(right.eid).flags.road_classification;
|
||||
|
||||
if (left_classification.GetPriority() > right_classification.GetPriority())
|
||||
{
|
||||
|
||||
@@ -34,11 +34,12 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
} // namespace
|
||||
|
||||
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
LaneDescriptionMap &lane_description_map,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
util::guidance::LaneDataIdMap &id_map)
|
||||
: node_based_graph(node_based_graph), lane_description_map(lane_description_map),
|
||||
turn_analysis(turn_analysis), id_map(id_map)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
lane_description_map(lane_description_map), turn_analysis(turn_analysis), id_map(id_map)
|
||||
{
|
||||
std::tie(turn_lane_offsets, turn_lane_masks) =
|
||||
transformTurnLaneMapIntoArrays(lane_description_map);
|
||||
@@ -149,7 +150,7 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
LaneDescriptionID &previous_description_id)
|
||||
{
|
||||
// as long as we don't want to emit lanes on roundabout, don't assign them
|
||||
if (node_based_graph.GetEdgeData(via_edge).roundabout)
|
||||
if (node_based_graph.GetEdgeData(via_edge).flags.roundabout)
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
// really don't touch roundabouts (#2626)
|
||||
@@ -179,7 +180,9 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
(intersection.size() == 2 &&
|
||||
((lane_description_id != INVALID_LANE_DESCRIPTIONID &&
|
||||
lane_description_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].eid).lane_description_id) &&
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(intersection[1].eid).annotation_data)
|
||||
.lane_description_id) &&
|
||||
angularDeviation(intersection[1].angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE));
|
||||
|
||||
if (is_going_straight_and_turns_continue)
|
||||
@@ -367,7 +370,8 @@ void TurnLaneHandler::extractLaneData(const EdgeID via_edge,
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data) const
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &edge_data =
|
||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data);
|
||||
lane_description_id = edge_data.lane_description_id;
|
||||
// create an empty lane data
|
||||
if (INVALID_LANE_DESCRIPTIONID != lane_description_id)
|
||||
@@ -719,9 +723,13 @@ Intersection TurnLaneHandler::handleSliproadTurn(Intersection intersection,
|
||||
return previous_intersection[sliproad_index + 1];
|
||||
}();
|
||||
const auto main_description_id =
|
||||
node_based_graph.GetEdgeData(main_road.eid).lane_description_id;
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(main_road.eid).annotation_data)
|
||||
.lane_description_id;
|
||||
const auto sliproad_description_id =
|
||||
node_based_graph.GetEdgeData(sliproad.eid).lane_description_id;
|
||||
node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(sliproad.eid).annotation_data)
|
||||
.lane_description_id;
|
||||
|
||||
if (main_description_id == INVALID_LANE_DESCRIPTIONID ||
|
||||
sliproad_description_id == INVALID_LANE_DESCRIPTIONID)
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "extractor/node_based_graph_factory.hpp"
|
||||
#include "extractor/graph_compressor.hpp"
|
||||
#include "storage/io.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
NodeBasedGraphFactory::NodeBasedGraphFactory(
|
||||
const boost::filesystem::path &input_file,
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions)
|
||||
{
|
||||
LoadDataFromFile(input_file);
|
||||
Compress(scripting_environment, turn_restrictions, conditional_turn_restrictions);
|
||||
CompressGeometry();
|
||||
CompressAnnotationData();
|
||||
}
|
||||
|
||||
// load the data serialised during the extraction run
|
||||
void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &input_file)
|
||||
{
|
||||
// the extraction_containers serialise all data necessary to create the node-based graph into a
|
||||
// single file, the *.osrm file. It contains nodes, basic information about which of these nodes
|
||||
// are traffic signals/stop signs. It also contains Edges and purely annotative meta-data
|
||||
storage::io::FileReader file_reader(input_file, storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
auto barriers_iter = inserter(barriers, end(barriers));
|
||||
auto traffic_signals_iter = inserter(traffic_signals, end(traffic_signals));
|
||||
|
||||
const auto number_of_node_based_nodes = util::loadNodesFromFile(
|
||||
file_reader, barriers_iter, traffic_signals_iter, coordinates, osm_node_ids);
|
||||
|
||||
std::vector<NodeBasedEdge> edge_list;
|
||||
util::loadEdgesFromFile(file_reader, edge_list);
|
||||
|
||||
if (edge_list.empty())
|
||||
{
|
||||
throw util::exception("Node-based-graph (" + input_file.string() + ") contains no edges." +
|
||||
SOURCE_REF);
|
||||
}
|
||||
|
||||
util::loadAnnotationData(file_reader, annotation_data);
|
||||
|
||||
// at this point, the data isn't compressed, but since we update the graph in-place, we assign
|
||||
// it here.
|
||||
compressed_output_graph =
|
||||
util::NodeBasedDynamicGraphFromEdges(number_of_node_based_nodes, edge_list);
|
||||
|
||||
// check whether the graph is sane
|
||||
BOOST_ASSERT([this]() {
|
||||
for (const auto nbg_node_u : util::irange(0u, compressed_output_graph.GetNumberOfNodes()))
|
||||
{
|
||||
for (EdgeID nbg_edge_id : compressed_output_graph.GetAdjacentEdgeRange(nbg_node_u))
|
||||
{
|
||||
// we cannot have invalid edge-ids in the graph
|
||||
if (nbg_edge_id == SPECIAL_EDGEID)
|
||||
return false;
|
||||
|
||||
const auto nbg_node_v = compressed_output_graph.GetTarget(nbg_edge_id);
|
||||
|
||||
auto reverse = compressed_output_graph.FindEdge(nbg_node_v, nbg_node_u);
|
||||
|
||||
// found an edge that is reversed in both directions, should be two distinct edges
|
||||
if (compressed_output_graph.GetEdgeData(nbg_edge_id).reversed &&
|
||||
compressed_output_graph.GetEdgeData(reverse).reversed)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
void NodeBasedGraphFactory::Compress(
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions)
|
||||
{
|
||||
GraphCompressor graph_compressor;
|
||||
graph_compressor.Compress(barriers,
|
||||
traffic_signals,
|
||||
scripting_environment,
|
||||
turn_restrictions,
|
||||
conditional_turn_restrictions,
|
||||
compressed_output_graph,
|
||||
annotation_data,
|
||||
compressed_edge_container);
|
||||
}
|
||||
|
||||
void NodeBasedGraphFactory::CompressGeometry()
|
||||
{
|
||||
for (const auto nbg_node_u : util::irange(0u, compressed_output_graph.GetNumberOfNodes()))
|
||||
{
|
||||
for (EdgeID nbg_edge_id : compressed_output_graph.GetAdjacentEdgeRange(nbg_node_u))
|
||||
{
|
||||
BOOST_ASSERT(nbg_edge_id != SPECIAL_EDGEID);
|
||||
|
||||
const auto &nbg_edge_data = compressed_output_graph.GetEdgeData(nbg_edge_id);
|
||||
const auto nbg_node_v = compressed_output_graph.GetTarget(nbg_edge_id);
|
||||
BOOST_ASSERT(nbg_node_v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(nbg_node_u != nbg_node_v);
|
||||
|
||||
// pick only every other edge, since we have every edge as an outgoing
|
||||
// and incoming egde
|
||||
if (nbg_node_u >= nbg_node_v)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto from = nbg_node_u, to = nbg_node_v;
|
||||
// if we found a non-forward edge reverse and try again
|
||||
if (nbg_edge_data.reversed)
|
||||
std::swap(from, to);
|
||||
|
||||
// find forward edge id and
|
||||
const EdgeID edge_id_1 = compressed_output_graph.FindEdge(from, to);
|
||||
BOOST_ASSERT(edge_id_1 != SPECIAL_EDGEID);
|
||||
|
||||
// find reverse edge id and
|
||||
const EdgeID edge_id_2 = compressed_output_graph.FindEdge(to, from);
|
||||
BOOST_ASSERT(edge_id_2 != SPECIAL_EDGEID);
|
||||
|
||||
auto packed_geometry_id = compressed_edge_container.ZipEdges(edge_id_1, edge_id_2);
|
||||
|
||||
// remember the geometry ID for both edges in the node-based graph
|
||||
compressed_output_graph.GetEdgeData(edge_id_1).geometry_id = {packed_geometry_id, true};
|
||||
compressed_output_graph.GetEdgeData(edge_id_2).geometry_id = {packed_geometry_id,
|
||||
false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeBasedGraphFactory::CompressAnnotationData()
|
||||
{
|
||||
const constexpr AnnotationID INVALID_ANNOTATIONID = -1;
|
||||
// remap all entries to find which are used
|
||||
std::vector<AnnotationID> annotation_mapping(annotation_data.size(), INVALID_ANNOTATIONID);
|
||||
|
||||
// first we mark entries, by setting their mapping to 0
|
||||
for (const auto nbg_node_u : util::irange(0u, compressed_output_graph.GetNumberOfNodes()))
|
||||
{
|
||||
BOOST_ASSERT(nbg_node_u != SPECIAL_NODEID);
|
||||
for (EdgeID nbg_edge_id : compressed_output_graph.GetAdjacentEdgeRange(nbg_node_u))
|
||||
{
|
||||
auto const &edge = compressed_output_graph.GetEdgeData(nbg_edge_id);
|
||||
annotation_mapping[edge.annotation_data] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// now compute a prefix sum on all entries that are 0 to find the new mapping
|
||||
AnnotationID prefix_sum = 0;
|
||||
for (std::size_t i = 0; i < annotation_mapping.size(); ++i)
|
||||
{
|
||||
if (annotation_mapping[i] == 0)
|
||||
annotation_mapping[i] = prefix_sum++;
|
||||
else
|
||||
{
|
||||
// flag for removal
|
||||
annotation_data[i].name_id = INVALID_NAMEID;
|
||||
}
|
||||
}
|
||||
|
||||
// apply the mapping
|
||||
for (const auto nbg_node_u : util::irange(0u, compressed_output_graph.GetNumberOfNodes()))
|
||||
{
|
||||
BOOST_ASSERT(nbg_node_u != SPECIAL_NODEID);
|
||||
for (EdgeID nbg_edge_id : compressed_output_graph.GetAdjacentEdgeRange(nbg_node_u))
|
||||
{
|
||||
auto &edge = compressed_output_graph.GetEdgeData(nbg_edge_id);
|
||||
edge.annotation_data = annotation_mapping[edge.annotation_data];
|
||||
BOOST_ASSERT(edge.annotation_data != INVALID_ANNOTATIONID);
|
||||
}
|
||||
}
|
||||
|
||||
// remove unreferenced entries, shifting other entries to the front
|
||||
const auto new_end =
|
||||
std::remove_if(annotation_data.begin(), annotation_data.end(), [&](auto const &data) {
|
||||
// both elements are considered equal (to remove the second
|
||||
// one) if the annotation mapping of the second one is
|
||||
// invalid
|
||||
return data.name_id == INVALID_NAMEID;
|
||||
});
|
||||
|
||||
const auto old_size = annotation_data.size();
|
||||
// remove all remaining elements
|
||||
annotation_data.erase(new_end, annotation_data.end());
|
||||
util::Log() << " graoh compression removed " << (old_size - annotation_data.size())
|
||||
<< " annotations of " << old_size;
|
||||
}
|
||||
|
||||
void NodeBasedGraphFactory::ReleaseOsmNodes()
|
||||
{
|
||||
// replace with a new vector to release old memory
|
||||
extractor::PackedOSMIDs().swap(osm_node_ids);
|
||||
}
|
||||
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
Reference in New Issue
Block a user