Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,26 @@
project(routing_common)
set(SRC
bicycle_model.cpp
bicycle_model.hpp
car_model.cpp
car_model.hpp
car_model_coefs.hpp # This file is generated by running ../configure.sh script.
maxspeed_conversion.cpp
maxspeed_conversion.hpp
num_mwm_id.hpp
pedestrian_model.cpp
pedestrian_model.hpp
vehicle_model.cpp
vehicle_model.hpp
)
omim_add_library(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
PUBLIC
base
indexer
platform
)
omim_add_test_subdirectory(routing_common_tests)

View file

@ -0,0 +1,304 @@
#include "routing_common/bicycle_model.hpp"
#include "indexer/classificator.hpp"
#include "indexer/feature.hpp"
namespace bicycle_model
{
using namespace routing;
// See model specifics in different countries here:
// https://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Access-Restrictions
// Document contains proposals for some countries, but we assume that some kinds of roads are ready for bicycle routing,
// but not listed in tables in the document. For example, steps are not listed, paths, roads and services features also
// can be treated as ready for bicycle routing. These road types were added to lists below.
// See road types here:
// https://wiki.openstreetmap.org/wiki/Key:highway
// Heuristics:
// For less bicycle roads we add fine by setting smaller value of weight speed, and for more bicycle roads we
// set greater values of weight speed. Algorithm picks roads with greater weight speed first,
// preferencing a more bicycle roads over less bicycle.
// As result of such heuristic road is not totally the shortest, but it avoids non bicycle roads, which were
// not marked as "hwtag=nobicycle" in OSM.
HighwayBasedFactors const kDefaultFactors = GetOneFactorsForBicycleAndPedestrianModel();
SpeedKMpH constexpr kSpeedOffroadKMpH = {1.5 /* weight */, 3.0 /* eta */};
SpeedKMpH constexpr kSpeedDismountKMpH = {2.0 /* weight */, 4.0 /* eta */};
// Applies only to countries where cycling is allowed on footways (by default the above dismount speed is used).
SpeedKMpH constexpr kSpeedOnFootwayKMpH = {8.0 /* weight */, 10.0 /* eta */};
HighwayBasedSpeeds const kDefaultSpeeds = {
// {highway class : InOutCitySpeedKMpH(in city(weight, eta), out city(weight eta))}
// Note that roads with hwtag=yesbicycle get high speed of 0.9 * Cycleway.
/// @see Russia_UseTrunk test for Trunk weights.
{HighwayType::HighwayTrunk, InOutCitySpeedKMpH(SpeedKMpH(7.0, 17.0), SpeedKMpH(9.0, 19.0))},
// Presence of link roads usually means that connected roads are high traffic.
// And complex intersections themselves are not nice for cyclists. We can't
// easily extrapolate this to the main roads, but at least penalize the link roads a bit.
// https://github.com/organicmaps/organicmaps/pull/9692#discussion_r1851442568
{HighwayType::HighwayTrunkLink, InOutCitySpeedKMpH(SpeedKMpH(6.0, 17.0), SpeedKMpH(8.0, 19.0))},
{HighwayType::HighwayPrimary, InOutCitySpeedKMpH(SpeedKMpH(10.0, 17.0), SpeedKMpH(12.0, 19.0))},
{HighwayType::HighwayPrimaryLink, InOutCitySpeedKMpH(SpeedKMpH(8.0, 17.0), SpeedKMpH(11.0, 19.0))},
{HighwayType::HighwaySecondary, InOutCitySpeedKMpH(SpeedKMpH(13.0, 17.0), SpeedKMpH(15.0, 19.0))},
{HighwayType::HighwaySecondaryLink, InOutCitySpeedKMpH(SpeedKMpH(11.0, 17.0), SpeedKMpH(13.0, 19.0))},
{HighwayType::HighwayTertiary, InOutCitySpeedKMpH(SpeedKMpH(14.0, 17.0), SpeedKMpH(17.0, 19.0))},
{HighwayType::HighwayTertiaryLink, InOutCitySpeedKMpH(SpeedKMpH(13.0, 17.0), SpeedKMpH(16.0, 19.0))},
{HighwayType::HighwayUnclassified, InOutCitySpeedKMpH(SpeedKMpH(13.0, 17.0), SpeedKMpH(15.0, 19.0))},
{HighwayType::HighwayResidential, InOutCitySpeedKMpH(SpeedKMpH(12.0, 14.0), SpeedKMpH(14.0, 17.0))},
{HighwayType::HighwayService, InOutCitySpeedKMpH(SpeedKMpH(13.0, 15.0), SpeedKMpH(15.0, 17.0))},
{HighwayType::HighwayRoad, InOutCitySpeedKMpH(SpeedKMpH(11.0, 15.0), SpeedKMpH(14.0, 17.0))},
{HighwayType::HighwayTrack, InOutCitySpeedKMpH(SpeedKMpH(8.0, 12.0), SpeedKMpH(10.0, 14.0))},
{HighwayType::HighwayPath, InOutCitySpeedKMpH(SpeedKMpH(6.0, 10.0), SpeedKMpH(7.0, 12.0))},
{HighwayType::HighwayBridleway, InOutCitySpeedKMpH(SpeedKMpH(4.0, 10.0), SpeedKMpH(5.0, 12.0))},
{HighwayType::HighwayCycleway, InOutCitySpeedKMpH(SpeedKMpH(21.0, 18.0), SpeedKMpH(23.0, 20.0))},
{HighwayType::HighwayLivingStreet, InOutCitySpeedKMpH(SpeedKMpH(12.0, 10.0), SpeedKMpH(14.0, 12.0))},
// Steps have obvious inconvenience of a bike in hands.
{HighwayType::HighwaySteps, InOutCitySpeedKMpH(SpeedKMpH(1.0, 1.0))},
{HighwayType::HighwayPedestrian, InOutCitySpeedKMpH(kSpeedDismountKMpH)},
{HighwayType::HighwayFootway, InOutCitySpeedKMpH(kSpeedDismountKMpH)},
{HighwayType::ManMadePier, InOutCitySpeedKMpH(kSpeedOnFootwayKMpH)},
/// @todo A car ferry has {10, 10}. Weight = 9 is 60% from reasonable 15 average speed.
{HighwayType::RouteFerry, InOutCitySpeedKMpH(SpeedKMpH(9.0, 20.0))},
};
// Default, no bridleway.
VehicleModel::LimitsInitList const kDefaultOptions = {
// {HighwayType, passThroughAllowed}
{HighwayType::HighwayTrunk, true},
{HighwayType::HighwayTrunkLink, true},
{HighwayType::HighwayPrimary, true},
{HighwayType::HighwayPrimaryLink, true},
{HighwayType::HighwaySecondary, true},
{HighwayType::HighwaySecondaryLink, true},
{HighwayType::HighwayTertiary, true},
{HighwayType::HighwayTertiaryLink, true},
{HighwayType::HighwayService, true},
{HighwayType::HighwayUnclassified, true},
{HighwayType::HighwayRoad, true},
{HighwayType::HighwayTrack, true},
{HighwayType::HighwayPath, true},
// HighwayBridleway is missing
{HighwayType::HighwayCycleway, true},
{HighwayType::HighwayResidential, true},
{HighwayType::HighwayLivingStreet, true},
// HighwayLadder is missing
{HighwayType::HighwaySteps, true},
{HighwayType::HighwayPedestrian, true},
{HighwayType::HighwayFootway, true},
{HighwayType::ManMadePier, true},
{HighwayType::RouteFerry, true}};
// Same as defaults except trunk and trunk_link are not allowed
VehicleModel::LimitsInitList NoTrunk()
{
VehicleModel::LimitsInitList res;
res.reserve(kDefaultOptions.size() - 2);
for (auto const & e : kDefaultOptions)
if (e.m_type != HighwayType::HighwayTrunk && e.m_type != HighwayType::HighwayTrunkLink)
res.push_back(e);
return res;
}
// Same as defaults except pedestrian is allowed
HighwayBasedSpeeds NormalPedestrianSpeed()
{
HighwayBasedSpeeds res = kDefaultSpeeds;
res.Replace(HighwayType::HighwayPedestrian, InOutCitySpeedKMpH(kSpeedOnFootwayKMpH));
return res;
}
// Same as defaults except bridleway is allowed
VehicleModel::LimitsInitList AllAllowed()
{
auto res = kDefaultOptions;
res.push_back({HighwayType::HighwayBridleway, true});
return res;
}
// Same as defaults except pedestrian and footway are allowed
HighwayBasedSpeeds NormalPedestrianAndFootwaySpeed()
{
HighwayBasedSpeeds res = kDefaultSpeeds;
InOutCitySpeedKMpH const footSpeed(kSpeedOnFootwayKMpH);
res.Replace(HighwayType::HighwayPedestrian, footSpeed);
res.Replace(HighwayType::HighwayFootway, footSpeed);
return res;
}
HighwayBasedSpeeds DismountPathSpeed()
{
HighwayBasedSpeeds res = kDefaultSpeeds;
res.Replace(HighwayType::HighwayPath, InOutCitySpeedKMpH(kSpeedDismountKMpH));
return res;
}
HighwayBasedSpeeds PreferFootwaysToRoads()
{
HighwayBasedSpeeds res = kDefaultSpeeds;
// Decrease secondary/tertiary weight speed (-20% from default).
InOutCitySpeedKMpH roadSpeed = InOutCitySpeedKMpH(SpeedKMpH(11.0, 17.0), SpeedKMpH(16.0, 19.0));
res.Replace(HighwayType::HighwaySecondary, roadSpeed);
res.Replace(HighwayType::HighwaySecondaryLink, roadSpeed);
res.Replace(HighwayType::HighwayTertiary, roadSpeed);
res.Replace(HighwayType::HighwayTertiaryLink, roadSpeed);
// Increase footway speed to make bigger than other roads (+20% from default roads).
InOutCitySpeedKMpH footSpeed = InOutCitySpeedKMpH(SpeedKMpH(17.0, 12.0), SpeedKMpH(20.0, 15.0));
res.Replace(HighwayType::HighwayPedestrian, footSpeed);
res.Replace(HighwayType::HighwayFootway, footSpeed);
return res;
}
// No trunk, No pass through living_street and service
VehicleModel::LimitsInitList UkraineOptions()
{
auto res = NoTrunk();
for (auto & e : res)
if (e.m_type == HighwayType::HighwayLivingStreet || e.m_type == HighwayType::HighwayService)
e.m_isPassThroughAllowed = false;
return res;
}
VehicleModel::SurfaceInitList const kBicycleSurface = {
// {{surfaceType}, {weightFactor, etaFactor}}
{{"psurface", "paved_good"}, {1.0, 1.0}},
{{"psurface", "paved_bad"}, {0.8, 0.8}},
{{"psurface", "unpaved_good"}, {0.9, 0.9}},
{{"psurface", "unpaved_bad"}, {0.3, 0.3}},
// No dedicated cycleway doesn't mean that bicycle is not allowed, just lower weight.
// If nocycleway is tagged explicitly then there is no cycling infra for sure.
// Otherwise there is a small chance cycling infra is present though not mapped?
/// @todo(pastk): this heuristic is controversial, maybe remove completely?
{{"hwtag", "nocycleway"}, {0.95, 0.95}},
};
} // namespace bicycle_model
namespace routing
{
BicycleModel::BicycleModel() : BicycleModel(bicycle_model::kDefaultOptions) {}
BicycleModel::BicycleModel(VehicleModel::LimitsInitList const & limits)
: BicycleModel(limits, bicycle_model::kDefaultSpeeds)
{}
BicycleModel::BicycleModel(VehicleModel::LimitsInitList const & limits, HighwayBasedSpeeds const & speeds)
: VehicleModel(classif(), limits, bicycle_model::kBicycleSurface, {speeds, bicycle_model::kDefaultFactors})
{
using namespace bicycle_model;
// No bridleway in default.
ASSERT_EQUAL(kDefaultOptions.size(), kDefaultSpeeds.size() - 1, ());
std::vector<std::string> hwtagYesBicycle = {"hwtag", "yesbicycle"};
auto const & cl = classif();
m_noType = cl.GetTypeByPath({"hwtag", "nobicycle"});
m_yesType = cl.GetTypeByPath(hwtagYesBicycle);
m_bidirBicycleType = cl.GetTypeByPath({"hwtag", "bidir_bicycle"});
m_onedirBicycleType = cl.GetTypeByPath({"hwtag", "onedir_bicycle"});
// Assign 90% of max cycleway speed for bicycle=yes to keep choosing most preferred cycleway.
auto const yesSpeed = kDefaultSpeeds.Get(HighwayType::HighwayCycleway).m_inCity * 0.9;
AddAdditionalRoadTypes(cl, {{std::move(hwtagYesBicycle), InOutCitySpeedKMpH(yesSpeed)}});
// Update max speed with possible ferry transfer and bicycle speed downhill.
// See EdgeEstimator::CalcHeuristic, GetBicycleClimbPenalty.
SpeedKMpH constexpr kMaxBicycleSpeedKMpH(100.0);
CHECK_LESS(m_maxModelSpeed, kMaxBicycleSpeedKMpH, ());
m_maxModelSpeed = kMaxBicycleSpeedKMpH;
}
bool BicycleModel::IsBicycleBidir(feature::TypesHolder const & types) const
{
return types.Has(m_bidirBicycleType);
}
bool BicycleModel::IsBicycleOnedir(feature::TypesHolder const & types) const
{
return types.Has(m_onedirBicycleType);
}
SpeedKMpH BicycleModel::GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const
{
return GetTypeSpeedImpl(types, speedParams, false /* isCar */);
}
bool BicycleModel::IsOneWay(FeatureTypes const & types) const
{
if (IsBicycleOnedir(types))
return true;
if (IsBicycleBidir(types))
return false;
return VehicleModel::IsOneWay(types);
}
SpeedKMpH const & BicycleModel::GetOffroadSpeed() const
{
return bicycle_model::kSpeedOffroadKMpH;
}
// If one of feature types will be disabled for bicycles, features of this type will be simplified
// in generator. Look FeatureBuilder1::IsRoad() for more details.
// static
BicycleModel const & BicycleModel::AllLimitsInstance()
{
static BicycleModel const instance(bicycle_model::AllAllowed(), bicycle_model::NormalPedestrianAndFootwaySpeed());
return instance;
}
// static
SpeedKMpH BicycleModel::DismountSpeed()
{
return bicycle_model::kSpeedDismountKMpH;
}
BicycleModelFactory::BicycleModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn)
: VehicleModelFactory(countryParentNameGetterFn)
{
using namespace bicycle_model;
using std::make_shared;
// Names must be the same with country names from countries.txt
m_models[""] = make_shared<BicycleModel>(kDefaultOptions);
m_models["Australia"] = make_shared<BicycleModel>(AllAllowed(), NormalPedestrianAndFootwaySpeed());
m_models["Austria"] = make_shared<BicycleModel>(NoTrunk(), DismountPathSpeed());
// Belarus law demands to use footways for bicycles where possible.
m_models["Belarus"] = make_shared<BicycleModel>(kDefaultOptions, PreferFootwaysToRoads());
m_models["Belgium"] = make_shared<BicycleModel>(NoTrunk(), NormalPedestrianSpeed());
m_models["Brazil"] = make_shared<BicycleModel>(AllAllowed());
m_models["Denmark"] = make_shared<BicycleModel>(NoTrunk());
m_models["France"] = make_shared<BicycleModel>(NoTrunk(), NormalPedestrianSpeed());
m_models["Finland"] = make_shared<BicycleModel>(kDefaultOptions, NormalPedestrianSpeed());
m_models["Hungary"] = make_shared<BicycleModel>(NoTrunk());
m_models["Iceland"] = make_shared<BicycleModel>(AllAllowed(), NormalPedestrianAndFootwaySpeed());
m_models["Ireland"] = make_shared<BicycleModel>(AllAllowed());
m_models["Italy"] = make_shared<BicycleModel>(kDefaultOptions, NormalPedestrianSpeed());
m_models["Netherlands"] = make_shared<BicycleModel>(NoTrunk());
m_models["Norway"] = make_shared<BicycleModel>(AllAllowed(), NormalPedestrianAndFootwaySpeed());
m_models["Oman"] = make_shared<BicycleModel>(AllAllowed());
m_models["Philippines"] = make_shared<BicycleModel>(AllAllowed(), NormalPedestrianSpeed());
m_models["Poland"] = make_shared<BicycleModel>(NoTrunk());
m_models["Romania"] = make_shared<BicycleModel>(AllAllowed());
// Note. Despite the fact that according to
// https://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Access-Restrictions passing through service and
// living_street with a bicycle is prohibited it's allowed according to Russian traffic rules.
m_models["Russian Federation"] = make_shared<BicycleModel>(kDefaultOptions, NormalPedestrianAndFootwaySpeed());
m_models["Slovakia"] = make_shared<BicycleModel>(NoTrunk());
m_models["Spain"] = make_shared<BicycleModel>(NoTrunk(), NormalPedestrianSpeed());
m_models["Sweden"] = make_shared<BicycleModel>(kDefaultOptions, NormalPedestrianSpeed());
m_models["Switzerland"] = make_shared<BicycleModel>(NoTrunk(), NormalPedestrianAndFootwaySpeed());
m_models["Ukraine"] = make_shared<BicycleModel>(UkraineOptions());
m_models["United Kingdom"] = make_shared<BicycleModel>(AllAllowed());
m_models["United States of America"] = make_shared<BicycleModel>(AllAllowed(), NormalPedestrianSpeed());
}
} // namespace routing

View file

@ -0,0 +1,40 @@
#pragma once
#include "routing_common/vehicle_model.hpp"
namespace routing
{
class BicycleModel : public VehicleModel
{
public:
BicycleModel();
explicit BicycleModel(VehicleModel::LimitsInitList const & limits);
BicycleModel(VehicleModel::LimitsInitList const & limits, HighwayBasedSpeeds const & speeds);
/// VehicleModelInterface overrides:
SpeedKMpH GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const override;
bool IsOneWay(FeatureTypes const & types) const override;
SpeedKMpH const & GetOffroadSpeed() const override;
static BicycleModel const & AllLimitsInstance();
static SpeedKMpH DismountSpeed();
private:
/// @return true if it is allowed to ride a bicycle in both directions.
bool IsBicycleBidir(feature::TypesHolder const & types) const;
// Returns true if the road is explicitly set oneway for bicycles.
bool IsBicycleOnedir(feature::TypesHolder const & types) const;
uint32_t m_bidirBicycleType = 0;
uint32_t m_onedirBicycleType = 0;
};
class BicycleModelFactory : public VehicleModelFactory
{
public:
// TODO: remove countryParentNameGetterFn default value after removing unused bicycle routing
// from road_graph_router
BicycleModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn = {});
};
} // namespace routing

View file

@ -0,0 +1,158 @@
#include "routing_common/car_model.hpp"
#include "routing_common/car_model_coefs.hpp"
#include "indexer/classificator.hpp"
namespace car_model
{
using namespace routing;
// See model specifics in different countries here:
// https://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Access-Restrictions
// See road types here:
// https://wiki.openstreetmap.org/wiki/Key:highway
// |kSpeedOffroadKMpH| is a speed which is used for edges that don't lie on road features.
// For example for pure fake edges. In car routing, off road speed for calculation ETA is not used.
// The weight of such edges is considered as 0 seconds. It's especially actual when an airport is
// a start or finish. On the other hand, while route calculation the fake edges are considered
// as quite heavy. The idea behind that is to use the closest edge for the start and the finish
// of the route except for some edge cases.
SpeedKMpH constexpr kSpeedOffroadKMpH = {0.01 /* weight */, kNotUsed /* eta */};
VehicleModel::LimitsInitList const kDefaultOptions = {
// {HighwayType, passThroughAllowed}
{HighwayType::HighwayMotorway, true}, {HighwayType::HighwayMotorwayLink, true},
{HighwayType::HighwayTrunk, true}, {HighwayType::HighwayTrunkLink, true},
{HighwayType::HighwayPrimary, true}, {HighwayType::HighwayPrimaryLink, true},
{HighwayType::HighwaySecondary, true}, {HighwayType::HighwaySecondaryLink, true},
{HighwayType::HighwayTertiary, true}, {HighwayType::HighwayTertiaryLink, true},
{HighwayType::HighwayResidential, true}, {HighwayType::HighwayUnclassified, true},
{HighwayType::HighwayService, true}, {HighwayType::HighwayLivingStreet, true},
{HighwayType::HighwayRoad, true}, {HighwayType::HighwayTrack, true},
{HighwayType::RouteShuttleTrain, true}, {HighwayType::RouteFerry, true},
{HighwayType::ManMadePier, true}};
VehicleModel::LimitsInitList NoPassThroughLivingStreet()
{
auto res = kDefaultOptions;
for (auto & e : res)
if (e.m_type == HighwayType::HighwayLivingStreet)
e.m_isPassThroughAllowed = false;
return res;
}
VehicleModel::LimitsInitList NoPassThroughService(VehicleModel::LimitsInitList res = kDefaultOptions)
{
for (auto & e : res)
if (e.m_type == HighwayType::HighwayService)
e.m_isPassThroughAllowed = false;
return res;
}
VehicleModel::LimitsInitList NoTrack()
{
VehicleModel::LimitsInitList res;
res.reserve(kDefaultOptions.size() - 1);
for (auto const & e : kDefaultOptions)
if (e.m_type != HighwayType::HighwayTrack)
res.push_back(e);
return res;
}
VehicleModel::LimitsInitList NoPassThroughTrack()
{
auto res = kDefaultOptions;
for (auto & e : res)
if (e.m_type == HighwayType::HighwayTrack)
e.m_isPassThroughAllowed = false;
return res;
}
/// @todo Should make some compare constrains (like in CarModel_TrackVsGravelTertiary test)
/// to better fit these factors with reality. I have no idea, how they were set.
VehicleModel::SurfaceInitList const kCarSurface = {
// {{surfaceType, surfaceType}, {weightFactor, etaFactor}}
{{"psurface", "paved_good"}, {1.0, 1.0}},
{{"psurface", "paved_bad"}, {0.6, 0.7}},
{{"psurface", "unpaved_good"}, {0.4, 0.7}},
{{"psurface", "unpaved_bad"}, {0.2, 0.3}}};
} // namespace car_model
namespace routing
{
CarModel::CarModel() : CarModel(car_model::kDefaultOptions) {}
CarModel::CarModel(VehicleModel::LimitsInitList const & roadLimits)
: VehicleModel(classif(), roadLimits, car_model::kCarSurface, {kHighwayBasedSpeeds, kHighwayBasedFactors})
{
ASSERT_EQUAL(kHighwayBasedSpeeds.size(), kHighwayBasedFactors.size(), ());
ASSERT_EQUAL(kHighwayBasedSpeeds.size(), car_model::kDefaultOptions.size(), ());
std::vector<std::string> hwtagYesCar = {"hwtag", "yescar"};
auto const & cl = classif();
m_noType = cl.GetTypeByPath({"hwtag", "nocar"});
m_yesType = cl.GetTypeByPath(hwtagYesCar);
// Set small track speed if highway is not in kHighwayBasedSpeeds (path, pedestrian), but marked as yescar.
AddAdditionalRoadTypes(cl, {{std::move(hwtagYesCar), kHighwayBasedSpeeds.Get(HighwayType::HighwayTrack)}});
// Set max possible (reasonable) car speed. See EdgeEstimator::CalcHeuristic.
SpeedKMpH constexpr kMaxCarSpeedKMpH(200.0);
CHECK_LESS(m_maxModelSpeed, kMaxCarSpeedKMpH, ());
m_maxModelSpeed = kMaxCarSpeedKMpH;
}
SpeedKMpH CarModel::GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const
{
return GetTypeSpeedImpl(types, speedParams, true /* isCar */);
}
SpeedKMpH const & CarModel::GetOffroadSpeed() const
{
return car_model::kSpeedOffroadKMpH;
}
// static
CarModel const & CarModel::AllLimitsInstance()
{
static CarModel const instance;
return instance;
}
// static
VehicleModel::LimitsInitList const & CarModel::GetOptions()
{
return car_model::kDefaultOptions;
}
// static
VehicleModel::SurfaceInitList const & CarModel::GetSurfaces()
{
return car_model::kCarSurface;
}
CarModelFactory::CarModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn)
: VehicleModelFactory(countryParentNameGetterFn)
{
using namespace car_model;
using std::make_shared;
// Names must be the same with country names from countries.txt
m_models[""] = make_shared<CarModel>();
m_models["Austria"] = make_shared<CarModel>(NoPassThroughLivingStreet());
m_models["Belarus"] = make_shared<CarModel>(NoPassThroughLivingStreet());
m_models["Brazil"] = make_shared<CarModel>(NoPassThroughService(NoPassThroughTrack()));
m_models["Denmark"] = make_shared<CarModel>(NoTrack());
m_models["Germany"] = make_shared<CarModel>(NoPassThroughTrack());
m_models["Hungary"] = make_shared<CarModel>(NoPassThroughLivingStreet());
m_models["Poland"] = make_shared<CarModel>(NoPassThroughService());
m_models["Romania"] = make_shared<CarModel>(NoPassThroughLivingStreet());
m_models["Russian Federation"] = make_shared<CarModel>(NoPassThroughService(NoPassThroughLivingStreet()));
m_models["Slovakia"] = make_shared<CarModel>(NoPassThroughLivingStreet());
m_models["Ukraine"] = make_shared<CarModel>(NoPassThroughService(NoPassThroughLivingStreet()));
}
} // namespace routing

View file

@ -0,0 +1,28 @@
#pragma once
#include "routing_common/vehicle_model.hpp"
namespace routing
{
class CarModel : public VehicleModel
{
public:
CarModel();
explicit CarModel(LimitsInitList const & roadLimits);
/// VehicleModelInterface overrides:
SpeedKMpH GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const override;
SpeedKMpH const & GetOffroadSpeed() const override;
static CarModel const & AllLimitsInstance();
static LimitsInitList const & GetOptions();
static SurfaceInitList const & GetSurfaces();
};
class CarModelFactory : public VehicleModelFactory
{
public:
CarModelFactory(CountryParentNameGetterFn const & countryParentNameGetterF);
};
} // namespace routing

View file

@ -0,0 +1,82 @@
#pragma once
#include "routing_common/vehicle_model.hpp"
// These are default car model coefficients for open source developers.
namespace routing
{
HighwayBasedFactors const kHighwayBasedFactors = {
// {highway class : InOutCityFactor(in city, out city)}
// Tier 1 (decrease ETA a bit according to the tests):
{HighwayType::HighwayMotorway, InOutCityFactor(SpeedFactor{0.95 /* weight */, 0.93 /* eta */})},
// See XXX_KeepMotorway integration tests.
{HighwayType::HighwayMotorwayLink, InOutCityFactor(0.75)}, // 0.20 less
{HighwayType::HighwayTrunk, InOutCityFactor(0.90)},
{HighwayType::HighwayTrunkLink, InOutCityFactor(0.80)}, // 0.15 less
// Tier 2:
{HighwayType::HighwayPrimary,
InOutCityFactor(SpeedFactor{0.95 /* weight */, 0.90 /* eta */} /* in city */, 0.85 /* out city */)},
{HighwayType::HighwayPrimaryLink, InOutCityFactor(0.70 /* in city */, 0.75 /* out city */)}, // 0.10 less
{HighwayType::HighwaySecondary, InOutCityFactor(0.80 /* in city */, 0.85 /* out city */)},
{HighwayType::HighwaySecondaryLink, InOutCityFactor(0.70 /* in city */, 0.75 /* out city */)}, // 0.10 less
// Tier 3:
{HighwayType::HighwayTertiary,
InOutCityFactor(0.70 /* in city */, 0.75 /* out city */)}, // 0.10 less than Secondary
{HighwayType::HighwayTertiaryLink, InOutCityFactor(0.60 /* in city */, 0.65 /* out city */)}, // 0.10 less
{HighwayType::HighwayUnclassified, InOutCityFactor(0.70 /* in city */, 0.75 /* out city */)},
// Tier 4:
{HighwayType::HighwayResidential, InOutCityFactor(0.70)},
{HighwayType::HighwayLivingStreet, InOutCityFactor(0.70)},
// The rest:
// By VNG: Changed 0.3 -> 0.95 for Road and 0.3 -> 1.0 for Track.
// They are already have very small speeds (10, 5 respectively).
// There are no (99%) traffic lights or pedestrian crossings on this kind of roads.
{HighwayType::HighwayService, InOutCityFactor(0.70)},
{HighwayType::HighwayRoad, InOutCityFactor(0.90)},
{HighwayType::HighwayTrack, InOutCityFactor(1.0)},
{HighwayType::ManMadePier, InOutCityFactor(0.90)},
{HighwayType::RouteFerry, InOutCityFactor(0.90)},
{HighwayType::RouteShuttleTrain, InOutCityFactor(0.90)},
};
HighwayBasedSpeeds const kHighwayBasedSpeeds = {
// {highway class : InOutCitySpeedKMpH(in city, out city)}
// Tier 1:
{HighwayType::HighwayMotorway, InOutCitySpeedKMpH(118.0 /* in city */, 124.0 /* out city */)},
{HighwayType::HighwayMotorwayLink, InOutCitySpeedKMpH(109.00 /* in city */, 115.00 /* out city */)},
{HighwayType::HighwayTrunk, InOutCitySpeedKMpH(90.00 /* in city */, 103.00 /* out city */)},
{HighwayType::HighwayTrunkLink, InOutCitySpeedKMpH(77.00 /* in city */, 91.00 /* out city */)},
// Tier 2:
{HighwayType::HighwayPrimary, InOutCitySpeedKMpH(65.00 /* in city */, 82.00 /* out city */)},
{HighwayType::HighwayPrimaryLink, InOutCitySpeedKMpH(58.00 /* in city */, 72.00 /* out city */)},
{HighwayType::HighwaySecondary, InOutCitySpeedKMpH(60.00 /* in city */, 70.00 /* out city */)},
{HighwayType::HighwaySecondaryLink, InOutCitySpeedKMpH(48.00 /* in city */, 56.00 /* out city */)},
// Tier 3:
{HighwayType::HighwayTertiary, InOutCitySpeedKMpH(50.00 /* in city */, 50.00 /* out city */)},
{HighwayType::HighwayTertiaryLink, InOutCitySpeedKMpH({40.95, 34.97} /* in city */, {45.45, 39.73} /* out city */)},
{HighwayType::HighwayUnclassified, InOutCitySpeedKMpH(30.00 /* in city */, 40.00 /* out city */)},
// Tier 4:
{HighwayType::HighwayResidential, InOutCitySpeedKMpH({20.00, 26.00} /* in city */, {26.00, 26.00} /* out city */)},
{HighwayType::HighwayLivingStreet, InOutCitySpeedKMpH(10.00 /* in city */, 10.00 /* out city */)},
// The rest:
{HighwayType::HighwayService, InOutCitySpeedKMpH(15.00 /* in city */, 15.00 /* out city */)},
{HighwayType::HighwayRoad, InOutCitySpeedKMpH(10.00 /* in city */, 10.00 /* out city */)},
{HighwayType::HighwayTrack, InOutCitySpeedKMpH(5.00 /* in city */, 5.00 /* out city */)},
{HighwayType::ManMadePier, InOutCitySpeedKMpH({17.00, 10.00} /* in city */, {17.00, 10.00} /* out city */)},
{HighwayType::RouteFerry, InOutCitySpeedKMpH(10.00 /* in city */, 10.00 /* out city */)},
{HighwayType::RouteShuttleTrain, InOutCitySpeedKMpH(25.00 /* in city */, 25.00 /* out city */)},
};
} // namespace routing

View file

@ -0,0 +1,373 @@
#include "routing_common/maxspeed_conversion.hpp"
#include "base/assert.hpp"
#include <sstream>
#include <tuple>
#include <utility>
#include <vector>
namespace routing
{
using namespace std;
using namespace measurement_utils;
// SpeedInUnits ------------------------------------------------------------------------------------
bool SpeedInUnits::operator==(SpeedInUnits const & rhs) const
{
return m_speed == rhs.m_speed && m_units == rhs.m_units;
}
bool SpeedInUnits::IsNumeric() const
{
return routing::IsNumeric(m_speed);
}
MaxspeedType SpeedInUnits::GetSpeedKmPH() const
{
return static_cast<MaxspeedType>(ToSpeedKmPH(m_speed, m_units));
}
// Maxspeed ----------------------------------------------------------------------------------------
Maxspeed::Maxspeed(Units units, MaxspeedType forward, MaxspeedType backward)
: m_units(units)
, m_forward(forward)
, m_backward(backward)
{}
bool Maxspeed::operator==(Maxspeed const & rhs) const
{
return m_units == rhs.m_units && m_forward == rhs.m_forward && m_backward == rhs.m_backward;
}
MaxspeedType Maxspeed::GetSpeedInUnits(bool forward) const
{
return (forward || !IsBidirectional()) ? m_forward : m_backward;
}
MaxspeedType Maxspeed::GetSpeedKmPH(bool forward) const
{
auto const speedInUnits = GetSpeedInUnits(forward);
switch (speedInUnits)
{
case kInvalidSpeed:
return kInvalidSpeed; // That means IsValid() returns false.
// A feature is marked as a feature without any speed limits (maxspeed=="none").
// Should be less than CarModel::kMaxCarSpeedKMpH.
case kNoneMaxSpeed: return MaxspeedType{130};
// A feature is marked with the maxspeed=="walk" tag, a speed of a walking person.
case kWalkMaxSpeed: return MaxspeedType{6};
case kCommonMaxSpeedValue: return MaxspeedType{60};
}
return static_cast<MaxspeedType>(ToSpeedKmPH(speedInUnits, m_units));
}
// FeatureMaxspeed ---------------------------------------------------------------------------------
FeatureMaxspeed::FeatureMaxspeed(uint32_t fid, measurement_utils::Units units, MaxspeedType forward,
MaxspeedType backward /* = kInvalidSpeed */) noexcept
: m_featureId(fid)
, m_maxspeed(units, forward, backward)
{}
bool FeatureMaxspeed::operator==(FeatureMaxspeed const & rhs) const
{
return m_featureId == rhs.m_featureId && m_maxspeed == rhs.m_maxspeed;
}
SpeedInUnits FeatureMaxspeed::GetForwardSpeedInUnits() const
{
return SpeedInUnits(m_maxspeed.GetForward(), m_maxspeed.GetUnits());
}
SpeedInUnits FeatureMaxspeed::GetBackwardSpeedInUnits() const
{
return SpeedInUnits(m_maxspeed.GetBackward(), m_maxspeed.GetUnits());
}
// MaxspeedConverter -------------------------------------------------------------------------------
MaxspeedConverter::MaxspeedConverter()
{
vector<tuple<SpeedMacro, MaxspeedType, Units>> const table = {
// Special values.
{SpeedMacro::Undefined, kInvalidSpeed /* speed */, Units::Metric},
{SpeedMacro::None, kNoneMaxSpeed /* speed */, Units::Metric},
{SpeedMacro::Walk, kWalkMaxSpeed /* speed */, Units::Metric},
// Km per hour.
{SpeedMacro::Speed1KmPH, 1 /* speed */, Units::Metric},
{SpeedMacro::Speed2KmPH, 2 /* speed */, Units::Metric},
{SpeedMacro::Speed3KmPH, 3 /* speed */, Units::Metric},
{SpeedMacro::Speed4KmPH, 4 /* speed */, Units::Metric},
{SpeedMacro::Speed5KmPH, 5 /* speed */, Units::Metric},
{SpeedMacro::Speed6KmPH, 6 /* speed */, Units::Metric},
{SpeedMacro::Speed7KmPH, 7 /* speed */, Units::Metric},
{SpeedMacro::Speed8KmPH, 8 /* speed */, Units::Metric},
{SpeedMacro::Speed9KmPH, 9 /* speed */, Units::Metric},
{SpeedMacro::Speed10KmPH, 10 /* speed */, Units::Metric},
{SpeedMacro::Speed11KmPH, 11 /* speed */, Units::Metric},
{SpeedMacro::Speed12KmPH, 12 /* speed */, Units::Metric},
{SpeedMacro::Speed13KmPH, 13 /* speed */, Units::Metric},
{SpeedMacro::Speed14KmPH, 14 /* speed */, Units::Metric},
{SpeedMacro::Speed15KmPH, 15 /* speed */, Units::Metric},
{SpeedMacro::Speed16KmPH, 16 /* speed */, Units::Metric},
{SpeedMacro::Speed18KmPH, 18 /* speed */, Units::Metric},
{SpeedMacro::Speed20KmPH, 20 /* speed */, Units::Metric},
{SpeedMacro::Speed22KmPH, 22 /* speed */, Units::Metric},
{SpeedMacro::Speed25KmPH, 25 /* speed */, Units::Metric},
{SpeedMacro::Speed24KmPH, 24 /* speed */, Units::Metric},
{SpeedMacro::Speed28KmPH, 28 /* speed */, Units::Metric},
{SpeedMacro::Speed30KmPH, 30 /* speed */, Units::Metric},
{SpeedMacro::Speed32KmPH, 32 /* speed */, Units::Metric},
{SpeedMacro::Speed35KmPH, 35 /* speed */, Units::Metric},
{SpeedMacro::Speed36KmPH, 36 /* speed */, Units::Metric},
{SpeedMacro::Speed39KmPH, 39 /* speed */, Units::Metric},
{SpeedMacro::Speed40KmPH, 40 /* speed */, Units::Metric},
{SpeedMacro::Speed45KmPH, 45 /* speed */, Units::Metric},
{SpeedMacro::Speed50KmPH, 50 /* speed */, Units::Metric},
{SpeedMacro::Speed55KmPH, 55 /* speed */, Units::Metric},
{SpeedMacro::Speed56KmPH, 56 /* speed */, Units::Metric},
{SpeedMacro::Speed60KmPH, 60 /* speed */, Units::Metric},
{SpeedMacro::Speed64KmPH, 64 /* speed */, Units::Metric},
{SpeedMacro::Speed65KmPH, 65 /* speed */, Units::Metric},
{SpeedMacro::Speed70KmPH, 70 /* speed */, Units::Metric},
{SpeedMacro::Speed72KmPH, 72 /* speed */, Units::Metric},
{SpeedMacro::Speed75KmPH, 75 /* speed */, Units::Metric},
{SpeedMacro::Speed80KmPH, 80 /* speed */, Units::Metric},
{SpeedMacro::Speed82KmPH, 82 /* speed */, Units::Metric},
{SpeedMacro::Speed85KmPH, 85 /* speed */, Units::Metric},
{SpeedMacro::Speed89KmPH, 89 /* speed */, Units::Metric},
{SpeedMacro::Speed90KmPH, 90 /* speed */, Units::Metric},
{SpeedMacro::Speed93KmPH, 93 /* speed */, Units::Metric},
{SpeedMacro::Speed95KmPH, 95 /* speed */, Units::Metric},
{SpeedMacro::Speed96KmPH, 96 /* speed */, Units::Metric},
{SpeedMacro::Speed100KmPH, 100 /* speed */, Units::Metric},
{SpeedMacro::Speed104KmPH, 104 /* speed */, Units::Metric},
{SpeedMacro::Speed105KmPH, 105 /* speed */, Units::Metric},
{SpeedMacro::Speed106KmPH, 106 /* speed */, Units::Metric},
{SpeedMacro::Speed110KmPH, 110 /* speed */, Units::Metric},
{SpeedMacro::Speed112KmPH, 112 /* speed */, Units::Metric},
{SpeedMacro::Speed115KmPH, 115 /* speed */, Units::Metric},
{SpeedMacro::Speed120KmPH, 120 /* speed */, Units::Metric},
{SpeedMacro::Speed125KmPH, 125 /* speed */, Units::Metric},
{SpeedMacro::Speed127KmPH, 127 /* speed */, Units::Metric},
{SpeedMacro::Speed130KmPH, 130 /* speed */, Units::Metric},
{SpeedMacro::Speed135KmPH, 135 /* speed */, Units::Metric},
{SpeedMacro::Speed140KmPH, 140 /* speed */, Units::Metric},
{SpeedMacro::Speed141KmPH, 141 /* speed */, Units::Metric},
{SpeedMacro::Speed145KmPH, 145 /* speed */, Units::Metric},
{SpeedMacro::Speed150KmPH, 150 /* speed */, Units::Metric},
{SpeedMacro::Speed155KmPH, 155 /* speed */, Units::Metric},
{SpeedMacro::Speed160KmPH, 160 /* speed */, Units::Metric},
{SpeedMacro::Speed165KmPH, 165 /* speed */, Units::Metric},
{SpeedMacro::Speed170KmPH, 170 /* speed */, Units::Metric},
{SpeedMacro::Speed177KmPH, 177 /* speed */, Units::Metric},
{SpeedMacro::Speed180KmPH, 180 /* speed */, Units::Metric},
{SpeedMacro::Speed185KmPH, 185 /* speed */, Units::Metric},
{SpeedMacro::Speed190KmPH, 190 /* speed */, Units::Metric},
{SpeedMacro::Speed193KmPH, 193 /* speed */, Units::Metric},
{SpeedMacro::Speed195KmPH, 195 /* speed */, Units::Metric},
{SpeedMacro::Speed200KmPH, 200 /* speed */, Units::Metric},
{SpeedMacro::Speed201KmPH, 201 /* speed */, Units::Metric},
{SpeedMacro::Speed210KmPH, 210 /* speed */, Units::Metric},
{SpeedMacro::Speed217KmPH, 217 /* speed */, Units::Metric},
{SpeedMacro::Speed220KmPH, 220 /* speed */, Units::Metric},
{SpeedMacro::Speed230KmPH, 230 /* speed */, Units::Metric},
{SpeedMacro::Speed240KmPH, 240 /* speed */, Units::Metric},
{SpeedMacro::Speed250KmPH, 250 /* speed */, Units::Metric},
{SpeedMacro::Speed260KmPH, 260 /* speed */, Units::Metric},
{SpeedMacro::Speed270KmPH, 270 /* speed */, Units::Metric},
{SpeedMacro::Speed275KmPH, 275 /* speed */, Units::Metric},
{SpeedMacro::Speed280KmPH, 280 /* speed */, Units::Metric},
{SpeedMacro::Speed285KmPH, 285 /* speed */, Units::Metric},
{SpeedMacro::Speed300KmPH, 300 /* speed */, Units::Metric},
{SpeedMacro::Speed305KmPH, 305 /* speed */, Units::Metric},
{SpeedMacro::Speed310KmPH, 310 /* speed */, Units::Metric},
{SpeedMacro::Speed320KmPH, 320 /* speed */, Units::Metric},
{SpeedMacro::Speed350KmPH, 350 /* speed */, Units::Metric},
{SpeedMacro::Speed380KmPH, 380 /* speed */, Units::Metric},
// Miles per hour.
{SpeedMacro::Speed3MPH, 3 /* speed */, Units::Imperial},
{SpeedMacro::Speed4MPH, 4 /* speed */, Units::Imperial},
{SpeedMacro::Speed5MPH, 5 /* speed */, Units::Imperial},
{SpeedMacro::Speed6MPH, 6 /* speed */, Units::Imperial},
{SpeedMacro::Speed7MPH, 7 /* speed */, Units::Imperial},
{SpeedMacro::Speed8MPH, 8 /* speed */, Units::Imperial},
{SpeedMacro::Speed9MPH, 9 /* speed */, Units::Imperial},
{SpeedMacro::Speed10MPH, 10 /* speed */, Units::Imperial},
{SpeedMacro::Speed12MPH, 12 /* speed */, Units::Imperial},
{SpeedMacro::Speed13MPH, 13 /* speed */, Units::Imperial},
{SpeedMacro::Speed14MPH, 14 /* speed */, Units::Imperial},
{SpeedMacro::Speed15MPH, 15 /* speed */, Units::Imperial},
{SpeedMacro::Speed17MPH, 17 /* speed */, Units::Imperial},
{SpeedMacro::Speed18MPH, 18 /* speed */, Units::Imperial},
{SpeedMacro::Speed19MPH, 19 /* speed */, Units::Imperial},
{SpeedMacro::Speed20MPH, 20 /* speed */, Units::Imperial},
{SpeedMacro::Speed24MPH, 24 /* speed */, Units::Imperial},
{SpeedMacro::Speed25MPH, 25 /* speed */, Units::Imperial},
{SpeedMacro::Speed30MPH, 30 /* speed */, Units::Imperial},
{SpeedMacro::Speed35MPH, 35 /* speed */, Units::Imperial},
{SpeedMacro::Speed40MPH, 40 /* speed */, Units::Imperial},
{SpeedMacro::Speed45MPH, 45 /* speed */, Units::Imperial},
{SpeedMacro::Speed50MPH, 50 /* speed */, Units::Imperial},
{SpeedMacro::Speed55MPH, 55 /* speed */, Units::Imperial},
{SpeedMacro::Speed59MPH, 59 /* speed */, Units::Imperial},
{SpeedMacro::Speed60MPH, 60 /* speed */, Units::Imperial},
{SpeedMacro::Speed65MPH, 65 /* speed */, Units::Imperial},
{SpeedMacro::Speed70MPH, 70 /* speed */, Units::Imperial},
{SpeedMacro::Speed75MPH, 75 /* speed */, Units::Imperial},
{SpeedMacro::Speed79MPH, 79 /* speed */, Units::Imperial},
{SpeedMacro::Speed80MPH, 80 /* speed */, Units::Imperial},
{SpeedMacro::Speed85MPH, 85 /* speed */, Units::Imperial},
{SpeedMacro::Speed90MPH, 90 /* speed */, Units::Imperial},
{SpeedMacro::Speed95MPH, 95 /* speed */, Units::Imperial},
{SpeedMacro::Speed100MPH, 100 /* speed */, Units::Imperial},
{SpeedMacro::Speed105MPH, 105 /* speed */, Units::Imperial},
{SpeedMacro::Speed110MPH, 110 /* speed */, Units::Imperial},
{SpeedMacro::Speed115MPH, 115 /* speed */, Units::Imperial},
{SpeedMacro::Speed120MPH, 120 /* speed */, Units::Imperial},
{SpeedMacro::Speed125MPH, 125 /* speed */, Units::Imperial},
};
for (auto const & e : table)
m_macroToSpeed[static_cast<uint8_t>(get<0>(e))] = SpeedInUnits(get<1>(e), get<2>(e));
ASSERT_EQUAL(static_cast<uint8_t>(SpeedMacro::Undefined), 0, ());
m_speedToMacro.insert(make_pair(SpeedInUnits(kInvalidSpeed, Units::Metric), SpeedMacro::Undefined));
for (size_t i = 1; i < numeric_limits<uint8_t>::max(); ++i)
{
auto const & speed = m_macroToSpeed[i];
if (speed.IsValid())
m_speedToMacro.insert(make_pair(speed, static_cast<SpeedMacro>(i)));
}
}
SpeedInUnits MaxspeedConverter::MacroToSpeed(SpeedMacro macro) const
{
uint8_t const m = static_cast<uint8_t>(macro);
ASSERT_LESS(m, m_macroToSpeed.size(), ());
return m_macroToSpeed[m];
}
SpeedMacro MaxspeedConverter::SpeedToMacro(SpeedInUnits const & speed) const
{
auto const it = m_speedToMacro.find(speed);
if (it == m_speedToMacro.cend())
{
switch (speed.GetSpeed())
{
case kNoneMaxSpeed: return SpeedMacro::None;
case kWalkMaxSpeed: return SpeedMacro::Walk;
case kCommonMaxSpeedValue: return SpeedMacro::Speed60KmPH;
}
return SpeedMacro::Undefined;
}
return it->second;
}
SpeedInUnits MaxspeedConverter::ClosestValidMacro(SpeedInUnits const & speed) const
{
auto it = m_speedToMacro.lower_bound(speed);
if (it == m_speedToMacro.end())
{
--it;
}
else if (speed == it->first)
{
return speed;
}
else if (it != m_speedToMacro.begin())
{
auto it2 = it;
--it2;
ASSERT(speed.GetUnits() == it->first.GetUnits() || speed.GetUnits() == it2->first.GetUnits(), ());
auto const Diff = [&speed](SpeedInUnits const & rhs)
{
if (speed.GetUnits() != rhs.GetUnits())
return std::numeric_limits<int>::max();
return abs(int(rhs.GetSpeed()) - int(speed.GetSpeed()));
};
if (Diff(it2->first) < Diff(it->first))
it = it2;
}
return it->first;
}
// static
MaxspeedConverter const & MaxspeedConverter::Instance()
{
static MaxspeedConverter const inst;
return inst;
}
MaxspeedConverter const & GetMaxspeedConverter()
{
return MaxspeedConverter::Instance();
}
bool HaveSameUnits(SpeedInUnits const & lhs, SpeedInUnits const & rhs)
{
return lhs.GetUnits() == rhs.GetUnits() || !lhs.IsNumeric() || !rhs.IsNumeric();
}
bool IsNumeric(MaxspeedType speed)
{
return (speed != kInvalidSpeed && speed != kNoneMaxSpeed && speed != kWalkMaxSpeed && speed != kCommonMaxSpeedValue);
}
namespace
{
std::string PrintMaxspeedType(MaxspeedType s)
{
switch (s)
{
case kInvalidSpeed: return "Invalid";
case kNoneMaxSpeed: return "None";
case kWalkMaxSpeed: return "Walk";
case kCommonMaxSpeedValue: return "Common";
}
return std::to_string(int(s));
}
} // namespace
string DebugPrint(Maxspeed maxspeed)
{
ostringstream oss;
oss << "Maxspeed { m_units: " << DebugPrint(maxspeed.GetUnits())
<< ", m_forward: " << PrintMaxspeedType(maxspeed.GetForward())
<< ", m_backward: " << PrintMaxspeedType(maxspeed.GetBackward()) << " }";
return oss.str();
}
string DebugPrint(SpeedMacro maxspeed)
{
ostringstream oss;
oss << "SpeedMacro { " << static_cast<int>(maxspeed)
<< ", decoded: " << DebugPrint(GetMaxspeedConverter().MacroToSpeed(maxspeed)) << " }";
return oss.str();
}
string DebugPrint(SpeedInUnits const & speed)
{
ostringstream oss;
oss << "SpeedInUnits { m_speed: " << PrintMaxspeedType(speed.GetSpeed())
<< ", m_units: " << DebugPrint(speed.GetUnits()) << " }";
return oss.str();
}
string DebugPrint(FeatureMaxspeed const & featureMaxspeed)
{
ostringstream oss;
oss << "FeatureMaxspeed { m_featureId: " << featureMaxspeed.GetFeatureId()
<< ", m_maxspeed: " << DebugPrint(featureMaxspeed.GetMaxspeed()) << " }";
return oss.str();
}
} // namespace routing

View file

@ -0,0 +1,315 @@
#pragma once
#include "platform/measurement_utils.hpp"
#include <array>
#include <cstdint>
#include <limits>
#include <map>
#include <string>
namespace routing
{
/// \brief This enum class contains the most popular maxspeed values according to
/// https://taginfo.openstreetmap.org/keys/maxspeed#values.
/// \note Value of this enum is saved to mwm. So they should not be changed because of backward
/// compatibility. But it's possible to add some new values to this enum.
enum class SpeedMacro : uint8_t
{
// Special values.
Undefined,
None, // No maxspeed restriction (E.g. a motorway in Germany).
Walk, // Driver should move as a walking person.
// Km per hour.
Speed1KmPH = 10,
Speed2KmPH,
Speed3KmPH,
Speed4KmPH,
Speed5KmPH,
Speed6KmPH,
Speed7KmPH,
Speed8KmPH,
Speed9KmPH,
Speed10KmPH,
Speed11KmPH,
Speed12KmPH,
Speed13KmPH,
Speed14KmPH,
Speed15KmPH,
Speed16KmPH,
Speed18KmPH,
Speed20KmPH,
Speed22KmPH,
Speed25KmPH,
Speed24KmPH,
Speed28KmPH,
Speed30KmPH,
Speed32KmPH,
Speed35KmPH,
Speed36KmPH,
Speed39KmPH,
Speed40KmPH,
Speed45KmPH,
Speed50KmPH,
Speed55KmPH,
Speed56KmPH,
Speed60KmPH,
Speed64KmPH,
Speed65KmPH,
Speed70KmPH,
Speed72KmPH,
Speed75KmPH,
Speed80KmPH,
Speed82KmPH,
Speed85KmPH,
Speed89KmPH,
Speed90KmPH,
Speed93KmPH,
Speed95KmPH,
Speed96KmPH,
Speed100KmPH,
Speed104KmPH,
Speed105KmPH,
Speed106KmPH,
Speed110KmPH,
Speed112KmPH,
Speed115KmPH,
Speed120KmPH,
Speed125KmPH,
Speed127KmPH,
Speed130KmPH,
Speed135KmPH,
Speed140KmPH,
Speed141KmPH,
Speed145KmPH,
Speed150KmPH,
Speed155KmPH,
Speed160KmPH,
Speed165KmPH,
Speed170KmPH,
Speed177KmPH,
Speed180KmPH,
Speed185KmPH,
Speed190KmPH,
Speed193KmPH,
Speed195KmPH,
Speed200KmPH,
Speed201KmPH,
Speed210KmPH,
Speed217KmPH,
Speed220KmPH,
Speed230KmPH,
Speed240KmPH,
Speed250KmPH,
Speed260KmPH,
Speed270KmPH,
Speed275KmPH,
Speed280KmPH,
Speed285KmPH,
Speed300KmPH,
Speed305KmPH,
Speed310KmPH,
Speed320KmPH,
Speed350KmPH,
Speed380KmPH,
// Miles per hour.
Speed3MPH = 110,
Speed4MPH,
Speed5MPH,
Speed6MPH,
Speed7MPH,
Speed8MPH,
Speed9MPH,
Speed10MPH,
Speed12MPH,
Speed13MPH,
Speed14MPH,
Speed15MPH,
Speed17MPH,
Speed18MPH,
Speed19MPH,
Speed20MPH,
Speed24MPH,
Speed25MPH,
Speed30MPH,
Speed35MPH,
Speed40MPH,
Speed45MPH,
Speed50MPH,
Speed55MPH,
Speed59MPH,
Speed60MPH,
Speed65MPH,
Speed70MPH,
Speed75MPH,
Speed79MPH,
Speed80MPH,
Speed85MPH,
Speed90MPH,
Speed95MPH,
Speed100MPH,
Speed105MPH,
Speed110MPH,
Speed115MPH,
Speed120MPH,
Speed125MPH,
};
using MaxspeedType = uint16_t;
MaxspeedType constexpr kInvalidSpeed = std::numeric_limits<MaxspeedType>::max();
MaxspeedType constexpr kNoneMaxSpeed = std::numeric_limits<MaxspeedType>::max() - 1;
MaxspeedType constexpr kWalkMaxSpeed = std::numeric_limits<MaxspeedType>::max() - 2;
MaxspeedType constexpr kCommonMaxSpeedValue = std::numeric_limits<MaxspeedType>::max() - 3;
class SpeedInUnits
{
public:
SpeedInUnits() = default;
SpeedInUnits(MaxspeedType speed, measurement_utils::Units units) noexcept : m_speed(speed), m_units(units) {}
void SetSpeed(MaxspeedType speed) { m_speed = speed; }
void SetUnits(measurement_utils::Units units) { m_units = units; }
MaxspeedType GetSpeed() const { return m_speed; }
measurement_utils::Units GetUnits() const { return m_units; }
bool operator==(SpeedInUnits const & rhs) const;
/// @note Used as map keys compare. Doesn't make real speed comparison.
struct Less
{
bool operator()(SpeedInUnits const & l, SpeedInUnits const & r) const
{
if (l.m_units == r.m_units)
return l.m_speed < r.m_speed;
return l.m_units < r.m_units;
}
};
bool IsNumeric() const;
bool IsValid() const { return m_speed != kInvalidSpeed; }
/// @pre IsNumeric() == true.
MaxspeedType GetSpeedKmPH() const;
private:
// Speed in km per hour or mile per hour depends on m_units value.
MaxspeedType m_speed = kInvalidSpeed;
// |m_units| is undefined in case of SpeedMacro::None and SpeedMacro::Walk.
measurement_utils::Units m_units = measurement_utils::Units::Metric;
};
class Maxspeed
{
public:
Maxspeed() = default;
Maxspeed(measurement_utils::Units units, MaxspeedType forward, MaxspeedType backward);
bool operator==(Maxspeed const & rhs) const;
void SetUnits(measurement_utils::Units units) { m_units = units; }
void SetForward(MaxspeedType forward) { m_forward = forward; }
void SetBackward(MaxspeedType backward) { m_backward = backward; }
measurement_utils::Units GetUnits() const { return m_units; }
MaxspeedType GetForward() const { return m_forward; }
MaxspeedType GetBackward() const { return m_backward; }
bool IsValid() const { return m_forward != kInvalidSpeed; }
/// \returns true if Maxspeed is considered as Bidirectional(). It means different
/// speed is set for forward and backward direction. Otherwise returns false. It means
/// |m_forward| speed should be used for the both directions.
bool IsBidirectional() const { return IsValid() && m_backward != kInvalidSpeed; }
/// \brief returns speed according to |m_units|. |kInvalidSpeed|, |kNoneMaxSpeed| or
/// |kWalkMaxSpeed| may be returned.
MaxspeedType GetSpeedInUnits(bool forward) const;
/// \brief returns speed in km per hour. If it's not valid |kInvalidSpeed| is
/// returned. Otherwise forward or backward speed in km per hour is returned. |kNoneMaxSpeed| and
/// |kWalkMaxSpeed| are converted to some numbers.
MaxspeedType GetSpeedKmPH(bool forward) const;
private:
measurement_utils::Units m_units = measurement_utils::Units::Metric;
// Speed in km per hour or mile per hour depends on |m_units|.
MaxspeedType m_forward = kInvalidSpeed;
// Speed in km per hour or mile per hour depends on |m_units|. If |m_backward| == kInvalidSpeed
// |m_forward| speed should be used for the both directions.
MaxspeedType m_backward = kInvalidSpeed;
};
/// \brief Feature id and corresponding maxspeed tag value. |m_forward| and |m_backward| fields
/// reflect the fact that a feature may have different maxspeed tag value for different directions.
/// If |m_backward| is invalid it means that |m_forward| tag contains maxspeed for
/// the both directions. If a feature has maxspeed forward and maxspeed backward in different units
/// it's considered as an invalid one and it's not saved into mwm.
class FeatureMaxspeed
{
public:
FeatureMaxspeed(uint32_t fid, measurement_utils::Units units, MaxspeedType forward,
MaxspeedType backward = kInvalidSpeed) noexcept;
bool operator==(FeatureMaxspeed const & rhs) const;
struct Less
{
bool operator()(FeatureMaxspeed const & l, FeatureMaxspeed const & r) const
{
return l.m_featureId < r.m_featureId;
}
bool operator()(uint32_t l, FeatureMaxspeed const & r) const { return l < r.m_featureId; }
bool operator()(FeatureMaxspeed const & l, uint32_t r) const { return l.m_featureId < r; }
};
bool IsValid() const { return m_maxspeed.IsValid(); }
bool IsBidirectional() const { return m_maxspeed.IsBidirectional(); }
uint32_t GetFeatureId() const { return m_featureId; }
Maxspeed const & GetMaxspeed() const { return m_maxspeed; }
SpeedInUnits GetForwardSpeedInUnits() const;
SpeedInUnits GetBackwardSpeedInUnits() const;
private:
uint32_t m_featureId = 0;
Maxspeed m_maxspeed;
};
/// \brief Generator converts real speed (SpeedInUnits) into 1-byte values for serialization (SpeedMacro),
/// based on most used speeds (see MaxspeedConverter ctor).
/// If you make any manipulation with speeds and want to save it, consider using ClosestValidMacro.
class MaxspeedConverter
{
public:
SpeedInUnits MacroToSpeed(SpeedMacro macro) const;
SpeedMacro SpeedToMacro(SpeedInUnits const & speed) const;
SpeedInUnits ClosestValidMacro(SpeedInUnits const & speed) const;
static MaxspeedConverter const & Instance();
private:
MaxspeedConverter();
std::array<SpeedInUnits, std::numeric_limits<uint8_t>::max()> m_macroToSpeed;
std::map<SpeedInUnits, SpeedMacro, SpeedInUnits::Less> m_speedToMacro;
};
MaxspeedConverter const & GetMaxspeedConverter();
bool HaveSameUnits(SpeedInUnits const & lhs, SpeedInUnits const & rhs);
bool IsFeatureIdLess(FeatureMaxspeed const & lhs, FeatureMaxspeed const & rhs);
/// \returns false if \a speed is equal to predefined values
/// {kInvalidSpeed, kNoneMaxSpeed, kWalkMaxSpeed, kCommonMaxSpeedValue}
/// \param speed in km per hour or mile per hour.
bool IsNumeric(MaxspeedType speed);
std::string DebugPrint(Maxspeed maxspeed);
std::string DebugPrint(SpeedMacro maxspeed);
std::string DebugPrint(SpeedInUnits const & speed);
std::string DebugPrint(FeatureMaxspeed const & featureMaxspeed);
} // namespace routing

View file

@ -0,0 +1,65 @@
#pragma once
#include "platform/country_file.hpp"
#include "base/assert.hpp"
#include "base/checked_cast.hpp"
#include "base/logging.hpp"
#include <cstdint>
#include <limits>
#include <map>
#include <vector>
namespace routing
{
using NumMwmId = std::uint16_t;
NumMwmId constexpr kFakeNumMwmId = std::numeric_limits<NumMwmId>::max();
NumMwmId constexpr kGeneratorMwmId = 0;
class NumMwmIds final
{
public:
bool IsEmpty() const { return m_idToFile.empty(); }
void RegisterFile(platform::CountryFile const & file)
{
if (ContainsFile(file))
return;
NumMwmId const id = base::asserted_cast<NumMwmId>(m_idToFile.size());
m_idToFile.push_back(file);
m_fileToId[file] = id;
// LOG(LDEBUG, ("MWM:", file.GetName(), "=", id));
}
bool ContainsFile(platform::CountryFile const & file) const { return m_fileToId.find(file) != m_fileToId.cend(); }
bool ContainsFileForMwm(NumMwmId mwmId) const { return mwmId < m_idToFile.size(); }
platform::CountryFile const & GetFile(NumMwmId mwmId) const
{
ASSERT_LESS(mwmId, m_idToFile.size(), ());
return m_idToFile[mwmId];
}
NumMwmId GetId(platform::CountryFile const & file) const
{
auto const it = m_fileToId.find(file);
ASSERT(it != m_fileToId.cend(), ("Can't find mwm id for", file));
return it->second;
}
template <typename F>
void ForEachId(F && f) const
{
for (NumMwmId id = 0; id < base::asserted_cast<NumMwmId>(m_idToFile.size()); ++id)
f(id);
}
private:
std::vector<platform::CountryFile> m_idToFile;
std::map<platform::CountryFile, NumMwmId> m_fileToId;
};
} // namespace routing

View file

@ -0,0 +1,231 @@
#include "routing_common/pedestrian_model.hpp"
#include "indexer/classificator.hpp"
namespace pedestrian_model
{
using namespace routing;
// See model specifics in different countries here:
// https://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Access-Restrictions
// Document contains proposals for some countries, but we assume that some kinds of roads are ready for pedestrian
// routing, but not listed in tables in the document. For example, steps are not listed, paths, roads and services
// features also can be treated as ready for pedestrian routing. These road types were added to lists below.
// See road types here:
// https://wiki.openstreetmap.org/wiki/Key:highway
// Heuristics:
// For less pedestrian roads we add fine by setting smaller value of weight speed, and for more pedestrian roads we
// set greater values of weight speed. Algorithm picks roads with greater speed first,
// preferencing a more pedestrian roads over less pedestrian.
// As result of such heuristic road is not totally the shortest, but it avoids non pedestrian roads, which were
// not marked as "foot=no" in OSM.
HighwayBasedFactors const kDefaultFactors = GetOneFactorsForBicycleAndPedestrianModel();
HighwayBasedSpeeds const kDefaultSpeeds = {
// {highway class : InOutCitySpeedKMpH(in city(weight, eta), out city(weight eta))}
{HighwayType::HighwayTrunk, InOutCitySpeedKMpH(SpeedKMpH(1.0, 5.0))},
{HighwayType::HighwayTrunkLink, InOutCitySpeedKMpH(SpeedKMpH(1.0, 5.0))},
{HighwayType::HighwayPrimary, InOutCitySpeedKMpH(SpeedKMpH(2.0, 5.0))},
{HighwayType::HighwayPrimaryLink, InOutCitySpeedKMpH(SpeedKMpH(2.0, 5.0))},
{HighwayType::HighwaySecondary, InOutCitySpeedKMpH(SpeedKMpH(3.0, 5.0))},
{HighwayType::HighwaySecondaryLink, InOutCitySpeedKMpH(SpeedKMpH(3.0, 5.0))},
{HighwayType::HighwayTertiary, InOutCitySpeedKMpH(SpeedKMpH(4.0, 5.0))},
{HighwayType::HighwayTertiaryLink, InOutCitySpeedKMpH(SpeedKMpH(4.0, 5.0))},
{HighwayType::HighwayRoad, InOutCitySpeedKMpH(SpeedKMpH(4.0, 5.0))},
{HighwayType::HighwayService, InOutCitySpeedKMpH(SpeedKMpH(4.5, 5.0))},
{HighwayType::HighwayUnclassified, InOutCitySpeedKMpH(SpeedKMpH(4.5, 5.0))},
{HighwayType::HighwayResidential, InOutCitySpeedKMpH(SpeedKMpH(4.5, 5.0))},
{HighwayType::HighwayBridleway, InOutCitySpeedKMpH(SpeedKMpH(1.0, 5.0))},
{HighwayType::HighwayLadder, InOutCitySpeedKMpH(SpeedKMpH(0.5))},
{HighwayType::HighwaySteps, InOutCitySpeedKMpH(SpeedKMpH(2.5))},
{HighwayType::HighwayCycleway, InOutCitySpeedKMpH(SpeedKMpH(4.0, 5.0))},
{HighwayType::HighwayTrack, InOutCitySpeedKMpH(SpeedKMpH(5.0))},
{HighwayType::HighwayPath, InOutCitySpeedKMpH(SpeedKMpH(5.0))},
{HighwayType::HighwayLivingStreet, InOutCitySpeedKMpH(SpeedKMpH(5.0))},
{HighwayType::ManMadePier, InOutCitySpeedKMpH(SpeedKMpH(5.0))},
// Set 10% higher weight (than default 5) for foot designated ways.
{HighwayType::HighwayPedestrian, InOutCitySpeedKMpH(SpeedKMpH(5.5, 5.0))},
{HighwayType::HighwayFootway, InOutCitySpeedKMpH(SpeedKMpH(5.5, 5.0))},
/// @todo A car ferry has {10, 10}. Weight = 3 is 60% from reasonable 5 max speed.
{HighwayType::RouteFerry, InOutCitySpeedKMpH(SpeedKMpH(3.0, 20.0))},
};
// https://github.com/organicmaps/organicmaps/issues/2492
// 3 kmph (was before) is a big default offroad speed, almost as normal walking speed.
SpeedKMpH constexpr kSpeedOffroadKMpH = {0.5 /* weight */, 3.0 /* eta */};
// Default, no bridleway and cycleway
VehicleModel::LimitsInitList const kDefaultOptions = {
// {HighwayType, passThroughAllowed}
{HighwayType::HighwayTrunk, true},
{HighwayType::HighwayTrunkLink, true},
{HighwayType::HighwayPrimary, true},
{HighwayType::HighwayPrimaryLink, true},
{HighwayType::HighwaySecondary, true},
{HighwayType::HighwaySecondaryLink, true},
{HighwayType::HighwayTertiary, true},
{HighwayType::HighwayTertiaryLink, true},
{HighwayType::HighwayService, true},
{HighwayType::HighwayUnclassified, true},
{HighwayType::HighwayRoad, true},
{HighwayType::HighwayTrack, true},
{HighwayType::HighwayPath, true},
// HighwayBridleway, HighwayCycleway are missing
{HighwayType::HighwayResidential, true},
{HighwayType::HighwayLivingStreet, true},
{HighwayType::HighwayLadder, true},
{HighwayType::HighwaySteps, true},
{HighwayType::HighwayPedestrian, true},
{HighwayType::HighwayFootway, true},
{HighwayType::ManMadePier, true},
{HighwayType::RouteFerry, true}};
// Same as defaults except bridleway and cycleway are allowed.
VehicleModel::LimitsInitList AllAllowed()
{
auto res = kDefaultOptions;
res.push_back({HighwayType::HighwayBridleway, true});
res.push_back({HighwayType::HighwayCycleway, true});
return res;
}
// Same as defaults except trunk and trunk link are not allowed.
VehicleModel::LimitsInitList NoTrunk()
{
VehicleModel::LimitsInitList res;
res.reserve(kDefaultOptions.size() - 2);
for (auto const & e : kDefaultOptions)
if (e.m_type != HighwayType::HighwayTrunk && e.m_type != HighwayType::HighwayTrunkLink)
res.push_back(e);
return res;
}
VehicleModel::LimitsInitList YesCycleway(VehicleModel::LimitsInitList res = kDefaultOptions)
{
res.push_back({HighwayType::HighwayCycleway, true});
return res;
}
VehicleModel::LimitsInitList YesBridleway(VehicleModel::LimitsInitList res = kDefaultOptions)
{
res.push_back({HighwayType::HighwayBridleway, true});
return res;
}
/// @see Turkey_UsePrimary, Georgia_UsePrimary test.
HighwayBasedSpeeds IncreasePrimary()
{
/// @todo Probably, should make Primary = Secondary = 4.
HighwayBasedSpeeds res = pedestrian_model::kDefaultSpeeds;
res.Replace(HighwayType::HighwayPrimary, InOutCitySpeedKMpH(SpeedKMpH(3.0, 5.0)));
res.Replace(HighwayType::HighwayPrimaryLink, InOutCitySpeedKMpH(SpeedKMpH(3.0, 5.0)));
return res;
}
VehicleModel::SurfaceInitList const kPedestrianSurface = {
// {{surfaceType}, {weightFactor, etaFactor}}
{{"psurface", "paved_good"}, {1.0, 1.0}},
{{"psurface", "paved_bad"}, {1.0, 1.0}},
{{"psurface", "unpaved_good"}, {1.0, 1.0}},
{{"psurface", "unpaved_bad"}, {0.8, 0.8}},
// no dedicated sidewalk, doesn't mean that foot is not allowed, just lower weight
{{"hwtag", "nosidewalk"}, {0.8, 0.8}},
};
} // namespace pedestrian_model
namespace routing
{
PedestrianModel::PedestrianModel() : PedestrianModel(pedestrian_model::kDefaultOptions) {}
PedestrianModel::PedestrianModel(VehicleModel::LimitsInitList const & limits)
: PedestrianModel(limits, pedestrian_model::kDefaultSpeeds)
{}
PedestrianModel::PedestrianModel(VehicleModel::LimitsInitList const & limits, HighwayBasedSpeeds const & speeds)
: VehicleModel(classif(), limits, pedestrian_model::kPedestrianSurface, {speeds, pedestrian_model::kDefaultFactors})
{
using namespace pedestrian_model;
// No bridleway and cycleway in default.
ASSERT_EQUAL(kDefaultOptions.size(), kDefaultSpeeds.size() - 2, ());
std::vector<std::string> hwtagYesFoot = {"hwtag", "yesfoot"};
auto const & cl = classif();
m_noType = cl.GetTypeByPath({"hwtag", "nofoot"});
m_yesType = cl.GetTypeByPath(hwtagYesFoot);
AddAdditionalRoadTypes(cl, {{std::move(hwtagYesFoot), kDefaultSpeeds.Get(HighwayType::HighwayLivingStreet)}});
// Update max pedestrian speed with possible ferry transfer. See EdgeEstimator::CalcHeuristic.
SpeedKMpH constexpr kMaxPedestrianSpeedKMpH(60.0);
CHECK_LESS(m_maxModelSpeed, kMaxPedestrianSpeedKMpH, ());
m_maxModelSpeed = kMaxPedestrianSpeedKMpH;
}
SpeedKMpH PedestrianModel::GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const
{
return GetTypeSpeedImpl(types, speedParams, false /* isCar */);
}
SpeedKMpH const & PedestrianModel::GetOffroadSpeed() const
{
return pedestrian_model::kSpeedOffroadKMpH;
}
// If one of feature types will be disabled for pedestrian, features of this type will be simplyfied
// in generator. Look FeatureBuilder1::IsRoad() for more details.
// static
PedestrianModel const & PedestrianModel::AllLimitsInstance()
{
static PedestrianModel const instance(pedestrian_model::AllAllowed());
return instance;
}
PedestrianModelFactory::PedestrianModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn)
: VehicleModelFactory(countryParentNameGetterFn)
{
using namespace pedestrian_model;
using std::make_shared;
// Names must be the same with country names from countries.txt
m_models[""] = make_shared<PedestrianModel>(kDefaultOptions);
m_models["Australia"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Austria"] = make_shared<PedestrianModel>(NoTrunk());
m_models["Belarus"] = make_shared<PedestrianModel>(YesCycleway());
m_models["Belgium"] = make_shared<PedestrianModel>(YesCycleway(YesBridleway(NoTrunk())));
m_models["Brazil"] = make_shared<PedestrianModel>(YesBridleway());
m_models["Denmark"] = make_shared<PedestrianModel>(YesCycleway(NoTrunk()));
m_models["France"] = make_shared<PedestrianModel>(NoTrunk());
m_models["Finland"] = make_shared<PedestrianModel>(YesCycleway());
m_models["Georgia"] = make_shared<PedestrianModel>(AllAllowed(), IncreasePrimary());
m_models["Greece"] = make_shared<PedestrianModel>(YesCycleway(YesBridleway(NoTrunk())));
m_models["Hungary"] = make_shared<PedestrianModel>(NoTrunk());
m_models["Iceland"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Ireland"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Netherlands"] = make_shared<PedestrianModel>(YesCycleway(NoTrunk()));
m_models["Norway"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Oman"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Philippines"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Poland"] = make_shared<PedestrianModel>(YesBridleway(NoTrunk()));
m_models["Romania"] = make_shared<PedestrianModel>(YesBridleway());
m_models["Russian Federation"] = make_shared<PedestrianModel>(YesCycleway());
m_models["Slovakia"] = make_shared<PedestrianModel>(NoTrunk());
m_models["Spain"] = make_shared<PedestrianModel>(NoTrunk());
m_models["Sweden"] = make_shared<PedestrianModel>(AllAllowed());
m_models["Switzerland"] = make_shared<PedestrianModel>(NoTrunk());
m_models["Turkey"] = make_shared<PedestrianModel>(AllAllowed(), IncreasePrimary());
m_models["Ukraine"] = make_shared<PedestrianModel>(NoTrunk());
m_models["United Kingdom"] = make_shared<PedestrianModel>(AllAllowed());
m_models["United States of America"] = make_shared<PedestrianModel>(AllAllowed());
}
} // namespace routing

View file

@ -0,0 +1,30 @@
#pragma once
#include "routing_common/vehicle_model.hpp"
namespace routing
{
class PedestrianModel : public VehicleModel
{
public:
PedestrianModel();
explicit PedestrianModel(VehicleModel::LimitsInitList const & speedLimits);
PedestrianModel(VehicleModel::LimitsInitList const & limits, HighwayBasedSpeeds const & speeds);
/// VehicleModelInterface overrides:
SpeedKMpH GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const override;
bool IsOneWay(FeatureTypes const &) const override { return false; }
SpeedKMpH const & GetOffroadSpeed() const override;
static PedestrianModel const & AllLimitsInstance();
};
class PedestrianModelFactory : public VehicleModelFactory
{
public:
// TODO: remove countryParentNameGetterFn default value after removing unused pedestrian routing
// from road_graph_router
PedestrianModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn = {});
};
} // namespace routing

View file

@ -0,0 +1,14 @@
project(routing_common_tests)
set(SRC
bicycle_model_test.cpp
vehicle_model_for_country_test.cpp
vehicle_model_test.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
indexer
routing_common
)

View file

@ -0,0 +1,39 @@
#include "testing/testing.hpp"
#include "routing_common/bicycle_model.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/feature_data.hpp"
namespace bicycle_model_test
{
using namespace routing;
class BicycleModelTest
{
public:
BicycleModelTest() { classificator::Load(); }
std::shared_ptr<VehicleModel> GetModel(std::string const & country)
{
return std::dynamic_pointer_cast<VehicleModel>(BicycleModelFactory().GetVehicleModelForCountry(country));
}
SpeedParams DefaultSpeedParams() { return {true /* forward */, true /* isCity */, Maxspeed()}; }
};
UNIT_CLASS_TEST(BicycleModelTest, Turkey)
{
auto const model = GetModel("Turkey");
TEST(model, ());
auto const & cl = classif();
feature::TypesHolder holder;
holder.Add(cl.GetTypeByPath({"highway", "footway", "tunnel"}));
TEST(model->HasRoadType(holder), ());
TEST_EQUAL(model->GetSpeed(holder, DefaultSpeedParams()), BicycleModel::DismountSpeed(), ());
}
} // namespace bicycle_model_test

View file

@ -0,0 +1,150 @@
#include "testing/testing.hpp"
#include "routing_common/bicycle_model.hpp"
#include "routing_common/car_model.hpp"
#include "routing_common/pedestrian_model.hpp"
#include "indexer/classificator_loader.hpp"
#include <memory>
#include <string>
namespace vehicle_model_for_country_test
{
using namespace routing;
using namespace std;
class VehicleModelForCountryTest
{
public:
VehicleModelForCountryTest() { classificator::Load(); }
};
string GetRegionParent(string const & id)
{
if (id == "Moscow")
return "Russian Federation";
if (id == "Munich")
return "Bavaria";
if (id == "Bavaria")
return "Germany";
if (id == "San Francisco")
return "California";
if (id == "California")
return "United States of America";
return "";
}
template <typename VehicleModelType, typename VehicleModelFactoryType>
void TestVehicleModelDefault()
{
auto defaultVehicleModel = make_shared<VehicleModelType>();
VehicleModelFactoryType vehicleModelFactory = VehicleModelFactoryType(GetRegionParent);
// Use static_pointer_cast here because VehicleModelInterface do not have EqualsForTests method
shared_ptr<VehicleModelType> defaultVehicleModelForCountry =
static_pointer_cast<VehicleModelType>(vehicleModelFactory.GetVehicleModelForCountry("Nonexistent Country Name"));
TEST(defaultVehicleModel->EqualsForTests(*defaultVehicleModelForCountry),
("Vehicle model for nonexistent counry is not equal to default."));
}
// DirectParent and IndirectParent tests require tested countries to have nondefault restriction
// If selected countries will change their restrictions to default, select other countries for tests
template <typename VehicleModelType, typename VehicleModelFactoryType>
void TestHaveNondefaultRestrictionForSelectedCountry(string country)
{
auto defaultVehicleModel = make_shared<VehicleModelType>();
VehicleModelFactoryType vehicleModelFactory = VehicleModelFactoryType(GetRegionParent);
shared_ptr<VehicleModelType> vehicleModelCountry =
static_pointer_cast<VehicleModelType>(vehicleModelFactory.GetVehicleModelForCountry(country));
TEST(!(vehicleModelCountry->EqualsForTests(*defaultVehicleModel)),
(country,
"has default model. It may be ok if traffic restrictions was changed. "
"If so, select other country for this and next test."));
}
template <typename VehicleModelType, typename VehicleModelFactoryType>
void ParentTest(string child, string parent)
{
VehicleModelFactoryType vehicleModelFactory = VehicleModelFactoryType(GetRegionParent);
shared_ptr<VehicleModelType> vehicleModelChild =
static_pointer_cast<VehicleModelType>(vehicleModelFactory.GetVehicleModelForCountry(child));
shared_ptr<VehicleModelType> vehicleModelParent =
static_pointer_cast<VehicleModelType>(vehicleModelFactory.GetVehicleModelForCountry(parent));
TEST(vehicleModelChild->EqualsForTests(*vehicleModelParent), ("Can not expand car model for", child, "to", parent));
}
// Test we have default vehicle models for nonexistent(unknown) country
UNIT_CLASS_TEST(VehicleModelForCountryTest, CarModel_Default)
{
TestVehicleModelDefault<CarModel, CarModelFactory>();
}
UNIT_CLASS_TEST(VehicleModelForCountryTest, BicycleModel_Default)
{
TestVehicleModelDefault<BicycleModel, BicycleModelFactory>();
}
UNIT_CLASS_TEST(VehicleModelForCountryTest, PedestrianModel_Default)
{
TestVehicleModelDefault<PedestrianModel, PedestrianModelFactory>();
}
// 1. Test we have nondefault car model for Russia
// 2. Test we can get car model for Moscow using GetRegionParent callback: car model for Moscow equals
// car model for Russia and it's not default model.
UNIT_CLASS_TEST(VehicleModelForCountryTest, CarModel_DirectParent)
{
TestHaveNondefaultRestrictionForSelectedCountry<CarModel, CarModelFactory>("Russian Federation");
ParentTest<CarModel, CarModelFactory>("Moscow", "Russian Federation");
}
// 1. Test we have nondefault bicycle model for Russia
// 2. Test we can get bicycle model for Moscow using GetRegionParent callback: bicycle model for Moscow
// equals bicycle model for Russia and it's not default model.
UNIT_CLASS_TEST(VehicleModelForCountryTest, BicycleModel_DirectParent)
{
// Road types for RF are equal with defaults (speeds are not compared).
// TestHaveNondefaultRestrictionForSelectedCountry<BicycleModel, BicycleModelFactory>("Russian Federation");
ParentTest<BicycleModel, BicycleModelFactory>("Moscow", "Russian Federation");
}
// 1. Test we have nondefault pedestrian model for Russia
// 2. Test we can get pedestrian model for Moscow using GetRegionParent callback: pedestrian model for
// Moscow equals pedestrian model for Russia and it's not default model.
UNIT_CLASS_TEST(VehicleModelForCountryTest, PedestrianModel_DirectParent)
{
TestHaveNondefaultRestrictionForSelectedCountry<PedestrianModel, PedestrianModelFactory>("Russian Federation");
ParentTest<PedestrianModel, PedestrianModelFactory>("Moscow", "Russian Federation");
}
// Test has the same idea as previous one except Germany is not direct parent of Munich
// in GetRegionParent function: Munich -> Bavaria -> Germany
UNIT_CLASS_TEST(VehicleModelForCountryTest, CarModel_InirectParent)
{
TestHaveNondefaultRestrictionForSelectedCountry<CarModel, CarModelFactory>("Germany");
ParentTest<CarModel, CarModelFactory>("Munich", "Germany");
}
// Test has the same idea as previous one except United States of America are not direct parent of
// San Francisco in GetRegionParent function: San Francisco -> California -> United States of America
UNIT_CLASS_TEST(VehicleModelForCountryTest, BicycleModel_IndirectParent)
{
TestHaveNondefaultRestrictionForSelectedCountry<BicycleModel, BicycleModelFactory>("United States of America");
ParentTest<BicycleModel, BicycleModelFactory>("San Francisco", "United States of America");
}
// Test has the same idea as previous one except United States of America are not direct parent of
// San Francisco in GetRegionParent function: San Francisco -> California -> United States of America
UNIT_CLASS_TEST(VehicleModelForCountryTest, PedestrianModel_IndirectParent)
{
TestHaveNondefaultRestrictionForSelectedCountry<PedestrianModel, PedestrianModelFactory>("United States of America");
ParentTest<PedestrianModel, PedestrianModelFactory>("San Francisco", "United States of America");
}
} // namespace vehicle_model_for_country_test

View file

@ -0,0 +1,504 @@
#include "testing/testing.hpp"
#include "routing_common/bicycle_model.hpp"
#include "routing_common/car_model.hpp"
#include "routing_common/car_model_coefs.hpp"
#include "routing_common/maxspeed_conversion.hpp"
#include "routing_common/pedestrian_model.hpp"
#include "routing_common/vehicle_model.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/feature_data.hpp"
#include "platform/measurement_utils.hpp"
#include "base/math.hpp"
namespace vehicle_model_test
{
using namespace routing;
using namespace std;
HighwayBasedSpeeds const kDefaultSpeeds = {
{HighwayType::HighwayTrunk, InOutCitySpeedKMpH(100.0 /* in city */, 150.0 /* out city */)},
{HighwayType::HighwayPrimary, InOutCitySpeedKMpH(90.0 /* in city */, 120.0 /* out city */)},
{HighwayType::HighwaySecondary,
InOutCitySpeedKMpH(SpeedKMpH(80.0 /* weight */, 70.0 /* eta */) /* in and out city*/)},
{HighwayType::HighwayResidential, InOutCitySpeedKMpH(SpeedKMpH(45.0 /* weight */, 55.0 /* eta */) /* in city */,
SpeedKMpH(50.0 /* weight */, 60.0 /* eta */) /* out city */)},
{HighwayType::HighwayService, InOutCitySpeedKMpH(SpeedKMpH(47.0 /* weight */, 36.0 /* eta */) /* in city */,
SpeedKMpH(50.0 /* weight */, 40.0 /* eta */) /* out city */)}};
HighwayBasedFactors const kDefaultFactors = {{HighwayType::HighwayTrunk, InOutCityFactor(1.0)},
{HighwayType::HighwayPrimary, InOutCityFactor(1.0)},
{HighwayType::HighwaySecondary, InOutCityFactor(1.0)},
{HighwayType::HighwayResidential, InOutCityFactor(0.5)}};
VehicleModel::LimitsInitList const kTestLimits = {{HighwayType::HighwayTrunk, true},
{HighwayType::HighwayPrimary, true},
{HighwayType::HighwaySecondary, true},
{HighwayType::HighwayResidential, true},
{HighwayType::HighwayService, false}};
VehicleModel::SurfaceInitList const kCarSurface = {
{{"psurface", "paved_good"}, {0.8 /* weightFactor */, 0.9 /* etaFactor */}},
{{"psurface", "paved_bad"}, {0.4, 0.5}},
{{"psurface", "unpaved_good"}, {0.6, 0.8}},
{{"psurface", "unpaved_bad"}, {0.2, 0.2}}};
class VehicleModelTest
{
public:
VehicleModelTest()
{
classificator::Load();
auto const & c = classif();
primary = c.GetTypeByPath({"highway", "primary"});
secondary = c.GetTypeByPath({"highway", "secondary"});
secondaryBridge = c.GetTypeByPath({"highway", "secondary", "bridge"});
secondaryTunnel = c.GetTypeByPath({"highway", "secondary", "tunnel"});
residential = c.GetTypeByPath({"highway", "residential"});
path = c.GetTypeByPath({"highway", "path"});
footway = c.GetTypeByPath({"highway", "footway"});
cycleway = c.GetTypeByPath({"highway", "cycleway"});
yesBicycle = c.GetTypeByPath({"hwtag", "yesbicycle"});
yesFoot = c.GetTypeByPath({"hwtag", "yesfoot"});
oneway = c.GetTypeByPath({"hwtag", "oneway"});
pavedGood = c.GetTypeByPath({"psurface", "paved_good"});
pavedBad = c.GetTypeByPath({"psurface", "paved_bad"});
unpavedGood = c.GetTypeByPath({"psurface", "unpaved_good"});
unpavedBad = c.GetTypeByPath({"psurface", "unpaved_bad"});
}
uint32_t primary, secondary, secondaryTunnel, secondaryBridge, residential;
uint32_t path, footway, cycleway, yesBicycle, yesFoot;
uint32_t oneway, pavedGood, pavedBad, unpavedGood, unpavedBad;
static SpeedParams DefaultParams() { return {{}, kInvalidSpeed, false /* inCity */}; }
};
class VehicleModelStub : public VehicleModel
{
public:
VehicleModelStub() : VehicleModel(classif(), kTestLimits, kCarSurface, {kDefaultSpeeds, kDefaultFactors}) {}
SpeedKMpH GetSpeed(feature::TypesHolder const & types, SpeedParams const & params) const override
{
return GetTypeSpeedImpl(types, params, true /* isCar */);
}
// We are not going to use offroad routing in these tests.
SpeedKMpH const & GetOffroadSpeed() const override
{
static SpeedKMpH offroad{0.0 /* weight */, 0.0 /* eta */};
return offroad;
}
};
void CheckSpeedWithParams(initializer_list<uint32_t> const & types, SpeedParams const & params,
SpeedKMpH const & expectedSpeed)
{
VehicleModelStub model;
feature::TypesHolder h;
for (uint32_t t : types)
h.Add(t);
TEST_EQUAL(model.GetSpeed(h, params), expectedSpeed, ());
}
void CheckSpeed(initializer_list<uint32_t> const & types, InOutCitySpeedKMpH const & expectedSpeed)
{
SpeedParams const inCity(true /* forward */, true /* in city */, Maxspeed());
CheckSpeedWithParams(types, inCity, expectedSpeed.m_inCity);
SpeedParams const outCity(true /* forward */, false /* in city */, Maxspeed());
CheckSpeedWithParams(types, outCity, expectedSpeed.m_outCity);
}
void CheckOneWay(initializer_list<uint32_t> const & types, bool expectedValue)
{
VehicleModelStub model;
feature::TypesHolder h;
for (uint32_t t : types)
h.Add(t);
TEST_EQUAL(model.IsOneWay(h), expectedValue, ());
}
void CheckPassThroughAllowed(initializer_list<uint32_t> const & types, bool expectedValue)
{
VehicleModelStub model;
feature::TypesHolder h;
for (uint32_t t : types)
h.Add(t);
TEST_EQUAL(model.IsPassThroughAllowed(h), expectedValue, ());
}
UNIT_CLASS_TEST(VehicleModelStub, MaxSpeed)
{
TEST_EQUAL(GetMaxWeightSpeed(), 150.0, ());
}
UNIT_CLASS_TEST(VehicleModelTest, Speed)
{
{
CheckSpeed({secondaryBridge}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckSpeed({secondaryTunnel}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckSpeed({secondary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
}
CheckSpeed({classif().GetTypeByPath({"highway", "trunk"})},
{SpeedKMpH(100.0 /* weight */, 100.0 /* eta */) /* in city */,
SpeedKMpH(150.0 /* weight */, 150.0 /* eta */) /* out of city */});
CheckSpeed({primary}, {SpeedKMpH(90.0, 90.0), SpeedKMpH(120.0, 120.0)});
CheckSpeed({residential}, {SpeedKMpH(22.5, 27.5), SpeedKMpH(25.0, 30.0)});
}
UNIT_CLASS_TEST(VehicleModelTest, Speed_MultiTypes)
{
uint32_t const typeHighway = classif().GetTypeByPath({"highway"});
CheckSpeed({secondaryTunnel, secondary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckSpeed({secondaryTunnel, typeHighway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckSpeed({typeHighway, secondaryTunnel}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
}
UNIT_CLASS_TEST(VehicleModelTest, OneWay)
{
CheckSpeed({secondaryBridge, oneway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckOneWay({secondaryBridge, oneway}, true);
CheckSpeed({oneway, secondaryBridge}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckOneWay({oneway, secondaryBridge}, true);
CheckOneWay({oneway}, true);
}
UNIT_CLASS_TEST(VehicleModelTest, DifferentSpeeds)
{
// What is the purpose of this artificial test with several highway types? To show that order is important?
CheckSpeed({secondary, primary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary));
CheckSpeed({oneway, primary, secondary}, kDefaultSpeeds.Get(HighwayType::HighwayPrimary));
CheckOneWay({primary, oneway, secondary}, true);
}
UNIT_CLASS_TEST(VehicleModelTest, PassThroughAllowed)
{
CheckPassThroughAllowed({secondary}, true);
CheckPassThroughAllowed({primary}, true);
CheckPassThroughAllowed({classif().GetTypeByPath({"highway", "service"})}, false);
}
UNIT_CLASS_TEST(VehicleModelTest, SpeedFactor)
{
CheckSpeed({secondary, pavedGood}, {SpeedKMpH(64.0 /* weight */, 63.0 /* eta */) /* in city */,
SpeedKMpH(64.0 /* weight */, 63.0 /* eta */) /* out of city */});
CheckSpeed({secondary, pavedBad}, {SpeedKMpH(32.0, 35.0), SpeedKMpH(32.0, 35.0)});
CheckSpeed({secondary, unpavedGood}, {SpeedKMpH(48.0, 56.0), SpeedKMpH(48.0, 56.0)});
CheckSpeed({secondary, unpavedBad}, {SpeedKMpH(16.0, 14.0), SpeedKMpH(16.0, 14.0)});
CheckSpeed({residential, pavedGood}, {SpeedKMpH(18.0, 24.75), SpeedKMpH(20.0, 27.0)});
CheckSpeed({residential, pavedBad}, {SpeedKMpH(9.0, 13.75), SpeedKMpH(10.0, 15.0)});
CheckSpeed({residential, unpavedGood}, {SpeedKMpH(13.5, 22.0), SpeedKMpH(15.0, 24.0)});
CheckSpeed({residential, unpavedBad}, {SpeedKMpH(4.5, 5.5), SpeedKMpH(5.0, 6.0)});
}
UNIT_CLASS_TEST(VehicleModelTest, MaxspeedFactor)
{
auto constexpr units = measurement_utils::Units::Metric;
Maxspeed const maxspeed90 = Maxspeed(units, 90, kInvalidSpeed);
// pavedBad == unpavedBad for the roads with explicitly defined speeds.
CheckSpeedWithParams({secondary, unpavedBad}, SpeedParams(true /* forward */, false /* in city */, maxspeed90),
SpeedKMpH(36.0, 45.0));
CheckSpeedWithParams({secondary, pavedBad}, SpeedParams(true /* forward */, false /* in city */, maxspeed90),
SpeedKMpH(36.0, 45.0));
CheckSpeedWithParams({primary, pavedGood}, SpeedParams(true /* forward */, false /* in city */, maxspeed90),
SpeedKMpH(72.0, 81.0));
Maxspeed const maxspeed9070 = Maxspeed(units, 90, 70);
CheckSpeedWithParams({primary, pavedGood}, SpeedParams(true /* forward */, false /* in city */, maxspeed9070),
SpeedKMpH(72.0, 81.0));
CheckSpeedWithParams({primary, pavedGood}, SpeedParams(false /* forward */, false /* in city */, maxspeed9070),
SpeedKMpH(56.0, 63.0));
Maxspeed const maxspeed60 = Maxspeed(units, 60, kInvalidSpeed);
CheckSpeedWithParams({residential, pavedGood}, SpeedParams(true /* forward */, false /* in city */, maxspeed60),
SpeedKMpH(24.0, 27.0));
}
namespace
{
bool LessSpeed(SpeedKMpH const & l, SpeedKMpH const & r)
{
TEST(l.IsValid() && r.IsValid(), (l, r));
// Weight should be strict less, ETA may be equal.
return l.m_weight < r.m_weight && l.m_eta <= r.m_eta;
}
#define TEST_LESS_SPEED(l, r) TEST(LessSpeed(l, r), (l, r))
bool LessOrEqualSpeed(SpeedKMpH const & l, SpeedKMpH const & r)
{
TEST(l.IsValid() && r.IsValid(), (l, r));
return l.m_weight <= r.m_weight;
}
#define TEST_LESS_OR_EQUAL_SPEED(l, r) TEST(LessOrEqualSpeed(l, r), (l, r))
} // namespace
UNIT_CLASS_TEST(VehicleModelTest, CarModel_TrackVsGravelTertiary)
{
auto const & model = CarModel::AllLimitsInstance();
auto const & c = classif();
auto const p = DefaultParams();
feature::TypesHolder h1;
h1.Add(c.GetTypeByPath({"highway", "track"}));
feature::TypesHolder h2;
h2.Add(c.GetTypeByPath({"highway", "tertiary"}));
h2.Add(unpavedBad); // from OSM surface=gravel
// https://www.openstreetmap.org/#map=19/45.43640/36.39689
// Obvious that gravel tertiary (moreover with maxspeed=60kmh) should be better than track.
{
SpeedParams p2({measurement_utils::Units::Metric, 60, 60}, kInvalidSpeed, false /* inCity */);
TEST_LESS_SPEED(model.GetSpeed(h1, p), model.GetSpeed(h2, p2));
}
{
TEST_LESS_SPEED(model.GetSpeed(h1, p), model.GetSpeed(h2, p));
}
}
UNIT_CLASS_TEST(VehicleModelTest, CarModel_Smoke)
{
auto const & model = CarModel::AllLimitsInstance();
auto const & c = classif();
auto const p = DefaultParams();
feature::TypesHolder h1;
h1.Add(secondary);
feature::TypesHolder h2;
h2.Add(secondary);
h2.Add(c.GetTypeByPath({"hwtag", "yescar"}));
feature::TypesHolder h3;
h3.Add(c.GetTypeByPath({"highway", "tertiary"}));
TEST_EQUAL(model.GetSpeed(h1, p), model.GetSpeed(h2, p), ());
TEST_LESS_SPEED(model.GetSpeed(h3, p), model.GetSpeed(h2, p));
}
UNIT_CLASS_TEST(VehicleModelTest, BicycleModel_Smoke)
{
auto const & model = BicycleModel::AllLimitsInstance();
auto const & c = classif();
auto const p = DefaultParams();
feature::TypesHolder h1;
h1.Add(cycleway);
h1.Add(yesBicycle);
feature::TypesHolder h2;
h2.Add(cycleway);
feature::TypesHolder h3;
h3.Add(secondary);
h3.Add(yesBicycle);
feature::TypesHolder h4;
h4.Add(secondary);
feature::TypesHolder h5;
h5.Add(secondary);
h5.Add(c.GetTypeByPath({"hwtag", "nocycleway"}));
TEST_EQUAL(model.GetSpeed(h1, p), model.GetSpeed(h2, p), ());
TEST_LESS_SPEED(model.GetSpeed(h3, p), model.GetSpeed(h2, p));
TEST_LESS_SPEED(model.GetSpeed(h4, p), model.GetSpeed(h3, p));
TEST_LESS_SPEED(model.GetSpeed(h5, p), model.GetSpeed(h4, p));
}
UNIT_CLASS_TEST(VehicleModelTest, BicycleModel_Speeds)
{
auto const & model = BicycleModel::AllLimitsInstance();
auto const p = DefaultParams();
std::vector<std::vector<uint32_t>> const highways = {
{cycleway, pavedGood}, // TODO: should be higher than next, but is equal
{cycleway},
{cycleway, unpavedGood}, // TODO: should be lower than previous, but is equal
{footway, yesBicycle, pavedGood}, // TODO: should be higher than next, but is equal
{footway, yesBicycle},
{path,
yesBicycle}, // TODO: unpaved by default, so should be lower than shared footways or cycleways, but is equal
{cycleway, pavedBad},
{footway, yesBicycle, pavedBad},
{footway}, // If allowed in the region.
{cycleway, unpavedBad},
{path, unpavedGood}, // Its controversial what is preferrable: a good path or a bad cycleway
{path, yesBicycle, unpavedBad},
/// @todo(pastk): "nobicycle" is ignored in speed calculation atm, the routing is just forbidden there.
/// But "nobicycle" should result in a dismount speed instead, see
/// https://github.com/organicmaps/organicmaps/issues/9784
// {footway, c.GetTypeByPath({"hwtag", "nobicycle"})},
// {path, c.GetTypeByPath({"hwtag", "nobicycle"})},
{path, unpavedBad},
};
feature::TypesHolder hprev;
for (size_t i = 0; i < highways.size(); ++i)
{
feature::TypesHolder h;
for (uint32_t t : highways[i])
h.Add(t);
LOG(LINFO, (h, model.GetSpeed(h, p)));
if (i > 0)
TEST_LESS_OR_EQUAL_SPEED(model.GetSpeed(h, p), model.GetSpeed(hprev, p));
hprev = h;
}
}
UNIT_CLASS_TEST(VehicleModelTest, PedestrianModel_Smoke)
{
auto const & model = PedestrianModel::AllLimitsInstance();
auto const & c = classif();
auto const p = DefaultParams();
feature::TypesHolder h1;
h1.Add(residential);
h1.Add(c.GetTypeByPath({"hwtag", "yesfoot"}));
feature::TypesHolder h2;
h2.Add(residential);
feature::TypesHolder h3;
h3.Add(residential);
h3.Add(c.GetTypeByPath({"hwtag", "nosidewalk"}));
TEST_LESS_SPEED(model.GetSpeed(h2, p), model.GetSpeed(h1, p));
TEST_LESS_SPEED(model.GetSpeed(h3, p), model.GetSpeed(h2, p));
}
UNIT_CLASS_TEST(VehicleModelTest, PedestrianModel_Speeds)
{
auto const & model = PedestrianModel::AllLimitsInstance();
// auto const & c = classif();
auto const p = DefaultParams();
std::vector<std::vector<uint32_t>> const highways = {
{footway, pavedGood}, // TODO: should be higher than next, but is equal
{footway},
{footway, pavedBad}, // TODO: should be lower than previous, but is equal
{footway, yesBicycle}, // TODO: should be lower than previous, but is equal
{path, yesFoot}, // TODO: should be higher than previous, but is equal
{path, unpavedGood}, // TODO: should be lower than previous, but is equal
{path, yesBicycle}, // TODO: should be lower than previous, but is equal
{cycleway},
{path, unpavedBad},
{cycleway, unpavedBad},
// {path, c.GetTypeByPath({"hwtag", "nofoot"})}, // TODO: should be forbidden, but has no effect ATM
// {cycleway, c.GetTypeByPath({"hwtag", "nofoot"})}, // TODO: should be forbidden, but has no effect ATM
};
feature::TypesHolder hprev;
for (size_t i = 0; i < highways.size(); ++i)
{
feature::TypesHolder h;
for (uint32_t t : highways[i])
h.Add(t);
LOG(LINFO, (h, model.GetSpeed(h, p)));
if (i > 0)
TEST_LESS_OR_EQUAL_SPEED(model.GetSpeed(h, p), model.GetSpeed(hprev, p));
hprev = h;
}
}
#undef TEST_LESS_SPEED
#undef TEST_LESS_OR_EQUAL_SPEED
UNIT_TEST(VehicleModel_MultiplicationOperatorTest)
{
SpeedKMpH const speed(90 /* weight */, 100 /* eta */);
SpeedFactor const factor(1.0, 1.1);
SpeedKMpH const lResult = speed * factor;
SpeedKMpH const rResult = factor * speed;
TEST_EQUAL(lResult, rResult, ());
TEST(AlmostEqualULPs(lResult.m_weight, 90.0), ());
TEST(AlmostEqualULPs(lResult.m_eta, 110.0), ());
}
UNIT_TEST(VehicleModel_CarModelValidation)
{
HighwayType const carRoadTypes[] = {
HighwayType::HighwayLivingStreet, HighwayType::HighwayMotorway, HighwayType::HighwayMotorwayLink,
HighwayType::HighwayPrimary, HighwayType::HighwayPrimaryLink, HighwayType::HighwayResidential,
HighwayType::HighwayRoad, HighwayType::HighwaySecondary, HighwayType::HighwaySecondaryLink,
HighwayType::HighwayService, HighwayType::HighwayTertiary, HighwayType::HighwayTertiaryLink,
HighwayType::HighwayTrack, HighwayType::HighwayTrunk, HighwayType::HighwayTrunkLink,
HighwayType::HighwayUnclassified, HighwayType::ManMadePier, HighwayType::RouteShuttleTrain,
HighwayType::RouteFerry,
};
for (auto const hwType : carRoadTypes)
{
auto const * factor = kHighwayBasedFactors.Find(hwType);
TEST(factor, (hwType));
TEST(factor->IsValid(), (hwType, *factor));
auto const * speed = kHighwayBasedSpeeds.Find(hwType);
TEST(speed, (hwType));
TEST(speed->IsValid(), (hwType, *speed));
}
}
UNIT_TEST(VehicleModel_HighwayType_Values)
{
classificator::Load();
auto const & cl = classif();
auto const check = [&cl](HighwayType hwType, base::StringIL clType)
{ return static_cast<uint16_t>(hwType) == cl.GetIndexForType(cl.GetTypeByPath(clType)); };
TEST(check(HighwayType::HighwayResidential, {"highway", "residential"}), ());
TEST(check(HighwayType::HighwayService, {"highway", "service"}), ());
TEST(check(HighwayType::HighwayUnclassified, {"highway", "unclassified"}), ());
TEST(check(HighwayType::HighwayFootway, {"highway", "footway"}), ());
TEST(check(HighwayType::HighwayTrack, {"highway", "track"}), ());
TEST(check(HighwayType::HighwayTertiary, {"highway", "tertiary"}), ());
TEST(check(HighwayType::HighwaySecondary, {"highway", "secondary"}), ());
TEST(check(HighwayType::HighwayPath, {"highway", "path"}), ());
TEST(check(HighwayType::HighwayPrimary, {"highway", "primary"}), ());
TEST(check(HighwayType::HighwayRoad, {"highway", "road"}), ());
TEST(check(HighwayType::HighwayCycleway, {"highway", "cycleway"}), ());
TEST(check(HighwayType::HighwayMotorwayLink, {"highway", "motorway_link"}), ());
TEST(check(HighwayType::HighwayLivingStreet, {"highway", "living_street"}), ());
TEST(check(HighwayType::HighwayMotorway, {"highway", "motorway"}), ());
TEST(check(HighwayType::HighwayLadder, {"highway", "ladder"}), ());
TEST(check(HighwayType::HighwaySteps, {"highway", "steps"}), ());
TEST(check(HighwayType::HighwayTrunk, {"highway", "trunk"}), ());
TEST(check(HighwayType::HighwayPedestrian, {"highway", "pedestrian"}), ());
TEST(check(HighwayType::HighwayTrunkLink, {"highway", "trunk_link"}), ());
TEST(check(HighwayType::HighwayPrimaryLink, {"highway", "primary_link"}), ());
TEST(check(HighwayType::ManMadePier, {"man_made", "pier"}), ());
TEST(check(HighwayType::HighwayBridleway, {"highway", "bridleway"}), ());
TEST(check(HighwayType::HighwaySecondaryLink, {"highway", "secondary_link"}), ());
TEST(check(HighwayType::RouteFerry, {"route", "ferry"}), ());
TEST(check(HighwayType::HighwayTertiaryLink, {"highway", "tertiary_link"}), ());
TEST(check(HighwayType::HighwayLadder, {"highway", "ladder"}), ());
TEST(check(HighwayType::HighwayBusway, {"highway", "busway"}), ());
TEST(check(HighwayType::RouteShuttleTrain, {"route", "shuttle_train"}), ());
}
} // namespace vehicle_model_test

View file

@ -0,0 +1,421 @@
#include "routing_common/vehicle_model.hpp"
#include "indexer/classificator.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "base/assert.hpp"
#include <algorithm>
#include <sstream>
#include <unordered_map>
namespace routing
{
using namespace std;
namespace
{
template <double const & (*F)(double const &, double const &), typename WeightAndETA>
WeightAndETA Pick(WeightAndETA const & lhs, WeightAndETA const & rhs)
{
return {F(lhs.m_weight, rhs.m_weight), F(lhs.m_eta, rhs.m_eta)};
}
SpeedKMpH Max(SpeedKMpH const & lhs, InOutCitySpeedKMpH const & rhs)
{
return Pick<max>(lhs, Pick<max>(rhs.m_inCity, rhs.m_outCity));
}
} // namespace
VehicleModel::VehicleModel(Classificator const & classif, LimitsInitList const & featureTypeLimits,
SurfaceInitList const & featureTypeSurface, HighwayBasedInfo const & info)
: m_highwayBasedInfo(info)
, m_onewayType(ftypes::IsOneWayChecker::Instance().GetType())
{
m_roadTypes.Reserve(featureTypeLimits.size());
for (auto const & v : featureTypeLimits)
{
auto const * speed = info.m_speeds.Find(v.m_type);
ASSERT(speed, ("Can't found speed for", v.m_type));
m_maxModelSpeed = Max(m_maxModelSpeed, *speed);
m_roadTypes.Insert(classif.GetTypeForIndex(static_cast<uint32_t>(v.m_type)), v.m_isPassThroughAllowed);
}
m_roadTypes.FinishBuilding();
m_surfaceFactors.Reserve(featureTypeSurface.size());
for (auto const & v : featureTypeSurface)
{
auto const & speedFactor = v.m_factor;
ASSERT_LESS_OR_EQUAL(speedFactor.m_weight, 1.0, ());
ASSERT_LESS_OR_EQUAL(speedFactor.m_eta, 1.0, ());
ASSERT_GREATER(speedFactor.m_weight, 0.0, ());
ASSERT_GREATER(speedFactor.m_eta, 0.0, ());
m_surfaceFactors.Insert(classif.GetTypeByPath(v.m_type), speedFactor);
if (v.m_type[1] == "paved_bad")
m_minSurfaceFactorForMaxspeed = speedFactor;
}
}
void VehicleModel::AddAdditionalRoadTypes(Classificator const & classif, AdditionalRoadsList const & roads)
{
for (auto const & r : roads)
{
uint32_t const type = classif.GetTypeByPath(r.m_type);
if (m_roadTypes.Find(type) == nullptr)
{
m_addRoadTypes.Insert(type, r.m_speed);
m_maxModelSpeed = Max(m_maxModelSpeed, r.m_speed);
}
}
}
std::optional<HighwayType> VehicleModel::GetHighwayType(FeatureTypes const & types) const
{
for (uint32_t t : types)
{
ftype::TruncValue(t, 2);
auto const ret = GetHighwayType(t);
if (ret)
return *ret;
}
// For example Denmark has "No track" profile (see kCarOptionsDenmark), but tracks exist in MWM.
return {};
}
double VehicleModel::GetMaxWeightSpeed() const
{
return m_maxModelSpeed.m_weight;
}
optional<HighwayType> VehicleModel::GetHighwayType(uint32_t type) const
{
auto const * value = m_roadTypes.Find(type);
if (value)
return static_cast<HighwayType>(classif().GetIndexForType(type));
return {};
}
void VehicleModel::GetSurfaceFactor(uint32_t type, SpeedFactor & factor) const
{
auto const * surface = m_surfaceFactors.Find(type);
if (surface)
factor = Pick<min>(factor, *surface);
ASSERT_LESS_OR_EQUAL(factor.m_weight, 1.0, ());
ASSERT_LESS_OR_EQUAL(factor.m_eta, 1.0, ());
ASSERT_GREATER(factor.m_weight, 0.0, ());
ASSERT_GREATER(factor.m_eta, 0.0, ());
}
void VehicleModel::GetAdditionalRoadSpeed(uint32_t type, bool isCityRoad, optional<SpeedKMpH> & speed) const
{
auto const * s = m_addRoadTypes.Find(type);
if (s)
{
// Now we have only 1 additional type "yes" for all models.
ASSERT(!speed, ());
speed = isCityRoad ? s->m_inCity : s->m_outCity;
}
}
/// @note Saved speed |params| is taken into account only if isCar == true.
SpeedKMpH VehicleModel::GetTypeSpeedImpl(FeatureTypes const & types, SpeedParams const & params, bool isCar) const
{
bool const isCityRoad = params.m_inCity;
optional<HighwayType> hwType;
SpeedFactor surfaceFactor;
optional<SpeedKMpH> additionalRoadSpeed;
for (uint32_t t : types)
{
ftype::TruncValue(t, 2);
if (!hwType)
hwType = GetHighwayType(t);
GetSurfaceFactor(t, surfaceFactor);
GetAdditionalRoadSpeed(t, isCityRoad, additionalRoadSpeed);
}
SpeedKMpH speed;
if (hwType)
{
if (isCar && params.m_maxspeed.IsValid())
{
MaxspeedType const s = params.m_maxspeed.GetSpeedKmPH(params.m_forward);
ASSERT(s != kInvalidSpeed, (*hwType, params.m_forward, params.m_maxspeed));
speed = {static_cast<double>(s)};
// Looks like a crutch, limit surface factor if maxspeed is set.
/// @see USA_UseDirt_WithMaxspeed test.
surfaceFactor.SetMin(m_minSurfaceFactorForMaxspeed);
}
else
{
auto const * s = m_highwayBasedInfo.m_speeds.Find(*hwType);
ASSERT(s, ("Key:", *hwType, "is not found."));
speed = s->GetSpeed(isCityRoad);
if (isCar)
{
// Override the global default speed with the MWM's saved default speed if they are not significantly differ
// (2x), to avoid anomaly peaks (especially for tracks).
/// @todo MWM saved speeds should be validated in generator.
if (params.m_defSpeedKmPH != kInvalidSpeed &&
fabs(speed.m_weight - params.m_defSpeedKmPH) / speed.m_weight < 1.0)
{
double const factor = speed.m_eta / speed.m_weight;
speed.m_weight = params.m_defSpeedKmPH;
speed.m_eta = speed.m_weight * factor;
}
}
else
{
// Take additional speed for bicycle and pedestrian only.
if (additionalRoadSpeed)
speed = Pick<max>(speed, *additionalRoadSpeed);
}
}
auto const typeKey = *hwType;
auto const * factor = m_highwayBasedInfo.m_factors.Find(typeKey);
ASSERT(factor, ("Key:", typeKey, "is not found."));
auto const & f = factor->GetFactor(isCityRoad);
speed.m_weight *= f.m_weight;
speed.m_eta *= f.m_eta;
}
else
{
// In case if we have highway=footway + motorcar=yes :)
ASSERT(additionalRoadSpeed, ());
if (additionalRoadSpeed)
speed = *additionalRoadSpeed;
}
ASSERT(!(m_maxModelSpeed < speed), (speed, m_maxModelSpeed, types));
return speed * surfaceFactor;
}
bool VehicleModel::IsOneWay(FeatureTypes const & types) const
{
return types.Has(m_onewayType);
}
bool VehicleModel::IsRoad(FeatureTypes const & types) const
{
return types.GetGeomType() == feature::GeomType::Line && IsRoadImpl(types);
}
bool VehicleModel::IsPassThroughAllowed(FeatureTypes const & types) const
{
for (uint32_t t : types)
{
ftype::TruncValue(t, 2);
// Additional types (like ferry) are always pass-through now.
if (m_addRoadTypes.Find(t))
return true;
bool const * allow = m_roadTypes.Find(t);
if (allow && *allow)
return true;
}
return false;
}
bool VehicleModel::IsRoadType(uint32_t type) const
{
ftype::TruncValue(type, 2);
return m_addRoadTypes.Find(type) || m_roadTypes.Find(type);
}
bool VehicleModel::IsRoadImpl(FeatureTypes const & types) const
{
for (uint32_t const t : types)
{
// Assume that Yes and No are not possible at the same time. Return first flag, otherwise.
if (t == m_yesType)
return true;
if (t == m_noType)
return false;
}
return HasRoadType(types);
}
VehicleModelFactory::VehicleModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn)
: m_countryParentNameGetterFn(countryParentNameGetterFn)
{}
shared_ptr<VehicleModelInterface> VehicleModelFactory::GetVehicleModel() const
{
auto const itr = m_models.find("");
ASSERT(itr != m_models.end(), ());
return itr->second;
}
shared_ptr<VehicleModelInterface> VehicleModelFactory::GetVehicleModelForCountry(string const & country) const
{
string parent = country;
while (!parent.empty())
{
auto it = m_models.find(parent);
if (it != m_models.end())
return it->second;
parent = GetParent(parent);
}
return GetVehicleModel();
}
string VehicleModelFactory::GetParent(string const & country) const
{
if (!m_countryParentNameGetterFn)
return string();
return m_countryParentNameGetterFn(country);
}
HighwayBasedFactors GetOneFactorsForBicycleAndPedestrianModel()
{
return HighwayBasedFactors{
{HighwayType::HighwayTrunk, InOutCityFactor(1.0)},
{HighwayType::HighwayTrunkLink, InOutCityFactor(1.0)},
{HighwayType::HighwayPrimary, InOutCityFactor(1.0)},
{HighwayType::HighwayPrimaryLink, InOutCityFactor(1.0)},
{HighwayType::HighwaySecondary, InOutCityFactor(1.0)},
{HighwayType::HighwaySecondaryLink, InOutCityFactor(1.0)},
{HighwayType::HighwayTertiary, InOutCityFactor(1.0)},
{HighwayType::HighwayTertiaryLink, InOutCityFactor(1.0)},
{HighwayType::HighwayService, InOutCityFactor(1.0)},
{HighwayType::HighwayUnclassified, InOutCityFactor(1.0)},
{HighwayType::HighwayRoad, InOutCityFactor(1.0)},
{HighwayType::HighwayTrack, InOutCityFactor(1.0)},
{HighwayType::HighwayPath, InOutCityFactor(1.0)},
{HighwayType::HighwayBridleway, InOutCityFactor(1.0)},
{HighwayType::HighwayCycleway, InOutCityFactor(1.0)},
{HighwayType::HighwayResidential, InOutCityFactor(1.0)},
{HighwayType::HighwayLivingStreet, InOutCityFactor(1.0)},
{HighwayType::HighwayLadder, InOutCityFactor(1.0)},
{HighwayType::HighwaySteps, InOutCityFactor(1.0)},
{HighwayType::HighwayPedestrian, InOutCityFactor(1.0)},
{HighwayType::HighwayFootway, InOutCityFactor(1.0)},
{HighwayType::ManMadePier, InOutCityFactor(1.0)},
{HighwayType::RouteFerry, InOutCityFactor(1.0)},
};
}
string DebugPrint(SpeedKMpH const & speed)
{
ostringstream oss;
oss << "SpeedKMpH [ ";
oss << "weight:" << speed.m_weight << ", ";
oss << "eta:" << speed.m_eta << " ]";
return oss.str();
}
std::string DebugPrint(SpeedFactor const & speedFactor)
{
ostringstream oss;
oss << "SpeedFactor [ ";
oss << "weight:" << speedFactor.m_weight << ", ";
oss << "eta:" << speedFactor.m_eta << " ]";
return oss.str();
}
string DebugPrint(InOutCitySpeedKMpH const & speed)
{
ostringstream oss;
oss << "InOutCitySpeedKMpH [ ";
oss << "inCity:" << DebugPrint(speed.m_inCity) << ", ";
oss << "outCity:" << DebugPrint(speed.m_outCity) << " ]";
return oss.str();
}
string DebugPrint(InOutCityFactor const & speedFactor)
{
ostringstream oss;
oss << "InOutCityFactor [ ";
oss << "inCity:" << DebugPrint(speedFactor.m_inCity) << ", ";
oss << "outCity:" << DebugPrint(speedFactor.m_outCity) << " ]";
return oss.str();
}
string DebugPrint(HighwayType type)
{
switch (type)
{
case HighwayType::HighwayResidential: return "highway-residential";
case HighwayType::HighwayService: return "highway-service";
case HighwayType::HighwayUnclassified: return "highway-unclassified";
case HighwayType::HighwayFootway: return "highway-footway";
case HighwayType::HighwayTrack: return "highway-track";
case HighwayType::HighwayTertiary: return "highway-tertiary";
case HighwayType::HighwaySecondary: return "highway-secondary";
case HighwayType::HighwayPath: return "highway-path";
case HighwayType::HighwayPrimary: return "highway-primary";
case HighwayType::HighwayRoad: return "highway-road";
case HighwayType::HighwayCycleway: return "highway-cycleway";
case HighwayType::HighwayMotorwayLink: return "highway-motorway_link";
case HighwayType::HighwayLivingStreet: return "highway-living_street";
case HighwayType::HighwayMotorway: return "highway-motorway";
case HighwayType::HighwayLadder: return "highway-ladder";
case HighwayType::HighwaySteps: return "highway-steps";
case HighwayType::HighwayTrunk: return "highway-trunk";
case HighwayType::HighwayPedestrian: return "highway-pedestrian";
case HighwayType::HighwayTrunkLink: return "highway-trunk_link";
case HighwayType::HighwayPrimaryLink: return "highway-primary_link";
case HighwayType::ManMadePier: return "man_made-pier";
case HighwayType::HighwayBridleway: return "highway-bridleway";
case HighwayType::HighwaySecondaryLink: return "highway-secondary_link";
case HighwayType::RouteFerry: return "route-ferry";
case HighwayType::HighwayTertiaryLink: return "highway-tertiary_link";
case HighwayType::HighwayBusway: return "highway-busway";
case HighwayType::RouteShuttleTrain: return "route-shuttle_train";
}
UNREACHABLE();
}
void FromString(std::string_view s, HighwayType & highwayType)
{
// Build reverse lookup from DebugPrint function
static std::unordered_map<std::string, HighwayType> const stringToEnum = []()
{
std::unordered_map<std::string, HighwayType> map;
// All possible HighwayType values
constexpr HighwayType allTypes[] = {
HighwayType::HighwayResidential, HighwayType::HighwayService, HighwayType::HighwayUnclassified,
HighwayType::HighwayFootway, HighwayType::HighwayTrack, HighwayType::HighwayTertiary,
HighwayType::HighwaySecondary, HighwayType::HighwayPath, HighwayType::HighwayPrimary,
HighwayType::HighwayRoad, HighwayType::HighwayCycleway, HighwayType::HighwayMotorwayLink,
HighwayType::HighwayLivingStreet, HighwayType::HighwayMotorway, HighwayType::HighwaySteps,
HighwayType::HighwayTrunk, HighwayType::HighwayPedestrian, HighwayType::HighwayTrunkLink,
HighwayType::HighwayPrimaryLink, HighwayType::ManMadePier, HighwayType::HighwayBridleway,
HighwayType::HighwaySecondaryLink, HighwayType::RouteFerry, HighwayType::HighwayTertiaryLink,
HighwayType::HighwayBusway, HighwayType::RouteShuttleTrain};
for (auto type : allTypes)
map[DebugPrint(type)] = type;
return map;
}();
auto it = stringToEnum.find(std::string(s));
if (it != stringToEnum.end())
{
highwayType = it->second;
}
else
{
ASSERT(false, ("Could not read HighwayType from string", s));
highwayType = HighwayType::HighwayResidential; // default fallback
}
}
} // namespace routing

View file

@ -0,0 +1,371 @@
#pragma once
#include "routing_common/maxspeed_conversion.hpp"
#include "indexer/feature_data.hpp"
#include "base/small_map.hpp"
#include <functional>
#include <initializer_list>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
class Classificator;
class FeatureType;
namespace routing
{
double constexpr kNotUsed = std::numeric_limits<double>::max();
struct InOutCityFactor;
struct InOutCitySpeedKMpH;
// Each value is equal to the corresponding 0-based type index from types.txt
// (an ID from mapcss-mapping.csv minus 1).
// The ascending order is strict. Check for static_cast<HighwayType> in vehicle_model.cpp
enum class HighwayType : uint16_t
{
HighwayResidential = 1,
HighwayService = 2,
HighwayUnclassified = 4,
HighwayFootway = 6,
HighwayTrack = 7,
HighwayTertiary = 8,
HighwaySecondary = 12,
HighwayPath = 15,
HighwayPrimary = 26,
HighwayRoad = 410,
HighwayCycleway = 36,
HighwayMotorwayLink = 43,
HighwayLivingStreet = 54,
HighwayMotorway = 57,
HighwaySteps = 58,
HighwayTrunk = 65,
HighwayPedestrian = 69,
HighwayTrunkLink = 90,
HighwayPrimaryLink = 95,
ManMadePier = 119,
HighwayBridleway = 167,
HighwaySecondaryLink = 176,
RouteFerry = 259,
HighwayTertiaryLink = 272,
HighwayLadder = 478,
HighwayBusway = 857, // reserve type here, but this type is not used for any routing by default
RouteShuttleTrain = 1054,
};
using HighwayBasedFactors = base::SmallMap<HighwayType, InOutCityFactor>;
using HighwayBasedSpeeds = base::SmallMap<HighwayType, InOutCitySpeedKMpH>;
/// \brief Params for calculation of an approximate speed on a feature.
struct SpeedParams
{
/// @deprecated For unit tests compatibility.
SpeedParams(bool forward, bool inCity, Maxspeed const & maxspeed)
: m_maxspeed(maxspeed)
, m_defSpeedKmPH(kInvalidSpeed)
, m_inCity(inCity)
, m_forward(forward)
{}
SpeedParams(Maxspeed const & maxspeed, MaxspeedType defSpeedKmPH, bool inCity)
: m_maxspeed(maxspeed)
, m_defSpeedKmPH(defSpeedKmPH)
, m_inCity(inCity)
{}
// Maxspeed stored for feature, if any.
Maxspeed m_maxspeed;
// Default speed for this feature type in MWM, if any (kInvalidSpeed otherwise).
MaxspeedType m_defSpeedKmPH;
// If a corresponding feature lies inside a city of a town.
bool m_inCity;
// Retrieve forward (true) or backward (false) speed.
bool m_forward;
};
/// \brief Speeds which are used for edge weight and ETA estimations.
struct SpeedKMpH
{
constexpr SpeedKMpH() = default;
constexpr SpeedKMpH(double weight) noexcept : m_weight(weight), m_eta(weight) {}
constexpr SpeedKMpH(double weight, double eta) noexcept : m_weight(weight), m_eta(eta) {}
bool operator==(SpeedKMpH const & rhs) const { return m_weight == rhs.m_weight && m_eta == rhs.m_eta; }
bool operator!=(SpeedKMpH const & rhs) const { return !(*this == rhs); }
bool operator<(SpeedKMpH const & rhs) const { return m_weight < rhs.m_weight && m_eta < rhs.m_eta; }
bool IsValid() const { return m_weight > 0 && m_eta > 0; }
double m_weight =
0.0; // KMpH - speed in km/h adjusted for desirability
// cycling on very large road may be fast but speed used for route finding will be treated as much lower
double m_eta = 0.0; // KMpH - actual expected speed in km/h, used to display expected arrival time
};
/// \brief Factors which modify weight and ETA speed on feature in case of bad pavement (reduce)
/// or good highway class (increase).
/// Both should be greater then 0.
struct SpeedFactor
{
constexpr SpeedFactor() = default;
constexpr SpeedFactor(double factor) noexcept : m_weight(factor), m_eta(factor) {}
constexpr SpeedFactor(double weight, double eta) noexcept : m_weight(weight), m_eta(eta) {}
bool IsValid() const { return m_weight > 0.0 && m_weight <= 1.0 && m_eta > 0.0 && m_eta <= 1.0; }
void SetMin(SpeedFactor const & f)
{
ASSERT(f.IsValid(), ());
m_weight = std::max(f.m_weight, m_weight);
m_eta = std::max(f.m_eta, m_eta);
}
bool operator==(SpeedFactor const & rhs) const { return m_weight == rhs.m_weight && m_eta == rhs.m_eta; }
bool operator!=(SpeedFactor const & rhs) const { return !(*this == rhs); }
double m_weight = 1.0;
double m_eta = 1.0;
};
inline SpeedKMpH operator*(SpeedFactor const & factor, SpeedKMpH const & speed)
{
return {speed.m_weight * factor.m_weight, speed.m_eta * factor.m_eta};
}
inline SpeedKMpH operator*(SpeedKMpH const & speed, SpeedFactor const & factor)
{
return {speed.m_weight * factor.m_weight, speed.m_eta * factor.m_eta};
}
struct InOutCitySpeedKMpH
{
constexpr InOutCitySpeedKMpH() = default;
constexpr explicit InOutCitySpeedKMpH(SpeedKMpH const & speed) noexcept : m_inCity(speed), m_outCity(speed) {}
constexpr InOutCitySpeedKMpH(SpeedKMpH const & inCity, SpeedKMpH const & outCity) noexcept
: m_inCity(inCity)
, m_outCity(outCity)
{}
bool operator==(InOutCitySpeedKMpH const & rhs) const
{
return m_inCity == rhs.m_inCity && m_outCity == rhs.m_outCity;
}
SpeedKMpH const & GetSpeed(bool isCity) const { return isCity ? m_inCity : m_outCity; }
bool IsValid() const { return m_inCity.IsValid() && m_outCity.IsValid(); }
SpeedKMpH m_inCity;
SpeedKMpH m_outCity;
};
struct InOutCityFactor
{
constexpr explicit InOutCityFactor(SpeedFactor const & factor) noexcept : m_inCity(factor), m_outCity(factor) {}
constexpr InOutCityFactor(SpeedFactor const & inCity, SpeedFactor const & outCity) noexcept
: m_inCity(inCity)
, m_outCity(outCity)
{}
bool operator==(InOutCityFactor const & rhs) const { return m_inCity == rhs.m_inCity && m_outCity == rhs.m_outCity; }
SpeedFactor const & GetFactor(bool isCity) const { return isCity ? m_inCity : m_outCity; }
bool IsValid() const { return m_inCity.IsValid() && m_outCity.IsValid(); }
SpeedFactor m_inCity;
SpeedFactor m_outCity;
};
struct HighwayBasedInfo
{
HighwayBasedInfo(HighwayBasedSpeeds const & speeds, HighwayBasedFactors const & factors)
: m_speeds(speeds)
, m_factors(factors)
{}
HighwayBasedSpeeds m_speeds;
HighwayBasedFactors const & m_factors;
};
class VehicleModelInterface
{
public:
virtual ~VehicleModelInterface() = default;
using FeatureTypes = feature::TypesHolder;
/// @return Allowed weight and ETA speed in KMpH.
/// 0 means that it's forbidden to move on this feature or it's not a road at all.
/// Weight and ETA should be less than max model speed's values respectively.
/// @param inCity is true if |f| lies in a city of town.
virtual SpeedKMpH GetSpeed(FeatureTypes const & types, SpeedParams const & speedParams) const = 0;
virtual std::optional<HighwayType> GetHighwayType(FeatureTypes const & types) const = 0;
/// @return Maximum model weight speed (km/h).
/// All speeds which the model returns must be less or equal to this speed.
/// @see EdgeEstimator::CalcHeuristic.
virtual double GetMaxWeightSpeed() const = 0;
/// @return Offroad speed in KMpH for vehicle. This speed should be used for non-feature routing
/// e.g. to connect start point to nearest feature.
virtual SpeedKMpH const & GetOffroadSpeed() const = 0;
virtual bool IsOneWay(FeatureTypes const & types) const = 0;
/// @returns true iff feature |f| can be used for routing with corresponding vehicle model.
virtual bool IsRoad(FeatureTypes const & types) const = 0;
/// @returns true iff feature |f| can be used for through passage with corresponding vehicle model.
/// e.g. in Russia roads tagged "highway = service" are not allowed for through passage;
/// however, road with this tag can be be used if it is close enough to the start or destination
/// point of the route.
/// Roads with additional types e.g. "path = ferry", "vehicle_type = yes" considered as allowed
/// to pass through.
virtual bool IsPassThroughAllowed(FeatureTypes const & types) const = 0;
};
class VehicleModelFactoryInterface
{
public:
virtual ~VehicleModelFactoryInterface() = default;
/// @return Default vehicle model which corresponds for all countrines,
/// but it may be non optimal for some countries
virtual std::shared_ptr<VehicleModelInterface> GetVehicleModel() const = 0;
/// @return The most optimal vehicle model for specified country
virtual std::shared_ptr<VehicleModelInterface> GetVehicleModelForCountry(std::string const & country) const = 0;
};
class VehicleModel : public VehicleModelInterface
{
public:
struct FeatureTypeLimits
{
HighwayType m_type;
bool m_isPassThroughAllowed; // pass through this road type is allowed
};
struct FeatureTypeSurface
{
std::vector<std::string> m_type; // road surface type 'psurface=*'
SpeedFactor m_factor;
};
struct AdditionalRoad
{
std::vector<std::string> m_type;
InOutCitySpeedKMpH m_speed;
};
using AdditionalRoadsList = std::initializer_list<AdditionalRoad>;
using LimitsInitList = std::vector<FeatureTypeLimits>;
using SurfaceInitList = std::initializer_list<FeatureTypeSurface>;
VehicleModel(Classificator const & classif, LimitsInitList const & featureTypeLimits,
SurfaceInitList const & featureTypeSurface, HighwayBasedInfo const & info);
/// @name VehicleModelInterface overrides.
/// @{
std::optional<HighwayType> GetHighwayType(FeatureTypes const & types) const override;
double GetMaxWeightSpeed() const override;
/// \returns true if |types| is a oneway feature.
/// \note According to OSM, tag "oneway" could have value "-1". That means it's a oneway feature
/// with reversed geometry. In that case at map generation the geometry of such features
/// is reversed (the order of points is changed) so in vehicle model all oneway feature
/// may be considered as features with forward geometry.
bool IsOneWay(FeatureTypes const & types) const override;
bool IsRoad(FeatureTypes const & types) const override;
bool IsPassThroughAllowed(FeatureTypes const & types) const override;
/// @}
// Made public to have simple access from unit tests.
public:
/// @returns true if |m_highwayTypes| or |m_addRoadTypes| contains |type| and false otherwise.
bool IsRoadType(uint32_t type) const;
template <class TList>
bool HasRoadType(TList const & types) const
{
for (uint32_t t : types)
if (IsRoadType(t))
return true;
return false;
}
bool EqualsForTests(VehicleModel const & rhs) const
{
return (m_roadTypes == rhs.m_roadTypes) && (m_addRoadTypes == rhs.m_addRoadTypes) &&
(m_onewayType == rhs.m_onewayType);
}
protected:
uint32_t m_yesType, m_noType;
bool IsRoadImpl(FeatureTypes const & types) const;
SpeedKMpH GetTypeSpeedImpl(FeatureTypes const & types, SpeedParams const & params, bool isCar) const;
void AddAdditionalRoadTypes(Classificator const & classif, AdditionalRoadsList const & roads);
uint32_t PrepareToMatchType(uint32_t type) const;
/// \brief Maximum within all the speed limits set in a model (car model, bicycle model and so on).
/// Do not mix with maxspeed value tag, which defines maximum legal speed on a feature.
SpeedKMpH m_maxModelSpeed;
private:
std::optional<HighwayType> GetHighwayType(uint32_t type) const;
void GetSurfaceFactor(uint32_t type, SpeedFactor & factor) const;
void GetAdditionalRoadSpeed(uint32_t type, bool isCityRoad, std::optional<SpeedKMpH> & speed) const;
// HW type -> speed and factor.
HighwayBasedInfo m_highwayBasedInfo;
uint32_t m_onewayType;
// HW type -> allow pass through.
base::SmallMap<uint32_t, bool> m_roadTypes;
// Mapping surface types psurface={paved_good/paved_bad/unpaved_good/unpaved_bad} to surface speed factors.
base::SmallMapBase<uint32_t, SpeedFactor> m_surfaceFactors;
SpeedFactor m_minSurfaceFactorForMaxspeed;
/// @todo Do we really need a separate map here or can merge with the m_roadTypes map?
base::SmallMapBase<uint32_t, InOutCitySpeedKMpH> m_addRoadTypes;
};
class VehicleModelFactory : public VehicleModelFactoryInterface
{
public:
// Callback which takes territory name (mwm graph node name) and returns its parent
// territory name. Used to properly find local restrictions in GetVehicleModelForCountry.
// For territories which do not have parent (countries) or have multiple parents
// (disputed territories) it should return empty name.
// For example "Munich" -> "Bavaria"; "Bavaria" -> "Germany"; "Germany" -> ""
using CountryParentNameGetterFn = std::function<std::string(std::string const &)>;
std::shared_ptr<VehicleModelInterface> GetVehicleModel() const override;
std::shared_ptr<VehicleModelInterface> GetVehicleModelForCountry(std::string const & country) const override;
protected:
explicit VehicleModelFactory(CountryParentNameGetterFn const & countryParentNameGetterFn);
std::string GetParent(std::string const & country) const;
std::unordered_map<std::string, std::shared_ptr<VehicleModelInterface>> m_models;
CountryParentNameGetterFn m_countryParentNameGetterFn;
};
HighwayBasedFactors GetOneFactorsForBicycleAndPedestrianModel();
std::string DebugPrint(SpeedKMpH const & speed);
std::string DebugPrint(SpeedFactor const & speedFactor);
std::string DebugPrint(InOutCitySpeedKMpH const & speed);
std::string DebugPrint(InOutCityFactor const & speedFactor);
std::string DebugPrint(HighwayType type);
void FromString(std::string_view s, HighwayType & highwayType);
} // namespace routing