Implement arbitrary turn penalty file IO and integration (#2306)

Closes #1830
This commit is contained in:
Lauren Budorick
2016-04-29 00:48:13 -07:00
parent cf17bd38eb
commit b8f7569e93
17 changed files with 346 additions and 60 deletions
+115 -37
View File
@@ -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));
+18 -2
View File
@@ -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;
}
}
+32 -2
View File
@@ -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));
}
}
}
+4
View File
@@ -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.");