mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 05:13:58 +00:00
WIP: [traffic] Implement basic TraFF parsing, currently from hardcoded path
Not feature complete, produces incorrect results for some test cases Some parts of the implementation are not very elegant yet Inefficient as the whole set of messages is parsed on update Lots of verbose debug logging Lots of dead code from old traffic module (#ifdef traffic_dead_code) Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
|
|
||||||
|
#include "traffxml/traff_model_xml.hpp"
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -25,6 +27,10 @@ auto constexpr kOutdatedDataTimeout = minutes(5) + kUpdateInterval;
|
|||||||
auto constexpr kNetworkErrorTimeout = minutes(20);
|
auto constexpr kNetworkErrorTimeout = minutes(20);
|
||||||
|
|
||||||
auto constexpr kMaxRetriesCount = 5;
|
auto constexpr kMaxRetriesCount = 5;
|
||||||
|
|
||||||
|
// Number of identical data sources to create for the OpenLR decoder, one source per worker thread.
|
||||||
|
// TODO how to determine the best number of worker threads?
|
||||||
|
auto constexpr kNumDecoderThreads = 1;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TrafficManager::CacheEntry::CacheEntry()
|
TrafficManager::CacheEntry::CacheEntry()
|
||||||
@@ -53,7 +59,10 @@ TrafficManager::TrafficManager(const CountryParentNameGetterFn &countryParentNam
|
|||||||
, m_observer(observer)
|
, m_observer(observer)
|
||||||
, m_currentDataVersion(0)
|
, m_currentDataVersion(0)
|
||||||
, m_state(TrafficState::Disabled)
|
, m_state(TrafficState::Disabled)
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
, m_maxCacheSizeBytes(maxCacheSizeBytes)
|
, m_maxCacheSizeBytes(maxCacheSizeBytes)
|
||||||
|
#endif
|
||||||
, m_isRunning(true)
|
, m_isRunning(true)
|
||||||
, m_isPaused(false)
|
, m_isPaused(false)
|
||||||
, m_thread(&TrafficManager::ThreadRoutine, this)
|
, m_thread(&TrafficManager::ThreadRoutine, this)
|
||||||
@@ -113,7 +122,10 @@ void TrafficManager::SetEnabled(bool enabled)
|
|||||||
|
|
||||||
void TrafficManager::Clear()
|
void TrafficManager::Clear()
|
||||||
{
|
{
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
m_currentCacheSizeBytes = 0;
|
m_currentCacheSizeBytes = 0;
|
||||||
|
#endif
|
||||||
m_mwmCache.clear();
|
m_mwmCache.clear();
|
||||||
m_lastDrapeMwmsByRect.clear();
|
m_lastDrapeMwmsByRect.clear();
|
||||||
m_lastRoutingMwmsByRect.clear();
|
m_lastRoutingMwmsByRect.clear();
|
||||||
@@ -190,6 +202,7 @@ void TrafficManager::UpdateActiveMwms(m2::RectD const & rect,
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_activeMwmsChanged = true;
|
||||||
activeMwms.clear();
|
activeMwms.clear();
|
||||||
for (auto const & mwm : mwms)
|
for (auto const & mwm : mwms)
|
||||||
{
|
{
|
||||||
@@ -232,11 +245,309 @@ void TrafficManager::UpdateViewport(ScreenBase const & screen)
|
|||||||
UpdateActiveMwms(screen.ClipRect(), m_lastDrapeMwmsByRect, m_activeDrapeMwms);
|
UpdateActiveMwms(screen.ClipRect(), m_lastDrapeMwmsByRect, m_activeDrapeMwms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO make this work with multiple sources (e.g. Android)
|
||||||
|
bool TrafficManager::Subscribe(std::set<MwmSet::MwmId> & mwms)
|
||||||
|
{
|
||||||
|
// TODO what if we’re subscribed already?
|
||||||
|
// TODO
|
||||||
|
LOG(LINFO, ("Would subscribe to", mwms));
|
||||||
|
m_subscriptionId = "placeholder_subscription_id";
|
||||||
|
m_isPollNeeded = true; // would be false if we got a feed here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make this work with multiple sources (e.g. Android)
|
||||||
|
bool TrafficManager::ChangeSubscription(std::set<MwmSet::MwmId> & mwms)
|
||||||
|
{
|
||||||
|
// TODO what if we’re not subscribed yet?
|
||||||
|
// TODO
|
||||||
|
LOG(LINFO, ("Would change subscription", m_subscriptionId, "to", mwms));
|
||||||
|
m_isPollNeeded = true; // would be false if we got a feed here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TrafficManager::SetSubscriptionArea()
|
||||||
|
{
|
||||||
|
std::set<MwmSet::MwmId> activeMwms;
|
||||||
|
if (!IsSubscribed())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_activeMwmsChanged = false;
|
||||||
|
UniteActiveMwms(activeMwms);
|
||||||
|
}
|
||||||
|
if (!Subscribe(activeMwms))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (m_activeMwmsChanged)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_activeMwmsChanged = false;
|
||||||
|
UniteActiveMwms(activeMwms);
|
||||||
|
}
|
||||||
|
if (!ChangeSubscription(activeMwms))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make this work with multiple sources (e.g. Android)
|
||||||
|
void TrafficManager::Unsubscribe()
|
||||||
|
{
|
||||||
|
if (!IsSubscribed())
|
||||||
|
return;
|
||||||
|
// TODO
|
||||||
|
LOG(LINFO, ("Would unsubscribe from", m_subscriptionId));
|
||||||
|
m_subscriptionId.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TrafficManager::IsSubscribed()
|
||||||
|
{
|
||||||
|
return !m_subscriptionId.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make this work with multiple sources (e.g. Android)
|
||||||
|
// TODO deal with subscriptions rejected by the server (delete, resubscribe)
|
||||||
|
bool TrafficManager::Poll()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
//std::string path("/home/michael/src/organicmaps/data/test_data/traff/PL-A18-Krzyzowa-Lipiany.xml");
|
||||||
|
std::string path("/home/michael/src/organicmaps/data/test_data/traff/PL-A18-Krzyzowa-Lipiany-bidir.xml");
|
||||||
|
//std::string path("/home/michael/src/organicmaps/data/test_data/traff/LT-A1-Vezaiciai-Endriejavas.xml");
|
||||||
|
pugi::xml_document document;
|
||||||
|
auto const load_result = document.load_file(path.data());
|
||||||
|
if (!load_result)
|
||||||
|
{
|
||||||
|
LOG(LERROR, ("Can't load file", path, ":", load_result.description()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::setlocale(LC_ALL, "en_US.UTF-8");
|
||||||
|
traffxml::TraffFeed feed;
|
||||||
|
if (traffxml::ParseTraff(document, feed))
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_feeds.push_back(feed);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("An error occurred parsing the TraFF feed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrafficManager::Push(traffxml::TraffFeed feed)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_feeds.push_back(feed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrafficManager::UpdateMessageCache(std::map<std::string, traffxml::TraffMessage> & cache)
|
||||||
|
{
|
||||||
|
traffxml::TraffFeed feed;
|
||||||
|
// Thread-safe iteration over m_feeds, releasing the mutex during the loop
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
if (!m_feeds.empty())
|
||||||
|
{
|
||||||
|
feed = m_feeds.front();
|
||||||
|
m_feeds.erase(m_feeds.begin());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto message : feed)
|
||||||
|
{
|
||||||
|
LOG(LINFO, (" message:", message));
|
||||||
|
auto it = cache.find(message.m_id);
|
||||||
|
bool process = (it == cache.end());
|
||||||
|
if (!process)
|
||||||
|
process = (timegm(&(it->second.m_updateTime)) < timegm(&(message.m_updateTime)));
|
||||||
|
if (process)
|
||||||
|
cache.insert_or_assign(message.m_id, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrafficManager::InitializeDataSources(std::vector<FrozenDataSource> & dataSources)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* TODO can we include all available MWMs in the list (including non-active ones)?
|
||||||
|
* Then we could initialize the decoder once and for all.
|
||||||
|
*/
|
||||||
|
ForEachActiveMwm([this, &dataSources](MwmSet::MwmId const & mwmId) {
|
||||||
|
ASSERT(mwmId.IsAlive(), ());
|
||||||
|
// TODO do we need .SyncWithDisk() for the file?
|
||||||
|
for (size_t i = 0; i < dataSources.size(); i++)
|
||||||
|
dataSources[i].RegisterMap(mwmId.GetInfo()->GetLocalFile());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
void TrafficManager::DecodeMessage(openlr::OpenLRDecoder & decoder,
|
||||||
|
traffxml::TraffMessage & message, std::map<std::string,
|
||||||
|
traffic::TrafficInfo::Coloring> & trafficCache)
|
||||||
|
{
|
||||||
|
if (message.m_location)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
decoder.DecodeV3(segments, kNumDecoderThreads, paths);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < paths.size(); i++)
|
||||||
|
{
|
||||||
|
LOG(LINFO, (" Path", i));
|
||||||
|
LOG(LINFO, (" Partner segment ID:", paths[i].m_segmentId));
|
||||||
|
LOG(LINFO, (" Edges:", paths[i].m_path.size()));
|
||||||
|
for (size_t j = 0; j < paths[i].m_path.size(); j++)
|
||||||
|
{
|
||||||
|
LOG(LINFO, (" ", paths[i].m_path[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO store maxspeed in edges
|
||||||
|
// store decoded paths and speed groups in trafficCache
|
||||||
|
if (impact)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
for (size_t i = 0; i < paths.size(); i++)
|
||||||
|
for (size_t j = 0; j < paths[i].m_path.size(); j++)
|
||||||
|
{
|
||||||
|
std::string countryName = paths[i].m_path[j].GetFeatureId().m_mwmId.GetInfo()->GetCountryName();
|
||||||
|
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;
|
||||||
|
// TODO process all TrafficImpact fields and determine the speed group based on that
|
||||||
|
trafficCache[countryName][traffic::TrafficInfo::RoadSegmentId(fid, segment, direction)] = impact.value().m_speedGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TrafficManager::ThreadRoutine()
|
void TrafficManager::ThreadRoutine()
|
||||||
{
|
{
|
||||||
std::vector<MwmSet::MwmId> mwms;
|
std::vector<MwmSet::MwmId> mwms;
|
||||||
while (WaitForRequest(mwms))
|
while (WaitForRequest(mwms))
|
||||||
{
|
{
|
||||||
|
// TODO clean out expired messages
|
||||||
|
|
||||||
|
// poll is always needed, unless a new subscription or a subscription change returns a feed
|
||||||
|
m_isPollNeeded = true;
|
||||||
|
|
||||||
|
if (!SetSubscriptionArea())
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("SetSubscriptionArea failed."));
|
||||||
|
if (!IsSubscribed())
|
||||||
|
// do not skip out of the loop, we may need to process pushed feeds
|
||||||
|
LOG(LWARNING, ("No subscription, no traffic data will be retrieved."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch traffic data if subscribed, unless this has already happened in the previous step
|
||||||
|
if (m_isPollNeeded && IsSubscribed())
|
||||||
|
{
|
||||||
|
if (!Poll())
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Poll failed."));
|
||||||
|
// TODO set failed status somewhere and retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG(LINFO, (m_feeds.size(), "feed(s) in queue"));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO call on a temp struct, then unite with m_messageCache, processing only messages with changes
|
||||||
|
* (adding segments for new messages, removing segments for deleted messages, replacing segments
|
||||||
|
* for updated messages) and leaving all other segments untouched
|
||||||
|
*/
|
||||||
|
UpdateMessageCache(m_messageCache);
|
||||||
|
LOG(LINFO, (m_messageCache.size(), "message(s) in cache"));
|
||||||
|
|
||||||
|
// initialize the decoder
|
||||||
|
/*
|
||||||
|
* Access to `DataSource` is not thread-safe. The main app, which works with
|
||||||
|
* `EditableDataSource` (as the map can be edited), wraps map operations into a
|
||||||
|
* `FeaturesLoaderGuard`. The OpenLR decoder expects one `FrozenDataSource` (a read-only
|
||||||
|
* subclass) per worker thread – which works as long as the map is not modified.
|
||||||
|
* Edits are not relevant to the OpenLR decoder. However, if the edits modify MWM files (rather
|
||||||
|
* than being stored separately), this might confuse the `FrozenDataSource`. In this case, we
|
||||||
|
* would need to rewrite the OpenLR decoder to work with a `FeaturesLoaderGuard` (which is
|
||||||
|
* probably the more elegant way to do this anyway).
|
||||||
|
*/
|
||||||
|
std::vector<FrozenDataSource> dataSources(kNumDecoderThreads);
|
||||||
|
// TODO test with data source from framework
|
||||||
|
InitializeDataSources(dataSources);
|
||||||
|
openlr::OpenLRDecoder decoder(dataSources, m_countryParentNameGetterFn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map between country names and their colorings.
|
||||||
|
* TODO use MwmId as map keys:
|
||||||
|
* As long as we don’t/can‘t use the framework’s `DataSource` instance for the OpenLR decoder,
|
||||||
|
* `MwmId` instances from the decoder will not match those from the framework because of the
|
||||||
|
* way the identity operator is currently implemented (comparing `MwmInfo` instances rather than
|
||||||
|
* their contents). The ultimate goal is to do matching based on `MwmId`s, but that requires
|
||||||
|
* either running the OpenLR decoder off the shared `DataSource` or changing the way `MwmInfo`
|
||||||
|
* comparison works, eitehr of which may come with regressions and needs to be tested.
|
||||||
|
*/
|
||||||
|
std::map<std::string, traffic::TrafficInfo::Coloring> allMwmColoring;
|
||||||
|
for (auto [id, message] : m_messageCache)
|
||||||
|
{
|
||||||
|
LOG(LINFO, (" ", id, ":", message));
|
||||||
|
DecodeMessage(decoder, message, allMwmColoring);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set new coloring for MWMs
|
||||||
|
OnTrafficDataUpdate(allMwmColoring);
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
for (auto const & mwm : mwms)
|
for (auto const & mwm : mwms)
|
||||||
{
|
{
|
||||||
if (!mwm.IsAlive())
|
if (!mwm.IsAlive())
|
||||||
@@ -265,8 +576,11 @@ void TrafficManager::ThreadRoutine()
|
|||||||
m_trafficETags[mwm] = tag;
|
m_trafficETags[mwm] = tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
mwms.clear();
|
mwms.clear();
|
||||||
}
|
}
|
||||||
|
// Calling Unsubscribe() form the worker thread on exit makes thread synchronization easier
|
||||||
|
Unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TrafficManager::WaitForRequest(std::vector<MwmSet::MwmId> & mwms)
|
bool TrafficManager::WaitForRequest(std::vector<MwmSet::MwmId> & mwms)
|
||||||
@@ -335,6 +649,8 @@ void TrafficManager::RequestTrafficData()
|
|||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo && info)
|
void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo && info)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
@@ -364,7 +680,57 @@ void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo && info)
|
|||||||
|
|
||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void TrafficManager::OnTrafficDataUpdate(std::map<std::string, traffic::TrafficInfo::Coloring> & trafficCache)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Much of this code is copied and pasted together from old MWM code, with some minor adaptations:
|
||||||
|
*
|
||||||
|
* ForEachActiveMwm and the assertion (not the rest of the body) is from RequestTrafficData().
|
||||||
|
* trafficCache lookup is original code.
|
||||||
|
* TrafficInfo construction is taken fron TheadRoutine(), with modifications (different constructor).
|
||||||
|
* Updating m_mwmCache is from RequestTrafficData(MwmSet::MwmId const &, bool), with modifications.
|
||||||
|
* The remainder of the loop is from OnTrafficDataResponse(traffic::TrafficInfo &&), with minor modifications
|
||||||
|
*/
|
||||||
|
ForEachActiveMwm([this, trafficCache](MwmSet::MwmId const & mwmId) {
|
||||||
|
ASSERT(mwmId.IsAlive(), ());
|
||||||
|
auto tcit = trafficCache.find(mwmId.GetInfo()->GetCountryName());
|
||||||
|
if (tcit != trafficCache.end())
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
traffic::TrafficInfo::Coloring coloring = tcit->second;
|
||||||
|
LOG(LINFO, ("Setting new coloring for", mwmId, "with", coloring.size(), "entries"));
|
||||||
|
traffic::TrafficInfo info(mwmId, std::move(coloring));
|
||||||
|
|
||||||
|
m_mwmCache.try_emplace(mwmId, CacheEntry(steady_clock::now()));
|
||||||
|
|
||||||
|
auto it = m_mwmCache.find(mwmId);
|
||||||
|
if (it != m_mwmCache.end())
|
||||||
|
{
|
||||||
|
it->second.m_isLoaded = true;
|
||||||
|
it->second.m_lastResponseTime = steady_clock::now();
|
||||||
|
it->second.m_isWaitingForResponse = false;
|
||||||
|
it->second.m_lastAvailability = info.GetAvailability();
|
||||||
|
|
||||||
|
UpdateState();
|
||||||
|
|
||||||
|
if (!info.GetColoring().empty())
|
||||||
|
{
|
||||||
|
m_drapeEngine.SafeCall(&df::DrapeEngine::UpdateTraffic,
|
||||||
|
static_cast<traffic::TrafficInfo const &>(info));
|
||||||
|
|
||||||
|
// Update traffic colors for routing.
|
||||||
|
m_observer.OnTrafficInfoAdded(std::move(info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo && info)
|
void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo && info)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -401,6 +767,7 @@ void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo && info)
|
|||||||
m_observer.OnTrafficInfoAdded(std::move(info));
|
m_observer.OnTrafficInfoAdded(std::move(info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void TrafficManager::UniteActiveMwms(std::set<MwmSet::MwmId> & activeMwms) const
|
void TrafficManager::UniteActiveMwms(std::set<MwmSet::MwmId> & activeMwms) const
|
||||||
{
|
{
|
||||||
@@ -408,6 +775,8 @@ void TrafficManager::UniteActiveMwms(std::set<MwmSet::MwmId> & activeMwms) const
|
|||||||
activeMwms.insert(m_activeRoutingMwms.cbegin(), m_activeRoutingMwms.cend());
|
activeMwms.insert(m_activeRoutingMwms.cbegin(), m_activeRoutingMwms.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
void TrafficManager::ShrinkCacheToAllowableSize()
|
void TrafficManager::ShrinkCacheToAllowableSize()
|
||||||
{
|
{
|
||||||
// Calculating number of different active mwms.
|
// Calculating number of different active mwms.
|
||||||
@@ -429,6 +798,7 @@ void TrafficManager::ShrinkCacheToAllowableSize()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void TrafficManager::ClearCache(MwmSet::MwmId const & mwmId)
|
void TrafficManager::ClearCache(MwmSet::MwmId const & mwmId)
|
||||||
{
|
{
|
||||||
@@ -438,8 +808,11 @@ void TrafficManager::ClearCache(MwmSet::MwmId const & mwmId)
|
|||||||
|
|
||||||
if (it->second.m_isLoaded)
|
if (it->second.m_isLoaded)
|
||||||
{
|
{
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
ASSERT_GREATER_OR_EQUAL(m_currentCacheSizeBytes, it->second.m_dataSize, ());
|
ASSERT_GREATER_OR_EQUAL(m_currentCacheSizeBytes, it->second.m_dataSize, ());
|
||||||
m_currentCacheSizeBytes -= it->second.m_dataSize;
|
m_currentCacheSizeBytes -= it->second.m_dataSize;
|
||||||
|
#endif
|
||||||
|
|
||||||
m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTrafficCache, mwmId);
|
m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTrafficCache, mwmId);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include "openlr/openlr_decoder.hpp"
|
#include "openlr/openlr_decoder.hpp"
|
||||||
|
|
||||||
|
#include "traffxml/traff_model.hpp"
|
||||||
|
|
||||||
#include "geometry/point2d.hpp"
|
#include "geometry/point2d.hpp"
|
||||||
#include "geometry/polyline2d.hpp"
|
#include "geometry/polyline2d.hpp"
|
||||||
#include "geometry/screenbase.hpp"
|
#include "geometry/screenbase.hpp"
|
||||||
@@ -179,6 +181,85 @@ private:
|
|||||||
traffic::TrafficInfo::Availability m_lastAvailability;
|
traffic::TrafficInfo::Availability m_lastAvailability;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribes to a traffic service.
|
||||||
|
*
|
||||||
|
* @param mwms The MWMs for which data is needed.
|
||||||
|
* @return true on success, false on failure.
|
||||||
|
*/
|
||||||
|
bool Subscribe(std::set<MwmSet::MwmId> & mwms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Changes an existing traffic subscription.
|
||||||
|
*
|
||||||
|
* @param mwms The new set of MWMs for which data is needed.
|
||||||
|
* @return true on success, false on failure.
|
||||||
|
*/
|
||||||
|
bool ChangeSubscription(std::set<MwmSet::MwmId> & mwms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ensures we have a subscription covering all currently active MWMs.
|
||||||
|
*
|
||||||
|
* This method subscribes to a traffic service if not already subscribed, or changes the existing
|
||||||
|
* subscription otherwise.
|
||||||
|
*
|
||||||
|
* @return true on success, false on failure.
|
||||||
|
*/
|
||||||
|
bool SetSubscriptionArea();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unsubscribes from a traffic service we are subscribed to.
|
||||||
|
*/
|
||||||
|
void Unsubscribe();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether we are currently subscribed to a traffic service.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool IsSubscribed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Polls the traffic service for updates.
|
||||||
|
*
|
||||||
|
* @return true on success, false on failure.
|
||||||
|
*/
|
||||||
|
bool Poll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes a traffic feed received through a push operation.
|
||||||
|
*
|
||||||
|
* Push operations are not supported on all platforms.
|
||||||
|
*
|
||||||
|
* @param feed The traffic feed.
|
||||||
|
*/
|
||||||
|
void Push(traffxml::TraffFeed feed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Merges new messages from `m_feeds` into a message cache.
|
||||||
|
*
|
||||||
|
* Existing messages in `cache` will be overwritten by newer messages with the same ID in `m_feeds`.
|
||||||
|
*
|
||||||
|
* @param cache The message cache.
|
||||||
|
*/
|
||||||
|
void UpdateMessageCache(std::map<std::string, traffxml::TraffMessage> & cache);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the data sources for an OpenLR decoder.
|
||||||
|
*
|
||||||
|
* @param dataSources Receives the data sources for the decoder (one per worker thread).
|
||||||
|
*/
|
||||||
|
void InitializeDataSources(std::vector<FrozenDataSource> &dataSources);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a single message to its segments and their speed groups.
|
||||||
|
*
|
||||||
|
* @param decoder The OpenLR decoder instance.
|
||||||
|
* @param message The message to decode.
|
||||||
|
* @param trafficCache The cache in which all decoded paths with their speed groups will be stored.
|
||||||
|
*/
|
||||||
|
void DecodeMessage(openlr::OpenLRDecoder &decoder, traffxml::TraffMessage & message,
|
||||||
|
std::map<std::string, traffic::TrafficInfo::Coloring> & trafficCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Event loop for the traffic worker thread.
|
* @brief Event loop for the traffic worker thread.
|
||||||
*
|
*
|
||||||
@@ -199,8 +280,18 @@ private:
|
|||||||
* @param mwms Receives a list of MWMs for which to update traffic data.
|
* @param mwms Receives a list of MWMs for which to update traffic data.
|
||||||
* @return `true` during normal operation, `false` during teardown (signaling the event loop to exit).
|
* @return `true` during normal operation, `false` during teardown (signaling the event loop to exit).
|
||||||
*/
|
*/
|
||||||
|
// TODO mwms argument is no longer needed
|
||||||
bool WaitForRequest(std::vector<MwmSet::MwmId> & mwms);
|
bool WaitForRequest(std::vector<MwmSet::MwmId> & mwms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes new traffic data.
|
||||||
|
*
|
||||||
|
* @param trafficCache The new per-MWM colorings (preprocessed traffic information).
|
||||||
|
*/
|
||||||
|
void OnTrafficDataUpdate(std::map<std::string, traffic::TrafficInfo::Coloring> & trafficCache);
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
void OnTrafficDataResponse(traffic::TrafficInfo && info);
|
void OnTrafficDataResponse(traffic::TrafficInfo && info);
|
||||||
/**
|
/**
|
||||||
* @brief Processes a failed traffic request.
|
* @brief Processes a failed traffic request.
|
||||||
@@ -218,6 +309,7 @@ private:
|
|||||||
* @param info
|
* @param info
|
||||||
*/
|
*/
|
||||||
void OnTrafficRequestFailed(traffic::TrafficInfo && info);
|
void OnTrafficRequestFailed(traffic::TrafficInfo && info);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Updates `activeMwms` and requests traffic data.
|
* @brief Updates `activeMwms` and requests traffic data.
|
||||||
@@ -275,7 +367,10 @@ private:
|
|||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
void ClearCache(MwmSet::MwmId const & mwmId);
|
void ClearCache(MwmSet::MwmId const & mwmId);
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
void ShrinkCacheToAllowableSize();
|
void ShrinkCacheToAllowableSize();
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Updates the state of the traffic manager based on the state of all MWMs used by the renderer.
|
* @brief Updates the state of the traffic manager based on the state of all MWMs used by the renderer.
|
||||||
@@ -323,8 +418,11 @@ private:
|
|||||||
|
|
||||||
bool m_hasSimplifiedColorScheme = true;
|
bool m_hasSimplifiedColorScheme = true;
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
size_t m_maxCacheSizeBytes;
|
size_t m_maxCacheSizeBytes;
|
||||||
size_t m_currentCacheSizeBytes = 0;
|
size_t m_currentCacheSizeBytes = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::map<MwmSet::MwmId, CacheEntry> m_mwmCache;
|
std::map<MwmSet::MwmId, CacheEntry> m_mwmCache;
|
||||||
|
|
||||||
@@ -378,6 +476,41 @@ private:
|
|||||||
* @brief Worker thread which fetches traffic updates.
|
* @brief Worker thread which fetches traffic updates.
|
||||||
*/
|
*/
|
||||||
threads::SimpleThread m_thread;
|
threads::SimpleThread m_thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether active MWMs have changed since the last request.
|
||||||
|
*/
|
||||||
|
bool m_activeMwmsChanged = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The subscription ID received from the traffic server.
|
||||||
|
*
|
||||||
|
* An empty subscription ID means no subscription.
|
||||||
|
*/
|
||||||
|
std::string m_subscriptionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether a poll operation is needed.
|
||||||
|
*
|
||||||
|
* Used in the worker thread. A poll operation is needed unless a subscription (or subscription
|
||||||
|
* change) operation was performed before and a feed was received a part of it.
|
||||||
|
*/
|
||||||
|
bool m_isPollNeeded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Queue of feeds waiting to be processed.
|
||||||
|
*
|
||||||
|
* Threads must lock `m_mutex` before accessing `m_feeds`, as some platforms may receive feeds
|
||||||
|
* on multiple threads.
|
||||||
|
*/
|
||||||
|
std::vector<traffxml::TraffFeed> m_feeds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cache of all currently active TraFF messages.
|
||||||
|
*
|
||||||
|
* Keys are message IDs, values are messages.
|
||||||
|
*/
|
||||||
|
std::map<std::string, traffxml::TraffMessage> m_messageCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::string DebugPrint(TrafficManager::TrafficState state);
|
extern std::string DebugPrint(TrafficManager::TrafficState state);
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ bool ReadRemoteFile(string const & url, vector<uint8_t> & contents, int & errorC
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
string MakeRemoteURL(string const & name, uint64_t version)
|
string MakeRemoteURL(string const & name, uint64_t version)
|
||||||
{
|
{
|
||||||
if (string(TRAFFIC_DATA_BASE_URL).empty())
|
if (string(TRAFFIC_DATA_BASE_URL).empty())
|
||||||
@@ -73,6 +75,7 @@ string MakeRemoteURL(string const & name, uint64_t version)
|
|||||||
ss << url::UrlEncode(name) << TRAFFIC_FILE_EXTENSION;
|
ss << url::UrlEncode(name) << TRAFFIC_FILE_EXTENSION;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
char const kETag[] = "etag";
|
char const kETag[] = "etag";
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -91,6 +94,8 @@ TrafficInfo::RoadSegmentId::RoadSegmentId(uint32_t fid, uint16_t idx, uint8_t di
|
|||||||
uint8_t const TrafficInfo::kLatestKeysVersion = 0;
|
uint8_t const TrafficInfo::kLatestKeysVersion = 0;
|
||||||
uint8_t const TrafficInfo::kLatestValuesVersion = 0;
|
uint8_t const TrafficInfo::kLatestValuesVersion = 0;
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion)
|
TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion)
|
||||||
: m_mwmId(mwmId)
|
: m_mwmId(mwmId)
|
||||||
, m_currentDataVersion(currentDataVersion)
|
, m_currentDataVersion(currentDataVersion)
|
||||||
@@ -132,6 +137,7 @@ TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion
|
|||||||
LOG(LWARNING, ("Could not initialize traffic keys"));
|
LOG(LWARNING, ("Could not initialize traffic keys"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, Coloring && coloring)
|
TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, Coloring && coloring)
|
||||||
: m_mwmId(mwmId)
|
: m_mwmId(mwmId)
|
||||||
@@ -154,6 +160,8 @@ void TrafficInfo::SetTrafficKeysForTesting(vector<RoadSegmentId> const & keys)
|
|||||||
m_availability = Availability::IsAvailable;
|
m_availability = Availability::IsAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
bool TrafficInfo::ReceiveTrafficData(string & etag)
|
bool TrafficInfo::ReceiveTrafficData(string & etag)
|
||||||
{
|
{
|
||||||
vector<SpeedGroup> values;
|
vector<SpeedGroup> values;
|
||||||
@@ -169,6 +177,7 @@ bool TrafficInfo::ReceiveTrafficData(string & etag)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SpeedGroup TrafficInfo::GetSpeedGroup(RoadSegmentId const & id) const
|
SpeedGroup TrafficInfo::GetSpeedGroup(RoadSegmentId const & id) const
|
||||||
{
|
{
|
||||||
@@ -399,6 +408,8 @@ void TrafficInfo::DeserializeTrafficValues(vector<uint8_t> const & data,
|
|||||||
ASSERT_EQUAL(src.Size(), 0, ());
|
ASSERT_EQUAL(src.Size(), 0, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
// todo(@m) This is a temporary method. Do not refactor it.
|
// todo(@m) This is a temporary method. Do not refactor it.
|
||||||
bool TrafficInfo::ReceiveTrafficKeys()
|
bool TrafficInfo::ReceiveTrafficKeys()
|
||||||
{
|
{
|
||||||
@@ -437,7 +448,10 @@ bool TrafficInfo::ReceiveTrafficKeys()
|
|||||||
m_keys.swap(keys);
|
m_keys.swap(keys);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
TrafficInfo::ServerDataStatus TrafficInfo::ReceiveTrafficValues(string & etag, vector<SpeedGroup> & values)
|
TrafficInfo::ServerDataStatus TrafficInfo::ReceiveTrafficValues(string & etag, vector<SpeedGroup> & values)
|
||||||
{
|
{
|
||||||
if (!m_mwmId.IsAlive())
|
if (!m_mwmId.IsAlive())
|
||||||
@@ -481,6 +495,7 @@ TrafficInfo::ServerDataStatus TrafficInfo::ReceiveTrafficValues(string & etag, v
|
|||||||
m_availability = Availability::IsAvailable;
|
m_availability = Availability::IsAvailable;
|
||||||
return ServerDataStatus::New;
|
return ServerDataStatus::New;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool TrafficInfo::UpdateTrafficData(vector<SpeedGroup> const & values)
|
bool TrafficInfo::UpdateTrafficData(vector<SpeedGroup> const & values)
|
||||||
{
|
{
|
||||||
@@ -504,6 +519,8 @@ bool TrafficInfo::UpdateTrafficData(vector<SpeedGroup> const & values)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
TrafficInfo::ServerDataStatus TrafficInfo::ProcessFailure(platform::HttpClient const & request, int64_t const mwmVersion)
|
TrafficInfo::ServerDataStatus TrafficInfo::ProcessFailure(platform::HttpClient const & request, int64_t const mwmVersion)
|
||||||
{
|
{
|
||||||
switch (request.ErrorCode())
|
switch (request.ErrorCode())
|
||||||
@@ -532,6 +549,7 @@ TrafficInfo::ServerDataStatus TrafficInfo::ProcessFailure(platform::HttpClient c
|
|||||||
|
|
||||||
return ServerDataStatus::Error;
|
return ServerDataStatus::Error;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
string DebugPrint(TrafficInfo::RoadSegmentId const & id)
|
string DebugPrint(TrafficInfo::RoadSegmentId const & id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -105,9 +105,13 @@ public:
|
|||||||
|
|
||||||
TrafficInfo() = default;
|
TrafficInfo() = default;
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion);
|
TrafficInfo(MwmSet::MwmId const & mwmId, int64_t currentDataVersion);
|
||||||
|
#endif
|
||||||
|
|
||||||
TrafficInfo(MwmSet::MwmId const & mwmId, Coloring && coloring);
|
TrafficInfo(MwmSet::MwmId const & mwmId, Coloring && coloring);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a `TrafficInfo` instance with pre-populated traffic information.
|
* @brief Returns a `TrafficInfo` instance with pre-populated traffic information.
|
||||||
* @param coloring The traffic information (road segments and their speed group)
|
* @param coloring The traffic information (road segments and their speed group)
|
||||||
@@ -116,6 +120,8 @@ public:
|
|||||||
static TrafficInfo BuildForTesting(Coloring && coloring);
|
static TrafficInfo BuildForTesting(Coloring && coloring);
|
||||||
void SetTrafficKeysForTesting(std::vector<RoadSegmentId> const & keys);
|
void SetTrafficKeysForTesting(std::vector<RoadSegmentId> const & keys);
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
/**
|
/**
|
||||||
* @brief Fetches the latest traffic data from the server and updates the coloring and ETag.
|
* @brief Fetches the latest traffic data from the server and updates the coloring and ETag.
|
||||||
*
|
*
|
||||||
@@ -131,6 +137,7 @@ public:
|
|||||||
* @return True on success, false on failure.
|
* @return True on success, false on failure.
|
||||||
*/
|
*/
|
||||||
bool ReceiveTrafficData(std::string & etag);
|
bool ReceiveTrafficData(std::string & etag);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the latest known speed group by a feature segment's ID.
|
* @brief Returns the latest known speed group by a feature segment's ID.
|
||||||
@@ -198,19 +205,28 @@ private:
|
|||||||
|
|
||||||
friend void UnitTest_TrafficInfo_UpdateTrafficData();
|
friend void UnitTest_TrafficInfo_UpdateTrafficData();
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
// todo(@m) A temporary method. Remove it once the keys are added
|
// todo(@m) A temporary method. Remove it once the keys are added
|
||||||
// to the generator and the data is regenerated.
|
// to the generator and the data is regenerated.
|
||||||
bool ReceiveTrafficKeys();
|
bool ReceiveTrafficKeys();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
// Tries to read the values of the Coloring map from server into |values|.
|
// Tries to read the values of the Coloring map from server into |values|.
|
||||||
// Returns result of communicating with server as ServerDataStatus.
|
// Returns result of communicating with server as ServerDataStatus.
|
||||||
// Otherwise, returns false and does not change m_coloring.
|
// Otherwise, returns false and does not change m_coloring.
|
||||||
ServerDataStatus ReceiveTrafficValues(std::string & etag, std::vector<SpeedGroup> & values);
|
ServerDataStatus ReceiveTrafficValues(std::string & etag, std::vector<SpeedGroup> & values);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Updates the coloring and changes the availability status if needed.
|
// Updates the coloring and changes the availability status if needed.
|
||||||
bool UpdateTrafficData(std::vector<SpeedGroup> const & values);
|
bool UpdateTrafficData(std::vector<SpeedGroup> const & values);
|
||||||
|
|
||||||
|
// TODO no longer needed
|
||||||
|
#ifdef traffic_dead_code
|
||||||
ServerDataStatus ProcessFailure(platform::HttpClient const & request, int64_t const mwmVersion);
|
ServerDataStatus ProcessFailure(platform::HttpClient const & request, int64_t const mwmVersion);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The mapping from feature segments to speed groups (see speed_groups.hpp).
|
* @brief The mapping from feature segments to speed groups (see speed_groups.hpp).
|
||||||
|
|||||||
@@ -1,9 +1,246 @@
|
|||||||
#include "traffxml/traff_model.hpp"
|
#include "traffxml/traff_model.hpp"
|
||||||
|
|
||||||
|
#include "base/logging.hpp"
|
||||||
|
|
||||||
|
#include "geometry/mercator.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace traffxml
|
namespace traffxml
|
||||||
{
|
{
|
||||||
|
const std::map<EventType, traffic::SpeedGroup> kEventSpeedGroupMap{
|
||||||
|
// TODO Activity*, Authority*, Carpool* (not in enum yet)
|
||||||
|
{EventType::CongestionHeavyTraffic, traffic::SpeedGroup::G4},
|
||||||
|
{EventType::CongestionLongQueue, traffic::SpeedGroup::G0},
|
||||||
|
{EventType::CongestionNone, traffic::SpeedGroup::G5},
|
||||||
|
{EventType::CongestionNormalTraffic, traffic::SpeedGroup::G5},
|
||||||
|
{EventType::CongestionQueue, traffic::SpeedGroup::G2},
|
||||||
|
{EventType::CongestionQueueLikely, traffic::SpeedGroup::G3},
|
||||||
|
{EventType::CongestionSlowTraffic, traffic::SpeedGroup::G3},
|
||||||
|
{EventType::CongestionStationaryTraffic, traffic::SpeedGroup::G1},
|
||||||
|
{EventType::CongestionStationaryTrafficLikely, traffic::SpeedGroup::G2},
|
||||||
|
{EventType::CongestionTrafficBuildingUp, traffic::SpeedGroup::G4},
|
||||||
|
{EventType::CongestionTrafficCongestion, traffic::SpeedGroup::G3}, // TODO or G2? Unquantified, below normal
|
||||||
|
{EventType::CongestionTrafficFlowingFreely, traffic::SpeedGroup::G5},
|
||||||
|
{EventType::CongestionTrafficHeavierThanNormal, traffic::SpeedGroup::G4},
|
||||||
|
{EventType::CongestionTrafficLighterThanNormal, traffic::SpeedGroup::G5},
|
||||||
|
{EventType::CongestionTrafficMuchHeavierThanNormal, traffic::SpeedGroup::G3},
|
||||||
|
{EventType::CongestionTrafficProblem, traffic::SpeedGroup::G3}, // TODO or G2? Unquantified, below normal
|
||||||
|
// TODO Construction* (not in enum yet)
|
||||||
|
/*
|
||||||
|
* Some delay types have a duration which depends on the route. This is better expressed as a
|
||||||
|
* speed group, although the mapping may be somewhat arbitrary and may need to be corrected.
|
||||||
|
*/
|
||||||
|
{EventType::DelayDelay, traffic::SpeedGroup::G2},
|
||||||
|
{EventType::DelayDelayPossible, traffic::SpeedGroup::G3},
|
||||||
|
{EventType::DelayLongDelay, traffic::SpeedGroup::G1},
|
||||||
|
{EventType::DelayVeryLongDelay, traffic::SpeedGroup::G0},
|
||||||
|
// TODO Environment*, EquipmentStatus*, Hazard*, Incident* (not in enum yet)
|
||||||
|
// TODO complete Restriction* (not in enum yet)
|
||||||
|
{EventType::RestrictionBlocked, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionBlockedAhead, traffic::SpeedGroup::TempBlock},
|
||||||
|
//{EventType::RestrictionCarriagewayBlocked, traffic::SpeedGroup::TempBlock}, // TODO FIXME other carriageways may still be open
|
||||||
|
//{EventType::RestrictionCarriagewayClosed, traffic::SpeedGroup::TempBlock}, // TODO FIXME other carriageways may still be open
|
||||||
|
{EventType::RestrictionClosed, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionClosedAhead, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionEntryBlocked, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionExitBlocked, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionRampBlocked, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionRampClosed, traffic::SpeedGroup::TempBlock},
|
||||||
|
{EventType::RestrictionSpeedLimit, traffic::SpeedGroup::G4},
|
||||||
|
// TODO Security*, Transport*, Weather* (not in enum yet)
|
||||||
|
};
|
||||||
|
|
||||||
|
// none of the currently define events imply an explicit maxspeed
|
||||||
|
#if 0
|
||||||
|
const std::map<EventType, uint8_t> kEventMaxspeedMap{
|
||||||
|
// TODO Activity*, Authority*, Carpool* (not in enum yet)
|
||||||
|
// TODO Construction* (not in enum yet)
|
||||||
|
// TODO Environment*, EquipmentStatus*, Hazard*, Incident* (not in enum yet)
|
||||||
|
// TODO complete Restriction* (not in enum yet)
|
||||||
|
// TODO Security*, Transport*, Weather* (not in enum yet)
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const std::map<EventType, uint16_t> kEventDelayMap{
|
||||||
|
// TODO Activity*, Authority*, Carpool* (not in enum yet)
|
||||||
|
// TODO Construction* (not in enum yet)
|
||||||
|
//{EventType::DelayDelay, }, // mapped to speed group
|
||||||
|
//{EventType::DelayDelayPossible, }, // mapped to speed group
|
||||||
|
//{EventType::DelayLongDelay, }, // mapped to speed group
|
||||||
|
{EventType::DelaySeveralHours, 150}, // assumption: 2.5 hours
|
||||||
|
{EventType::DelayUncertainDuration, 60}, // assumption: 1 hour
|
||||||
|
//{EventType::DelayVeryLongDelay, }, // mapped to speed group
|
||||||
|
// TODO Environment*, EquipmentStatus*, Hazard*, Incident* (not in enum yet)
|
||||||
|
// TODO complete Restriction* (not in enum yet)
|
||||||
|
// TODO Security*, Transport*, Weather* (not in enum yet)
|
||||||
|
};
|
||||||
|
|
||||||
|
openlr::LocationReferencePoint Point::ToLrp()
|
||||||
|
{
|
||||||
|
openlr::LocationReferencePoint result;
|
||||||
|
result.m_latLon = ms::LatLon(this->m_coordinates.m_lat, this->m_coordinates.m_lon);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
openlr::LinearLocationReference TraffLocation::ToLinearLocationReference(bool backwards)
|
||||||
|
{
|
||||||
|
openlr::LinearLocationReference locationReference;
|
||||||
|
locationReference.m_points.clear();
|
||||||
|
std::vector<Point> points;
|
||||||
|
if (m_from)
|
||||||
|
points.push_back(m_from.value());
|
||||||
|
if (m_at)
|
||||||
|
points.push_back(m_at.value());
|
||||||
|
else if (m_via)
|
||||||
|
points.push_back(m_via.value());
|
||||||
|
if (m_to)
|
||||||
|
points.push_back(m_to.value());
|
||||||
|
if (backwards)
|
||||||
|
std::reverse(points.begin(), points.end());
|
||||||
|
// m_notVia is ignored as OpenLR does not support this functionality.
|
||||||
|
// TODO do we ensure a minimum of two reference points (from/to/at) when building the location?
|
||||||
|
CHECK_GREATER(points.size(), 1, ("At least two reference points must be given"));
|
||||||
|
for (auto point : points)
|
||||||
|
{
|
||||||
|
openlr::LocationReferencePoint lrp = point.ToLrp();
|
||||||
|
if (!locationReference.m_points.empty())
|
||||||
|
{
|
||||||
|
locationReference.m_points.back().m_distanceToNextPoint
|
||||||
|
= GuessDnp(locationReference.m_points.back(), lrp);
|
||||||
|
locationReference.m_points.back().m_functionalRoadClass = GetFrc();
|
||||||
|
}
|
||||||
|
locationReference.m_points.push_back(lrp);
|
||||||
|
}
|
||||||
|
return locationReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make segment ID in OpenLR a string value, and store messageId
|
||||||
|
std::vector<openlr::LinearSegment> TraffLocation::ToOpenLrSegments(std::string & messageId)
|
||||||
|
{
|
||||||
|
// Convert the location to a format understood by the OpenLR decoder.
|
||||||
|
std::vector<openlr::LinearSegment> segments;
|
||||||
|
int dirs = (m_directionality == Directionality::BothDirections) ? 2 : 1;
|
||||||
|
for (int dir = 0; dir < dirs; dir++)
|
||||||
|
{
|
||||||
|
openlr::LinearSegment segment;
|
||||||
|
// TODO make this a reference to the TraFF message ID
|
||||||
|
segment.m_segmentId = 42;
|
||||||
|
/*
|
||||||
|
* Segments generated from coordinates can have any number of points. Each point, except for
|
||||||
|
* the last point, must indicate the distance to the next point. Line properties (functional
|
||||||
|
* road class (FRC), form of way, bearing) or path properties other than distance to next point
|
||||||
|
* (lowest FRC to next point, againstDrivingDirection) are ignored.
|
||||||
|
* Segment length is never evaluated.
|
||||||
|
* TODO update OpenLR decoder to make all line and path properties optional.
|
||||||
|
*/
|
||||||
|
segment.m_source = openlr::LinearSegmentSource::FromCoordinatesTag;
|
||||||
|
segment.m_locationReference = this->ToLinearLocationReference(dir == 0 ? false : true);
|
||||||
|
|
||||||
|
segments.push_back(segment);
|
||||||
|
}
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
openlr::FunctionalRoadClass TraffLocation::GetFrc()
|
||||||
|
{
|
||||||
|
if (!m_roadClass)
|
||||||
|
return openlr::FunctionalRoadClass::NotAValue;
|
||||||
|
switch (m_roadClass.value())
|
||||||
|
{
|
||||||
|
case RoadClass::Motorway: return openlr::FunctionalRoadClass::FRC0;
|
||||||
|
case RoadClass::Trunk: return openlr::FunctionalRoadClass::FRC0;
|
||||||
|
case RoadClass::Primary: return openlr::FunctionalRoadClass::FRC1;
|
||||||
|
case RoadClass::Secondary: return openlr::FunctionalRoadClass::FRC2;
|
||||||
|
case RoadClass::Tertiary: return openlr::FunctionalRoadClass::FRC3;
|
||||||
|
/*
|
||||||
|
* TODO Revisit FRC for Other.
|
||||||
|
* Other corresponds to FRC4–7.
|
||||||
|
* FRC4 matches secondary/tertiary (zero score) and anything below (full score).
|
||||||
|
* FRC5–7 match anything below tertiary (full score); secondary/tertiary never match.
|
||||||
|
* Primary and above never matches any of these FRCs.
|
||||||
|
*/
|
||||||
|
case RoadClass::Other: return openlr::FunctionalRoadClass::FRC4;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TrafficImpact> TraffMessage::GetTrafficImpact()
|
||||||
|
{
|
||||||
|
// no events, no impact
|
||||||
|
if (m_events.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
// examine events
|
||||||
|
std::vector<TrafficImpact> impacts;
|
||||||
|
for (auto event : m_events)
|
||||||
|
{
|
||||||
|
TrafficImpact impact;
|
||||||
|
|
||||||
|
if (auto it = kEventSpeedGroupMap.find(event.m_type); it != kEventSpeedGroupMap.end())
|
||||||
|
impact.m_speedGroup = it->second;
|
||||||
|
|
||||||
|
if (event.m_speed)
|
||||||
|
impact.m_maxspeed = event.m_speed.value();
|
||||||
|
// TODO if no explicit speed given, look up in kEventMaxspeedMap (once we have entries)
|
||||||
|
|
||||||
|
// TODO if event is in delay class and has an explicit duration quantifier, use that and skip the map lookup.
|
||||||
|
if (auto it = kEventDelayMap.find(event.m_type); it != kEventDelayMap.end())
|
||||||
|
impact.m_delayMins = it->second;
|
||||||
|
|
||||||
|
// TempBlock overrules everything else, return immediately
|
||||||
|
if (impact.m_speedGroup == traffic::SpeedGroup::TempBlock)
|
||||||
|
return impact;
|
||||||
|
// if there is no actual impact, discard
|
||||||
|
if ((impact.m_maxspeed < kMaxspeedNone)
|
||||||
|
|| (impact.m_delayMins > 0)
|
||||||
|
|| (impact.m_speedGroup != traffic::SpeedGroup::Unknown))
|
||||||
|
impacts.push_back(impact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impacts.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
TrafficImpact result;
|
||||||
|
for (auto impact : impacts)
|
||||||
|
{
|
||||||
|
ASSERT(impact.m_speedGroup != traffic::SpeedGroup::TempBlock, ("Got SpeedGroup::TempBlock, which should not happen at this stage"));
|
||||||
|
if (result.m_speedGroup == traffic::SpeedGroup::Unknown)
|
||||||
|
result.m_speedGroup = impact.m_speedGroup;
|
||||||
|
// TempBlock cannot occur here, so we can do just a simple comparison
|
||||||
|
else if ((impact.m_speedGroup != traffic::SpeedGroup::Unknown) && (impact.m_speedGroup < result.m_speedGroup))
|
||||||
|
result.m_speedGroup = impact.m_speedGroup;
|
||||||
|
|
||||||
|
if (impact.m_maxspeed < result.m_maxspeed)
|
||||||
|
result.m_maxspeed = impact.m_maxspeed;
|
||||||
|
|
||||||
|
if (impact.m_delayMins > result.m_delayMins)
|
||||||
|
result.m_delayMins = impact.m_delayMins;
|
||||||
|
}
|
||||||
|
if ((result.m_maxspeed < kMaxspeedNone)
|
||||||
|
|| (result.m_delayMins > 0)
|
||||||
|
|| (result.m_speedGroup != traffic::SpeedGroup::Unknown))
|
||||||
|
return result;
|
||||||
|
else
|
||||||
|
// should never happen, unless we have a bug somewhere
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GuessDnp(openlr::LocationReferencePoint & p1, openlr::LocationReferencePoint & p2)
|
||||||
|
{
|
||||||
|
double doe = mercator::DistanceOnEarth(mercator::FromLatLon(p1.m_latLon),
|
||||||
|
mercator::FromLatLon(p2.m_latLon));
|
||||||
|
/*
|
||||||
|
* The tolerance factor is currently 1/0.6, or ~1.67, so that direct distance is just at the
|
||||||
|
* lower boundary for `openlr::LinearSegmentSource::FromLocationReferenceTag`. Since we use
|
||||||
|
* `openlr::LinearSegmentSource::FromCoordinatesTag`, different tolerance values apply,
|
||||||
|
* so direct distance is well within the lower boundary, where the upper boundary is ~6.67 times
|
||||||
|
* the direct distance. This should work even in mountain areas, where the shortest route from
|
||||||
|
* one valley to the next, using long-distance roads, can be up to ~3 times the direct distance.
|
||||||
|
*/
|
||||||
|
return doe / 0.6f + 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
string DebugPrint(LinearSegmentSource source)
|
string DebugPrint(LinearSegmentSource source)
|
||||||
{
|
{
|
||||||
@@ -140,6 +377,17 @@ std::string DebugPrint(EventType eventType)
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DebugPrint(TrafficImpact impact)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "TrafficImpact { ";
|
||||||
|
os << "speedGroup: " << DebugPrint(impact.m_speedGroup) << ", ";
|
||||||
|
os << "maxspeed: " << (impact.m_maxspeed == kMaxspeedNone ? "none" : std::to_string(impact.m_maxspeed)) << ", ";
|
||||||
|
os << "delayMins: " << impact.m_delayMins;
|
||||||
|
os << " }";
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string DebugPrint(Point point)
|
std::string DebugPrint(Point point)
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
|||||||
@@ -5,11 +5,17 @@
|
|||||||
#include "geometry/latlon.hpp"
|
#include "geometry/latlon.hpp"
|
||||||
#include "geometry/point2d.hpp"
|
#include "geometry/point2d.hpp"
|
||||||
|
|
||||||
|
#include "openlr/openlr_model.hpp"
|
||||||
|
|
||||||
|
#include "traffic/speed_groups.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace traffxml
|
namespace traffxml
|
||||||
{
|
{
|
||||||
|
constexpr uint8_t kMaxspeedNone = 255;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Date and time decoded from ISO 8601.
|
* @brief Date and time decoded from ISO 8601.
|
||||||
*
|
*
|
||||||
@@ -55,6 +61,13 @@ enum class RoadClass
|
|||||||
Other
|
Other
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When adding a new event class to this enum, be sure to do the following:
|
||||||
|
*
|
||||||
|
* * in `traff_model_xml.cpp`, add the corresponding mapping to `kEventClassMap`
|
||||||
|
* * in `traff_model.cpp`, extend `DebugPrint(EventClass)` to correctly process the new event classes
|
||||||
|
* * in this file, add event types for this class to `EventType`
|
||||||
|
*/
|
||||||
enum class EventClass
|
enum class EventClass
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
@@ -74,6 +87,16 @@ enum class EventClass
|
|||||||
Weather
|
Weather
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When adding a new event type to this enum, be sure to do the following:
|
||||||
|
*
|
||||||
|
* * in `traff_model_xml.cpp`, add the corresponding mapping to `kEventTypeMap`
|
||||||
|
* * in `traff_model.cpp`:
|
||||||
|
* * add speed group mappings in `kEventSpeedGroupMap`, if any
|
||||||
|
* * add maxspeed mappings in `kEventMaxspeedMap`, if any (uncomment if needed)
|
||||||
|
* * add delay mappings in `kEventDelayMap`, if any
|
||||||
|
* * extend `DebugPrint(TraffEvent)` to correctly process the new events
|
||||||
|
*/
|
||||||
enum class EventType
|
enum class EventType
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
@@ -128,8 +151,57 @@ enum class EventType
|
|||||||
// TODO Security*, Transport*, Weather*
|
// TODO Security*, Transport*, Weather*
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents the impact of one or more traffic events.
|
||||||
|
*
|
||||||
|
* Impact can be expressed in three ways:
|
||||||
|
*
|
||||||
|
* Traffic may flow at a certain percentage of the posted limit, often divided in bins. This is
|
||||||
|
* used by some traffic services which report e.g. “slow traffic”, “stationary traffic” or
|
||||||
|
* “queues”, and maps to speed groups in a straightforward way.
|
||||||
|
*
|
||||||
|
* Traffic may flow at, or be restricted to, a given speed. This is common with traffic flow
|
||||||
|
* measurement data, or with temporary speed limits. Converting this to a speed group requires
|
||||||
|
* knowledge of the regular speed limit.
|
||||||
|
*
|
||||||
|
* There may be a fixed delay, expressed as a duration in time. This may happen at checkpoints,
|
||||||
|
* at sections where traffic flow is limited or where there is single alternate-lane traffic.
|
||||||
|
* As the routing data model does not provide for explicit delays, they have to be converted into
|
||||||
|
* speed groups. Again, this requires knowledge of the regular travel time along the route, as well
|
||||||
|
* as its length.
|
||||||
|
*
|
||||||
|
* Closures can be expressed by setting `m_speedGroup` to `traffic::SpeedGroup::TempBlock`. If that
|
||||||
|
* is the case, the other struct members are to be ignored.
|
||||||
|
*/
|
||||||
|
struct TrafficImpact
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief The speed group for the affected segments, or `traffic::SpeedGroup::Unknown` if unknown.
|
||||||
|
*/
|
||||||
|
traffic::SpeedGroup m_speedGroup = traffic::SpeedGroup::Unknown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The speed limit, or speed of flowing traffic; `kMaxspeedNone` if none or unknown.
|
||||||
|
*/
|
||||||
|
uint8_t m_maxspeed = kMaxspeedNone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The delay in minutes; 0 if none or unknown.
|
||||||
|
*/
|
||||||
|
uint16_t m_delayMins = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct Point
|
struct Point
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @brief Converts the point to an OpenLR location reference point.
|
||||||
|
*
|
||||||
|
* Only coordinates are populated.
|
||||||
|
*
|
||||||
|
* @return An OpenLR LRP with the coordinates of the point.
|
||||||
|
*/
|
||||||
|
openlr::LocationReferencePoint ToLrp();
|
||||||
|
|
||||||
// TODO role?
|
// TODO role?
|
||||||
ms::LatLon m_coordinates = ms::LatLon::Zero();
|
ms::LatLon m_coordinates = ms::LatLon::Zero();
|
||||||
// TODO optional float m_distance;
|
// TODO optional float m_distance;
|
||||||
@@ -139,6 +211,34 @@ struct Point
|
|||||||
|
|
||||||
struct TraffLocation
|
struct TraffLocation
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @brief Converts the location to an OpenLR linear location reference.
|
||||||
|
*
|
||||||
|
* @param backwards If true, gnerates a linear location reference for the backwards direction,
|
||||||
|
* with the order of points reversed.
|
||||||
|
* @return An OpenLR linear location reference which corresponds to the location.
|
||||||
|
*/
|
||||||
|
openlr::LinearLocationReference ToLinearLocationReference(bool backwards);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts the location to a vector of OpenLR segments.
|
||||||
|
*
|
||||||
|
* Depending on the directionality, the resulting vector will hold one or two elements: one for
|
||||||
|
* the forward direction, and for bidirectional locations, a second one for the backward
|
||||||
|
* direction.
|
||||||
|
*
|
||||||
|
* @param messageId The message ID
|
||||||
|
* @return A vector holding the resulting OpenLR segments.
|
||||||
|
*/
|
||||||
|
std::vector<openlr::LinearSegment> ToOpenLrSegments(std::string & messageId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the OpenLR functional road class (FRC) matching `m_roadClass`.
|
||||||
|
*
|
||||||
|
* @return The FRC.
|
||||||
|
*/
|
||||||
|
openlr::FunctionalRoadClass GetFrc();
|
||||||
|
|
||||||
std::optional<std::string> m_country;
|
std::optional<std::string> m_country;
|
||||||
std::optional<std::string> m_destination;
|
std::optional<std::string> m_destination;
|
||||||
std::optional<std::string> m_direction;
|
std::optional<std::string> m_direction;
|
||||||
@@ -173,6 +273,20 @@ struct TraffEvent
|
|||||||
|
|
||||||
struct TraffMessage
|
struct TraffMessage
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the traffic impact of all events.
|
||||||
|
*
|
||||||
|
* If the message has multiple events, the traffic impact is determined separately for each
|
||||||
|
* event and then aggregated. Aggregation takes the most restrictive value in each category
|
||||||
|
* (speed group, maxspeed, delay).
|
||||||
|
*
|
||||||
|
* If the aggregated traffic impact includes `SpeedGroup::TempBlock`, its other members are to
|
||||||
|
* be considered invalid.
|
||||||
|
*
|
||||||
|
* @return The aggregated traffic impact, or `std::nullopt` if the message has no events with traffic impact.
|
||||||
|
*/
|
||||||
|
std::optional<TrafficImpact> GetTrafficImpact();
|
||||||
|
|
||||||
std::string m_id;
|
std::string m_id;
|
||||||
IsoTime m_receiveTime = {};
|
IsoTime m_receiveTime = {};
|
||||||
IsoTime m_updateTime = {};
|
IsoTime m_updateTime = {};
|
||||||
@@ -189,12 +303,27 @@ struct TraffMessage
|
|||||||
|
|
||||||
using TraffFeed = std::vector<TraffMessage>;
|
using TraffFeed = std::vector<TraffMessage>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Guess the distance to the next point.
|
||||||
|
*
|
||||||
|
* This is calculated as direct distance, multiplied with a tolerance factor to account for the
|
||||||
|
* fact that the road is not always a straight line.
|
||||||
|
*
|
||||||
|
* The result can be used to provide some semi-valid DNP values.
|
||||||
|
*
|
||||||
|
* @param p1 The first point.
|
||||||
|
* @param p2 The second point.
|
||||||
|
* @return The approximate distance on the ground, in meters.
|
||||||
|
*/
|
||||||
|
uint32_t GuessDnp(openlr::LocationReferencePoint & p1, openlr::LocationReferencePoint & p2);
|
||||||
|
|
||||||
std::string DebugPrint(IsoTime time);
|
std::string DebugPrint(IsoTime time);
|
||||||
std::string DebugPrint(Directionality directionality);
|
std::string DebugPrint(Directionality directionality);
|
||||||
std::string DebugPrint(Ramps ramps);
|
std::string DebugPrint(Ramps ramps);
|
||||||
std::string DebugPrint(RoadClass roadClass);
|
std::string DebugPrint(RoadClass roadClass);
|
||||||
std::string DebugPrint(EventClass eventClass);
|
std::string DebugPrint(EventClass eventClass);
|
||||||
std::string DebugPrint(EventType eventType);
|
std::string DebugPrint(EventType eventType);
|
||||||
|
std::string DebugPrint(TrafficImpact impact);
|
||||||
std::string DebugPrint(Point point);
|
std::string DebugPrint(Point point);
|
||||||
std::string DebugPrint(TraffLocation location);
|
std::string DebugPrint(TraffLocation location);
|
||||||
std::string DebugPrint(TraffEvent event);
|
std::string DebugPrint(TraffEvent event);
|
||||||
|
|||||||
@@ -462,9 +462,14 @@ bool LocationFromXml(pugi::xml_node node, TraffLocation & location)
|
|||||||
location.m_via = OptionalPointFromXml(node.child("via"));
|
location.m_via = OptionalPointFromXml(node.child("via"));
|
||||||
location.m_notVia = OptionalPointFromXml(node.child("not_via"));
|
location.m_notVia = OptionalPointFromXml(node.child("not_via"));
|
||||||
|
|
||||||
if (!location.m_from && !location.m_to && !location.m_at)
|
int numPoints = 0;
|
||||||
|
for (std::optional<Point> point : {location.m_from, location.m_to, location.m_at})
|
||||||
|
if (point)
|
||||||
|
numPoints++;
|
||||||
|
// single-point locations are not supported, locations without points are not valid
|
||||||
|
if (numPoints < 2)
|
||||||
{
|
{
|
||||||
LOG(LWARNING, ("Neither from, to nor at point is specified, ignoring location"));
|
LOG(LWARNING, ("Only", numPoints, "points of from/to/at specified, ignoring location"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user