handle conditional via-way restrictions

- refactor conditional restriction handling to not use external data (first OSM nodes on ways)
 - BREAKING: changes internal file format of osrm.restrictions
 - add support for general conditional penalties based on edge-based nodes (requires unique edges between nodes)
This commit is contained in:
Moritz Kobitzsch
2017-08-01 17:18:12 +02:00
parent f34320a89b
commit 93299d6651
27 changed files with 931 additions and 497 deletions
+175 -30
View File
@@ -1,4 +1,5 @@
#include "extractor/edge_based_graph_factory.hpp"
#include "extractor/conditional_turn_penalty.hpp"
#include "extractor/edge_based_edge.hpp"
#include "extractor/files.hpp"
#include "extractor/guidance/turn_analysis.hpp"
@@ -6,6 +7,7 @@
#include "extractor/scripting_environment.hpp"
#include "extractor/suffix_table.hpp"
#include "extractor/serialization.hpp"
#include "storage/io.hpp"
#include "util/assert.hpp"
@@ -20,8 +22,10 @@
#include "util/timing_util.hpp"
#include <boost/assert.hpp>
#include <boost/functional/hash.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include "boost/unordered_map.hpp"
#include <algorithm>
#include <cmath>
#include <iomanip>
@@ -35,12 +39,26 @@
#include <tbb/pipeline.h>
#include <tbb/task_scheduler_init.h>
namespace std
{
template <> struct hash<std::pair<NodeID, NodeID>>
{
std::size_t operator()(const std::pair<NodeID, NodeID> &mk) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, mk.first);
boost::hash_combine(seed, mk.second);
return seed;
}
};
}
namespace osrm
{
namespace extractor
{
// Configuration to find representative candidate for turn angle calculations
// Configuration to find representative candidate for turn angle calculations
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
std::shared_ptr<util::NodeBasedDynamicGraph> node_based_graph,
CompressedEdgeContainer &compressed_edge_container,
@@ -199,7 +217,9 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
const std::string &turn_duration_penalties_filename,
const std::string &turn_penalties_index_filename,
const std::string &cnbg_ebg_mapping_path,
const std::string &conditional_penalties_filename,
const RestrictionMap &node_restriction_map,
const ConditionalRestrictionMap &conditional_node_restriction_map,
const WayRestrictionMap &way_restriction_map)
{
TIMER_START(renumber);
@@ -220,7 +240,9 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment,
turn_weight_penalties_filename,
turn_duration_penalties_filename,
turn_penalties_index_filename,
conditional_penalties_filename,
node_restriction_map,
conditional_node_restriction_map,
way_restriction_map);
TIMER_STOP(generate_edges);
@@ -384,7 +406,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const std::string &turn_weight_penalties_filename,
const std::string &turn_duration_penalties_filename,
const std::string &turn_penalties_index_filename,
const std::string &conditional_penalties_filename,
const RestrictionMap &node_restriction_map,
const ConditionalRestrictionMap &conditional_restriction_map,
const WayRestrictionMap &way_restriction_map)
{
@@ -427,6 +451,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const auto weight_multiplier =
scripting_environment.GetProfileProperties().GetWeightMultiplier();
// filled in during next stage, kept alive through following scope
std::vector<Conditional> conditionals;
// The following block generates the edge-based-edges using a parallel processing
// pipeline. Sets of intersection IDs are batched in groups of GRAINSIZE (100)
// `generator_stage`,
@@ -482,7 +508,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
};
// same as IntersectionData, but grouped with edge to allow sorting after creating. Edges
// are out of order
// can be out of order
struct EdgeWithData
{
EdgeBasedEdge edge;
@@ -497,10 +523,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
std::size_t nodes_processed = 0;
IntersectionData continuous_data;
std::vector<EdgeWithData> delayed_data;
std::vector<Conditional> conditionals;
};
// Generate edges for either artificial nodes or the main graph
const auto generate_edge = [this, &scripting_environment, weight_multiplier](
const auto generate_edge = [this,
&scripting_environment,
weight_multiplier,
&conditional_restriction_map](
// what nodes will be used? In most cases this will be the id stored in the edge_data.
// In case of duplicated nodes (e.g. due to via-way restrictions), one/both of these
// might refer to a newly added edge based node
@@ -514,6 +544,24 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const auto &intersection,
const auto &turn,
const auto entry_class_id) {
const auto node_restricted = isRestricted(node_along_road_entering,
node_at_center_of_intersection,
m_node_based_graph->GetTarget(turn.eid),
conditional_restriction_map);
boost::optional<Conditional> conditional = boost::none;
if (node_restricted.first)
{
auto const &conditions = node_restricted.second->condition;
// get conditions of the restriction limiting the node
conditional = {{edge_based_node_from,
edge_based_node_to,
{static_cast<std::uint64_t>(-1),
m_coordinates[node_at_center_of_intersection],
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);
@@ -573,15 +621,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const auto &from_node =
isTrivial ? node_along_road_entering
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
const auto &via_node =
m_compressed_edge_container.GetLastEdgeTargetID(node_based_edge_from);
const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
lookup::TurnIndexBlock turn_index_block = {from_node, via_node, to_node};
lookup::TurnIndexBlock turn_index_block = {
from_node, node_at_center_of_intersection, to_node};
// insert data into the designated buffer
return EdgeWithData{
edge_based_edge, turn_index_block, weight_penalty, duration_penalty, turn_data};
return std::make_pair(
EdgeWithData{
edge_based_edge, turn_index_block, weight_penalty, duration_penalty, turn_data},
conditional);
};
// Second part of the pipeline is where the intersection analysis is done for
@@ -720,7 +769,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
m_number_of_edge_based_nodes);
{ // scope to forget edge_with_data after
const auto edge_with_data =
const auto edge_with_data_and_condition =
generate_edge(edge_data1.edge_id,
target_id,
node_along_road_entering,
@@ -731,15 +780,21 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
turn,
entry_class_id);
buffer->continuous_data.edges_list.push_back(edge_with_data.edge);
buffer->continuous_data.edges_list.push_back(
edge_with_data_and_condition.first.edge);
buffer->continuous_data.turn_indexes.push_back(
edge_with_data.turn_index);
edge_with_data_and_condition.first.turn_index);
buffer->continuous_data.turn_weight_penalties.push_back(
edge_with_data.turn_weight_penalty);
edge_with_data_and_condition.first.turn_weight_penalty);
buffer->continuous_data.turn_duration_penalties.push_back(
edge_with_data.turn_duration_penalty);
edge_with_data_and_condition.first.turn_duration_penalty);
buffer->continuous_data.turn_data_container.push_back(
edge_with_data.turn_data);
edge_with_data_and_condition.first.turn_data);
if (edge_with_data_and_condition.second)
{
buffer->conditionals.push_back(
*edge_with_data_and_condition.second);
}
}
// when turning off a a via-way turn restriction, we need to not only
@@ -764,25 +819,73 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
auto const node_at_end_of_turn =
m_node_based_graph->GetTarget(turn.eid);
const auto is_restricted = way_restriction_map.IsRestricted(
const auto is_way_restricted = way_restriction_map.IsRestricted(
duplicated_node_id, node_at_end_of_turn);
if (is_restricted)
continue;
if (is_way_restricted)
{
// add into delayed data
auto edge_with_data = 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 const restriction = way_restriction_map.GetRestriction(
duplicated_node_id, node_at_end_of_turn);
buffer->delayed_data.push_back(std::move(edge_with_data));
if (restriction.condition.empty())
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);
buffer->delayed_data.push_back(
std::move(edge_with_data_and_condition.first));
if (edge_with_data_and_condition.second)
{
buffer->conditionals.push_back(
*edge_with_data_and_condition.second);
}
// also add the conditions for the way
if (is_way_restricted && !restriction.condition.empty())
{
// 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,
{static_cast<std::uint64_t>(-1),
m_coordinates[node_at_center_of_intersection],
restriction.condition}});
}
}
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);
buffer->delayed_data.push_back(
std::move(edge_with_data_and_condition.first));
if (edge_with_data_and_condition.second)
{
buffer->conditionals.push_back(
*edge_with_data_and_condition.second);
}
}
}
}
}
@@ -827,6 +930,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
data.turn_indexes.begin(),
data.turn_indexes.end());
conditionals.insert(
conditionals.end(), buffer->conditionals.begin(), buffer->conditionals.end());
// Buffer writes to reduce syscall count
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
{
@@ -879,6 +985,20 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
}
});
// re-hash conditionals to ocnnect to their respective edge-based edges. Due to the
// ordering, we
// do not really have a choice but to index the conditional penalties and walk over all
// edge-based-edges to find the ID of the edge
auto const indexed_conditionals = IndexConditionals(std::move(conditionals));
{
util::Log() << "Writing " << indexed_conditionals.size()
<< " conditional turn penalties...";
// write conditional turn penalties into the restrictions file
storage::io::FileWriter writer(conditional_penalties_filename,
storage::io::FileWriter::GenerateFingerprint);
extractor::serialization::write(writer, indexed_conditionals);
}
// write weight penalties per turn
BOOST_ASSERT(turn_weight_penalties.size() == turn_duration_penalties.size());
{
@@ -918,11 +1038,36 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
util::Log() << " contains " << m_edge_based_edge_list.size() << " edges";
util::Log() << " skips " << restricted_turns_counter << " turns, "
"defined by "
<< node_restriction_map.size() << " restrictions";
<< node_restriction_map.Size() << " restrictions";
util::Log() << " skips " << skipped_uturns_counter << " U turns";
util::Log() << " skips " << skipped_barrier_turns_counter << " turns over barriers";
}
std::vector<ConditionalTurnPenalty>
EdgeBasedGraphFactory::IndexConditionals(std::vector<Conditional> &&conditionals) const
{
boost::unordered_multimap<std::pair<NodeID, NodeID>, ConditionalTurnPenalty *> index;
// build and index of all conditional restrictions
for (auto &conditional : conditionals)
index.insert(std::make_pair(std::make_pair(conditional.from_node, conditional.to_node),
&conditional.penalty));
std::vector<ConditionalTurnPenalty> indexed_restrictions;
for (auto const &edge : m_edge_based_edge_list)
{
auto const range = index.equal_range(std::make_pair(edge.source, edge.target));
for (auto itr = range.first; itr != range.second; ++itr)
{
itr->second->turn_offset = edge.data.turn_id;
indexed_restrictions.push_back(*itr->second);
}
}
return indexed_restrictions;
}
std::vector<util::guidance::BearingClass> EdgeBasedGraphFactory::GetBearingClasses() const
{
std::vector<util::guidance::BearingClass> result(bearing_class_hash.data.size());