[traffic] Refactor TraFF decoder into separate class

Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
mvglasow
2025-05-18 18:11:39 +03:00
parent 2894218573
commit bd178932c1
7 changed files with 490 additions and 405 deletions

View File

@@ -1,10 +1,7 @@
#include "map/traffic_manager.hpp"
#include "routing/maxspeeds.hpp"
#include "routing/routing_helpers.hpp"
#include "routing_common/maxspeed_conversion.hpp"
#include "drape_frontend/drape_engine.hpp"
#include "drape_frontend/visual_params.hpp"
@@ -13,10 +10,6 @@
#include "geometry/mercator.hpp"
#include "openlr/decoded_path.hpp"
#include "openlr/openlr_decoder.hpp"
#include "openlr/openlr_model.hpp"
#include "platform/platform.hpp"
#include "traffxml/traff_model_xml.hpp"
@@ -43,15 +36,6 @@ auto constexpr kDrapeUpdateInterval = seconds(10);
* Interval at which the traffic observer gets traffic updates while messages are being processed.
*/
auto constexpr kObserverUpdateInterval = minutes(1);
// Number of worker threads for the OpenLR decoder
/*
* TODO how to determine the best number of worker threads?
* One per direction? Does not seem to help with bidirectional locations (two reference points).
* One per segment (fromvia/fromat, viato/atto)? Not yet tested.
* Otherwise there is little to be gained, as we decode messages one at a time.
*/
auto constexpr kNumDecoderThreads = 1;
} // namespace
TrafficManager::CacheEntry::CacheEntry()
@@ -78,7 +62,7 @@ TrafficManager::TrafficManager(DataSource & dataSource,
traffic::TrafficObserver & observer)
: m_dataSource(dataSource)
, m_countryParentNameGetterFn(countryParentNameGetter)
, m_openLrDecoder(m_dataSource, countryParentNameGetter)
, m_traffDecoder(dataSource, countryParentNameGetter, m_messageCache)
, m_getMwmsByRectFn(getMwmsByRectFn)
, m_observer(observer)
, m_currentDataVersion(0)
@@ -439,146 +423,6 @@ void TrafficManager::ConsolidateFeedQueue()
++it;
}
void TrafficManager::DecodeLocation(traffxml::TraffMessage & message, traffxml::MultiMwmColoring & decoded)
{
decoded.clear();
// Convert the location to a format understood by the OpenLR decoder.
std::vector<openlr::LinearSegment> segments
= message.m_location.value().ToOpenLrSegments(message.m_id);
for (auto segment : segments)
{
LOG(LINFO, (" Segment:", segment.m_segmentId));
for (int i = 0; i < segment.m_locationReference.m_points.size(); i++)
{
LOG(LINFO, (" ", i, ":", segment.m_locationReference.m_points[i].m_latLon));
if (i < segment.m_locationReference.m_points.size() - 1)
{
LOG(LINFO, (" FRC:", segment.m_locationReference.m_points[i].m_functionalRoadClass));
LOG(LINFO, (" DNP:", segment.m_locationReference.m_points[i].m_distanceToNextPoint));
}
}
}
// Decode the location into a path on the map.
// One path per segment
std::vector<openlr::DecodedPath> paths(segments.size());
m_openLrDecoder.DecodeV3(segments, kNumDecoderThreads, paths);
for (size_t i = 0; i < paths.size(); i++)
for (size_t j = 0; j < paths[i].m_path.size(); j++)
{
auto fid = paths[i].m_path[j].GetFeatureId().m_index;
auto segment = paths[i].m_path[j].GetSegId();
uint8_t direction = paths[i].m_path[j].IsForward() ?
traffic::TrafficInfo::RoadSegmentId::kForwardDirection :
traffic::TrafficInfo::RoadSegmentId::kReverseDirection;
decoded[paths[i].m_path[j].GetFeatureId().m_mwmId][traffic::TrafficInfo::RoadSegmentId(fid, segment, direction)] = traffic::SpeedGroup::Unknown;
}
}
void TrafficManager::ApplyTrafficImpact(traffxml::TrafficImpact & impact, traffxml::MultiMwmColoring & decoded)
{
for (auto dit = decoded.begin(); dit != decoded.end(); dit++)
for (auto cit = dit->second.begin(); cit != dit->second.end(); cit++)
{
/*
* Consolidate TrafficImpact into a single SpeedGroup per segment.
* Exception: if TrafficImpact already has SpeedGrup::TempBlock, no need to evaluate
* the rest.
*/
traffic::SpeedGroup sg = impact.m_speedGroup;
/*
* TODO also process m_delayMins if greater than zero.
* This would require a separate pass over all edges, calculating length,
* total (normal) travel time (length / maxspeed), then a speed group based on
* (normal_travel_time / delayed_travel_time) which is the same as the ratio between
* reduced and normal speed. That would give us a third potential speed group.
*/
if ((sg != traffic::SpeedGroup::TempBlock) && (impact.m_maxspeed != traffxml::kMaxspeedNone))
{
auto const handle = m_dataSource.GetMwmHandleById(dit->first);
auto const speeds = routing::LoadMaxspeeds(handle);
if (speeds)
{
traffic::SpeedGroup fromMaxspeed = traffic::SpeedGroup::Unknown;
auto const speed = speeds->GetMaxspeed(cit->first.GetFid());
auto const speedKmPH = speed.GetSpeedKmPH(cit->first.GetDir() == traffic::TrafficInfo::RoadSegmentId::kForwardDirection);
if (speedKmPH != routing::kInvalidSpeed)
{
fromMaxspeed = traffic::GetSpeedGroupByPercentage(impact.m_maxspeed * 100.0f / speedKmPH);
if ((sg == traffic::SpeedGroup::Unknown) || (fromMaxspeed < sg))
sg = fromMaxspeed;
}
}
/*
* TODO fully process TrafficImpact (unless m_speedGroup is TempBlock, which overrules everything else)
* If no maxspeed or delay is set, just give out speed groups.
* Else, examine segments, length, normal travel time, travel time considering impact, and
* determine the closest matching speed group.
*/
}
// TODO process all TrafficImpact fields and determine the speed group based on that
cit->second = sg;
}
}
/*
* TODO the OpenLR decoder is designed to handle multiple segments (i.e. locations).
* Decoding message by message kind of defeats the purpose.
* But after decoding the location, we need to examine the map features we got in order to
* determine the speed groups, thus we may need to decode one by one (TBD).
* If we batch-decode segments, we need to fix the [partner] segment IDs in the segment and path
* structures to accept a TraFF message ID (string) rather than an integer, or derive
* [partner] segment IDs from TraFF message IDs.
*/
void TrafficManager::DecodeMessage(traffxml::TraffMessage & message)
{
if (!message.m_location)
return;
// Decode events into consolidated traffic impact
std::optional<traffxml::TrafficImpact> impact = message.GetTrafficImpact();
LOG(LINFO, (" Impact: ", impact));
// Skip further processing if there is no impact
if (!impact)
return;
traffxml::MultiMwmColoring decoded;
auto it = m_messageCache.find(message.m_id);
if ((it != m_messageCache.end())
&& !it->second.m_decoded.empty()
&& (it->second.m_location == message.m_location))
{
// cache already has a message with reusable location
LOG(LINFO, (" Location for message", message.m_id, "can be reused from cache"));
std::optional<traffxml::TrafficImpact> cachedImpact = it->second.GetTrafficImpact();
if (cachedImpact.has_value() && cachedImpact.value() == impact.value())
{
LOG(LINFO, (" Impact for message", message.m_id, "unchanged, reusing cached coloring"));
// same impact, m_decoded can be reused altogether
message.m_decoded = it->second.m_decoded;
return;
}
else
decoded = it->second.m_decoded;
}
else
DecodeLocation(message, decoded);
if (impact)
{
ApplyTrafficImpact(impact.value(), decoded);
std::swap(message.m_decoded, decoded);
}
}
void TrafficManager::DecodeFirstMessage()
{
traffxml::TraffMessage message;
@@ -618,7 +462,7 @@ void TrafficManager::DecodeFirstMessage()
}
LOG(LINFO, (" ", message.m_id, ":", message));
DecodeMessage(message);
m_traffDecoder.DecodeMessage(message);
// store message in cache
m_messageCache.insert_or_assign(message.m_id, message);
// store message coloring in AllMwmColoring