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:
Karen Shea
2017-05-11 12:13:52 +02:00
committed by GitHub
parent 12f47708cd
commit 799a677e7a
42 changed files with 2116 additions and 1310 deletions
+194 -201
View File
@@ -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);