Allow specifing a weight for routing that is independent of duration

This commit is contained in:
Patrick Niklaus
2016-05-12 18:50:10 +02:00
committed by Patrick Niklaus
parent e463733138
commit 279f8aabfb
85 changed files with 2100 additions and 853 deletions
+172 -80
View File
@@ -74,7 +74,9 @@ struct Segment final
struct SpeedSource final
{
SpeedSource() : speed(0), weight(INVALID_EDGE_WEIGHT) {}
unsigned speed;
EdgeWeight weight;
std::uint8_t source;
};
@@ -86,7 +88,7 @@ struct Turn final
: from(from), via(via), to(to)
{
}
Turn(const osrm::extractor::lookup::PenaltyBlock &turn)
Turn(const osrm::extractor::lookup::TurnIndexBlock &turn)
: from(static_cast<std::uint64_t>(turn.from_id)),
via(static_cast<std::uint64_t>(turn.via_id)), to(static_cast<std::uint64_t>(turn.to_id))
{
@@ -103,7 +105,9 @@ struct Turn final
struct PenaltySource final
{
double penalty;
PenaltySource() : duration(0.), weight(std::numeric_limits<double>::quiet_NaN()) {}
double duration;
double weight;
std::uint8_t source;
};
@@ -115,11 +119,15 @@ template <typename T> inline bool is_aligned(const void *pointer)
} // anon ns
BOOST_FUSION_ADAPT_STRUCT(Segment, (decltype(Segment::from), from)(decltype(Segment::to), to))
BOOST_FUSION_ADAPT_STRUCT(SpeedSource, (decltype(SpeedSource::speed), speed))
BOOST_FUSION_ADAPT_STRUCT(SpeedSource,
(decltype(SpeedSource::speed), speed)(decltype(SpeedSource::weight),
weight))
BOOST_FUSION_ADAPT_STRUCT(Turn,
(decltype(Turn::from), from)(decltype(Turn::via), via)(decltype(Turn::to),
to))
BOOST_FUSION_ADAPT_STRUCT(PenaltySource, (decltype(PenaltySource::penalty), penalty))
BOOST_FUSION_ADAPT_STRUCT(PenaltySource,
(decltype(PenaltySource::duration),
duration)(decltype(PenaltySource::weight), weight))
namespace osrm
{
@@ -240,29 +248,37 @@ inline EdgeWeight ConvertToDuration(double distance_in_meters, double speed_in_k
BOOST_ASSERT(speed_in_kmh > 0);
const double speed_in_ms = speed_in_kmh / 3.6;
const double duration = distance_in_meters / speed_in_ms;
return std::max<EdgeWeight>(1, static_cast<EdgeWeight>(std::round(duration * 10)));
return std::max<EdgeWeight>(1, static_cast<EdgeWeight>(std::round(duration * 10.)));
}
// Returns updated edge weight
EdgeWeight GetNewWeight(const SpeedSource &value,
const double &segment_length,
const std::vector<std::string> &segment_speed_filenames,
const EdgeWeight old_weight,
const double log_edge_updates_factor,
const OSMNodeID &from,
const OSMNodeID &to)
void GetNewWeight(const SpeedSource &value,
const double &segment_length,
const std::vector<std::string> &segment_speed_filenames,
const EdgeWeight current_duration,
const double log_edge_updates_factor,
const OSMNodeID from,
const OSMNodeID to,
EdgeWeight &new_segment_weight,
EdgeWeight &new_segment_duration)
{
const auto new_segment_weight =
// Update the edge duration as distance/speed
new_segment_duration =
(value.speed > 0) ? ConvertToDuration(segment_length, value.speed) : INVALID_EDGE_WEIGHT;
// the check here is enabled by the `--edge-weight-updates-over-factor` flag it logs
// a warning if the new weight exceeds a heuristic of what a reasonable weight update is
if (log_edge_updates_factor > 0 && old_weight != 0)
// Update the edge weight or fallback to the new edge duration
new_segment_weight =
(value.weight == INVALID_EDGE_WEIGHT) ? new_segment_duration : value.weight;
// The check here is enabled by the `--edge-weight-updates-over-factor` flag it logs a warning
// if the new duration exceeds a heuristic of what a reasonable duration update is
if (log_edge_updates_factor > 0 && current_duration != 0)
{
auto new_secs = new_segment_weight / 10.0;
auto old_secs = old_weight / 10.0;
auto approx_original_speed = (segment_length / old_secs) * 3.6;
if (old_weight >= (new_segment_weight * log_edge_updates_factor))
if (current_duration >= (new_segment_duration * log_edge_updates_factor))
{
auto new_secs = new_segment_duration / 10.;
auto old_secs = current_duration / 10.;
auto approx_original_speed = (segment_length / old_secs) * 3.6;
auto speed_file = segment_speed_filenames.at(value.source - 1);
util::Log(logWARNING) << "[weight updates] Edge weight update from " << old_secs
<< "s to " << new_secs << "s New speed: " << value.speed
@@ -273,21 +289,10 @@ EdgeWeight GetNewWeight(const SpeedSource &value,
<< speed_file;
}
}
return new_segment_weight;
}
int Contractor::Run()
{
#ifdef WIN32
#pragma message("Memory consumption on Windows can be higher due to different bit packing")
#else
static_assert(sizeof(extractor::NodeBasedEdge) == 24,
"changing extractor::NodeBasedEdge type has influence on memory consumption!");
static_assert(sizeof(extractor::EdgeBasedEdge) == 16,
"changing EdgeBasedEdge type has influence on memory consumption!");
#endif
if (config.core_factor > 1.0 || config.core_factor < 0)
{
throw util::exception("Core factor must be between 0.0 to 1.0 (inclusive)" + SOURCE_REF);
@@ -314,7 +319,9 @@ int Contractor::Run()
edge_based_edge_list,
node_weights,
config.edge_segment_lookup_path,
config.edge_penalty_path,
config.turn_weight_penalties_path,
config.turn_duration_penalties_path,
config.turn_penalties_index_path,
config.segment_speed_lookup_paths,
config.turn_penalty_lookup_paths,
config.node_based_graph_path,
@@ -377,7 +384,9 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
const std::string &edge_segment_lookup_filename,
const std::string &edge_penalty_filename,
const std::string &turn_weight_penalties_filename,
const std::string &turn_duration_penalties_filename,
const std::string &turn_penalties_index_filename,
const std::vector<std::string> &segment_speed_filenames,
const std::vector<std::string> &turn_penalty_filenames,
const std::string &nodes_filename,
@@ -393,15 +402,14 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
util::Log() << "Opening " << edge_based_graph_filename;
auto mmap_file = [](const std::string &filename) {
auto mmap_file = [](const std::string &filename, boost::interprocess::mode_t mode) {
using boost::interprocess::file_mapping;
using boost::interprocess::mapped_region;
using boost::interprocess::read_only;
try
{
const file_mapping mapping{filename.c_str(), read_only};
mapped_region region{mapping, read_only};
const file_mapping mapping{filename.c_str(), mode};
mapped_region region{mapping, mode};
region.advise(mapped_region::advice_sequential);
return region;
}
@@ -412,15 +420,16 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
}
};
const auto edge_based_graph_region = mmap_file(edge_based_graph_filename);
const auto edge_based_graph_region =
mmap_file(edge_based_graph_filename, boost::interprocess::read_only);
const bool update_edge_weights = !segment_speed_filenames.empty();
const bool update_turn_penalties = !turn_penalty_filenames.empty();
const auto edge_penalty_region = [&] {
const auto turn_penalties_index_region = [&] {
if (update_edge_weights || update_turn_penalties)
{
return mmap_file(edge_penalty_filename);
return mmap_file(turn_penalties_index_filename, boost::interprocess::read_only);
}
return boost::interprocess::mapped_region();
}();
@@ -428,7 +437,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
const auto edge_segment_region = [&] {
if (update_edge_weights || update_turn_penalties)
{
return mmap_file(edge_segment_lookup_filename);
return mmap_file(edge_segment_lookup_filename, boost::interprocess::read_only);
}
return boost::interprocess::mapped_region();
}();
@@ -474,12 +483,13 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
util::Log() << "Reading " << graph_header.number_of_edges << " edges from the edge based graph";
auto segment_speed_lookup = CSVFilesParser<Segment, SpeedSource>(
1, qi::ulong_long >> ',' >> qi::ulong_long, qi::uint_)(segment_speed_filenames);
1, qi::ulong_long >> ',' >> qi::ulong_long, qi::uint_ >> -(',' >> qi::uint_))(
segment_speed_filenames);
auto turn_penalty_lookup = CSVFilesParser<Turn, PenaltySource>(
1 + segment_speed_filenames.size(),
qi::ulong_long >> ',' >> qi::ulong_long >> ',' >> qi::ulong_long,
qi::double_)(turn_penalty_filenames);
qi::double_ >> -(',' >> qi::double_))(turn_penalty_filenames);
// If we update the edge weights, this file will hold the datasource information for each
// segment; the other files will also be conditionally filled concurrently if we make an update
@@ -490,9 +500,11 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
std::vector<NodeID> geometry_node_list;
std::vector<EdgeWeight> geometry_fwd_weight_list;
std::vector<EdgeWeight> geometry_rev_weight_list;
std::vector<EdgeWeight> geometry_fwd_duration_list;
std::vector<EdgeWeight> geometry_rev_duration_list;
const auto maybe_load_internal_to_external_node_map = [&] {
if (!(update_edge_weights || update_turn_penalties))
if (!update_edge_weights)
return;
storage::io::FileReader nodes_file(nodes_filename,
@@ -503,7 +515,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
};
const auto maybe_load_geometries = [&] {
if (!(update_edge_weights || update_turn_penalties))
if (!update_edge_weights)
return;
storage::io::FileReader geometry_file(geometry_filename,
@@ -518,6 +530,8 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
geometry_node_list.resize(number_of_compressed_geometries);
geometry_fwd_weight_list.resize(number_of_compressed_geometries);
geometry_rev_weight_list.resize(number_of_compressed_geometries);
geometry_fwd_duration_list.resize(number_of_compressed_geometries);
geometry_rev_duration_list.resize(number_of_compressed_geometries);
if (number_of_compressed_geometries > 0)
{
@@ -526,13 +540,17 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
number_of_compressed_geometries);
geometry_file.ReadInto(geometry_rev_weight_list.data(),
number_of_compressed_geometries);
geometry_file.ReadInto(geometry_fwd_duration_list.data(),
number_of_compressed_geometries);
geometry_file.ReadInto(geometry_rev_duration_list.data(),
number_of_compressed_geometries);
}
};
// Folds all our actions into independently concurrently executing lambdas
tbb::parallel_invoke(maybe_load_internal_to_external_node_map, maybe_load_geometries);
if (update_edge_weights || update_turn_penalties)
if (update_edge_weights)
{
// Here, we have to update the compressed geometry weights
// First, we need the external-to-internal node lookup table
@@ -552,7 +570,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
using boost::interprocess::mapped_region;
auto region = mmap_file(rtree_leaf_filename.c_str());
auto region = mmap_file(rtree_leaf_filename.c_str(), boost::interprocess::read_only);
region.advise(mapped_region::advice_willneed);
BOOST_ASSERT(is_aligned<LeafNode>(region.get_address()));
@@ -589,32 +607,38 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
auto fwd_source = LUA_SOURCE, rev_source = LUA_SOURCE;
if (auto value = segment_speed_lookup({u.node_id, v.node_id}))
{
const auto current_fwd_weight = geometry_fwd_weight_list[u_index];
const auto new_segment_weight = GetNewWeight(*value,
segment_length,
segment_speed_filenames,
current_fwd_weight,
log_edge_updates_factor,
u.node_id,
v.node_id);
EdgeWeight new_segment_weight, new_segment_duration;
GetNewWeight(*value,
segment_length,
segment_speed_filenames,
geometry_fwd_duration_list[u_index],
log_edge_updates_factor,
u.node_id,
v.node_id,
new_segment_weight,
new_segment_duration);
geometry_fwd_weight_list[v_index] = new_segment_weight;
geometry_fwd_duration_list[v_index] = new_segment_duration;
geometry_datasource[v_index] = value->source;
fwd_source = value->source;
}
if (auto value = segment_speed_lookup({v.node_id, u.node_id}))
{
const auto current_rev_weight = geometry_rev_weight_list[u_index];
const auto new_segment_weight = GetNewWeight(*value,
segment_length,
segment_speed_filenames,
current_rev_weight,
log_edge_updates_factor,
v.node_id,
u.node_id);
EdgeWeight new_segment_weight, new_segment_duration;
GetNewWeight(*value,
segment_length,
segment_speed_filenames,
geometry_rev_duration_list[u_index],
log_edge_updates_factor,
v.node_id,
u.node_id,
new_segment_weight,
new_segment_duration);
geometry_rev_weight_list[u_index] = new_segment_weight;
geometry_rev_duration_list[u_index] = new_segment_duration;
geometry_datasource[u_index] = value->source;
rev_source = value->source;
}
@@ -652,7 +676,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
}
const auto maybe_save_geometries = [&] {
if (!(update_edge_weights || update_turn_penalties))
if (!update_edge_weights)
return;
// Now save out the updated compressed geometries
@@ -675,6 +699,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
number_of_compressed_geometries * sizeof(EdgeWeight));
geometry_stream.write(reinterpret_cast<char *>(&(geometry_rev_weight_list[0])),
number_of_compressed_geometries * sizeof(EdgeWeight));
geometry_stream.write(reinterpret_cast<char *>(&(geometry_fwd_duration_list[0])),
number_of_compressed_geometries * sizeof(EdgeWeight));
geometry_stream.write(reinterpret_cast<char *>(&(geometry_rev_duration_list[0])),
number_of_compressed_geometries * sizeof(EdgeWeight));
};
const auto save_datasource_indexes = [&] {
@@ -716,17 +744,47 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
tbb::parallel_invoke(maybe_save_geometries, save_datasource_indexes, save_datastore_names);
auto turn_penalty_blocks_ptr = reinterpret_cast<const extractor::lookup::PenaltyBlock *>(
edge_penalty_region.get_address());
BOOST_ASSERT(is_aligned<extractor::lookup::PenaltyBlock>(turn_penalty_blocks_ptr));
std::vector<TurnPenalty> turn_weight_penalties;
std::vector<TurnPenalty> turn_duration_penalties;
auto edge_based_edge_ptr = reinterpret_cast<extractor::EdgeBasedEdge *>(
const auto maybe_load_turn_weight_penalties = [&] {
if (!update_edge_weights && !update_turn_penalties)
return;
using storage::io::FileReader;
FileReader file(turn_weight_penalties_filename, FileReader::HasNoFingerprint);
file.DeserializeVector(turn_weight_penalties);
};
const auto maybe_load_turn_duration_penalties = [&] {
if (!update_turn_penalties)
return;
using storage::io::FileReader;
FileReader file(turn_duration_penalties_filename, FileReader::HasNoFingerprint);
file.DeserializeVector(turn_duration_penalties);
};
tbb::parallel_invoke(maybe_load_turn_weight_penalties, maybe_load_turn_duration_penalties);
if (update_turn_penalties && turn_duration_penalties.empty())
{ // Copy-on-write for duration penalties as turn weight penalties
turn_duration_penalties = turn_weight_penalties;
}
// Mapped file pointer for turn indices
const extractor::lookup::TurnIndexBlock *turn_index_blocks =
reinterpret_cast<const extractor::lookup::TurnIndexBlock *>(
turn_penalties_index_region.get_address());
BOOST_ASSERT(is_aligned<extractor::lookup::TurnIndexBlock>(turn_index_blocks));
// Mapped file pointers for edge-based graph edges
auto edge_based_edge_ptr = reinterpret_cast<const extractor::EdgeBasedEdge *>(
reinterpret_cast<char *>(edge_based_graph_region.get_address()) +
sizeof(EdgeBasedGraphHeader));
BOOST_ASSERT(is_aligned<extractor::EdgeBasedEdge>(edge_based_edge_ptr));
auto edge_segment_byte_ptr = reinterpret_cast<const char *>(edge_segment_region.get_address());
bool fallback_to_duration = true;
for (std::uint64_t edge_index = 0; edge_index < graph_header.number_of_edges; ++edge_index)
{
// Make a copy of the data from the memory map
@@ -759,7 +817,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
if (value->speed == 0)
return true;
segment_weight = ConvertToDuration(segment.segment_length, value->speed);
segment_weight =
value->weight == INVALID_EDGE_WEIGHT
? ConvertToDuration(segment.segment_length, value->speed)
: value->weight;
}
// Update the edge weight and the next OSM node ID
@@ -779,30 +840,61 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
continue;
// Get the turn penalty and update to the new value if required
const auto &turn_block = turn_penalty_blocks_ptr[edge_index];
EdgeWeight new_turn_penalty = turn_block.fixed_penalty;
if (auto value = turn_penalty_lookup(turn_block))
const auto &turn_index = turn_index_blocks[edge_index];
auto turn_weight_penalty = turn_weight_penalties[edge_index];
if (auto value = turn_penalty_lookup(turn_index))
{
new_turn_penalty = static_cast<EdgeWeight>(value->penalty * 10);
auto turn_duration_penalty =
boost::numeric_cast<TurnPenalty>(std::round(value->duration * 10.));
turn_weight_penalty =
std::isfinite(value->weight)
? boost::numeric_cast<TurnPenalty>(std::round(value->weight * 10))
: turn_duration_penalty;
if (new_weight + new_turn_penalty < static_cast<EdgeWeight>(header->num_osm_nodes))
const auto weight_min_value = static_cast<EdgeWeight>(header->num_osm_nodes);
if (turn_weight_penalty + new_weight < weight_min_value)
{
util::Log(logWARNING)
<< "turn penalty " << value->penalty << " for turn " << turn_block.from_id
<< "," << turn_block.via_id << "," << turn_block.to_id
<< " is too negative: clamping turn weight to " << header->num_osm_nodes;
util::Log(logWARNING) << "turn penalty " << turn_weight_penalty << " for turn "
<< turn_index.from_id << ", " << turn_index.via_id << ", "
<< turn_index.to_id
<< " is too negative: clamping turn weight to "
<< weight_min_value;
new_turn_penalty = header->num_osm_nodes - new_weight;
turn_weight_penalty = weight_min_value - new_weight;
}
turn_duration_penalties[edge_index] = turn_duration_penalty;
turn_weight_penalties[edge_index] = turn_weight_penalty;
// Is fallback of duration to weight values allowed
fallback_to_duration &= (turn_duration_penalty == turn_weight_penalty);
}
// Update edge weight
inbuffer.weight = new_weight + new_turn_penalty;
inbuffer.weight = new_weight + turn_weight_penalty;
}
edge_based_edge_list.emplace_back(std::move(inbuffer));
}
if (update_turn_penalties)
{
if (fallback_to_duration)
{ // Turn duration penalties are identical to turn weight penalties
// Save empty data vector, so turn weight penalties will be used by data facade.
turn_duration_penalties.clear();
}
const auto save_penalties = [](const auto &filename, const auto &data) -> void {
storage::io::FileWriter file(filename, storage::io::FileWriter::HasNoFingerprint);
file.SerializeVector(data);
};
tbb::parallel_invoke(
[&] { save_penalties(turn_weight_penalties_filename, turn_weight_penalties); },
[&] { save_penalties(turn_duration_penalties_filename, turn_duration_penalties); });
}
util::Log() << "Done reading edges";
return graph_header.max_edge_id;
}