Implement arbitrary turn penalty file IO and integration (#2306)
Closes #1830
This commit is contained in:
+115
-37
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
||||
namespace std
|
||||
{
|
||||
@@ -40,6 +42,18 @@ template <> struct hash<std::pair<OSMNodeID, OSMNodeID>>
|
||||
return static_cast<uint64_t>(k.first) ^ (static_cast<uint64_t>(k.second) << 12);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>>
|
||||
{
|
||||
std::size_t operator()(const std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> &k) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, static_cast<uint64_t>(std::get<0>(k)));
|
||||
boost::hash_combine(seed, static_cast<uint64_t>(std::get<1>(k)));
|
||||
boost::hash_combine(seed, static_cast<uint64_t>(std::get<2>(k)));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace osrm
|
||||
@@ -71,9 +85,9 @@ int Contractor::Run()
|
||||
|
||||
std::size_t max_edge_id = LoadEdgeExpandedGraph(
|
||||
config.edge_based_graph_path, edge_based_edge_list, config.edge_segment_lookup_path,
|
||||
config.edge_penalty_path, config.segment_speed_lookup_paths, config.node_based_graph_path,
|
||||
config.geometry_path, config.datasource_names_path, config.datasource_indexes_path,
|
||||
config.rtree_leaf_path);
|
||||
config.edge_penalty_path, config.segment_speed_lookup_paths,
|
||||
config.turn_penalty_lookup_paths, config.node_based_graph_path, config.geometry_path,
|
||||
config.datasource_names_path, config.datasource_indexes_path, config.rtree_leaf_path);
|
||||
|
||||
// Contracting the edge-expanded graph
|
||||
|
||||
@@ -129,6 +143,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_penalty_filename,
|
||||
const std::vector<std::string> &segment_speed_filenames,
|
||||
const std::vector<std::string> &turn_penalty_filenames,
|
||||
const std::string &nodes_filename,
|
||||
const std::string &geometry_filename,
|
||||
const std::string &datasource_names_filename,
|
||||
@@ -139,11 +154,12 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary);
|
||||
|
||||
const bool update_edge_weights = !segment_speed_filenames.empty();
|
||||
const bool update_turn_penalties = !turn_penalty_filenames.empty();
|
||||
|
||||
boost::filesystem::ifstream edge_segment_input_stream;
|
||||
boost::filesystem::ifstream edge_fixed_penalties_input_stream;
|
||||
|
||||
if (update_edge_weights)
|
||||
if (update_edge_weights || update_turn_penalties)
|
||||
{
|
||||
edge_segment_input_stream.open(edge_segment_lookup_filename, std::ios::binary);
|
||||
edge_fixed_penalties_input_stream.open(edge_penalty_filename, std::ios::binary);
|
||||
@@ -172,37 +188,74 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
|
||||
std::unordered_map<std::pair<OSMNodeID, OSMNodeID>, std::pair<unsigned, uint8_t>>
|
||||
segment_speed_lookup;
|
||||
std::unordered_map<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>, std::pair<double, uint8_t>>
|
||||
turn_penalty_lookup;
|
||||
|
||||
// If we update the edge weights, this file will hold the datasource information
|
||||
// for each segment
|
||||
std::vector<uint8_t> m_geometry_datasource;
|
||||
|
||||
if (update_edge_weights)
|
||||
if (update_edge_weights || update_turn_penalties)
|
||||
{
|
||||
uint8_t file_id = 1;
|
||||
for (auto segment_speed_filename : segment_speed_filenames)
|
||||
{
|
||||
util::SimpleLogger().Write()
|
||||
<< "Segment speed data supplied, will update edge weights from "
|
||||
<< segment_speed_filename;
|
||||
io::CSVReader<3> csv_in(segment_speed_filename);
|
||||
csv_in.set_header("from_node", "to_node", "speed");
|
||||
uint64_t from_node_id{};
|
||||
uint64_t to_node_id{};
|
||||
unsigned speed{};
|
||||
while (csv_in.read_row(from_node_id, to_node_id, speed))
|
||||
{
|
||||
segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id),
|
||||
OSMNodeID(to_node_id))] =
|
||||
std::make_pair(speed, file_id);
|
||||
}
|
||||
++file_id;
|
||||
std::uint8_t segment_file_id = 1;
|
||||
std::uint8_t turn_file_id = 1;
|
||||
|
||||
// Check for overflow
|
||||
if (file_id == 0)
|
||||
if (update_edge_weights)
|
||||
{
|
||||
for (auto segment_speed_filename : segment_speed_filenames)
|
||||
{
|
||||
throw util::exception(
|
||||
"Sorry, there's a limit of 254 segment speed files, you supplied too many");
|
||||
util::SimpleLogger().Write()
|
||||
<< "Segment speed data supplied, will update edge weights from "
|
||||
<< segment_speed_filename;
|
||||
io::CSVReader<3> csv_in(segment_speed_filename);
|
||||
csv_in.set_header("from_node", "to_node", "speed");
|
||||
std::uint64_t from_node_id{};
|
||||
std::uint64_t to_node_id{};
|
||||
unsigned speed{};
|
||||
while (csv_in.read_row(from_node_id, to_node_id, speed))
|
||||
{
|
||||
segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id),
|
||||
OSMNodeID(to_node_id))] =
|
||||
std::make_pair(speed, segment_file_id);
|
||||
}
|
||||
++segment_file_id;
|
||||
|
||||
// Check for overflow
|
||||
if (segment_file_id == 0)
|
||||
{
|
||||
throw util::exception(
|
||||
"Sorry, there's a limit of 255 segment speed files; you supplied too many");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update_turn_penalties)
|
||||
{
|
||||
for (auto turn_penalty_filename : turn_penalty_filenames)
|
||||
{
|
||||
util::SimpleLogger().Write()
|
||||
<< "Turn penalty data supplied, will update turn penalties from "
|
||||
<< turn_penalty_filename;
|
||||
io::CSVReader<4> csv_in(turn_penalty_filename);
|
||||
csv_in.set_header("from_node", "via_node", "to_node", "penalty");
|
||||
uint64_t from_node_id{};
|
||||
uint64_t via_node_id{};
|
||||
uint64_t to_node_id{};
|
||||
double penalty{};
|
||||
while (csv_in.read_row(from_node_id, via_node_id, to_node_id, penalty))
|
||||
{
|
||||
turn_penalty_lookup[std::make_tuple(
|
||||
OSMNodeID(from_node_id), OSMNodeID(via_node_id), OSMNodeID(to_node_id))] =
|
||||
std::make_pair(penalty, turn_file_id);
|
||||
}
|
||||
++turn_file_id;
|
||||
|
||||
// Check for overflow
|
||||
if (turn_file_id == 0)
|
||||
{
|
||||
throw util::exception(
|
||||
"Sorry, there's a limit of 255 turn penalty files; you supplied too many");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,10 +411,9 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
u = &(internal_to_external_node_map
|
||||
[m_geometry_list[reverse_begin + rev_segment_position - 1]
|
||||
.node_id]);
|
||||
v = &(
|
||||
internal_to_external_node_map[m_geometry_list[reverse_begin +
|
||||
rev_segment_position]
|
||||
.node_id]);
|
||||
v = &(internal_to_external_node_map
|
||||
[m_geometry_list[reverse_begin + rev_segment_position]
|
||||
.node_id]);
|
||||
}
|
||||
const double segment_length =
|
||||
util::coordinate_calculation::greatCircleDistance(
|
||||
@@ -412,8 +464,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
std::ofstream datasource_stream(datasource_indexes_filename, std::ios::binary);
|
||||
if (!datasource_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + datasource_indexes_filename +
|
||||
" for writing");
|
||||
throw util::exception("Failed to open " + datasource_indexes_filename + " for writing");
|
||||
}
|
||||
auto number_of_datasource_entries = m_geometry_datasource.size();
|
||||
datasource_stream.write(reinterpret_cast<const char *>(&number_of_datasource_entries),
|
||||
@@ -429,8 +480,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
std::ofstream datasource_stream(datasource_names_filename, std::ios::binary);
|
||||
if (!datasource_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + datasource_names_filename +
|
||||
" for writing");
|
||||
throw util::exception("Failed to open " + datasource_names_filename + " for writing");
|
||||
}
|
||||
datasource_stream << "lua profile" << std::endl;
|
||||
for (auto const &name : segment_speed_filenames)
|
||||
@@ -445,7 +495,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
{
|
||||
extractor::EdgeBasedEdge inbuffer;
|
||||
input_stream.read((char *)&inbuffer, sizeof(extractor::EdgeBasedEdge));
|
||||
if (update_edge_weights)
|
||||
if (update_edge_weights || update_turn_penalties)
|
||||
{
|
||||
// Processing-time edge updates
|
||||
unsigned fixed_penalty;
|
||||
@@ -463,6 +513,7 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
OSMNodeID this_osm_node_id;
|
||||
double segment_length;
|
||||
int segment_weight;
|
||||
int compressed_edge_nodes = static_cast<int>(num_osm_nodes);
|
||||
--num_osm_nodes;
|
||||
for (; num_osm_nodes != 0; --num_osm_nodes)
|
||||
{
|
||||
@@ -494,7 +545,34 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
previous_osm_node_id = this_osm_node_id;
|
||||
}
|
||||
|
||||
inbuffer.weight = fixed_penalty + new_weight;
|
||||
OSMNodeID from_id;
|
||||
OSMNodeID via_id;
|
||||
OSMNodeID to_id;
|
||||
edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&from_id),
|
||||
sizeof(from_id));
|
||||
edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&via_id),
|
||||
sizeof(via_id));
|
||||
edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&to_id), sizeof(to_id));
|
||||
|
||||
auto turn_iter = turn_penalty_lookup.find(std::make_tuple(from_id, via_id, to_id));
|
||||
if (turn_iter != turn_penalty_lookup.end())
|
||||
{
|
||||
int new_turn_weight = static_cast<int>(turn_iter->second.first * 10);
|
||||
|
||||
if (new_turn_weight + new_weight < compressed_edge_nodes)
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING)
|
||||
<< "turn penalty " << turn_iter->second.first << " for turn " << from_id
|
||||
<< ", " << via_id << ", " << to_id
|
||||
<< " is too negative: clamping turn weight to " << compressed_edge_nodes;
|
||||
}
|
||||
|
||||
inbuffer.weight = std::max(new_turn_weight + new_weight, compressed_edge_nodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
inbuffer.weight = fixed_penalty + new_weight;
|
||||
}
|
||||
}
|
||||
|
||||
edge_based_edge_list.emplace_back(std::move(inbuffer));
|
||||
|
||||
@@ -250,16 +250,32 @@ CompressedEdgeContainer::GetBucketReference(const EdgeID edge_id) const
|
||||
return m_compressed_geometries.at(index);
|
||||
}
|
||||
|
||||
// Since all edges are technically in the compressed geometry container,
|
||||
// regardless of whether a compressed edge actually contains multiple
|
||||
// original segments, we use 'Trivial' here to describe compressed edges
|
||||
// that only contain one original segment
|
||||
bool CompressedEdgeContainer::IsTrivial(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
return bucket.size() == 1;
|
||||
}
|
||||
|
||||
NodeID CompressedEdgeContainer::GetFirstEdgeTargetID(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
BOOST_ASSERT(bucket.size() >= 2);
|
||||
BOOST_ASSERT(bucket.size() >= 1);
|
||||
return bucket.front().node_id;
|
||||
}
|
||||
NodeID CompressedEdgeContainer::GetLastEdgeTargetID(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
BOOST_ASSERT(bucket.size() >= 1);
|
||||
return bucket.back().node_id;
|
||||
}
|
||||
NodeID CompressedEdgeContainer::GetLastEdgeSourceID(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
BOOST_ASSERT(bucket.size() >= 2);
|
||||
BOOST_ASSERT(bucket.size() >= 1);
|
||||
return bucket[bucket.size() - 2].node_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,8 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI
|
||||
|
||||
NodeID current_edge_source_coordinate_id = node_u;
|
||||
|
||||
const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id) {
|
||||
const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id)
|
||||
{
|
||||
if (edge_based_node_id == SPECIAL_NODEID)
|
||||
{
|
||||
return SegmentID{SPECIAL_SEGMENTID, false};
|
||||
@@ -339,7 +340,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u);
|
||||
|
||||
const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
|
||||
|
||||
for (const auto turn : possible_turns)
|
||||
{
|
||||
const double turn_angle = turn.angle;
|
||||
@@ -434,6 +434,36 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
sizeof(target_node.weight));
|
||||
previous = target_node.node_id;
|
||||
}
|
||||
|
||||
// We also now write out the mapping between the edge-expanded edges and the
|
||||
// original nodes. Since each edge represents a possible maneuver, external
|
||||
// programs can use this to quickly perform updates to edge weights in order
|
||||
// to penalize certain turns.
|
||||
|
||||
// If this edge is 'trivial' -- where the compressed edge corresponds
|
||||
// exactly to an original OSM segment -- we can pull the turn's preceding
|
||||
// node ID directly with `node_u`; otherwise, we need to look up the node
|
||||
// immediately preceding the turn from the compressed edge container.
|
||||
const bool isTrivial = m_compressed_edge_container.IsTrivial(edge_from_u);
|
||||
|
||||
const auto &from_node =
|
||||
isTrivial
|
||||
? m_node_info_list[node_u]
|
||||
: m_node_info_list[m_compressed_edge_container.GetLastEdgeSourceID(
|
||||
edge_from_u)];
|
||||
const auto &via_node =
|
||||
m_node_info_list[m_compressed_edge_container.GetLastEdgeTargetID(
|
||||
edge_from_u)];
|
||||
const auto &to_node =
|
||||
m_node_info_list[m_compressed_edge_container.GetFirstEdgeTargetID(
|
||||
turn.eid)];
|
||||
|
||||
edge_penalty_file.write(reinterpret_cast<const char *>(&from_node.node_id),
|
||||
sizeof(from_node.node_id));
|
||||
edge_penalty_file.write(reinterpret_cast<const char *>(&via_node.node_id),
|
||||
sizeof(via_node.node_id));
|
||||
edge_penalty_file.write(reinterpret_cast<const char *>(&to_node.node_id),
|
||||
sizeof(to_node.node_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig
|
||||
&contractor_config.segment_speed_lookup_paths)
|
||||
->composing(),
|
||||
"Lookup files containing nodeA, nodeB, speed data to adjust edge weights")(
|
||||
"turn-penalty-file", boost::program_options::value<std::vector<std::string>>(
|
||||
&contractor_config.turn_penalty_lookup_paths)
|
||||
->composing(),
|
||||
"Lookup files containing from_, to_, via_nodes, and turn penalties to adjust turn weights")(
|
||||
"level-cache,o", boost::program_options::value<bool>(&contractor_config.use_cached_priority)
|
||||
->default_value(false),
|
||||
"Use .level file to retain the contaction level for each node from the last run.");
|
||||
|
||||
Reference in New Issue
Block a user