From a1cbcc5885d54a27547dad3a56dd96d9efa426e7 Mon Sep 17 00:00:00 2001 From: Leonardo Bishop Date: Sat, 22 Nov 2025 16:43:53 +0000 Subject: [PATCH] [drape] Base zoom level on distance to next turn This commit changes the auto zoom level behaviour during navigation to be based off the distance to the next turn, rather than naively coupling it to the current speed. This will improve the navigation experience during driving. Signed-off-by: Leonardo Bishop --- libs/drape_frontend/drape_engine.cpp | 9 +- libs/drape_frontend/drape_engine.hpp | 3 +- libs/drape_frontend/frontend_renderer.cpp | 3 +- libs/drape_frontend/message_subclasses.hpp | 9 +- .../drape_frontend/my_position_controller.cpp | 97 +++++++++++++------ .../drape_frontend/my_position_controller.hpp | 3 +- libs/map/routing_manager.cpp | 5 +- libs/routing/routing_session.cpp | 35 +++++-- libs/routing/routing_session.hpp | 2 + 9 files changed, 117 insertions(+), 49 deletions(-) diff --git a/libs/drape_frontend/drape_engine.cpp b/libs/drape_frontend/drape_engine.cpp index 05d0b4389..dcd1d1cbc 100644 --- a/libs/drape_frontend/drape_engine.cpp +++ b/libs/drape_frontend/drape_engine.cpp @@ -441,12 +441,13 @@ void DrapeEngine::SetCompassInfo(location::CompassInfo const & info) MessagePriority::Normal); } -void DrapeEngine::SetGpsInfo(location::GpsInfo const & info, bool isNavigable, +void DrapeEngine::SetGpsInfo(location::GpsInfo const & info, bool isNavigable, double distToNextTurn, double speedLimit, location::RouteMatchingInfo const & routeInfo) { - m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(info, isNavigable, routeInfo), - MessagePriority::Normal); + m_threadCommutator->PostMessage( + ThreadsCommutator::RenderThread, + make_unique_dp(info, isNavigable, distToNextTurn, speedLimit, routeInfo), + MessagePriority::Normal); } void DrapeEngine::SwitchMyPositionNextMode() diff --git a/libs/drape_frontend/drape_engine.hpp b/libs/drape_frontend/drape_engine.hpp index 39be90efa..9b83c1959 100644 --- a/libs/drape_frontend/drape_engine.hpp +++ b/libs/drape_frontend/drape_engine.hpp @@ -154,7 +154,8 @@ public: void UpdateMapStyle(); void SetCompassInfo(location::CompassInfo const & info); - void SetGpsInfo(location::GpsInfo const & info, bool isNavigable, location::RouteMatchingInfo const & routeInfo); + void SetGpsInfo(location::GpsInfo const & info, bool isNavigable, double distToNextTurn, double speedLimit, + location::RouteMatchingInfo const & routeInfo); void SwitchMyPositionNextMode(); void LoseLocation(); void StopLocationFollow(); diff --git a/libs/drape_frontend/frontend_renderer.cpp b/libs/drape_frontend/frontend_renderer.cpp index 3e38022b7..94fc630e9 100644 --- a/libs/drape_frontend/frontend_renderer.cpp +++ b/libs/drape_frontend/frontend_renderer.cpp @@ -438,7 +438,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; #endif ref_ptr msg = message; - m_myPositionController->OnLocationUpdate(msg->GetInfo(), msg->IsNavigable(), m_userEventStream.GetCurrentScreen()); + m_myPositionController->OnLocationUpdate(msg->GetInfo(), msg->IsNavigable(), msg->GetDistanceToNextTurn(), + msg->GetSpeedLimit(), m_userEventStream.GetCurrentScreen()); location::RouteMatchingInfo const & info = msg->GetRouteInfo(); if (info.HasDistanceFromBegin()) diff --git a/libs/drape_frontend/message_subclasses.hpp b/libs/drape_frontend/message_subclasses.hpp index 00325e25c..e0fc00574 100644 --- a/libs/drape_frontend/message_subclasses.hpp +++ b/libs/drape_frontend/message_subclasses.hpp @@ -481,9 +481,12 @@ private: class GpsInfoMessage : public Message { public: - GpsInfoMessage(location::GpsInfo const & info, bool isNavigable, location::RouteMatchingInfo const & routeInfo) + GpsInfoMessage(location::GpsInfo const & info, bool isNavigable, double distToNextTurn, double speedLimit, + location::RouteMatchingInfo const & routeInfo) : m_info(info) , m_isNavigable(isNavigable) + , m_distToNextTurn(distToNextTurn) + , m_speedLimit(speedLimit) , m_routeInfo(routeInfo) {} @@ -491,11 +494,15 @@ public: location::GpsInfo const & GetInfo() const { return m_info; } bool IsNavigable() const { return m_isNavigable; } + double const & GetSpeedLimit() const { return m_speedLimit; } + double const & GetDistanceToNextTurn() const { return m_distToNextTurn; } location::RouteMatchingInfo const & GetRouteInfo() const { return m_routeInfo; } private: location::GpsInfo const m_info; bool const m_isNavigable; + double const m_distToNextTurn; + double const m_speedLimit; location::RouteMatchingInfo const m_routeInfo; }; diff --git a/libs/drape_frontend/my_position_controller.cpp b/libs/drape_frontend/my_position_controller.cpp index ec02f6d60..f46fa8879 100644 --- a/libs/drape_frontend/my_position_controller.cpp +++ b/libs/drape_frontend/my_position_controller.cpp @@ -56,32 +56,18 @@ inline double GetVisualScale() return df::VisualParams::Instance().GetVisualScale(); } -// Calculate zoom value in meters per pixel -double CalculateZoomBySpeed(double speedMpS, bool isPerspectiveAllowed) +double CalculateZoomByMaxSpeed(double speedMpS, bool isPerspectiveAllowed) { using TSpeedScale = std::pair; - static std::array const scales3d = {{ - {20.0, 0.25}, - {40.0, 0.75}, - {60.0, 1.50}, - {75.0, 2.50}, - {85.0, 3.75}, - {95.0, 6.00}, + static std::array const scales2d = {{ + {0.0, 1.75}, {77.0, 4.50} // 48 mph }}; + static std::array const scales3d = {{{0.0, 1.00}, {77.0, 4.50}}}; - static std::array const scales2d = {{ - {20.0, 0.70}, - {40.0, 1.25}, - {60.0, 2.25}, - {75.0, 3.00}, - {85.0, 3.75}, - {95.0, 6.00}, - }}; + std::array const & scales = isPerspectiveAllowed ? scales3d : scales2d; - std::array const & scales = isPerspectiveAllowed ? scales3d : scales2d; - - double constexpr kDefaultSpeedKmpH = 80.0; - double const speedKmpH = speedMpS >= 0 ? measurement_utils::MpsToKmph(speedMpS) : kDefaultSpeedKmpH; + double constexpr kDefaultSpeedLimitKmpH = 50.0; + double const speedKmpH = speedMpS > 0 ? measurement_utils::MpsToKmph(speedMpS) : kDefaultSpeedLimitKmpH; size_t i = 0; for (size_t sz = scales.size(); i < sz; ++i) @@ -92,20 +78,64 @@ double CalculateZoomBySpeed(double speedMpS, bool isPerspectiveAllowed) if (i == 0) return scales.front().second / vs; - if (i == scales.size()) - return scales.back().second / vs; - double const minSpeed = scales[i - 1].first; - double const maxSpeed = scales[i].first; - double const k = (speedKmpH - minSpeed) / (maxSpeed - minSpeed); + return scales[i - 1].second / vs; +} - double const minScale = scales[i - 1].second; - double const maxScale = scales[i].second; +double CalculateZoomByDistanceToTurn(double distance, bool isPerspectiveAllowed) +{ + using TSpeedScale = std::pair; + static std::array const scales2d = {{ + {200, 0.50}, + {500, 0.80}, // 0.3 mi + {1200, 1.75}, // 0.75 mi + {2000, 3.50}, // 1.2 mi + {5000, 6.50} // 3 mi + }}; + static std::array const scales3d = {{ + {200, 0.25}, + {500, 0.60}, // 0.3 mi + {1200, 1.25}, // 0.75 mi + {2000, 3.50}, // 1.2 mi + {5000, 6.50} // 3 mi + }}; + + std::array const & scale = isPerspectiveAllowed ? scales3d : scales2d; + + double constexpr kDefaultDistance = 2000; + double const distanceM = distance >= 0 ? distance : kDefaultDistance; + + size_t i = 0; + for (size_t sz = scale.size(); i < sz; ++i) + if (scale[i].first >= distanceM) + break; + + double const vs = GetVisualScale(); + + if (i == 0) + return scale.front().second / vs; + if (i == scale.size()) + return scale.back().second / vs; + + double const minDist = scale[i - 1].first; + double const maxDist = scale[i].first; + double const k = (distanceM - minDist) / (maxDist - minDist); + + double const minScale = scale[i - 1].second; + double const maxScale = scale[i].second; double const zoom = minScale + k * (maxScale - minScale); return zoom / vs; } +double CalculateAutoZoom(double speedMpS, double distanceToTurn, bool isPerspectiveAllowed) +{ + double const zoomByDistance = CalculateZoomByDistanceToTurn(distanceToTurn, isPerspectiveAllowed); + double const zoomByMaxSpeed = CalculateZoomByMaxSpeed(speedMpS, isPerspectiveAllowed); + + return std::min(zoomByDistance, zoomByMaxSpeed); +} + void ResetNotification(uint64_t & notifyId) { notifyId = DrapeNotifier::kInvalidId; @@ -395,7 +425,8 @@ void MyPositionController::NextMode(ScreenBase const & screen) } } -void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool isNavigable, ScreenBase const & screen) +void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool isNavigable, double distanceToNextTurn, + double speedLimit, ScreenBase const & screen) { m2::PointD const oldPos = GetDrawablePosition(); double const oldAzimut = GetDrawableAzimut(); @@ -407,11 +438,13 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool m_errorRadius = rect.SizeX() * 0.5; m_horizontalAccuracy = info.m_horizontalAccuracy; - if (info.m_speed > 0.0) + if (distanceToNextTurn >= 0.0 || speedLimit >= 0.0) { double const mercatorPerMeter = m_errorRadius / info.m_horizontalAccuracy; - m_autoScale2d = mercatorPerMeter * CalculateZoomBySpeed(info.m_speed, false /* isPerspectiveAllowed */); - m_autoScale3d = mercatorPerMeter * CalculateZoomBySpeed(info.m_speed, true /* isPerspectiveAllowed */); + m_autoScale2d = + mercatorPerMeter * CalculateAutoZoom(speedLimit, distanceToNextTurn, false /* isPerspectiveAllowed */); + m_autoScale3d = + mercatorPerMeter * CalculateAutoZoom(speedLimit, distanceToNextTurn, true /* isPerspectiveAllowed */); } else { diff --git a/libs/drape_frontend/my_position_controller.hpp b/libs/drape_frontend/my_position_controller.hpp index 177016e7e..0ac4a638e 100644 --- a/libs/drape_frontend/my_position_controller.hpp +++ b/libs/drape_frontend/my_position_controller.hpp @@ -117,7 +117,8 @@ public: void OnEnterBackground(); void OnCompassTapped(); - void OnLocationUpdate(location::GpsInfo const & info, bool isNavigable, ScreenBase const & screen); + void OnLocationUpdate(location::GpsInfo const & info, bool isNavigable, double distanceToNextTurn, double speedLimit, + ScreenBase const & screen); void OnCompassUpdate(location::CompassInfo const & info, ScreenBase const & screen); void Render(ref_ptr context, ref_ptr mng, ScreenBase const & screen, diff --git a/libs/map/routing_manager.cpp b/libs/map/routing_manager.cpp index 5bb711350..5034ed81f 100644 --- a/libs/map/routing_manager.cpp +++ b/libs/map/routing_manager.cpp @@ -1171,6 +1171,7 @@ void RoutingManager::SetDrapeEngine(ref_ptr engine, bool is3dAl { auto routeMatchingInfo = GetRouteMatchingInfo(*m_gpsInfoCache); m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, *m_gpsInfoCache, m_routingSession.IsNavigable(), + m_routingSession.GetDistanceToNextTurn(), m_routingSession.GetCurrentSpeedLimit(), routeMatchingInfo); m_gpsInfoCache.reset(); } @@ -1515,7 +1516,9 @@ void RoutingManager::OnExtrapolatedLocationUpdate(location::GpsInfo const & info m_gpsInfoCache = make_unique(gpsInfo); auto routeMatchingInfo = GetRouteMatchingInfo(gpsInfo); - m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, gpsInfo, m_routingSession.IsNavigable(), routeMatchingInfo); + m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, gpsInfo, m_routingSession.IsNavigable(), + m_routingSession.GetDistanceToNextTurn(), m_routingSession.GetCurrentSpeedLimit(), + routeMatchingInfo); } void RoutingManager::DeleteSavedRoutePoints() diff --git a/libs/routing/routing_session.cpp b/libs/routing/routing_session.cpp index 35bb5500b..006d30722 100644 --- a/libs/routing/routing_session.cpp +++ b/libs/routing/routing_session.cpp @@ -363,6 +363,32 @@ void GetFullRoadName(RouteSegment::RoadNameInfo & road, std::string & name) } } +double RoutingSession::GetDistanceToNextTurn() const +{ + if (!m_route->IsValid()) + return -1; + + double distanceToTurnMeters = 0.; + turns::TurnItem turn; + m_route->GetNearestTurn(distanceToTurnMeters, turn); + return distanceToTurnMeters; +} + +double RoutingSession::GetCurrentSpeedLimit() const +{ + if (!m_route->IsValid()) + return -1; + + SpeedInUnits speedLimit; + m_route->GetCurrentSpeedLimit(speedLimit); + if (speedLimit.IsNumeric()) + return measurement_utils::KmphToMps(speedLimit.GetSpeedKmPH()); + else if (speedLimit.GetSpeed() == kNoneMaxSpeed) + return 0; + else + return -1.0; +} + void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const { CHECK_THREAD_CHECKER(m_threadChecker, ()); @@ -392,14 +418,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const info.m_distToTurn = platform::Distance::CreateFormatted(distanceToTurnMeters); info.m_turn = turn.m_turn; - SpeedInUnits speedLimit; - m_route->GetCurrentSpeedLimit(speedLimit); - if (speedLimit.IsNumeric()) - info.m_speedLimitMps = measurement_utils::KmphToMps(speedLimit.GetSpeedKmPH()); - else if (speedLimit.GetSpeed() == kNoneMaxSpeed) - info.m_speedLimitMps = 0; - else - info.m_speedLimitMps = -1.0; + info.m_speedLimitMps = GetCurrentSpeedLimit(); // The turn after the next one. if (m_routingSettings.m_showTurnAfterNext) diff --git a/libs/routing/routing_session.hpp b/libs/routing/routing_session.hpp index 555c717cd..990b3492b 100644 --- a/libs/routing/routing_session.hpp +++ b/libs/routing/routing_session.hpp @@ -100,6 +100,8 @@ public: SessionState OnLocationPositionChanged(location::GpsInfo const & info); void GetRouteFollowingInfo(FollowingInfo & info) const; + double GetDistanceToNextTurn() const; + double GetCurrentSpeedLimit() const; bool MatchLocationToRoute(location::GpsInfo & location, location::RouteMatchingInfo & routeMatchingInfo); void MatchLocationToRoadGraph(location::GpsInfo & location);