diff --git a/routing/CMakeLists.txt b/routing/CMakeLists.txt index 4c81bb0359e..20870342503 100644 --- a/routing/CMakeLists.txt +++ b/routing/CMakeLists.txt @@ -87,6 +87,8 @@ set( maxspeeds.hpp maxspeeds_serialization.cpp maxspeeds_serialization.hpp + mwm_hierarchy_handler.cpp + mwm_hierarchy_handler.hpp nearest_edge_finder.cpp nearest_edge_finder.hpp online_absent_fetcher.cpp diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp index b8e0d6f9006..4a9e07ae1ac 100644 --- a/routing/index_graph_starter.cpp +++ b/routing/index_graph_starter.cpp @@ -62,8 +62,7 @@ size_t IndexGraphStarter::GetRouteNumPoints(vector const & segments) } IndexGraphStarter::IndexGraphStarter(FakeEnding const & startEnding, - FakeEnding const & finishEnding, - uint32_t fakeNumerationStart, + FakeEnding const & finishEnding, uint32_t fakeNumerationStart, bool strictForward, WorldGraph & graph) : m_graph(graph) { diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 72b3c541b2a..90610f5c7a0 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -11,6 +11,7 @@ #include "routing/junction_visitor.hpp" #include "routing/leaps_graph.hpp" #include "routing/leaps_postprocessor.hpp" +#include "routing/mwm_hierarchy_handler.hpp" #include "routing/pedestrian_directions.hpp" #include "routing/route.hpp" #include "routing/routing_exceptions.hpp" @@ -294,6 +295,7 @@ IndexRouter::IndexRouter(VehicleType vehicleType, bool loadAltitudes, m_vehicleType, CalcMaxSpeed(*m_numMwmIds, *m_vehicleModelFactory, m_vehicleType), CalcOffroadSpeed(*m_vehicleModelFactory), m_trafficStash)) , m_directionsEngine(CreateDirectionsEngine(m_vehicleType, m_numMwmIds, m_dataSource)) + , m_countryParentNameGetterFn(countryParentNameGetterFn) { CHECK(!m_name.empty(), ()); CHECK(m_numMwmIds, ()); @@ -561,6 +563,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, for (auto const & checkpoint : checkpoints.GetPoints()) { string const countryName = m_countryFileFn(checkpoint); + if (countryName.empty()) { LOG(LWARNING, ("For point", mercator::ToLatLon(checkpoint), @@ -841,7 +844,7 @@ RouterResultCode IndexRouter::CalculateSubrouteLeapsOnlyMode( RouterDelegate const & delegate, shared_ptr const & progress, vector & subroute) { - LeapsGraph leapsGraph(starter); + LeapsGraph leapsGraph(starter, MwmHierarchyHandler(m_numMwmIds, m_countryParentNameGetterFn)); using Vertex = LeapsGraph::Vertex; using Edge = LeapsGraph::Edge; @@ -1001,8 +1004,9 @@ unique_ptr IndexRouter::MakeWorldGraph() if (m_vehicleType != VehicleType::Transit) { - auto graph = make_unique(move(crossMwmGraph), move(indexGraphLoader), - m_estimator); + auto graph = make_unique( + move(crossMwmGraph), move(indexGraphLoader), m_estimator, + MwmHierarchyHandler(m_numMwmIds, m_countryParentNameGetterFn)); graph->SetRoutingOptions(routingOptions); return graph; } diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 87d35362eed..a4ac4c995e8 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -251,5 +251,7 @@ class IndexRouter : public IRouter // If a ckeckpoint is near to the guide track we need to build route through this track. GuidesConnections m_guides; + + CountryParentNameGetterFn m_countryParentNameGetterFn; }; } // namespace routing diff --git a/routing/leaps_graph.cpp b/routing/leaps_graph.cpp index 7159318be28..6a295865f52 100644 --- a/routing/leaps_graph.cpp +++ b/routing/leaps_graph.cpp @@ -1,14 +1,14 @@ #include "routing/leaps_graph.hpp" -#include "routing_common/num_mwm_id.hpp" - #include "base/assert.hpp" #include +#include namespace routing { -LeapsGraph::LeapsGraph(IndexGraphStarter & starter) : m_starter(starter) +LeapsGraph::LeapsGraph(IndexGraphStarter & starter, MwmHierarchyHandler && hierarchyHandler) + : m_starter(starter), m_hierarchyHandler(std::move(hierarchyHandler)) { m_startPoint = m_starter.GetPoint(m_starter.GetStartSegment(), true /* front */); m_finishPoint = m_starter.GetPoint(m_starter.GetFinishSegment(), true /* front */); @@ -59,12 +59,15 @@ void LeapsGraph::GetEdgesList(Segment const & segment, bool isOutgoing, return; auto & crossMwmGraph = m_starter.GetGraph().GetCrossMwmGraph(); + if (crossMwmGraph.IsTransition(segment, isOutgoing)) { + auto const segMwmId = segment.GetMwmId(); + std::vector twins; m_starter.GetGraph().GetTwinsInner(segment, isOutgoing, twins); for (auto const & twin : twins) - edges.emplace_back(twin, RouteWeight(0.0)); + edges.emplace_back(twin, m_hierarchyHandler.GetCrossBorderPenalty(segMwmId, twin.GetMwmId())); return; } diff --git a/routing/leaps_graph.hpp b/routing/leaps_graph.hpp index 6b8bc49b4f5..1f55391f5a4 100644 --- a/routing/leaps_graph.hpp +++ b/routing/leaps_graph.hpp @@ -2,8 +2,8 @@ #include "routing/base/astar_graph.hpp" #include "routing/base/astar_vertex_data.hpp" - #include "routing/index_graph_starter.hpp" +#include "routing/mwm_hierarchy_handler.hpp" #include "routing/route_weight.hpp" #include "routing/segment.hpp" @@ -16,7 +16,7 @@ namespace routing class LeapsGraph : public AStarGraph { public: - explicit LeapsGraph(IndexGraphStarter & starter); + explicit LeapsGraph(IndexGraphStarter & starter, MwmHierarchyHandler && hierarchyHandler); // AStarGraph overrides: // @{ @@ -45,5 +45,7 @@ class LeapsGraph : public AStarGraph Segment m_finishSegment; IndexGraphStarter & m_starter; + + MwmHierarchyHandler m_hierarchyHandler; }; } // namespace routing diff --git a/routing/mwm_hierarchy_handler.cpp b/routing/mwm_hierarchy_handler.cpp new file mode 100644 index 00000000000..eef6cc77195 --- /dev/null +++ b/routing/mwm_hierarchy_handler.cpp @@ -0,0 +1,103 @@ +#include "routing/mwm_hierarchy_handler.hpp" + +#include "base/logging.hpp" + +#include + +namespace routing +{ +// Time penalty in seconds for crossing the country border. +// We add no penalty for crossing borders of the countries that have officially abolished +// passport and other types of border control at their mutual borders. +inline size_t constexpr kCrossCountryPenaltyS = 60 * 60 * 2; + +// The Eurasian Economic Union (EAEU) list of countries. +std::unordered_set kEAEU{"Armenia", "Belarus", "Kazakhstan", "Kyrgyzstan", + "Russian Federation"}; + +// The Schengen Area list of countries. +std::unordered_set kSchengenArea{ + "Austria", "Belgium", "Czech Republic", "Denmark", "Estonia", "Finland", + "France", "Germany", "Greece", "Hungary", "Iceland", "Italy", + "Latvia", "Liechtenstein", "Lithuania", "Luxembourg", "Malta", "Netherlands", + "Norway", "Poland", "Portugal", "Slovakia", "Slovenia", "Spain", + "Sweden", "Switzerland"}; + +std::string GetCountryByMwmName(std::string const & mwmName, CountryParentNameGetterFn fn) +{ + static std::string const CountriesRoot = "Countries"; + std::string country; + + if (!fn) + return country; + + std::string parent = mwmName; + + while (parent != CountriesRoot) + { + country = parent; + if (country.empty()) + break; + + parent = fn(parent); + } + + return country; +} + +std::string GetCountryByMwmId(NumMwmId mwmId, CountryParentNameGetterFn fn, + std::shared_ptr const & numMwmIds) +{ + if (numMwmIds != nullptr && numMwmIds->ContainsFileForMwm(mwmId)) + { + std::string const mwmName = numMwmIds->GetFile(mwmId).GetName(); + return GetCountryByMwmName(mwmName, fn); + } + return {}; +} + +MwmHierarchyHandler::MwmHierarchyHandler(std::shared_ptr numMwmIds, + CountryParentNameGetterFn countryParentNameGetterFn) + : m_numMwmIds(numMwmIds), m_countryParentNameGetterFn(countryParentNameGetterFn) +{ +} + +std::string const & MwmHierarchyHandler::GetParentCountryByMwmId(NumMwmId mwmId) +{ + auto [it, inserted] = m_mwmCountriesCache.emplace(mwmId, ""); + if (inserted) + it->second = GetCountryByMwmId(mwmId, m_countryParentNameGetterFn, m_numMwmIds); + + return it->second; +} + +bool MwmHierarchyHandler::HasCrossBorderPenalty(NumMwmId mwmId1, NumMwmId mwmId2) +{ + if (mwmId1 == mwmId2) + return false; + + std::string const country1 = GetParentCountryByMwmId(mwmId1); + std::string const country2 = GetParentCountryByMwmId(mwmId2); + + // If one of the mwms belongs to the territorial dispute we add penalty for crossing its borders. + if (country1.empty() || country2.empty()) + return true; + + if (country1 == country2) + return false; + + if (kEAEU.find(country1) != kEAEU.end() && kEAEU.find(country2) != kEAEU.end()) + return false; + + return kSchengenArea.find(country1) == kSchengenArea.end() || + kSchengenArea.find(country2) == kSchengenArea.end(); +} + +RouteWeight MwmHierarchyHandler::GetCrossBorderPenalty(NumMwmId mwmId1, NumMwmId mwmId2) +{ + if (HasCrossBorderPenalty(mwmId1, mwmId2)) + return RouteWeight(kCrossCountryPenaltyS); + + return RouteWeight(0.0); +} +} // namespace routing diff --git a/routing/mwm_hierarchy_handler.hpp b/routing/mwm_hierarchy_handler.hpp new file mode 100644 index 00000000000..c647c102427 --- /dev/null +++ b/routing/mwm_hierarchy_handler.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "routing/route_weight.hpp" +#include "routing/router.hpp" + +#include "routing_common/num_mwm_id.hpp" + +#include +#include +#include + +namespace routing +{ +using MwmToCountry = std::unordered_map; + +// Class for calculating penalty while crossing country borders. Also finds parent country for mwm. +class MwmHierarchyHandler +{ +public: + MwmHierarchyHandler(std::shared_ptr numMwmIds, + CountryParentNameGetterFn countryParentNameGetterFn); + + RouteWeight GetCrossBorderPenalty(NumMwmId mwmId1, NumMwmId mwmId2); + +private: + bool HasCrossBorderPenalty(NumMwmId mwmId1, NumMwmId mwmId2); + + // Returns parent country name for |mwmId|. + std::string const & GetParentCountryByMwmId(NumMwmId mwmId); + + std::shared_ptr m_numMwmIds = nullptr; + CountryParentNameGetterFn m_countryParentNameGetterFn = nullptr; + MwmToCountry m_mwmCountriesCache; +}; +} // namespace routing diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index c448faa6269..3bf088649b6 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -420,7 +420,7 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator); + estimator, MwmHierarchyHandler(nullptr, nullptr)); } unique_ptr BuildIndexGraph(unique_ptr geometryLoader, @@ -442,7 +442,7 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator); + estimator, MwmHierarchyHandler(nullptr, nullptr)); } unique_ptr BuildWorldGraph(unique_ptr geometryLoader, diff --git a/routing/segment.hpp b/routing/segment.hpp index c827394bbf5..898d2cecd44 100644 --- a/routing/segment.hpp +++ b/routing/segment.hpp @@ -72,7 +72,7 @@ class SegmentEdge final Segment const & GetTarget() const { return m_target; } RouteWeight const & GetWeight() const { return m_weight; } - + RouteWeight & GetWeight() { return m_weight; } bool operator==(SegmentEdge const & edge) const; bool operator<(SegmentEdge const & edge) const; diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index 2f9b67fe9bb..eb40b9fccc5 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -21,8 +21,12 @@ SingleVehicleWorldGraph::AStarParents::kEmpty = {}; SingleVehicleWorldGraph::SingleVehicleWorldGraph(unique_ptr crossMwmGraph, unique_ptr loader, - shared_ptr estimator) - : m_crossMwmGraph(move(crossMwmGraph)), m_loader(move(loader)), m_estimator(move(estimator)) + shared_ptr estimator, + MwmHierarchyHandler && hierarchyHandler) + : m_crossMwmGraph(move(crossMwmGraph)) + , m_loader(move(loader)) + , m_estimator(move(estimator)) + , m_hierarchyHandler(std::move(hierarchyHandler)) { CHECK(m_loader, ()); CHECK(m_estimator, ()); @@ -35,13 +39,19 @@ void SingleVehicleWorldGraph::CheckAndProcessTransitFeatures(Segment const & par { bool opposite = !isOutgoing; vector newCrossMwmEdges; + + NumMwmId const mwmId = parent.GetMwmId(); + for (size_t i = 0; i < jointEdges.size(); ++i) { JointSegment const & target = jointEdges[i].GetTarget(); - if (!m_crossMwmGraph->IsFeatureTransit(target.GetMwmId(), target.GetFeatureId())) + + NumMwmId const edgeMwmId = target.GetMwmId(); + + if (!m_crossMwmGraph->IsFeatureTransit(edgeMwmId, target.GetFeatureId())) continue; - auto & currentIndexGraph = GetIndexGraph(parent.GetMwmId()); + auto & currentIndexGraph = GetIndexGraph(mwmId); vector twins; m_crossMwmGraph->GetTwinFeature(target.GetSegment(true /* start */), isOutgoing, twins); @@ -66,6 +76,8 @@ void SingleVehicleWorldGraph::CheckAndProcessTransitFeatures(Segment const & par newCrossMwmEdges.emplace_back(*edge); newCrossMwmEdges.back().GetTarget().SetFeatureId(twinFeatureId); newCrossMwmEdges.back().GetTarget().SetMwmId(twinMwmId); + newCrossMwmEdges.back().GetWeight() += + m_hierarchyHandler.GetCrossBorderPenalty(mwmId, twinMwmId); parentWeights.emplace_back(parentWeights[i]); } diff --git a/routing/single_vehicle_world_graph.hpp b/routing/single_vehicle_world_graph.hpp index 09776d91a33..2b4fad49ed1 100644 --- a/routing/single_vehicle_world_graph.hpp +++ b/routing/single_vehicle_world_graph.hpp @@ -6,6 +6,7 @@ #include "routing/index_graph.hpp" #include "routing/index_graph_loader.hpp" #include "routing/joint_segment.hpp" +#include "routing/mwm_hierarchy_handler.hpp" #include "routing/road_graph.hpp" #include "routing/route.hpp" #include "routing/segment.hpp" @@ -29,7 +30,8 @@ class SingleVehicleWorldGraph final : public WorldGraph public: SingleVehicleWorldGraph(std::unique_ptr crossMwmGraph, std::unique_ptr loader, - std::shared_ptr estimator); + std::shared_ptr estimator, + MwmHierarchyHandler && hierarchyHandler); // WorldGraph overrides: // @{ @@ -141,5 +143,7 @@ class SingleVehicleWorldGraph final : public WorldGraph AStarParents m_parentsForSegments; AStarParents m_parentsForJoints; + + MwmHierarchyHandler m_hierarchyHandler; }; } // namespace routing diff --git a/routing/world_graph.hpp b/routing/world_graph.hpp index fa04c1e34a5..ed1f827efb0 100644 --- a/routing/world_graph.hpp +++ b/routing/world_graph.hpp @@ -117,6 +117,7 @@ class WorldGraph virtual CrossMwmGraph & GetCrossMwmGraph(); virtual void GetTwinsInner(Segment const & segment, bool isOutgoing, std::vector & twins) = 0; + protected: void GetTwins(Segment const & segment, bool isOutgoing, bool useRoutingOptions, std::vector & edges); diff --git a/routing_common/num_mwm_id.hpp b/routing_common/num_mwm_id.hpp index 305da9a20f3..7a975a5d491 100644 --- a/routing_common/num_mwm_id.hpp +++ b/routing_common/num_mwm_id.hpp @@ -33,6 +33,12 @@ class NumMwmIds final return m_fileToId.find(file) != m_fileToId.cend(); } + bool ContainsFileForMwm(NumMwmId mwmId) const + { + size_t const index = base::asserted_cast(mwmId); + return index < m_idToFile.size(); + } + platform::CountryFile const & GetFile(NumMwmId mwmId) const { size_t const index = base::asserted_cast(mwmId);