Added weight multipliers for speed and turn updates

This commit is contained in:
Michael Krasnyk 2017-01-17 09:24:52 +01:00 committed by Patrick Niklaus
parent 279f8aabfb
commit c059d15cb9
12 changed files with 117 additions and 124 deletions

View File

@ -51,12 +51,12 @@ Feature: Traffic - speeds
Given the contract extra arguments "--segment-speed-file {speeds_file}"
Given the speed file
"""
1,2,1,200207
2,1,1,200207
2,3,27,7415
3,2,27,7415
1,4,27,12757
4,1,27,12757
1,2,1,20020.7
2,1,1,20020.7
2,3,27,741.5
3,2,27,741.5
1,4,27,1275.7
4,1,27,1275.7
"""
And I route I should get
| from | to | route | speed | weights |
@ -70,25 +70,33 @@ Feature: Traffic - speeds
Scenario: Weighting based on speed file weights, ETA based on file durations
Given the contract extra arguments "--segment-speed-file {speeds_file}"
Given the profile file "testbot" extended with
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_precision = 3
"""
And the contract extra arguments "--segment-speed-file {speeds_file}"
Given the speed file
"""
1,2,1,200207
2,1,1,200207
2,3,27,7415
3,2,27,7415
1,4,1,344450
4,1,1,344450
1,2,1,20020.789
2,1,1,20020.123
2,3,27,741.56789
3,2,27,741.3
1,4,1,34445.12
4,1,1,34445.3
"""
And I route I should get
| from | to | route | speed | weights |
| a | b | ab,ab | 1 km/h | 20020.7,0 |
| a | c | ab,bc,bc | 2 km/h | 20020.7,741.5,0 |
| b | c | bc,bc | 27 km/h | 741.5,0 |
| a | d | ab,eb,de,de | 2 km/h | 20020.7,378.2,400.4,0 |
| d | c | dc,dc | 36 km/h | 956.8,0 |
| g | b | ab,ab | 1 km/h | 10010.4,0 |
| a | g | ab,ab | 1 km/h | 10010.3,0 |
| a | b | ab,ab | 1 km/h | 20020.789,0 |
| a | c | ab,bc,bc | 2 km/h | 20020.789,741.568,0 |
| b | c | bc,bc | 27 km/h | 741.568,0 |
| a | d | ab,eb,de,de | 2 km/h | 20020.789,378.169,400.415,0 |
| d | c | dc,dc | 36 km/h | 956.805,0 |
| g | b | ab,ab | 1 km/h | 10010.392,0 |
| a | g | ab,ab | 1 km/h | 10010.397,0 |
| g | a | ab,ab | 1 km/h | 10010.064,0 |
Scenario: Speeds that isolate a single node (a)

View File

@ -78,21 +78,9 @@ class Contractor
private:
ContractorConfig config;
EdgeID LoadEdgeExpandedGraph(const std::string &edge_based_graph_path,
EdgeID LoadEdgeExpandedGraph(const ContractorConfig &config,
std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
const std::string &edge_segment_lookup_path,
const std::string &turn_weight_penalties_path,
const std::string &turn_duration_penalties_path,
const std::string &turn_penalties_index_path,
const std::vector<std::string> &segment_speed_path,
const std::vector<std::string> &turn_penalty_path,
const std::string &nodes_filename,
const std::string &geometry_filename,
const std::string &datasource_names_filename,
const std::string &datasource_indexes_filename,
const std::string &rtree_leaf_filename,
const double log_edge_updates_factor);
std::vector<EdgeWeight> &node_weights);
};
}
}

View File

@ -39,7 +39,7 @@ namespace contractor
struct ContractorConfig
{
ContractorConfig() : requested_num_threads(0) {}
ContractorConfig() : requested_num_threads(0), weight_multiplier(10.) {}
// Infer the output names from the path of the .osrm file
void UseDefaultOutputNames()
@ -57,6 +57,7 @@ struct ContractorConfig
rtree_leaf_path = osrm_input_path.string() + ".fileIndex";
datasource_names_path = osrm_input_path.string() + ".datasource_names";
datasource_indexes_path = osrm_input_path.string() + ".datasource_indexes";
profile_properties_path = osrm_input_path.string() + ".properties";
}
boost::filesystem::path config_file_path;
@ -78,6 +79,7 @@ struct ContractorConfig
unsigned requested_num_threads;
double log_edge_updates_factor;
double weight_multiplier;
// A percentage of vertices that will be contracted for the hierarchy.
// Offers a trade-off between preprocessing and query time.
@ -89,6 +91,7 @@ struct ContractorConfig
std::vector<std::string> turn_penalty_lookup_paths;
std::string datasource_indexes_path;
std::string datasource_names_path;
std::string profile_properties_path;
};
}
}

View File

@ -996,6 +996,11 @@ class ContiguousInternalMemoryDataFacade : public BaseDataFacade
return m_profile_properties->weight_precision;
}
double GetWeightMultiplier() const override final
{
return m_profile_properties->GetWeightMultiplier();
}
BearingClassID GetBearingClassID(const NodeID id) const override final
{
return m_bearing_class_id_table.at(id);

View File

@ -189,6 +189,8 @@ class BaseDataFacade
virtual unsigned GetWeightPrecision() const = 0;
virtual double GetWeightMultiplier() const = 0;
virtual BearingClassID GetBearingClassID(const NodeID id) const = 0;
virtual util::guidance::TurnBearing PreTurnBearing(const EdgeID eid) const = 0;

View File

@ -41,7 +41,7 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
const bool source_traversed_in_reverse,
const bool target_traversed_in_reverse)
{
const double weight_multiplier = std::pow(10., facade.GetWeightPrecision());
const double weight_multiplier = facade.GetWeightMultiplier();
const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0., ZERO_WEIGHT = 0;
const constexpr char *NO_ROTARY_NAME = "";

View File

@ -67,6 +67,8 @@ struct ProfileProperties
return std::string(weight_name);
}
double GetWeightMultiplier() const { return std::pow(10., weight_precision); }
//! penalty to cross a traffic light in deci-seconds
std::int32_t traffic_signal_penalty;
//! penalty to do a uturn in deci-seconds

View File

@ -74,9 +74,9 @@ struct Segment final
struct SpeedSource final
{
SpeedSource() : speed(0), weight(INVALID_EDGE_WEIGHT) {}
SpeedSource() : speed(0), weight(std::numeric_limits<double>::quiet_NaN()) {}
unsigned speed;
EdgeWeight weight;
double weight;
std::uint8_t source;
};
@ -245,41 +245,42 @@ template <typename Key, typename Value> struct CSVFilesParser
// Returns duration in deci-seconds
inline EdgeWeight ConvertToDuration(double distance_in_meters, double speed_in_kmh)
{
BOOST_ASSERT(speed_in_kmh > 0);
if (speed_in_kmh <= 0.)
return INVALID_EDGE_WEIGHT;
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.)));
}
// Returns updated edge weight
void GetNewWeight(const SpeedSource &value,
void GetNewWeight(const ContractorConfig &config,
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)
{
// Update the edge duration as distance/speed
new_segment_duration =
(value.speed > 0) ? ConvertToDuration(segment_length, value.speed) : INVALID_EDGE_WEIGHT;
new_segment_duration = ConvertToDuration(segment_length, value.speed);
// Update the edge weight or fallback to the new edge duration
new_segment_weight =
(value.weight == INVALID_EDGE_WEIGHT) ? new_segment_duration : value.weight;
new_segment_weight = std::isfinite(value.weight)
? std::round(value.weight * config.weight_multiplier)
: new_segment_duration;
// 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)
if (config.log_edge_updates_factor > 0 && current_duration != 0)
{
if (current_duration >= (new_segment_duration * log_edge_updates_factor))
if (current_duration >= (new_segment_duration * config.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);
auto speed_file = config.segment_speed_lookup_paths.at(value.source - 1);
util::Log(logWARNING) << "[weight updates] Edge weight update from " << old_secs
<< "s to " << new_secs << "s New speed: " << value.speed
<< " kph"
@ -315,21 +316,7 @@ int Contractor::Run()
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
EdgeID max_edge_id = LoadEdgeExpandedGraph(config.edge_based_graph_path,
edge_based_edge_list,
node_weights,
config.edge_segment_lookup_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,
config.geometry_path,
config.datasource_names_path,
config.datasource_indexes_path,
config.rtree_leaf_path,
config.log_edge_updates_factor);
EdgeID max_edge_id = LoadEdgeExpandedGraph(config, edge_based_edge_list, node_weights);
// Contracting the edge-expanded graph
@ -380,27 +367,15 @@ int Contractor::Run()
}
EdgeID
Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
Contractor::LoadEdgeExpandedGraph(const ContractorConfig &config,
std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
const std::string &edge_segment_lookup_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,
const std::string &geometry_filename,
const std::string &datasource_names_filename,
const std::string &datasource_indexes_filename,
const std::string &rtree_leaf_filename,
const double log_edge_updates_factor)
std::vector<EdgeWeight> &node_weights)
{
if (segment_speed_filenames.size() > 255 || turn_penalty_filenames.size() > 255)
if (config.segment_speed_lookup_paths.size() + config.turn_penalty_lookup_paths.size() > 255)
throw util::exception("Limit of 255 segment speed and turn penalty files each reached" +
SOURCE_REF);
util::Log() << "Opening " << edge_based_graph_filename;
util::Log() << "Opening " << config.edge_based_graph_path;
auto mmap_file = [](const std::string &filename, boost::interprocess::mode_t mode) {
using boost::interprocess::file_mapping;
@ -421,15 +396,15 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
};
const auto edge_based_graph_region =
mmap_file(edge_based_graph_filename, boost::interprocess::read_only);
mmap_file(config.edge_based_graph_path, boost::interprocess::read_only);
const bool update_edge_weights = !segment_speed_filenames.empty();
const bool update_turn_penalties = !turn_penalty_filenames.empty();
const bool update_edge_weights = !config.segment_speed_lookup_paths.empty();
const bool update_turn_penalties = !config.turn_penalty_lookup_paths.empty();
const auto turn_penalties_index_region = [&] {
if (update_edge_weights || update_turn_penalties)
{
return mmap_file(turn_penalties_index_filename, boost::interprocess::read_only);
return mmap_file(config.turn_penalties_index_path, boost::interprocess::read_only);
}
return boost::interprocess::mapped_region();
}();
@ -437,7 +412,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, boost::interprocess::read_only);
return mmap_file(config.edge_segment_lookup_path, boost::interprocess::read_only);
}
return boost::interprocess::mapped_region();
}();
@ -462,13 +437,13 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
const util::FingerPrint expected_fingerprint = util::FingerPrint::GetValid();
if (!graph_header.fingerprint.IsValid())
{
util::Log(logERROR) << edge_based_graph_filename << " does not have a valid fingerprint";
util::Log(logERROR) << config.edge_based_graph_path << " does not have a valid fingerprint";
throw util::exception("Invalid fingerprint");
}
if (!expected_fingerprint.IsDataCompatible(graph_header.fingerprint))
{
util::Log(logERROR) << edge_based_graph_filename
util::Log(logERROR) << config.edge_based_graph_path
<< " is not compatible with this version of OSRM.";
util::Log(logERROR) << "It was prepared with OSRM "
<< graph_header.fingerprint.GetMajorVersion() << "."
@ -483,13 +458,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_ >> -(',' >> qi::uint_))(
segment_speed_filenames);
1, qi::ulong_long >> ',' >> qi::ulong_long, qi::uint_ >> -(',' >> qi::double_))(
config.segment_speed_lookup_paths);
auto turn_penalty_lookup = CSVFilesParser<Turn, PenaltySource>(
1 + segment_speed_filenames.size(),
1 + config.segment_speed_lookup_paths.size(),
qi::ulong_long >> ',' >> qi::ulong_long >> ',' >> qi::ulong_long,
qi::double_ >> -(',' >> qi::double_))(turn_penalty_filenames);
qi::double_ >> -(',' >> qi::double_))(config.turn_penalty_lookup_paths);
// 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
@ -507,18 +482,17 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
if (!update_edge_weights)
return;
storage::io::FileReader nodes_file(nodes_filename,
storage::io::FileReader nodes_file(config.node_based_graph_path,
storage::io::FileReader::HasNoFingerprint);
nodes_file.DeserializeVector(internal_to_external_node_map);
};
const auto maybe_load_geometries = [&] {
if (!update_edge_weights)
return;
storage::io::FileReader geometry_file(geometry_filename,
storage::io::FileReader geometry_file(config.geometry_path,
storage::io::FileReader::HasNoFingerprint);
const auto number_of_indices = geometry_file.ReadElementCount32();
geometry_indices.resize(number_of_indices);
@ -570,7 +544,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
using boost::interprocess::mapped_region;
auto region = mmap_file(rtree_leaf_filename.c_str(), boost::interprocess::read_only);
auto region = mmap_file(config.rtree_leaf_path.c_str(), boost::interprocess::read_only);
region.advise(mapped_region::advice_willneed);
BOOST_ASSERT(is_aligned<LeafNode>(region.get_address()));
@ -581,7 +555,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
// vector to count used speeds for logging
// size offset by one since index 0 is used for speeds not from external file
using counters_type = std::vector<std::size_t>;
std::size_t num_counters = segment_speed_filenames.size() + 1;
std::size_t num_counters = config.segment_speed_lookup_paths.size() + 1;
tbb::enumerable_thread_specific<counters_type> segment_speeds_counters(
counters_type(num_counters, 0));
const constexpr auto LUA_SOURCE = 0;
@ -608,11 +582,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
if (auto value = segment_speed_lookup({u.node_id, v.node_id}))
{
EdgeWeight new_segment_weight, new_segment_duration;
GetNewWeight(*value,
GetNewWeight(config,
*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,
@ -627,11 +600,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
if (auto value = segment_speed_lookup({v.node_id, u.node_id}))
{
EdgeWeight new_segment_weight, new_segment_duration;
GetNewWeight(*value,
GetNewWeight(config,
*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,
@ -670,7 +642,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
// segments_speeds_counters has 0 as LUA, segment_speed_filenames not, thus we need
// to susbstract 1 to avoid off-by-one error
util::Log() << "Used " << merged_counters[i] << " speeds from "
<< segment_speed_filenames[i - 1];
<< config.segment_speed_lookup_paths[i - 1];
}
}
}
@ -680,10 +652,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
return;
// Now save out the updated compressed geometries
std::ofstream geometry_stream(geometry_filename, std::ios::binary);
std::ofstream geometry_stream(config.geometry_path, std::ios::binary);
if (!geometry_stream)
{
const std::string message{"Failed to open " + geometry_filename + " for writing"};
const std::string message{"Failed to open " + config.geometry_path + " for writing"};
throw util::exception(message + SOURCE_REF);
}
const unsigned number_of_indices = geometry_indices.size();
@ -706,10 +678,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
};
const auto save_datasource_indexes = [&] {
std::ofstream datasource_stream(datasource_indexes_filename, std::ios::binary);
std::ofstream datasource_stream(config.datasource_indexes_path, std::ios::binary);
if (!datasource_stream)
{
const std::string message{"Failed to open " + datasource_indexes_filename +
const std::string message{"Failed to open " + config.datasource_indexes_path +
" for writing"};
throw util::exception(message + SOURCE_REF);
}
@ -724,10 +696,10 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
};
const auto save_datastore_names = [&] {
std::ofstream datasource_stream(datasource_names_filename, std::ios::binary);
std::ofstream datasource_stream(config.datasource_names_path, std::ios::binary);
if (!datasource_stream)
{
const std::string message{"Failed to open " + datasource_names_filename +
const std::string message{"Failed to open " + config.datasource_names_path +
" for writing"};
throw util::exception(message + SOURCE_REF);
}
@ -736,7 +708,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
// Only write the filename, without path or extension.
// This prevents information leakage, and keeps names short
// for rendering in the debug tiles.
for (auto const &name : segment_speed_filenames)
for (auto const &name : config.segment_speed_lookup_paths)
{
datasource_stream << boost::filesystem::path(name).stem().string() << std::endl;
}
@ -751,7 +723,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
if (!update_edge_weights && !update_turn_penalties)
return;
using storage::io::FileReader;
FileReader file(turn_weight_penalties_filename, FileReader::HasNoFingerprint);
FileReader file(config.turn_weight_penalties_path, FileReader::HasNoFingerprint);
file.DeserializeVector(turn_weight_penalties);
};
@ -759,7 +731,7 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
if (!update_turn_penalties)
return;
using storage::io::FileReader;
FileReader file(turn_duration_penalties_filename, FileReader::HasNoFingerprint);
FileReader file(config.turn_duration_penalties_path, FileReader::HasNoFingerprint);
file.DeserializeVector(turn_duration_penalties);
};
@ -818,9 +790,9 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
return true;
segment_weight =
value->weight == INVALID_EDGE_WEIGHT
? ConvertToDuration(segment.segment_length, value->speed)
: value->weight;
std::isfinite(value->weight)
? std::round(value->weight * config.weight_multiplier)
: ConvertToDuration(segment.segment_length, value->speed);
}
// Update the edge weight and the next OSM node ID
@ -846,9 +818,9 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
{
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_weight_penalty = std::isfinite(value->weight)
? boost::numeric_cast<TurnPenalty>(std::round(
value->weight * config.weight_multiplier))
: turn_duration_penalty;
const auto weight_min_value = static_cast<EdgeWeight>(header->num_osm_nodes);
@ -891,8 +863,8 @@ Contractor::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename,
};
tbb::parallel_invoke(
[&] { save_penalties(turn_weight_penalties_filename, turn_weight_penalties); },
[&] { save_penalties(turn_duration_penalties_filename, turn_duration_penalties); });
[&] { save_penalties(config.turn_weight_penalties_path, turn_weight_penalties); },
[&] { save_penalties(config.turn_duration_penalties_path, turn_duration_penalties); });
}
util::Log() << "Done reading edges";

View File

@ -371,7 +371,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
std::vector<TurnPenalty> turn_duration_penalties;
const auto weight_multiplier =
std::pow(10, scripting_environment.GetProfileProperties().weight_precision);
scripting_environment.GetProfileProperties().GetWeightMultiplier();
{
util::UnbufferedLog log;

View File

@ -392,7 +392,7 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
const auto all_nodes_list_end_ = all_nodes_list.end();
const auto weight_multiplier =
std::pow(10, scripting_environment.GetProfileProperties().weight_precision);
scripting_environment.GetProfileProperties().GetWeightMultiplier();
while (edge_iterator != all_edges_list_end_ && node_iterator != all_nodes_list_end_)
{

View File

@ -1,5 +1,7 @@
#include "contractor/contractor.hpp"
#include "contractor/contractor_config.hpp"
#include "extractor/profile_properties.hpp"
#include "storage/io.hpp"
#include "util/log.hpp"
#include "util/version.hpp"
@ -162,6 +164,16 @@ int main(int argc, char *argv[]) try
return EXIT_FAILURE;
}
if (boost::filesystem::is_regular_file(contractor_config.osrm_input_path))
{
// Propagate profile properties to contractor configuration structure
extractor::ProfileProperties profile_properties;
storage::io::FileReader profile_properties_file(contractor_config.profile_properties_path,
storage::io::FileReader::HasNoFingerprint);
profile_properties_file.ReadInto<extractor::ProfileProperties>(&profile_properties, 1);
contractor_config.weight_multiplier = profile_properties.GetWeightMultiplier();
}
util::Log() << "Input file: " << contractor_config.osrm_input_path.filename().string();
util::Log() << "Threads: " << contractor_config.requested_num_threads;

View File

@ -226,6 +226,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
double GetMapMatchingMaxSpeed() const override { return 180 / 3.6; }
const char *GetWeightName() const override final { return "duration"; }
unsigned GetWeightPrecision() const override final { return 1; }
double GetWeightMultiplier() const override final { return 10.; }
BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; }
EntryClassID GetEntryClassID(const EdgeID /*id*/) const override { return 0; }