From 27ad70d72922de321ec24a64e00709f222575976 Mon Sep 17 00:00:00 2001 From: mvglasow Date: Sat, 8 Nov 2025 22:28:01 +0200 Subject: [PATCH] [traffic] Decode nearby messages first (based on position and viewport) Signed-off-by: mvglasow --- libs/map/traffic_manager.cpp | 91 ++++++++++++++++++++++++++++++++++-- libs/map/traffic_manager.hpp | 14 ++++++ 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/libs/map/traffic_manager.cpp b/libs/map/traffic_manager.cpp index fd0f83ba4..aaafc688b 100644 --- a/libs/map/traffic_manager.cpp +++ b/libs/map/traffic_manager.cpp @@ -8,6 +8,7 @@ #include "indexer/ftypes_matcher.hpp" #include "indexer/scales.hpp" +#include "geometry/distance_on_sphere.hpp" #include "geometry/mercator.hpp" #include "platform/platform.hpp" @@ -52,6 +53,12 @@ auto constexpr kStorageUpdateInterval = minutes(1); * File name at which traffic data is persisted. */ auto constexpr kTrafficXMLFileName = "traffic.xml"; + +/** + * Threshold by which viewport or user position can change without requiring the nessage queue to + * be resorted. + */ +auto constexpr kPositionThreshold = 1000.0; } // namespace TrafficManager::TrafficManager(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, @@ -381,6 +388,13 @@ void TrafficManager::UpdateMyPosition(MyPosition const & myPosition) double const kSquareSideM = 5000.0; m_currentPosition = {myPosition, true /* initialized */}; + if (!m_currentPositionLazy.second + || (mercator::DistanceOnEarth(m_currentPositionLazy.first.m_position, myPosition.m_position) > kPositionThreshold)) + { + m_isFeedQueueSortInvalid = true; + m_currentPositionLazy = m_currentPosition; + } + if (!IsEnabled() || IsInvalidState() || IsPausedAndNotRouting()) return; @@ -393,6 +407,13 @@ void TrafficManager::UpdateViewport(ScreenBase const & screen) { m_currentModelView = {screen, true /* initialized */}; + if (!m_currentModelViewLazy.second + || (mercator::DistanceOnEarth(m_currentModelViewLazy.first.ClipRect().Center(), screen.ClipRect().Center()) > kPositionThreshold)) + { + m_isFeedQueueSortInvalid = true; + m_currentModelViewLazy = m_currentModelView; + } + if (!IsEnabled() || IsInvalidState() || IsPausedAndNotRouting()) return; @@ -493,6 +514,7 @@ void TrafficManager::ReceiveFeed(traffxml::TraffFeed feed) { std::lock_guard lock(m_mutex); m_feedQueue.push_back(feed); + m_isFeedQueueSortInvalid = true; } m_condition.notify_one(); } @@ -588,12 +610,27 @@ void TrafficManager::ConsolidateFeedQueue() } } } - // remove empty feeds - for (auto it = m_feedQueue.begin(); it != m_feedQueue.end(); ) - if (it->empty()) - it = m_feedQueue.erase(it); + // remove empty feeds from the beginning of the queue + while (!m_feedQueue.empty() && m_feedQueue.front().empty()) + m_feedQueue.erase(m_feedQueue.begin()); + // merge everything into the first vector + for (size_t i = 1; i < m_feedQueue.size(); i++) + { + if (m_feedQueue[i].empty()) + continue; + m_feedQueue[0].insert(m_feedQueue[0].end(), + std::make_move_iterator(m_feedQueue[i].begin()), + std::make_move_iterator(m_feedQueue[i].end())); + m_feedQueue[i].clear(); + } + // remove empty feeds (any feeds after the first one are empty at this point) + if (!m_feedQueue.empty()) + { + if (!m_feedQueue.front().empty()) + m_feedQueue.resize(1); else - ++it; + m_feedQueue.clear(); + } } void TrafficManager::DecodeFirstMessage() @@ -608,6 +645,50 @@ void TrafficManager::DecodeFirstMessage() // if we have no more feeds, return (nothing to do) if (m_feedQueue.empty()) return; + if (m_isFeedQueueSortInvalid + && (m_currentPositionLazy.second || m_currentModelViewLazy.second)) + { + std::sort(m_feedQueue.front().begin(), m_feedQueue.front().end(), + [this](const traffxml::TraffMessage & a, const traffxml::TraffMessage & b){ + // return a < b + // cancellations before others + if (a.m_cancellation) + return !b.m_cancellation; + // sort by shortest distance between reference point and position/viewport + std::vector locations; + if (m_currentPositionLazy.second) + locations.push_back(mercator::ToLatLon(m_currentPositionLazy.first.m_position)); + if (m_currentModelViewLazy.second) + locations.push_back(mercator::ToLatLon(m_currentModelViewLazy.first.ClipRect().Center())); + double aDist = 4.5e+7; + double bDist = 4.5e+7; + for (auto const & [message, dist] : { std::pair{a, std::ref(aDist)}, {b, std::ref(bDist)} }) + { + // messages without location first + if (!message.m_location) + { + dist.get() = 0; + continue; + } + // for simplification, we are skipping the via point + for (auto const & point : { message.m_location.value().m_from, + message.m_location.value().m_at, + message.m_location.value().m_to }) + { + if (!point) + continue; + for (auto const & location : locations) + { + auto newdist = ms::DistanceOnEarth(point.value().m_coordinates, location); + if (newdist < dist.get()) + dist.get() = newdist; + } + } + } + return aDist < bDist; + }); + m_isFeedQueueSortInvalid = false; + } // retrieve the first message from the first feed, remove it from the feed std::swap(message, m_feedQueue.front().front()); m_feedQueue.front().erase(m_feedQueue.front().begin()); diff --git a/libs/map/traffic_manager.hpp b/libs/map/traffic_manager.hpp index 0fb39a76d..31ce2a4a2 100644 --- a/libs/map/traffic_manager.hpp +++ b/libs/map/traffic_manager.hpp @@ -586,8 +586,14 @@ private: std::atomic m_currentDataVersion; // These fields have a flag of their initialization. + /* + * The lazy ones get updated only if they are not initialized, or if their new position is more + * than a certain distance from the previously stored one. + */ std::pair m_currentPosition = {MyPosition(), false}; + std::pair m_currentPositionLazy = m_currentPosition; std::pair m_currentModelView = {ScreenBase(), false}; + std::pair m_currentModelViewLazy = m_currentModelView; /** * The mode in which the traffic manager is running. @@ -708,6 +714,14 @@ private: */ std::vector m_feedQueue; + /** + * @brief Whether the feed queue needs to be resorted. + * + * Resorting is needed when a new feed is added, or the current position or the viewport center + * has changed by more than a certain threshold. + */ + std::atomic m_isFeedQueueSortInvalid = false; + /** * @brief Cache of all currently active TraFF messages. *