Conditional turn restriction support (#3841)
* optionally include condition and via node coords in InputRestrictionContainer * only write conditionals to disk, custom serialization for restrictions * conditional turn lookup, reuse timezone validation from extract-conditionals * adapt updater to use coordinates/osm ids, remove internal to external map * add utc time now parameter to contraction * only compile timezone code where libshp is found, adapt test running * slight refactor, more tests * catch invalid via nodes in restriction parsing, set default cucumber origin to guinée * add another run to test mld routed paths * cosmetic review changes * Simplify Timezoner for windows build * Split declaration and parsing parts for opening hours * adjust conditional tests to run without shapefiles * always include parse conditionals option * Adjust travis timeout * Added dummy TZ shapefile with test timezone polygons * [skip ci] update changelog
This commit is contained in:
+194
-201
@@ -115,208 +115,216 @@ transformTurnLaneMapIntoArrays(const guidance::LaneDescriptionMap &turn_lane_map
|
||||
int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
{
|
||||
util::LogPolicy::GetInstance().Unmute();
|
||||
TIMER_START(extracting);
|
||||
|
||||
const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads();
|
||||
const auto number_of_threads = std::min(recommended_num_threads, config.requested_num_threads);
|
||||
tbb::task_scheduler_init init(number_of_threads ? number_of_threads
|
||||
: tbb::task_scheduler_init::automatic);
|
||||
|
||||
auto turn_restrictions = ParseOSMData(scripting_environment, number_of_threads);
|
||||
|
||||
// Transform the node-based graph that OSM is based on into an edge-based graph
|
||||
// that is better for routing. Every edge becomes a node, and every valid
|
||||
// movement (e.g. turn from A->B, and B->A) becomes an edge
|
||||
util::Log() << "Generating edge-expanded graph representation";
|
||||
|
||||
TIMER_START(expansion);
|
||||
|
||||
std::vector<EdgeBasedNode> edge_based_node_list;
|
||||
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_node_list,
|
||||
node_is_startpoint,
|
||||
edge_based_node_weights,
|
||||
edge_based_edge_list,
|
||||
config.intersection_class_data_output_path,
|
||||
turn_restrictions);
|
||||
|
||||
auto number_of_node_based_nodes = graph_size.first;
|
||||
auto max_edge_id = graph_size.second;
|
||||
|
||||
TIMER_STOP(expansion);
|
||||
|
||||
util::Log() << "Saving edge-based node weights to file.";
|
||||
TIMER_START(timer_write_node_weights);
|
||||
{
|
||||
util::Log() << "Input file: " << config.input_path.filename().string();
|
||||
if (!config.profile_path.empty())
|
||||
{
|
||||
util::Log() << "Profile: " << config.profile_path.filename().string();
|
||||
}
|
||||
util::Log() << "Threads: " << number_of_threads;
|
||||
|
||||
const osmium::io::File input_file(config.input_path.string());
|
||||
|
||||
osmium::io::Reader reader(
|
||||
input_file,
|
||||
(config.use_metadata ? osmium::io::read_meta::yes : osmium::io::read_meta::no));
|
||||
|
||||
const osmium::io::Header header = reader.header();
|
||||
|
||||
unsigned number_of_nodes = 0;
|
||||
unsigned number_of_ways = 0;
|
||||
unsigned number_of_relations = 0;
|
||||
|
||||
util::Log() << "Parsing in progress..";
|
||||
TIMER_START(parsing);
|
||||
|
||||
ExtractionContainers extraction_containers;
|
||||
auto extractor_callbacks = std::make_unique<ExtractorCallbacks>(
|
||||
extraction_containers, scripting_environment.GetProfileProperties());
|
||||
|
||||
// setup raster sources
|
||||
scripting_environment.SetupSources();
|
||||
|
||||
std::string generator = header.get("generator");
|
||||
if (generator.empty())
|
||||
{
|
||||
generator = "unknown tool";
|
||||
}
|
||||
util::Log() << "input file generated by " << generator;
|
||||
|
||||
// write .timestamp data file
|
||||
std::string timestamp = header.get("osmosis_replication_timestamp");
|
||||
if (timestamp.empty())
|
||||
{
|
||||
timestamp = "n/a";
|
||||
}
|
||||
util::Log() << "timestamp: " << timestamp;
|
||||
|
||||
storage::io::FileWriter timestamp_file(config.timestamp_file_name,
|
||||
storage::io::FileWriter::GenerateFingerprint);
|
||||
|
||||
timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
|
||||
|
||||
// initialize vectors holding parsed objects
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
|
||||
|
||||
// setup restriction parser
|
||||
const RestrictionParser restriction_parser(scripting_environment);
|
||||
|
||||
// create a vector of iterators into the buffer
|
||||
for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
|
||||
const osmium::memory::Buffer buffer = reader.read();
|
||||
osm_elements.clear())
|
||||
{
|
||||
for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
|
||||
{
|
||||
osm_elements.push_back(iter);
|
||||
}
|
||||
|
||||
// clear resulting vectors
|
||||
resulting_nodes.clear();
|
||||
resulting_ways.clear();
|
||||
resulting_restrictions.clear();
|
||||
|
||||
scripting_environment.ProcessElements(osm_elements,
|
||||
restriction_parser,
|
||||
resulting_nodes,
|
||||
resulting_ways,
|
||||
resulting_restrictions);
|
||||
|
||||
number_of_nodes += resulting_nodes.size();
|
||||
// put parsed objects thru extractor callbacks
|
||||
for (const auto &result : resulting_nodes)
|
||||
{
|
||||
extractor_callbacks->ProcessNode(
|
||||
static_cast<const osmium::Node &>(*(osm_elements[result.first])),
|
||||
result.second);
|
||||
}
|
||||
number_of_ways += resulting_ways.size();
|
||||
for (const auto &result : resulting_ways)
|
||||
{
|
||||
extractor_callbacks->ProcessWay(
|
||||
static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
|
||||
}
|
||||
number_of_relations += resulting_restrictions.size();
|
||||
for (const auto &result : resulting_restrictions)
|
||||
{
|
||||
extractor_callbacks->ProcessRestriction(result);
|
||||
}
|
||||
}
|
||||
TIMER_STOP(parsing);
|
||||
util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
|
||||
|
||||
util::Log() << "Raw input contains " << number_of_nodes << " nodes, " << number_of_ways
|
||||
<< " ways, and " << number_of_relations << " relations";
|
||||
|
||||
// take control over the turn lane map
|
||||
turn_lane_map = extractor_callbacks->moveOutLaneDescriptionMap();
|
||||
|
||||
extractor_callbacks.reset();
|
||||
|
||||
if (extraction_containers.all_edges_list.empty())
|
||||
{
|
||||
throw util::exception(std::string("There are no edges remaining after parsing.") +
|
||||
SOURCE_REF);
|
||||
}
|
||||
|
||||
extraction_containers.PrepareData(scripting_environment,
|
||||
config.output_file_name,
|
||||
config.restriction_file_name,
|
||||
config.names_file_name);
|
||||
|
||||
WriteProfileProperties(config.profile_properties_output_path,
|
||||
scripting_environment.GetProfileProperties());
|
||||
|
||||
TIMER_STOP(extracting);
|
||||
util::Log() << "extraction finished after " << TIMER_SEC(extracting) << "s";
|
||||
storage::io::FileWriter writer(config.edge_based_node_weights_output_path,
|
||||
storage::io::FileWriter::GenerateFingerprint);
|
||||
storage::serialization::write(writer, edge_based_node_weights);
|
||||
}
|
||||
TIMER_STOP(timer_write_node_weights);
|
||||
util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
|
||||
|
||||
{
|
||||
// Transform the node-based graph that OSM is based on into an edge-based graph
|
||||
// that is better for routing. Every edge becomes a node, and every valid
|
||||
// movement (e.g. turn from A->B, and B->A) becomes an edge
|
||||
util::Log() << "Generating edge-expanded graph representation";
|
||||
util::Log() << "Computing strictly connected components ...";
|
||||
FindComponents(max_edge_id, edge_based_edge_list, edge_based_node_list);
|
||||
|
||||
TIMER_START(expansion);
|
||||
util::Log() << "Building r-tree ...";
|
||||
TIMER_START(rtree);
|
||||
BuildRTree(std::move(edge_based_node_list), std::move(node_is_startpoint), coordinates);
|
||||
|
||||
std::vector<EdgeBasedNode> edge_based_node_list;
|
||||
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;
|
||||
TIMER_STOP(rtree);
|
||||
|
||||
auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
|
||||
coordinates,
|
||||
osm_node_ids,
|
||||
edge_based_node_list,
|
||||
node_is_startpoint,
|
||||
edge_based_node_weights,
|
||||
edge_based_edge_list,
|
||||
config.intersection_class_data_output_path);
|
||||
util::Log() << "Writing node map ...";
|
||||
files::writeNodes(config.node_output_path, coordinates, osm_node_ids);
|
||||
|
||||
auto number_of_node_based_nodes = graph_size.first;
|
||||
auto max_edge_id = graph_size.second;
|
||||
WriteEdgeBasedGraph(config.edge_graph_output_path, max_edge_id, edge_based_edge_list);
|
||||
|
||||
TIMER_STOP(expansion);
|
||||
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));
|
||||
|
||||
util::Log() << "Saving edge-based node weights to file.";
|
||||
TIMER_START(timer_write_node_weights);
|
||||
{
|
||||
storage::io::FileWriter writer(config.edge_based_node_weights_output_path,
|
||||
storage::io::FileWriter::GenerateFingerprint);
|
||||
storage::serialization::write(writer, edge_based_node_weights);
|
||||
}
|
||||
TIMER_STOP(timer_write_node_weights);
|
||||
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_list);
|
||||
|
||||
util::Log() << "Building r-tree ...";
|
||||
TIMER_START(rtree);
|
||||
BuildRTree(std::move(edge_based_node_list), std::move(node_is_startpoint), coordinates);
|
||||
|
||||
TIMER_STOP(rtree);
|
||||
|
||||
util::Log() << "Writing node map ...";
|
||||
files::writeNodes(config.node_output_path, coordinates, osm_node_ids);
|
||||
|
||||
WriteEdgeBasedGraph(config.edge_graph_output_path, max_edge_id, edge_based_edge_list);
|
||||
|
||||
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));
|
||||
|
||||
util::Log() << "Expansion: " << nodes_per_second << " nodes/sec and " << edges_per_second
|
||||
<< " edges/sec";
|
||||
util::Log() << "To prepare the data for routing, run: "
|
||||
<< "./osrm-contract " << config.output_file_name;
|
||||
}
|
||||
util::Log() << "Expansion: " << nodes_per_second << " nodes/sec and " << edges_per_second
|
||||
<< " edges/sec";
|
||||
util::Log() << "To prepare the data for routing, run: "
|
||||
<< "./osrm-contract " << config.output_file_name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
|
||||
const unsigned number_of_threads)
|
||||
{
|
||||
TIMER_START(extracting);
|
||||
|
||||
util::Log() << "Input file: " << config.input_path.filename().string();
|
||||
if (!config.profile_path.empty())
|
||||
{
|
||||
util::Log() << "Profile: " << config.profile_path.filename().string();
|
||||
}
|
||||
util::Log() << "Threads: " << number_of_threads;
|
||||
|
||||
const osmium::io::File input_file(config.input_path.string());
|
||||
|
||||
osmium::io::Reader reader(
|
||||
input_file, (config.use_metadata ? osmium::io::read_meta::yes : osmium::io::read_meta::no));
|
||||
|
||||
const osmium::io::Header header = reader.header();
|
||||
|
||||
unsigned number_of_nodes = 0;
|
||||
unsigned number_of_ways = 0;
|
||||
unsigned number_of_relations = 0;
|
||||
|
||||
util::Log() << "Parsing in progress..";
|
||||
TIMER_START(parsing);
|
||||
|
||||
ExtractionContainers extraction_containers;
|
||||
auto extractor_callbacks = std::make_unique<ExtractorCallbacks>(
|
||||
extraction_containers, scripting_environment.GetProfileProperties());
|
||||
|
||||
// setup raster sources
|
||||
scripting_environment.SetupSources();
|
||||
|
||||
std::string generator = header.get("generator");
|
||||
if (generator.empty())
|
||||
{
|
||||
generator = "unknown tool";
|
||||
}
|
||||
util::Log() << "input file generated by " << generator;
|
||||
|
||||
// write .timestamp data file
|
||||
std::string timestamp = header.get("osmosis_replication_timestamp");
|
||||
if (timestamp.empty())
|
||||
{
|
||||
timestamp = "n/a";
|
||||
}
|
||||
util::Log() << "timestamp: " << timestamp;
|
||||
|
||||
storage::io::FileWriter timestamp_file(config.timestamp_file_name,
|
||||
storage::io::FileWriter::GenerateFingerprint);
|
||||
|
||||
timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
|
||||
|
||||
// initialize vectors holding parsed objects
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
|
||||
|
||||
std::vector<std::string> restrictions = scripting_environment.GetRestrictions();
|
||||
// setup restriction parser
|
||||
const RestrictionParser restriction_parser(
|
||||
scripting_environment.GetProfileProperties().use_turn_restrictions,
|
||||
config.parse_conditionals,
|
||||
restrictions);
|
||||
|
||||
// create a vector of iterators into the buffer
|
||||
for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
|
||||
const osmium::memory::Buffer buffer = reader.read();
|
||||
osm_elements.clear())
|
||||
{
|
||||
for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
|
||||
{
|
||||
osm_elements.push_back(iter);
|
||||
}
|
||||
|
||||
// clear resulting vectors
|
||||
resulting_nodes.clear();
|
||||
resulting_ways.clear();
|
||||
resulting_restrictions.clear();
|
||||
|
||||
scripting_environment.ProcessElements(osm_elements,
|
||||
restriction_parser,
|
||||
resulting_nodes,
|
||||
resulting_ways,
|
||||
resulting_restrictions);
|
||||
|
||||
number_of_nodes += resulting_nodes.size();
|
||||
// put parsed objects thru extractor callbacks
|
||||
for (const auto &result : resulting_nodes)
|
||||
{
|
||||
extractor_callbacks->ProcessNode(
|
||||
static_cast<const osmium::Node &>(*(osm_elements[result.first])), result.second);
|
||||
}
|
||||
number_of_ways += resulting_ways.size();
|
||||
for (const auto &result : resulting_ways)
|
||||
{
|
||||
extractor_callbacks->ProcessWay(
|
||||
static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
|
||||
}
|
||||
number_of_relations += resulting_restrictions.size();
|
||||
for (const auto &result : resulting_restrictions)
|
||||
{
|
||||
extractor_callbacks->ProcessRestriction(result);
|
||||
}
|
||||
}
|
||||
TIMER_STOP(parsing);
|
||||
util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
|
||||
|
||||
util::Log() << "Raw input contains " << number_of_nodes << " nodes, " << number_of_ways
|
||||
<< " ways, and " << number_of_relations << " relations";
|
||||
|
||||
// take control over the turn lane map
|
||||
turn_lane_map = extractor_callbacks->moveOutLaneDescriptionMap();
|
||||
|
||||
extractor_callbacks.reset();
|
||||
|
||||
if (extraction_containers.all_edges_list.empty())
|
||||
{
|
||||
throw util::exception(std::string("There are no edges remaining after parsing.") +
|
||||
SOURCE_REF);
|
||||
}
|
||||
|
||||
extraction_containers.PrepareData(scripting_environment,
|
||||
config.output_file_name,
|
||||
config.restriction_file_name,
|
||||
config.names_file_name);
|
||||
|
||||
WriteProfileProperties(config.profile_properties_output_path,
|
||||
scripting_environment.GetProfileProperties());
|
||||
|
||||
TIMER_STOP(extracting);
|
||||
util::Log() << "extraction finished after " << TIMER_SEC(extracting) << "s";
|
||||
|
||||
return extraction_containers.unconditional_turn_restrictions;
|
||||
}
|
||||
|
||||
void Extractor::WriteProfileProperties(const std::string &output_path,
|
||||
const ProfileProperties &properties) const
|
||||
{
|
||||
@@ -384,22 +392,6 @@ void Extractor::FindComponents(unsigned max_edge_id,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Build load restrictions from .restriction file
|
||||
*/
|
||||
std::shared_ptr<RestrictionMap> Extractor::LoadRestrictionMap()
|
||||
{
|
||||
storage::io::FileReader file_reader(config.restriction_file_name,
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
std::vector<TurnRestriction> restriction_list;
|
||||
|
||||
util::loadRestrictionsFromFile(file_reader, restriction_list);
|
||||
|
||||
util::Log() << " - " << restriction_list.size() << " restrictions.";
|
||||
|
||||
return std::make_shared<RestrictionMap>(restriction_list);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Load node based graph from .osrm file
|
||||
*/
|
||||
@@ -444,12 +436,13 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
|
||||
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)
|
||||
const std::string &intersection_class_output_file,
|
||||
std::vector<TurnRestriction> &turn_restrictions)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes;
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
|
||||
auto restriction_map = LoadRestrictionMap();
|
||||
auto restriction_map = std::make_shared<RestrictionMap>(turn_restrictions);
|
||||
auto node_based_graph =
|
||||
LoadNodeBasedGraph(barrier_nodes, traffic_lights, coordinates, osm_node_ids);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user