diff options
25 files changed, 721 insertions, 216 deletions
diff --git a/android/jni/Android.mk b/android/jni/Android.mk index f7fa5083be..f51beee1b2 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -25,7 +25,7 @@ define add_prebuild_static_lib include $(PREBUILT_STATIC_LIBRARY) endef -prebuild_static_libs := map tracking traffic drape_frontend routing search storage indexer drape platform editor partners_api geometry coding base opening_hours +prebuild_static_libs := map tracking routing traffic drape_frontend search storage indexer drape platform editor partners_api geometry coding base opening_hours prebuild_static_libs += pugixml oauthcpp expat freetype fribidi minizip jansson protobuf osrm stats_client succinct $(foreach item,$(prebuild_static_libs),$(eval $(call add_prebuild_static_lib,$(item)))) diff --git a/map/framework.cpp b/map/framework.cpp index 9fcc720e9c..4d9a928ef9 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -356,7 +356,9 @@ Framework::Framework() , m_isRenderingEnabled(true) , m_trackingReporter(platform::CreateSocket(), TRACKING_REALTIME_HOST, TRACKING_REALTIME_PORT, tracking::Reporter::kPushDelayMs) - , m_trafficManager(bind(&Framework::GetMwmsByRect, this, _1), kMaxTrafficCacheSizeBytes) + , m_trafficManager(bind(&Framework::GetMwmsByRect, this, _1), kMaxTrafficCacheSizeBytes, + // Note. |m_routingSession| should be declared before |m_trafficManager|. + m_routingSession) , m_displacementModeManager([this](bool show) { int const mode = show ? dp::displacement::kHotelMode : dp::displacement::kDefaultMode; CallDrapeFunction(bind(&df::DrapeEngine::SetDisplacementMode, _1, mode)); @@ -2493,7 +2495,7 @@ void Framework::SetRouterImpl(RouterType type) router.reset( new CarRouter(m_model.GetIndex(), countryFileGetter, - SingleMwmRouter::CreateCarRouter(m_model.GetIndex()))); + SingleMwmRouter::CreateCarRouter(m_model.GetIndex(), m_routingSession))); fetcher.reset(new OnlineAbsentCountriesFetcher(countryFileGetter, localFileChecker)); m_routingSession.SetRoutingSettings(routing::GetCarRoutingSettings()); } diff --git a/map/map_tests/map_tests.pro b/map/map_tests/map_tests.pro index 78b428a9d6..59a471514e 100644 --- a/map/map_tests/map_tests.pro +++ b/map/map_tests/map_tests.pro @@ -6,7 +6,7 @@ CONFIG -= app_bundle TEMPLATE = app ROOT_DIR = ../.. -DEPENDENCIES = map traffic traffic drape_frontend routing search storage tracking drape indexer partners_api platform editor geometry coding base \ +DEPENDENCIES = map drape_frontend routing traffic search storage tracking drape indexer partners_api platform editor geometry coding base \ freetype fribidi expat protobuf jansson osrm stats_client minizip succinct pugixml stats_client DEPENDENCIES *= opening_hours diff --git a/map/traffic_manager.cpp b/map/traffic_manager.cpp index be2c9b4e3e..6d3110557f 100644 --- a/map/traffic_manager.cpp +++ b/map/traffic_manager.cpp @@ -37,8 +37,10 @@ TrafficManager::CacheEntry::CacheEntry(time_point<steady_clock> const & requestT , m_lastAvailability(traffic::TrafficInfo::Availability::Unknown) {} -TrafficManager::TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes) +TrafficManager::TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes, + traffic::TrafficObserver & observer) : m_getMwmsByRectFn(getMwmsByRectFn) + , m_observer(observer) , m_currentDataVersion(0) , m_state(TrafficState::Disabled) , m_maxCacheSizeBytes(maxCacheSizeBytes) @@ -92,6 +94,8 @@ void TrafficManager::SetEnabled(bool enabled) if (m_currentPosition.second) UpdateMyPosition(m_currentPosition.first); } + + m_observer.OnTrafficEnabled(enabled); } void TrafficManager::Clear() @@ -183,12 +187,12 @@ void TrafficManager::ThreadRoutine() if (info.ReceiveTrafficData()) { - OnTrafficDataResponse(info); + OnTrafficDataResponse(move(info)); } else { LOG(LWARNING, ("Traffic request failed. Mwm =", mwm)); - OnTrafficRequestFailed(info); + OnTrafficRequestFailed(move(info)); } } mwms.clear(); @@ -267,7 +271,7 @@ void TrafficManager::RequestTrafficData(MwmSet::MwmId const & mwmId) m_condition.notify_one(); } -void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo const & info) +void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo && info) { lock_guard<mutex> lock(m_mutex); @@ -300,7 +304,7 @@ void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo const & info) UpdateState(); } -void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo const & info) +void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo && info) { lock_guard<mutex> lock(m_mutex); @@ -315,17 +319,21 @@ void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo const & info) // Update cache. size_t constexpr kElementSize = sizeof(traffic::TrafficInfo::RoadSegmentId) + sizeof(traffic::SpeedGroup); + size_t const dataSize = info.GetColoring().size() * kElementSize; m_currentCacheSizeBytes += (dataSize - it->second.m_dataSize); it->second.m_dataSize = dataSize; CheckCacheSize(); - // Update traffic colors. + // Update traffic colors for drape. df::TrafficSegmentsColoring coloring; coloring[info.GetMwmId()] = info.GetColoring(); m_drapeEngine->UpdateTraffic(coloring); UpdateState(); + + // Update traffic colors for routing. + m_observer.OnTrafficInfoAdded(move(info)); } void TrafficManager::CheckCacheSize() @@ -344,8 +352,10 @@ void TrafficManager::CheckCacheSize() auto const it = m_mwmCache.find(mwmId); if (it->second.m_isLoaded) { + ASSERT_GREATER_OR_EQUAL(m_currentCacheSizeBytes, it->second.m_dataSize, ()); m_currentCacheSizeBytes -= it->second.m_dataSize; m_drapeEngine->ClearTrafficCache(mwmId); + m_observer.OnTrafficInfoRemoved(mwmId); } m_mwmCache.erase(it); ++itSeen; diff --git a/map/traffic_manager.hpp b/map/traffic_manager.hpp index b14cac579a..d2f208a356 100644 --- a/map/traffic_manager.hpp +++ b/map/traffic_manager.hpp @@ -57,7 +57,8 @@ public: using TrafficStateChangedFn = function<void(TrafficState)>; using GetMwmsByRectFn = function<vector<MwmSet::MwmId>(m2::RectD const &)>; - TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes); + TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes, + traffic::TrafficObserver & observer); ~TrafficManager(); void SetStateListener(TrafficStateChangedFn const & onStateChangedFn); @@ -76,8 +77,8 @@ private: void ThreadRoutine(); bool WaitForRequest(vector<MwmSet::MwmId> & mwms); - void OnTrafficDataResponse(traffic::TrafficInfo const & info); - void OnTrafficRequestFailed(traffic::TrafficInfo const & info); + void OnTrafficDataResponse(traffic::TrafficInfo && info); + void OnTrafficRequestFailed(traffic::TrafficInfo && info); private: // This is a group of methods that haven't their own synchronization inside. @@ -94,6 +95,7 @@ private: bool IsEnabled() const; GetMwmsByRectFn m_getMwmsByRectFn; + traffic::TrafficObserver & m_observer; ref_ptr<df::DrapeEngine> m_drapeEngine; atomic<int64_t> m_currentDataVersion; diff --git a/routing/edge_estimator.cpp b/routing/edge_estimator.cpp index 52cb10905a..86c27e8415 100644 --- a/routing/edge_estimator.cpp +++ b/routing/edge_estimator.cpp @@ -1,7 +1,22 @@ #include "routing/edge_estimator.hpp" +#include "traffic/traffic_info.hpp" + #include "std/algorithm.hpp" +using namespace traffic; + +namespace +{ +double CalcTrafficFactor(SpeedGroup speedGroup) +{ + double const percentage = + 0.01 * static_cast<double>(kSpeedGroupThresholdPercentage[static_cast<size_t>(speedGroup)]); + CHECK_GREATER(percentage, 0.0, ("Speed group:", speedGroup)); + return 1.0 / percentage; +} +} // namespace + namespace routing { double constexpr kKMPH2MPS = 1000.0 / (60 * 60); @@ -17,24 +32,39 @@ inline double TimeBetweenSec(m2::PointD const & from, m2::PointD const & to, dou class CarEdgeEstimator : public EdgeEstimator { public: - CarEdgeEstimator(IVehicleModel const & vehicleModel); + CarEdgeEstimator(IVehicleModel const & vehicleModel, traffic::TrafficCache const & trafficCache); // EdgeEstimator overrides: - double CalcEdgesWeight(RoadGeometry const & road, uint32_t pointFrom, + void Start(MwmSet::MwmId const & mwmId) override; + void Finish() override; + double CalcEdgesWeight(uint32_t featureId, RoadGeometry const & road, uint32_t pointFrom, uint32_t pointTo) const override; double CalcHeuristic(m2::PointD const & from, m2::PointD const & to) const override; private: + TrafficCache const & m_trafficCache; + shared_ptr<traffic::TrafficInfo> m_trafficInfo; double const m_maxSpeedMPS; }; -CarEdgeEstimator::CarEdgeEstimator(IVehicleModel const & vehicleModel) - : m_maxSpeedMPS(vehicleModel.GetMaxSpeed() * kKMPH2MPS) +CarEdgeEstimator::CarEdgeEstimator(IVehicleModel const & vehicleModel, + traffic::TrafficCache const & trafficCache) + : m_trafficCache(trafficCache), m_maxSpeedMPS(vehicleModel.GetMaxSpeed() * kKMPH2MPS) +{ +} + +void CarEdgeEstimator::Start(MwmSet::MwmId const & mwmId) +{ + m_trafficInfo = m_trafficCache.GetTrafficInfo(mwmId); +} + +void CarEdgeEstimator::Finish() { + m_trafficInfo.reset(); } -double CarEdgeEstimator::CalcEdgesWeight(RoadGeometry const & road, uint32_t pointFrom, - uint32_t pointTo) const +double CarEdgeEstimator::CalcEdgesWeight(uint32_t featureId, RoadGeometry const & road, + uint32_t pointFrom, uint32_t pointTo) const { uint32_t const start = min(pointFrom, pointTo); uint32_t const finish = max(pointFrom, pointTo); @@ -42,8 +72,20 @@ double CarEdgeEstimator::CalcEdgesWeight(RoadGeometry const & road, uint32_t poi double result = 0.0; double const speedMPS = road.GetSpeed() * kKMPH2MPS; + auto const dir = pointFrom < pointTo ? TrafficInfo::RoadSegmentId::kForwardDirection + : TrafficInfo::RoadSegmentId::kReverseDirection; for (uint32_t i = start; i < finish; ++i) - result += TimeBetweenSec(road.GetPoint(i), road.GetPoint(i + 1), speedMPS); + { + double edgeWeight = TimeBetweenSec(road.GetPoint(i), road.GetPoint(i + 1), speedMPS); + if (m_trafficInfo) + { + SpeedGroup const speedGroup = + m_trafficInfo->GetSpeedGroup(TrafficInfo::RoadSegmentId(featureId, i, dir)); + ASSERT_LESS(speedGroup, SpeedGroup::Count, ()); + edgeWeight *= CalcTrafficFactor(speedGroup); + } + result += edgeWeight; + } return result; } @@ -57,8 +99,9 @@ double CarEdgeEstimator::CalcHeuristic(m2::PointD const & from, m2::PointD const namespace routing { // static -shared_ptr<EdgeEstimator> EdgeEstimator::CreateForCar(IVehicleModel const & vehicleModel) +shared_ptr<EdgeEstimator> EdgeEstimator::CreateForCar(IVehicleModel const & vehicleModel, + traffic::TrafficCache const & trafficCache) { - return make_shared<CarEdgeEstimator>(vehicleModel); + return make_shared<CarEdgeEstimator>(vehicleModel, trafficCache); } } // namespace routing diff --git a/routing/edge_estimator.hpp b/routing/edge_estimator.hpp index 0a6ec2d1e0..f78f2a7c25 100644 --- a/routing/edge_estimator.hpp +++ b/routing/edge_estimator.hpp @@ -3,6 +3,10 @@ #include "routing/geometry.hpp" #include "routing/vehicle_model.hpp" +#include "traffic/traffic_cache.hpp" + +#include "indexer/mwm_set.hpp" + #include "geometry/point2d.hpp" #include "std/cstdint.hpp" @@ -15,10 +19,28 @@ class EdgeEstimator public: virtual ~EdgeEstimator() = default; - virtual double CalcEdgesWeight(RoadGeometry const & road, uint32_t pointFrom, + virtual void Start(MwmSet::MwmId const & mwmId) = 0; + virtual void Finish() = 0; + virtual double CalcEdgesWeight(uint32_t featureId, RoadGeometry const & road, uint32_t pointFrom, uint32_t pointTo) const = 0; virtual double CalcHeuristic(m2::PointD const & from, m2::PointD const & to) const = 0; - static shared_ptr<EdgeEstimator> CreateForCar(IVehicleModel const & vehicleModel); + + static shared_ptr<EdgeEstimator> CreateForCar(IVehicleModel const & vehicleModel, + traffic::TrafficCache const & trafficCache); +}; + +class EstimatorGuard final +{ +public: + EstimatorGuard(MwmSet::MwmId const & mwmId, EdgeEstimator & estimator) : m_estimator(estimator) + { + m_estimator.Start(mwmId); + } + + ~EstimatorGuard() { m_estimator.Finish(); } + +private: + EdgeEstimator & m_estimator; }; } // namespace routing diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index 39e8cd30ce..2a5fde1693 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -72,7 +72,8 @@ void IndexGraph::GetNeighboringEdge(RoadGeometry const & road, RoadPoint const & pair<Joint::Id, uint32_t> const & neighbor = m_roadIndex.FindNeighbor(rp, forward); if (neighbor.first != Joint::kInvalidId) { - double const distance = m_estimator->CalcEdgesWeight(road, rp.GetPointId(), neighbor.second); + double const distance = + m_estimator->CalcEdgesWeight(rp.GetFeatureId(), road, rp.GetPointId(), neighbor.second); edges.push_back({neighbor.first, distance}); } } @@ -87,7 +88,7 @@ void IndexGraph::GetDirectedEdge(uint32_t featureId, uint32_t pointFrom, uint32_ if (road.IsOneWay() && forward != (pointFrom < pointTo)) return; - double const distance = m_estimator->CalcEdgesWeight(road, pointFrom, pointTo); + double const distance = m_estimator->CalcEdgesWeight(featureId, road, pointFrom, pointTo); edges.emplace_back(target, distance); } } // namespace routing diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp index 5ae00512d6..158b7030b3 100644 --- a/routing/index_graph_starter.cpp +++ b/routing/index_graph_starter.cpp @@ -154,12 +154,12 @@ void IndexGraphStarter::FindPointsWithCommonFeature(Joint::Id jointId0, Joint::I // CalcEdgesWeight is very expensive. // So calculate it only if second common feature found. RoadGeometry const & prevRoad = m_graph.GetGeometry().GetRoad(result0.GetFeatureId()); - minWeight = m_graph.GetEstimator().CalcEdgesWeight(prevRoad, result0.GetPointId(), - result1.GetPointId()); + minWeight = m_graph.GetEstimator().CalcEdgesWeight( + rp0.GetFeatureId(), prevRoad, result0.GetPointId(), result1.GetPointId()); } - double const weight = - m_graph.GetEstimator().CalcEdgesWeight(road, rp0.GetPointId(), rp1.GetPointId()); + double const weight = m_graph.GetEstimator().CalcEdgesWeight( + rp0.GetFeatureId(), road, rp0.GetPointId(), rp1.GetPointId()); if (weight < minWeight) { minWeight = weight; diff --git a/routing/routing_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp index a982361670..b49bd5dae6 100644 --- a/routing/routing_integration_tests/routing_test_tools.cpp +++ b/routing/routing_integration_tests/routing_test_tools.cpp @@ -1,5 +1,7 @@ #include "routing/routing_integration_tests/routing_test_tools.hpp" +#include "routing/routing_tests/index_graph_tools.hpp" + #include "testing/testing.hpp" #include "map/feature_vec_model.hpp" @@ -29,8 +31,8 @@ #include <sys/resource.h> - using namespace routing; +using namespace routing_test; using TRouterFactory = function<unique_ptr<IRouter>(Index & index, TCountryFileFn const & countryFileFn)>; @@ -76,14 +78,15 @@ namespace integration } unique_ptr<CarRouter> CreateCarRouter(Index & index, - storage::CountryInfoGetter const & infoGetter) + storage::CountryInfoGetter const & infoGetter, + traffic::TrafficCache const & trafficCache) { auto const countryFileGetter = [&infoGetter](m2::PointD const & pt) { return infoGetter.GetRegionCountryId(pt); }; - auto carRouter = make_unique<CarRouter>( - index, countryFileGetter, SingleMwmRouter::CreateCarRouter(index)); + auto carRouter = make_unique<CarRouter>(index, countryFileGetter, + SingleMwmRouter::CreateCarRouter(index, trafficCache)); return carRouter; } @@ -106,12 +109,14 @@ namespace integration public: OsrmRouterComponents(vector<LocalCountryFile> const & localFiles) : IRouterComponents(localFiles) - , m_carRouter(CreateCarRouter(m_featuresFetcher->GetIndex(), *m_infoGetter)) + , m_carRouter(CreateCarRouter(m_featuresFetcher->GetIndex(), *m_infoGetter, m_trafficCache)) { } IRouter * GetRouter() const override { return m_carRouter.get(); } + private: + traffic::TrafficCache m_trafficCache; unique_ptr<CarRouter> m_carRouter; }; diff --git a/routing/routing_integration_tests/routing_test_tools.hpp b/routing/routing_integration_tests/routing_test_tools.hpp index cefa6b314b..6ead348743 100644 --- a/routing/routing_integration_tests/routing_test_tools.hpp +++ b/routing/routing_integration_tests/routing_test_tools.hpp @@ -50,107 +50,97 @@ shared_ptr<model::FeaturesFetcher> CreateFeaturesFetcher(vector<LocalCountryFile unique_ptr<storage::CountryInfoGetter> CreateCountryInfoGetter(); - class IRouterComponents +class IRouterComponents +{ +public: + IRouterComponents(vector<LocalCountryFile> const & localFiles) + : m_featuresFetcher(CreateFeaturesFetcher(localFiles)), m_infoGetter(CreateCountryInfoGetter()) + { + } + + virtual ~IRouterComponents() = default; + + virtual IRouter * GetRouter() const = 0; + + storage::CountryInfoGetter const & GetCountryInfoGetter() const noexcept { return *m_infoGetter; } + +protected: + shared_ptr<model::FeaturesFetcher> m_featuresFetcher; + unique_ptr<storage::CountryInfoGetter> m_infoGetter; +}; + +void TestOnlineCrosses(ms::LatLon const & startPoint, ms::LatLon const & finalPoint, + vector<string> const & expected, IRouterComponents & routerComponents); +void TestOnlineFetcher(ms::LatLon const & startPoint, ms::LatLon const & finalPoint, + vector<string> const & expected, IRouterComponents & routerComponents); + +/// Gets OSRM router components +IRouterComponents & GetOsrmComponents(); +shared_ptr<IRouterComponents> GetOsrmComponents(vector<platform::LocalCountryFile> const & localFiles); + +/// Gets pedestrian router components +IRouterComponents & GetPedestrianComponents(); +shared_ptr<IRouterComponents> GetPedestrianComponents(vector<platform::LocalCountryFile> const & localFiles); + +/// Gets bicycle router components. +IRouterComponents & GetBicycleComponents(); +shared_ptr<IRouterComponents> GetBicycleComponents(vector<platform::LocalCountryFile> const & localFiles); + +TRouteResult CalculateRoute(IRouterComponents const & routerComponents, + m2::PointD const & startPoint, m2::PointD const & startDirection, + m2::PointD const & finalPoint); + +void TestTurnCount(Route const & route, uint32_t expectedTurnCount); + +/// Testing route length. +/// It is used for checking if routes have expected(sample) length. +/// A created route will pass the test iff +/// expectedRouteMeters - expectedRouteMeters * relativeError <= route->GetDistance() +/// && expectedRouteMeters + expectedRouteMeters * relativeError >= route->GetDistance() +void TestRouteLength(Route const & route, double expectedRouteMeters, double relativeError = 0.01); +void TestRouteTime(Route const & route, double expectedRouteSeconds, double relativeError = 0.01); + +void CalculateRouteAndTestRouteLength(IRouterComponents const & routerComponents, + m2::PointD const & startPoint, + m2::PointD const & startDirection, + m2::PointD const & finalPoint, double expectedRouteMeters, + double relativeError = 0.07); + +class TestTurn +{ + friend TestTurn GetNthTurn(Route const & route, uint32_t turnNumber); + + m2::PointD const m_point; + TurnDirection const m_direction; + uint32_t const m_roundAboutExitNum; + bool const m_isValid; + + TestTurn() + : m_point({0.0, 0.0}) + , m_direction(TurnDirection::NoTurn) + , m_roundAboutExitNum(0) + , m_isValid(false) { - public: - IRouterComponents(vector<LocalCountryFile> const & localFiles) - : m_featuresFetcher(CreateFeaturesFetcher(localFiles)) - , m_infoGetter(CreateCountryInfoGetter()) - { - } - - virtual ~IRouterComponents() = default; - - virtual IRouter * GetRouter() const = 0; - - storage::CountryInfoGetter const & GetCountryInfoGetter() const noexcept - { - return *m_infoGetter; - } - - protected: - shared_ptr<model::FeaturesFetcher> m_featuresFetcher; - unique_ptr<storage::CountryInfoGetter> m_infoGetter; - }; - - void TestOnlineCrosses(ms::LatLon const & startPoint, ms::LatLon const & finalPoint, - vector<string> const & expected, IRouterComponents & routerComponents); - void TestOnlineFetcher(ms::LatLon const & startPoint, ms::LatLon const & finalPoint, - vector<string> const & expected, IRouterComponents & routerComponents); - - /// Gets OSRM router components - IRouterComponents & GetOsrmComponents(); - shared_ptr<IRouterComponents> GetOsrmComponents(vector<platform::LocalCountryFile> const & localFiles); - - /// Gets pedestrian router components - IRouterComponents & GetPedestrianComponents(); - shared_ptr<IRouterComponents> GetPedestrianComponents(vector<platform::LocalCountryFile> const & localFiles); - - /// Gets bicycle router components. - IRouterComponents & GetBicycleComponents(); - shared_ptr<IRouterComponents> GetBicycleComponents( - vector<platform::LocalCountryFile> const & localFiles); - - TRouteResult CalculateRoute(IRouterComponents const & routerComponents, - m2::PointD const & startPoint, m2::PointD const & startDirection, - m2::PointD const & finalPoint); - - void TestTurnCount(Route const & route, uint32_t expectedTurnCount); - - /// Testing route length. - /// It is used for checking if routes have expected(sample) length. - /// A created route will pass the test iff - /// expectedRouteMeters - expectedRouteMeters * relativeError <= route->GetDistance() - /// && expectedRouteMeters + expectedRouteMeters * relativeError >= route->GetDistance() - void TestRouteLength(Route const & route, double expectedRouteMeters, - double relativeError = 0.01); - void TestRouteTime(Route const & route, double expectedRouteSeconds, - double relativeError = 0.01); - - void CalculateRouteAndTestRouteLength(IRouterComponents const & routerComponents, - m2::PointD const & startPoint, - m2::PointD const & startDirection, - m2::PointD const & finalPoint, double expectedRouteMeters, - double relativeError = 0.07); - - class TestTurn + } + TestTurn(m2::PointD const & pnt, TurnDirection direction, uint32_t roundAboutExitNum) + : m_point(pnt), m_direction(direction), m_roundAboutExitNum(roundAboutExitNum), m_isValid(true) { - friend TestTurn GetNthTurn(Route const & route, uint32_t turnNumber); - - m2::PointD const m_point; - TurnDirection const m_direction; - uint32_t const m_roundAboutExitNum; - bool const m_isValid; - - TestTurn() - : m_point({0., 0.}), - m_direction(TurnDirection::NoTurn), - m_roundAboutExitNum(0), - m_isValid(false) - { - } - TestTurn(m2::PointD const & pnt, TurnDirection direction, uint32_t roundAboutExitNum) - : m_point(pnt), - m_direction(direction), - m_roundAboutExitNum(roundAboutExitNum), - m_isValid(true) - { - } - - public: - const TestTurn & TestValid() const; - const TestTurn & TestNotValid() const; - const TestTurn & TestPoint(m2::PointD const & expectedPoint, double inaccuracyMeters = 3.) const; - const TestTurn & TestDirection(TurnDirection expectedDirection) const; - const TestTurn & TestOneOfDirections(set<TurnDirection> const & expectedDirections) const; - const TestTurn & TestRoundAboutExitNum(uint32_t expectedRoundAboutExitNum) const; - }; - - /// Extracting appropriate TestTurn if any. If not TestTurn::isValid() returns false. - /// inaccuracy is set in meters. - TestTurn GetNthTurn(Route const & route, uint32_t turnNumber); - - void TestCurrentStreetName(routing::Route const & route, string const & expectedStreetName); - - void TestNextStreetName(routing::Route const & route, string const & expectedStreetName); -} + } + +public: + const TestTurn & TestValid() const; + const TestTurn & TestNotValid() const; + const TestTurn & TestPoint(m2::PointD const & expectedPoint, double inaccuracyMeters = 3.) const; + const TestTurn & TestDirection(TurnDirection expectedDirection) const; + const TestTurn & TestOneOfDirections(set<TurnDirection> const & expectedDirections) const; + const TestTurn & TestRoundAboutExitNum(uint32_t expectedRoundAboutExitNum) const; +}; + +/// Extracting appropriate TestTurn if any. If not TestTurn::isValid() returns false. +/// inaccuracy is set in meters. +TestTurn GetNthTurn(Route const & route, uint32_t turnNumber); + +void TestCurrentStreetName(routing::Route const & route, string const & expectedStreetName); + +void TestNextStreetName(routing::Route const & route, string const & expectedStreetName); +} // namespace integration diff --git a/routing/routing_session.cpp b/routing/routing_session.cpp index cca937ec48..4f3011a87d 100644 --- a/routing/routing_session.cpp +++ b/routing/routing_session.cpp @@ -10,9 +10,12 @@ #include "coding/internal/file_data.hpp" +#include "std/utility.hpp" + #include "3party/Alohalytics/src/alohalytics.h" using namespace location; +using namespace traffic; namespace { @@ -95,7 +98,7 @@ void RoutingSession::RebuildRoute(m2::PointD const & startPoint, // Use old-style callback construction, because lambda constructs buggy function on Android // (callback param isn't captured by value). m_router->CalculateRoute(startPoint, startPoint - m_lastGoodPosition, m_endPoint, - DoReadyCallback(*this, readyCallback, m_routeSessionMutex), + DoReadyCallback(*this, readyCallback, m_routingSessionMutex), progressCallback, timeoutSec); } @@ -131,7 +134,7 @@ void RoutingSession::RemoveRouteImpl() void RoutingSession::RemoveRoute() { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); RemoveRouteImpl(); @@ -141,7 +144,7 @@ void RoutingSession::Reset() { ASSERT(m_router != nullptr, ()); - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); RemoveRouteImpl(); @@ -166,7 +169,7 @@ RoutingSession::State RoutingSession::OnLocationPositionChanged(GpsInfo const & || m_state == RouteNotReady || m_state == RouteNoFollowing) return m_state; - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); ASSERT(m_route, ()); ASSERT(m_route->IsValid(), ()); @@ -258,7 +261,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const value.erase(delim); }; - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); ASSERT(m_route, ()); @@ -353,7 +356,7 @@ void RoutingSession::GenerateTurnNotifications(vector<string> & turnNotification { turnNotifications.clear(); - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); ASSERT(m_route, ()); @@ -410,7 +413,7 @@ void RoutingSession::MatchLocationToRoute(location::GpsInfo & location, if (!IsOnRoute()) return; - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); ASSERT(m_route, ()); @@ -443,7 +446,7 @@ bool RoutingSession::EnableFollowMode() void RoutingSession::SetRoutingSettings(RoutingSettings const & routingSettings) { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); m_routingSettings = routingSettings; } @@ -460,21 +463,21 @@ m2::PointD const & RoutingSession::GetUserCurrentPosition() const void RoutingSession::EnableTurnNotifications(bool enable) { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); m_turnNotificationsMgr.Enable(enable); } bool RoutingSession::AreTurnNotificationsEnabled() const { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); return m_turnNotificationsMgr.IsEnabled(); } void RoutingSession::SetTurnNotificationsUnits(measurement_utils::Units const units) { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); m_turnNotificationsMgr.SetLengthUnits(units); } @@ -482,14 +485,14 @@ void RoutingSession::SetTurnNotificationsUnits(measurement_utils::Units const un void RoutingSession::SetTurnNotificationsLocale(string const & locale) { LOG(LINFO, ("The language for turn notifications is", locale)); - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); m_turnNotificationsMgr.SetLocale(locale); } string RoutingSession::GetTurnNotificationsLocale() const { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); UNUSED_VALUE(guard); return m_turnNotificationsMgr.GetLocale(); } @@ -523,7 +526,7 @@ double RoutingSession::GetDistanceToCurrentCamM(SpeedCameraRestriction & camera, void RoutingSession::EmitCloseRoutingEvent() const { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); ASSERT(m_route, ()); if (!m_route->IsValid()) @@ -552,14 +555,14 @@ bool RoutingSession::HasRouteAltitudeImpl() const bool RoutingSession::HasRouteAltitude() const { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); return HasRouteAltitudeImpl(); } bool RoutingSession::GetRouteAltitudesAndDistancesM(vector<double> & routeSegDistanceM, feature::TAltitudes & routeAltitudesM) const { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); ASSERT(m_route, ()); if (!m_route->IsValid() || !HasRouteAltitudeImpl()) @@ -572,11 +575,40 @@ bool RoutingSession::GetRouteAltitudesAndDistancesM(vector<double> & routeSegDis shared_ptr<Route> const RoutingSession::GetRoute() const { - threads::MutexGuard guard(m_routeSessionMutex); + threads::MutexGuard guard(m_routingSessionMutex); ASSERT(m_route, ()); return m_route; } +void RoutingSession::OnTrafficEnabled(bool enable) +{ + threads::MutexGuard guard(m_routingSessionMutex); + UNUSED_VALUE(guard); + if (!enable) + Clear(); +} + +void RoutingSession::OnTrafficInfoAdded(TrafficInfo && info) +{ + threads::MutexGuard guard(m_routingSessionMutex); + UNUSED_VALUE(guard); + Set(move(info)); +} + +void RoutingSession::OnTrafficInfoRemoved(MwmSet::MwmId const & mwmId) +{ + threads::MutexGuard guard(m_routingSessionMutex); + UNUSED_VALUE(guard); + Remove(mwmId); +} + +shared_ptr<traffic::TrafficInfo> RoutingSession::GetTrafficInfo(MwmSet::MwmId const & mwmId) const +{ + threads::MutexGuard guard(m_routingSessionMutex); + UNUSED_VALUE(guard); + return TrafficCache::GetTrafficInfo(mwmId); +} + string DebugPrint(RoutingSession::State state) { switch (state) diff --git a/routing/routing_session.hpp b/routing/routing_session.hpp index b4fa257416..d22e9926b0 100644 --- a/routing/routing_session.hpp +++ b/routing/routing_session.hpp @@ -6,6 +6,9 @@ #include "routing/turns.hpp" #include "routing/turns_notification_manager.hpp" +#include "traffic/traffic_cache.hpp" +#include "traffic/traffic_info.hpp" + #include "platform/location.hpp" #include "platform/measurement_utils.hpp" @@ -16,6 +19,7 @@ #include "std/atomic.hpp" #include "std/limits.hpp" +#include "std/map.hpp" #include "std/shared_ptr.hpp" #include "std/unique_ptr.hpp" @@ -35,7 +39,7 @@ struct SpeedCameraRestriction SpeedCameraRestriction() : m_index(0), m_maxSpeedKmH(numeric_limits<uint8_t>::max()) {} }; -class RoutingSession +class RoutingSession : public traffic::TrafficObserver, public traffic::TrafficCache { friend void UnitTest_TestFollowRoutePercentTest(); @@ -150,6 +154,14 @@ public: void EmitCloseRoutingEvent() const; + // RoutingObserver overrides: + void OnTrafficEnabled(bool enable) override; + void OnTrafficInfoAdded(traffic::TrafficInfo && info) override; + void OnTrafficInfoRemoved(MwmSet::MwmId const & mwmId) override; + + // TrafficCache overrides: + shared_ptr<traffic::TrafficInfo> GetTrafficInfo(MwmSet::MwmId const & mwmId) const override; + private: struct DoReadyCallback { @@ -195,7 +207,7 @@ private: /// about camera will be sent at most once. mutable bool m_speedWarningSignal; - mutable threads::Mutex m_routeSessionMutex; + mutable threads::Mutex m_routingSessionMutex; /// Current position metrics to check for RouteNeedRebuild state. double m_lastDistance; diff --git a/routing/routing_tests/applying_traffic_test.cpp b/routing/routing_tests/applying_traffic_test.cpp new file mode 100644 index 0000000000..e19258df57 --- /dev/null +++ b/routing/routing_tests/applying_traffic_test.cpp @@ -0,0 +1,224 @@ +#include "testing/testing.hpp" + +#include "routing/car_model.hpp" +#include "routing/geometry.hpp" +#include "routing/index_graph.hpp" +#include "routing/index_graph_starter.hpp" +#include "routing/routing_session.hpp" + +#include "routing/routing_tests/index_graph_tools.hpp" + +#include "traffic/traffic_info.hpp" + +#include "indexer/classificator_loader.hpp" + +#include "geometry/point2d.hpp" + +#include "routing/base/astar_algorithm.hpp" + +#include "std/shared_ptr.hpp" +#include "std/unique_ptr.hpp" +#include "std/vector.hpp" + +namespace +{ +using namespace routing; +using namespace routing_test; +using namespace traffic; + +// @TODO(bykoianko) When PR with applying restricions is merged BuildXXGraph() +// should be moved to "routing/routing_tests/index_graph_tools.hpp" and it should +// be used by applying_traffic_test.cpp and cumulative_restriction_test.cpp. + +// Finish +// 3 * * +// ↖ ↗ +// F5 F6 +// ↖ ↗ +// 2 * * +// ↖ ↗ ↖ +// F2 F3 F4 +// ↖ ↗ ↖ +// 1 * * +// ↗ ↖ ^ +// F0 F1 F8 +// ↗ ↖ | +// 0 * *--F7--->* +// 0 1 2 3 +// Start +// Note. This graph contains of 9 one segment directed features. +unique_ptr<IndexGraph> BuildXXGraph(shared_ptr<EdgeEstimator> estimator) +{ + unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>(); + loader->AddRoad(0 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(1 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(2 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(3 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(4 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(5 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 2.0}, {1.0, 3.0}})); + loader->AddRoad(6 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 2.0}, {3.0, 3.0}})); + loader->AddRoad(7 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {3.0, 0.0}})); + loader->AddRoad(8 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {3.0, 1.0}})); + + vector<Joint> const joints = { + MakeJoint({{0 /* feature id */, 0 /* point id */}}), /* joint at point (0, 0) */ + MakeJoint({{1, 0}, {7, 0}}), /* joint at point (2, 0) */ + MakeJoint({{0, 1}, {1, 1}, {2, 0}, {3, 0}}), /* joint at point (1, 1) */ + MakeJoint({{2, 1}}), /* joint at point (0, 2) */ + MakeJoint({{3, 1}, {4, 1}, {5, 0}, {6, 0}}), /* joint at point (2, 2) */ + MakeJoint({{4, 0}, {8, 1}}), /* joint at point (3, 1) */ + MakeJoint({{5, 1}}), /* joint at point (1, 3) */ + MakeJoint({{6, 1}}), /* joint at point (3, 3) */ + MakeJoint({{7, 1}, {8, 0}}), /* joint at point (3, 0) */ + }; + + unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(move(loader), estimator); + graph->Import(joints); + return graph; +} + +class ApplyingTrafficTest +{ +public: + class TrafficCacheTest : public TrafficCache + { + public: + void SetTrafficInfo(traffic::TrafficInfo && info) { Set(move(info)); } + }; + + ApplyingTrafficTest() { classificator::Load(); } + + void SetEstimator(TrafficInfo::Coloring && coloring) + { + m_trafficCache = make_unique<TrafficCacheTest>(); + UpdateTrafficInfo(move(coloring)); + m_estimator = EdgeEstimator::CreateForCar(*make_shared<CarModelFactory>()->GetVehicleModel(), + *m_trafficCache); + } + + shared_ptr<EdgeEstimator> GetEstimator() const { return m_estimator; } + + void UpdateTrafficInfo(TrafficInfo::Coloring && coloring) + { + m_trafficCache->SetTrafficInfo(TrafficInfo::BuildForTesting(move(coloring))); + } + +private: + unique_ptr<TrafficCacheTest> m_trafficCache; + shared_ptr<EdgeEstimator> m_estimator; +}; + +// Route through XX graph without any traffic info. +UNIT_CLASS_TEST(ApplyingTrafficTest, XXGraph_EmptyTrafficColoring) +{ + SetEstimator({} /* coloring */); + + unique_ptr<IndexGraph> graph = BuildXXGraph(GetEstimator()); + IndexGraphStarter starter(*graph, RoadPoint(1, 0) /* start */, RoadPoint(6, 1) /* finish */); + vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {3, 3}}; + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom); +} + +// Route through XX graph with SpeedGroup::G0 on F3. +UNIT_CLASS_TEST(ApplyingTrafficTest, XXGraph_G0onF3) +{ + TrafficInfo::Coloring coloring = { + {{3 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G0}}; + SetEstimator(move(coloring)); + + unique_ptr<IndexGraph> graph = BuildXXGraph(GetEstimator()); + IndexGraphStarter starter(*graph, RoadPoint(1, 0) /* start */, RoadPoint(6, 1) /* finish */); + vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */}, {3, 0}, {3, 1}, {2, 2}, {3, 3}}; + EstimatorGuard guard(MwmSet::MwmId(), *GetEstimator()); + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom); +} + +// Route through XX graph with SpeedGroup::G0 in reverse direction on F3. +UNIT_CLASS_TEST(ApplyingTrafficTest, XXGraph_G0onF3ReverseDir) +{ + TrafficInfo::Coloring coloring = { + {{3 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kReverseDirection}, + SpeedGroup::G0}}; + SetEstimator(move(coloring)); + + unique_ptr<IndexGraph> graph = BuildXXGraph(GetEstimator()); + IndexGraphStarter starter(*graph, RoadPoint(1, 0) /* start */, RoadPoint(6, 1) /* finish */); + vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {3, 3}}; + EstimatorGuard guard(MwmSet::MwmId(), *GetEstimator()); + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom); +} + +// Route through XX graph SpeedGroup::G1 on F3 and F6, SpeedGroup::G4 on F8 and F4. +UNIT_CLASS_TEST(ApplyingTrafficTest, XXGraph_G0onF3andF6andG4onF8andF4) +{ + TrafficInfo::Coloring coloring = { + {{3 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G0}, + {{6 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G0}, + {{8 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G4}, + {{7 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G4}}; + SetEstimator(move(coloring)); + + unique_ptr<IndexGraph> graph = BuildXXGraph(GetEstimator()); + IndexGraphStarter starter(*graph, RoadPoint(1, 0) /* start */, RoadPoint(6, 1) /* finish */); + vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */}, {3, 0}, {3, 1}, {2, 2}, {3, 3}}; + EstimatorGuard guard(MwmSet::MwmId(), *GetEstimator()); + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom); +} + +// Route through XX graph with changing traffic. +UNIT_CLASS_TEST(ApplyingTrafficTest, XXGraph_ChangingTraffic) +{ + // No trafic at all. + SetEstimator({} /* coloring */); + unique_ptr<IndexGraph> graph = BuildXXGraph(GetEstimator()); + IndexGraphStarter starter(*graph, RoadPoint(1, 0) /* start */, RoadPoint(6, 1) /* finish */); + vector<m2::PointD> const noTrafficGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {3, 3}}; + { + EstimatorGuard guard(MwmSet::MwmId(), *GetEstimator()); + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, noTrafficGeom); + } + + // Heavy traffic (SpeedGroup::G0) on F3. + TrafficInfo::Coloring coloringHeavyF3 = { + {{3 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G0}}; + UpdateTrafficInfo(move(coloringHeavyF3)); + { + EstimatorGuard guard(MwmSet::MwmId(), *GetEstimator()); + vector<m2::PointD> const heavyF3Geom = {{2 /* x */, 0 /* y */}, {3, 0}, {3, 1}, {2, 2}, {3, 3}}; + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, heavyF3Geom); + } + + // Overloading traffic jam on F3. Middle traffic (SpeedGroup::G3) on F1, F3, F4, F7 and F8. + TrafficInfo::Coloring coloringMiddleF1F3F4F7F8 = { + {{1 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G3}, + {{3 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G3}, + {{4 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G3}, + {{7 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G3}, + {{8 /* feature id */, 0 /* segment id */, TrafficInfo::RoadSegmentId::kForwardDirection}, + SpeedGroup::G3}}; + UpdateTrafficInfo(move(coloringMiddleF1F3F4F7F8)); + { + EstimatorGuard guard(MwmSet::MwmId(), *GetEstimator()); + TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, noTrafficGeom); + } +} +} // namespace diff --git a/routing/routing_tests/index_graph_test.cpp b/routing/routing_tests/index_graph_test.cpp index 4dfb73de64..9d060f31ad 100644 --- a/routing/routing_tests/index_graph_test.cpp +++ b/routing/routing_tests/index_graph_test.cpp @@ -6,6 +6,8 @@ #include "routing/index_graph.hpp" #include "routing/index_graph_starter.hpp" +#include "routing/routing_tests/index_graph_tools.hpp" + #include "geometry/point2d.hpp" #include "base/assert.hpp" @@ -18,49 +20,7 @@ namespace { using namespace routing; - -class TestGeometryLoader final : public GeometryLoader -{ -public: - // GeometryLoader overrides: - void Load(uint32_t featureId, RoadGeometry & road) const override; - - void AddRoad(uint32_t featureId, bool oneWay, float speed, RoadGeometry::Points const & points); - -private: - unordered_map<uint32_t, RoadGeometry> m_roads; -}; - -void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road) const -{ - auto it = m_roads.find(featureId); - if (it == m_roads.cend()) - return; - - road = it->second; -} - -void TestGeometryLoader::AddRoad(uint32_t featureId, bool oneWay, float speed, - RoadGeometry::Points const & points) -{ - auto it = m_roads.find(featureId); - CHECK(it == m_roads.end(), ("Already contains feature", featureId)); - m_roads[featureId] = RoadGeometry(oneWay, speed, points); -} - -Joint MakeJoint(vector<RoadPoint> const & points) -{ - Joint joint; - for (auto const & point : points) - joint.AddPoint(point); - - return joint; -} - -shared_ptr<EdgeEstimator> CreateEstimator() -{ - return EdgeEstimator::CreateForCar(*make_shared<CarModelFactory>()->GetVehicleModel()); -} +using namespace routing_test; void TestRoute(IndexGraph & graph, RoadPoint const & start, RoadPoint const & finish, size_t expectedLength, vector<RoadPoint> const * expectedRoute = nullptr) @@ -68,16 +28,11 @@ void TestRoute(IndexGraph & graph, RoadPoint const & start, RoadPoint const & fi LOG(LINFO, ("Test route", start.GetFeatureId(), ",", start.GetPointId(), "=>", finish.GetFeatureId(), ",", finish.GetPointId())); - AStarAlgorithm<IndexGraphStarter> algorithm; - RoutingResult<Joint::Id> routingResult; - IndexGraphStarter starter(graph, start, finish); - auto const resultCode = algorithm.FindPath(starter, starter.GetStartJoint(), - starter.GetFinishJoint(), routingResult, {}, {}); + vector<RoadPoint> roadPoints; + auto const resultCode = CalculateRoute(starter, roadPoints); TEST_EQUAL(resultCode, AStarAlgorithm<IndexGraphStarter>::Result::OK, ()); - vector<RoadPoint> roadPoints; - starter.RedressRoute(routingResult.path, roadPoints); TEST_EQUAL(roadPoints.size(), expectedLength, ()); if (expectedRoute) @@ -145,7 +100,8 @@ UNIT_TEST(EdgesTest) loader->AddRoad(4 /* featureId */, true, 1.0 /* speed */, RoadGeometry::Points({{3.0, -1.0}, {3.0, 0.0}, {3.0, 1.0}})); - IndexGraph graph(move(loader), CreateEstimator()); + traffic::TrafficCache const trafficCache; + IndexGraph graph(move(loader), CreateEstimator(trafficCache)); vector<Joint> joints; joints.emplace_back(MakeJoint({{0, 1}, {3, 1}})); // J0 @@ -189,7 +145,8 @@ UNIT_TEST(FindPathCross) 1 /* featureId */, false, 1.0 /* speed */, RoadGeometry::Points({{0.0, -2.0}, {-1.0, 0.0}, {0.0, 0.0}, {0.0, 1.0}, {0.0, 2.0}})); - IndexGraph graph(move(loader), CreateEstimator()); + traffic::TrafficCache const trafficCache; + IndexGraph graph(move(loader), CreateEstimator(trafficCache)); graph.Import({MakeJoint({{0, 2}, {1, 2}})}); @@ -245,7 +202,8 @@ UNIT_TEST(FindPathManhattan) loader->AddRoad(i + kCitySize, false, 1.0 /* speed */, avenue); } - IndexGraph graph(move(loader), CreateEstimator()); + traffic::TrafficCache const trafficCache; + IndexGraph graph(move(loader), CreateEstimator(trafficCache)); vector<Joint> joints; for (uint32_t i = 0; i < kCitySize; ++i) @@ -295,7 +253,8 @@ UNIT_TEST(RedressRace) 2 /* featureId */, false, 1.0 /* speed */, RoadGeometry::Points({{0.0, 0.0}, {1.0, 1.0}, {2.0, 1.0}, {3.0, 1.0}, {4.0, 0.0}})); - IndexGraph graph(move(loader), CreateEstimator()); + traffic::TrafficCache const trafficCache; + IndexGraph graph(move(loader), CreateEstimator(trafficCache)); vector<Joint> joints; joints.emplace_back(MakeJoint({{0, 0}, {1, 0}, {2, 0}})); // J0 @@ -325,7 +284,8 @@ UNIT_TEST(RoadSpeed) loader->AddRoad(1 /* featureId */, false, 1.0 /* speed */, RoadGeometry::Points({{0.0, 0.0}, {2.0, 0.0}, {4.0, 0.0}})); - IndexGraph graph(move(loader), CreateEstimator()); + traffic::TrafficCache const trafficCache; + IndexGraph graph(move(loader), CreateEstimator(trafficCache)); vector<Joint> joints; joints.emplace_back(MakeJoint({{0, 0}, {1, 0}})); // J0 diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp new file mode 100644 index 0000000000..390ae86bd6 --- /dev/null +++ b/routing/routing_tests/index_graph_tools.cpp @@ -0,0 +1,70 @@ +#include "routing/routing_tests/index_graph_tools.hpp" + +#include "testing/testing.hpp" + +#include "routing/car_model.hpp" + +namespace routing_test +{ +using namespace routing; + +void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road) const +{ + auto it = m_roads.find(featureId); + if (it == m_roads.cend()) + return; + + road = it->second; +} + +void TestGeometryLoader::AddRoad(uint32_t featureId, bool oneWay, float speed, + RoadGeometry::Points const & points) +{ + auto it = m_roads.find(featureId); + CHECK(it == m_roads.end(), ("Already contains feature", featureId)); + m_roads[featureId] = RoadGeometry(oneWay, speed, points); +} + +Joint MakeJoint(vector<RoadPoint> const & points) +{ + Joint joint; + for (auto const & point : points) + joint.AddPoint(point); + + return joint; +} + +shared_ptr<EdgeEstimator> CreateEstimator(traffic::TrafficCache const & trafficCache) +{ + return EdgeEstimator::CreateForCar(*make_shared<CarModelFactory>()->GetVehicleModel(), trafficCache); +} + +AStarAlgorithm<IndexGraphStarter>::Result CalculateRoute(IndexGraphStarter & starter, + vector<RoadPoint> & roadPoints) +{ + AStarAlgorithm<IndexGraphStarter> algorithm; + RoutingResult<Joint::Id> routingResult; + auto const resultCode = algorithm.FindPath( + starter, starter.GetStartJoint(), starter.GetFinishJoint(), routingResult, {}, {}); + + starter.RedressRoute(routingResult.path, roadPoints); + return resultCode; +} + +void TestRouteGeometry(IndexGraphStarter & starter, + AStarAlgorithm<IndexGraphStarter>::Result expectedRouteResult, + vector<m2::PointD> const & expectedRouteGeom) +{ + vector<RoadPoint> route; + auto const resultCode = CalculateRoute(starter, route); + TEST_EQUAL(resultCode, expectedRouteResult, ()); + TEST_EQUAL(route.size(), expectedRouteGeom.size(), ()); + for (size_t i = 0; i < route.size(); ++i) + { + // When PR with applying restricions is merged IndexGraph::GetRoad() should be used here instead. + RoadGeometry roadGeom = starter.GetGraph().GetGeometry().GetRoad(route[i].GetFeatureId()); + CHECK_LESS(route[i].GetPointId(), roadGeom.GetPointsCount(), ()); + TEST_EQUAL(expectedRouteGeom[i], roadGeom.GetPoint(route[i].GetPointId()), ()); + } +} +} // namespace routing_test diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp new file mode 100644 index 0000000000..8cc0f0b94d --- /dev/null +++ b/routing/routing_tests/index_graph_tools.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "routing/edge_estimator.hpp" +#include "routing/index_graph.hpp" +#include "routing/index_graph_starter.hpp" +#include "routing/road_point.hpp" + +#include "routing/base/astar_algorithm.hpp" + +#include "traffic/traffic_info.hpp" + +#include "geometry/point2d.hpp" + +#include "std/algorithm.hpp" +#include "std/shared_ptr.hpp" +#include "std/unique_ptr.hpp" +#include "std/unordered_map.hpp" +#include "std/vector.hpp" + +namespace routing_test +{ +class TestGeometryLoader final : public routing::GeometryLoader +{ +public: + // GeometryLoader overrides: + void Load(uint32_t featureId, routing::RoadGeometry & road) const override; + + void AddRoad(uint32_t featureId, bool oneWay, float speed, + routing::RoadGeometry::Points const & points); + +private: + unordered_map<uint32_t, routing::RoadGeometry> m_roads; +}; + +routing::Joint MakeJoint(vector<routing::RoadPoint> const & points); + +shared_ptr<routing::EdgeEstimator> CreateEstimator(traffic::TrafficCache const & trafficCache); + +routing::AStarAlgorithm<routing::IndexGraphStarter>::Result CalculateRoute( + routing::IndexGraphStarter & graph, vector<routing::RoadPoint> & roadPoints); + +void TestRouteSegments( + routing::IndexGraphStarter & starter, + routing::AStarAlgorithm<routing::IndexGraphStarter>::Result expectedRouteResult, + vector<routing::RoadPoint> const & expectedRoute); + +void TestRouteGeometry( + routing::IndexGraphStarter & starter, + routing::AStarAlgorithm<routing::IndexGraphStarter>::Result expectedRouteResult, + vector<m2::PointD> const & expectedRouteGeom); +} // namespace routing_test diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro index 100399f067..49ee873ed0 100644 --- a/routing/routing_tests/routing_tests.pro +++ b/routing/routing_tests/routing_tests.pro @@ -21,6 +21,7 @@ INCLUDEPATH += $$ROOT_DIR/3party/jansson/src \ SOURCES += \ ../../testing/testingmain.cpp \ + applying_traffic_test.cpp \ astar_algorithm_test.cpp \ astar_progress_test.cpp \ astar_router_test.cpp \ @@ -28,6 +29,7 @@ SOURCES += \ cross_routing_tests.cpp \ followed_polyline_test.cpp \ index_graph_test.cpp \ + index_graph_tools.cpp \ nearest_edge_finder_tests.cpp \ online_cross_fetcher_test.cpp \ osrm_router_test.cpp \ @@ -42,4 +44,5 @@ SOURCES += \ vehicle_model_test.cpp \ HEADERS += \ + index_graph_tools.hpp \ road_graph_builder.hpp \ diff --git a/routing/single_mwm_router.cpp b/routing/single_mwm_router.cpp index 95461b857f..89648b59b6 100644 --- a/routing/single_mwm_router.cpp +++ b/routing/single_mwm_router.cpp @@ -106,6 +106,8 @@ IRouter::ResultCode SingleMwmRouter::DoCalculateRoute(MwmSet::MwmId const & mwmI RoadPoint const start(startEdge.GetFeatureId().m_index, startEdge.GetSegId()); RoadPoint const finish(finishEdge.GetFeatureId().m_index, finishEdge.GetSegId()); + EstimatorGuard guard(mwmId, *m_estimator); + IndexGraph graph(GeometryLoader::Create( m_index, mwmId, m_vehicleModelFactory->GetVehicleModelForCountry(country)), m_estimator); @@ -210,16 +212,17 @@ bool SingleMwmRouter::LoadIndex(MwmSet::MwmId const & mwmId, string const & coun } // static -unique_ptr<SingleMwmRouter> SingleMwmRouter::CreateCarRouter(Index const & index) +unique_ptr<SingleMwmRouter> SingleMwmRouter::CreateCarRouter( + Index const & index, traffic::TrafficCache const & trafficCache) { auto vehicleModelFactory = make_shared<CarModelFactory>(); // @TODO Bicycle turn generation engine is used now. It's ok for the time being. // But later a special car turn generation engine should be implemented. auto directionsEngine = make_unique<BicycleDirectionsEngine>(index); - auto estimator = EdgeEstimator::CreateForCar(*vehicleModelFactory->GetVehicleModel()); + auto estimator = EdgeEstimator::CreateForCar(*vehicleModelFactory->GetVehicleModel(), trafficCache); auto router = - make_unique<SingleMwmRouter>("astar-bidirectional-car", index, move(vehicleModelFactory), - estimator, move(directionsEngine)); + make_unique<SingleMwmRouter>("astar-bidirectional-car", index, + move(vehicleModelFactory), estimator, move(directionsEngine)); return router; } } // namespace routing diff --git a/routing/single_mwm_router.hpp b/routing/single_mwm_router.hpp index 96cd241453..fbc2413211 100644 --- a/routing/single_mwm_router.hpp +++ b/routing/single_mwm_router.hpp @@ -7,6 +7,7 @@ #include "routing/vehicle_model.hpp" #include "indexer/index.hpp" +#include "indexer/mwm_set.hpp" #include "std/shared_ptr.hpp" #include "std/unique_ptr.hpp" @@ -31,7 +32,8 @@ public: m2::PointD const & finalPoint, RouterDelegate const & delegate, Route & route); - static unique_ptr<SingleMwmRouter> CreateCarRouter(Index const & index); + static unique_ptr<SingleMwmRouter> CreateCarRouter(Index const & index, + traffic::TrafficCache const & trafficCache); private: IRouter::ResultCode DoCalculateRoute(MwmSet::MwmId const & mwmId, m2::PointD const & startPoint, diff --git a/traffic/traffic.pro b/traffic/traffic.pro index ecacd5f855..c02074c080 100644 --- a/traffic/traffic.pro +++ b/traffic/traffic.pro @@ -8,8 +8,10 @@ include($$ROOT_DIR/common.pri) SOURCES += \ speed_groups.cpp \ + traffic_cache.cpp \ traffic_info.cpp \ HEADERS += \ speed_groups.hpp \ + traffic_cache.hpp \ traffic_info.hpp \ diff --git a/traffic/traffic_cache.cpp b/traffic/traffic_cache.cpp new file mode 100644 index 0000000000..ec0373b7e6 --- /dev/null +++ b/traffic/traffic_cache.cpp @@ -0,0 +1,23 @@ +#include "traffic/traffic_cache.hpp" + +namespace traffic +{ +void TrafficCache::Set(TrafficInfo && info) +{ + MwmSet::MwmId const mwmId = info.GetMwmId(); + m_trafficInfo[mwmId] = make_shared<TrafficInfo>(move(info)); +} + +void TrafficCache::Remove(MwmSet::MwmId const & mwmId) { m_trafficInfo.erase(mwmId); } + +shared_ptr<TrafficInfo> TrafficCache::GetTrafficInfo(MwmSet::MwmId const & mwmId) const +{ + auto it = m_trafficInfo.find(mwmId); + + if (it == m_trafficInfo.cend()) + return shared_ptr<TrafficInfo>(); + return it->second; +} + +void TrafficCache::Clear() { m_trafficInfo.clear(); } +} // namespace traffic diff --git a/traffic/traffic_cache.hpp b/traffic/traffic_cache.hpp new file mode 100644 index 0000000000..ba79312a6f --- /dev/null +++ b/traffic/traffic_cache.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "traffic/traffic_info.hpp" + +#include "indexer/mwm_set.hpp" + +#include "std/map.hpp" +#include "std/shared_ptr.hpp" + +namespace traffic +{ +class TrafficCache +{ +public: + TrafficCache() : m_trafficInfo() {} + virtual ~TrafficCache() = default; + + virtual shared_ptr<traffic::TrafficInfo> GetTrafficInfo(MwmSet::MwmId const & mwmId) const; + +protected: + void Set(traffic::TrafficInfo && info); + void Remove(MwmSet::MwmId const & mwmId); + void Clear(); + +private: + map<MwmSet::MwmId, shared_ptr<traffic::TrafficInfo>> m_trafficInfo; +}; +} // namespace traffic diff --git a/traffic/traffic_info.cpp b/traffic/traffic_info.cpp index e02c00e900..879fe7a9cf 100644 --- a/traffic/traffic_info.cpp +++ b/traffic/traffic_info.cpp @@ -77,6 +77,14 @@ TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion , m_currentDataVersion(currentDataVersion) {} +// static +TrafficInfo TrafficInfo::BuildForTesting(Coloring && coloring) +{ + TrafficInfo info; + info.m_coloring = move(coloring); + return info; +} + bool TrafficInfo::ReceiveTrafficData() { auto const & info = m_mwmId.GetInfo(); diff --git a/traffic/traffic_info.hpp b/traffic/traffic_info.hpp index a07e4d8cc4..bafa0a4fa5 100644 --- a/traffic/traffic_info.hpp +++ b/traffic/traffic_info.hpp @@ -6,6 +6,7 @@ #include "std/cstdint.hpp" #include "std/map.hpp" +#include "std/shared_ptr.hpp" #include "std/vector.hpp" namespace traffic @@ -67,6 +68,8 @@ public: TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion); + static TrafficInfo BuildForTesting(Coloring && coloring); + // Fetches the latest traffic data from the server and updates the coloring. // Construct the url by passing an MwmId. // *NOTE* This method must not be called on the UI thread. @@ -90,4 +93,14 @@ private: Availability m_availability = Availability::Unknown; int64_t m_currentDataVersion = 0; }; + +class TrafficObserver +{ +public: + virtual ~TrafficObserver() = default; + + virtual void OnTrafficEnabled(bool enable) = 0; + virtual void OnTrafficInfoAdded(traffic::TrafficInfo && info) = 0; + virtual void OnTrafficInfoRemoved(MwmSet::MwmId const & mwmId) = 0; +}; } // namespace traffic |