[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 <me@leonardobishop.net>
This commit is contained in:
Leonardo Bishop
2025-11-22 16:43:53 +00:00
committed by x7z4w
parent 641f2308c6
commit a1cbcc5885
9 changed files with 117 additions and 49 deletions

View File

@@ -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<GpsInfoMessage>(info, isNavigable, routeInfo),
MessagePriority::Normal);
m_threadCommutator->PostMessage(
ThreadsCommutator::RenderThread,
make_unique_dp<GpsInfoMessage>(info, isNavigable, distToNextTurn, speedLimit, routeInfo),
MessagePriority::Normal);
}
void DrapeEngine::SwitchMyPositionNextMode()

View File

@@ -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();

View File

@@ -438,7 +438,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
#endif
ref_ptr<GpsInfoMessage> 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())

View File

@@ -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;
};

View File

@@ -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<double, double>;
static std::array<TSpeedScale, 6> 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<TSpeedScale, 2> const scales2d = {{
{0.0, 1.75}, {77.0, 4.50} // 48 mph
}};
static std::array<TSpeedScale, 2> const scales3d = {{{0.0, 1.00}, {77.0, 4.50}}};
static std::array<TSpeedScale, 6> 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<TSpeedScale, 2> const & scales = isPerspectiveAllowed ? scales3d : scales2d;
std::array<TSpeedScale, 6> 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<double, double>;
static std::array<TSpeedScale, 5> 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<TSpeedScale, 5> 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<TSpeedScale, 5> 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
{

View File

@@ -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<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen,

View File

@@ -1171,6 +1171,7 @@ void RoutingManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> 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<location::GpsInfo>(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()

View File

@@ -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)

View File

@@ -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);