Files
comaps/traffxml/traff_decoder.cpp
mvglasow 4c5fb21c33 [traffic] Use distances, not travel time, for weight in TraffEstimator
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00

667 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "traffxml/traff_decoder.hpp"
//#include "traffxml/traff_foo.hpp"
#include "openlr/decoded_path.hpp"
#include "openlr/openlr_decoder.hpp"
#include "openlr/openlr_model.hpp"
#include "routing/async_router.hpp"
#include "routing/checkpoints.hpp"
#include "routing/edge_estimator.hpp"
#include "routing/maxspeeds.hpp"
#include "routing/route.hpp"
#include "routing/router_delegate.hpp"
#include "routing_common/maxspeed_conversion.hpp"
#include "storage/routing_helpers.hpp"
#include "traffic/traffic_cache.hpp"
namespace traffxml
{
// 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;
// Timeout for the router in seconds, used by RoutingTraffDecoder
// TODO set to a sensible value
auto constexpr kRouterTimeoutSec = 30;
/*
* One meter per second. The TraffEstimator works on distance in meters, not travel time. For code
* which works with speeds and assumes cost to be time-based, a speed of 1 m/s means such
* calculations will effectively return distances in meters.
*/
auto constexpr kOneMpSInKmpH = 3.6;
/*
* Penalty factor for using a fake segment to get to a nearby road.
*/
auto constexpr kOffroadPenalty = 8;
/*
* Penalty factor for non-matching attributes
*/
auto constexpr kAttributePenalty = 4;
TraffDecoder::TraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter,
const CountryParentNameGetterFn & countryParentNameGetter,
std::map<std::string, TraffMessage> & messageCache)
: m_dataSource(dataSource)
, m_countryInfoGetterFn(countryInfoGetter)
, m_countryParentNameGetterFn(countryParentNameGetter)
, m_messageCache(messageCache)
{}
void TraffDecoder::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;
}
}
void TraffDecoder::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);
}
}
OpenLrV3TraffDecoder::OpenLrV3TraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter,
const CountryParentNameGetterFn & countryParentNameGetter,
std::map<std::string, TraffMessage> & messageCache)
: TraffDecoder(dataSource, countryInfoGetter, countryParentNameGetter, messageCache)
, m_openLrDecoder(dataSource, countryParentNameGetter)
{}
openlr::FunctionalRoadClass OpenLrV3TraffDecoder::GetRoadClassFrc(std::optional<RoadClass> & roadClass)
{
if (!roadClass)
return openlr::FunctionalRoadClass::NotAValue;
switch (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 FRC47.
* FRC4 matches secondary/tertiary (zero score) and anything below (full score).
* FRC57 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();
}
// TODO tweak formula based on FRC, FOW and direct distance (lower FRC roads may have more and sharper turns)
uint32_t OpenLrV3TraffDecoder::GuessDnp(Point & p1, Point & p2)
{
// direct distance
double doe = mercator::DistanceOnEarth(mercator::FromLatLon(p1.m_coordinates),
mercator::FromLatLon(p2.m_coordinates));
/*
* Acceptance boundaries for candidate paths are currently:
*
* for `openlr::LinearSegmentSource::FromLocationReferenceTag`, 0.6 to ~1.67 (i.e. 1/0.6) times
* the direct distance,
*
* for `openlr::LinearSegmentSource::FromCoordinatesTag`, 0.25 to 4 times the direct distance.
*
* A tolerance factor of 1/0.6 is the maximum for which direct distance would be accepted in all
* cases, with an upper boundary of at least ~2.78 times the direct distance. However, this may
* cause the actual distance to be overestimated and an incorrect route chosen as a result, as
* path candidates are scored based on the match between DNP and their length.
* Also, since we use `openlr::LinearSegmentSource::FromCoordinatesTag`, acceptance limits are
* much wider than that.
* In practice, the shortest route from one valley to the next in a mountain area is seldom more
* than 3 times the direct distance, based on a brief examination. This would be even within the
* limits of direct distance, hence we do not need a large correction factor for this scenario.
*
* Candidate values:
* 1.66 (1/0.6) upper boundary for direct distance to be just within the most stringent limits
* 1.41 (2^0.5) ratio between two sides of a square and its diagonal
* 1.3 close to the square root of 1.66 (halfway between 1 and 1.66)
* 1.19 close to the square root of 1.41
* 1 direct distance unmodified
*/
double dist = doe * 1.19f;
// if we have kilometric points, calculate nominal distance as the difference between them
if (p1.m_distance && p2.m_distance)
{
LOG(LINFO, ("Both points have distance, calculating nominal difference"));
float nominalDist = (p1.m_distance.value() - p2.m_distance.value()) * 1000.0;
if (nominalDist < 0)
nominalDist *= -1;
/*
* Plausibility check for nominal distance, as kilometric points along the route may not be
* continuous: discard if shorter than direct distance (geometrically impossible) or if longer
* than 4 times direct distance (somewhat arbitrary, based on the OpenLR acceptance limit for
* `openlr::LinearSegmentSource::FromCoordinatesTag`, as well as real-world observations of
* distances between two adjacent mountain valleys, which are up to roughly 3 times the direct
* distance). If nominal distance is outside these boundaries, discard it and use `dist` (direct
* distance with a tolerance factor).
*/
if ((nominalDist >= doe) && (nominalDist <= doe * 4))
dist = nominalDist;
else
LOG(LINFO, ("Nominal distance:", nominalDist, "direct distance:", doe, " discarding"));
}
return dist + 0.5f;
}
openlr::LocationReferencePoint OpenLrV3TraffDecoder::PointToLrp(Point & point)
{
openlr::LocationReferencePoint result;
result.m_latLon = ms::LatLon(point.m_coordinates.m_lat, point.m_coordinates.m_lon);
return result;
}
openlr::LinearLocationReference OpenLrV3TraffDecoder::TraffLocationToLinearLocationReference(TraffLocation & location, bool backwards)
{
openlr::LinearLocationReference locationReference;
locationReference.m_points.clear();
std::vector<Point> points;
if (location.m_from)
points.push_back(location.m_from.value());
if (location.m_at)
points.push_back(location.m_at.value());
else if (location.m_via)
points.push_back(location.m_via.value());
if (location.m_to)
points.push_back(location.m_to.value());
if (backwards)
std::reverse(points.begin(), points.end());
// m_notVia is ignored as OpenLR does not support this functionality.
CHECK_GREATER(points.size(), 1, ("At least two reference points must be given"));
for (size_t i = 0; i < points.size(); i++)
{
openlr::LocationReferencePoint lrp = PointToLrp(points[i]);
lrp.m_functionalRoadClass = GetRoadClassFrc(location.m_roadClass);
if (location.m_ramps.value_or(traffxml::Ramps::None) != traffxml::Ramps::None)
lrp.m_formOfWay = openlr::FormOfWay::Sliproad;
if (i < points.size() - 1)
{
lrp.m_distanceToNextPoint
= GuessDnp(points[i], points[i + 1]);
/*
* Somewhat hackish. LFRCNP is evaluated by the same function as FRC and the candidate is
* used or discarded based on whether a score was returned or not (the score itself is not
* used for LFRCNP). However, this means we can use FRC as LFRCNP.
*/
lrp.m_lfrcnp = GetRoadClassFrc(location.m_roadClass);
}
locationReference.m_points.push_back(lrp);
}
return locationReference;
}
// TODO make segment ID in OpenLR a string value, and store messageId
std::vector<openlr::LinearSegment> OpenLrV3TraffDecoder::TraffLocationToOpenLrSegments(TraffLocation & location, std::string & messageId)
{
// Convert the location to a format understood by the OpenLR decoder.
std::vector<openlr::LinearSegment> segments;
int dirs = (location.m_directionality == Directionality::BothDirections) ? 2 : 1;
for (int dir = 0; dir < dirs; dir++)
{
openlr::LinearSegment segment;
/*
* Segment IDs are used internally by the decoder but nowhere else.
* Since we decode TraFF locations one at a time, there are at most two segments in a single
* decoder instance (one segment per direction). Therefore, a segment ID derived from the
* direction is unique within the decoder instance.
*/
segment.m_segmentId = dir;
segment.m_messageId = messageId;
/*
* 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 = TraffLocationToLinearLocationReference(location, dir == 0 ? false : true);
segments.push_back(segment);
}
return segments;
}
/*
* 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 OpenLrV3TraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traffxml::MultiMwmColoring & decoded)
{
ASSERT(message.m_location, ("Message has no location"));
decoded.clear();
// Convert the location to a format understood by the OpenLR decoder.
std::vector<openlr::LinearSegment> segments
= TraffLocationToOpenLrSegments(message.m_location.value(), 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;
}
}
class TraffEstimator final : public routing::EdgeEstimator
{
public:
TraffEstimator(DataSource * dataSourcePtr, std::shared_ptr<routing::NumMwmIds> numMwmIds,
double maxWeightSpeedKMpH,
routing::SpeedKMpH const & offroadSpeedKMpH)
: EdgeEstimator(maxWeightSpeedKMpH, offroadSpeedKMpH, dataSourcePtr, numMwmIds)
{
}
// EdgeEstimator overrides:
double CalcSegmentWeight(routing::Segment const & segment, routing::RoadGeometry const & road, Purpose purpose) const override;
double GetUTurnPenalty(Purpose /* purpose */) const override
{
// Adds 2 minutes penalty for U-turn. The value is quite arbitrary
// and needs to be properly selected after a number of real-world
// experiments.
return 2 * 60; // seconds
}
double GetFerryLandingPenalty(Purpose purpose) const override
{
switch (purpose)
{
case Purpose::Weight: return 20 * 60; // seconds
case Purpose::ETA: return 20 * 60; // seconds
}
UNREACHABLE();
}
};
double TraffEstimator::CalcSegmentWeight(routing::Segment const & segment, routing::RoadGeometry const & road, Purpose purpose) const
{
double result = road.GetDistance(segment.GetSegmentIdx());
// TODO evaluate attributes and penalize accordingly
return result;
}
RoutingTraffDecoder::DecoderRouter::DecoderRouter(CountryParentNameGetterFn const & countryParentNameGetterFn,
routing::TCountryFileFn const & countryFileFn,
routing::CountryRectFn const & countryRectFn,
std::shared_ptr<routing::NumMwmIds> numMwmIds,
std::unique_ptr<m4::Tree<routing::NumMwmId>> numMwmTree,
DataSource & dataSource)
: routing::IndexRouter(routing::VehicleType::Car /* VehicleType vehicleType */,
false /* bool loadAltitudes */,
countryParentNameGetterFn,
countryFileFn,
countryRectFn,
numMwmIds,
std::move(numMwmTree),
//std::nullopt /* std::optional<traffic::TrafficCache> const & trafficCache */,
std::make_shared<TraffEstimator>(&dataSource, numMwmIds, kOneMpSInKmpH /* maxWeighSpeedKMpH */,
routing::SpeedKMpH(kOneMpSInKmpH / kOffroadPenalty /* weight */,
routing::kNotUsed /* eta */) /* offroadSpeedKMpH */),
dataSource)
//, m_directionsEngine(CreateDirectionsEngine(m_vehicleType, m_numMwmIds, m_dataSource)) // TODO we dont need directions, can we disable that?
{}
RoutingTraffDecoder::RoutingTraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter,
const CountryParentNameGetterFn & countryParentNameGetter,
std::map<std::string, TraffMessage> & messageCache)
: TraffDecoder(dataSource, countryInfoGetter, countryParentNameGetter, messageCache)
{
InitRouter();
}
bool RoutingTraffDecoder::InitRouter()
{
if (m_router)
return true;
// code mostly from RoutingManager::SetRouterImpl(RouterType)
/*
* RoutingManager::SetRouterImpl(RouterType) calls m_delegate.RegisterCountryFilesOnRoute(numMwmIds).
* m_delegate is the framework, and the routine cycles through the countries in storage.
* As we dont have access to storage, we get our country files from the data source.
*/
std::vector<std::shared_ptr<MwmInfo>> mwmsInfo;
m_dataSource.GetMwmsInfo(mwmsInfo);
for (auto mwmInfo : mwmsInfo)
m_numMwmIds->RegisterFile(mwmInfo->GetLocalFile().GetCountryFile());
if (m_numMwmIds->IsEmpty())
return false;
auto const countryFileGetter = [this](m2::PointD const & p) -> std::string {
// TODO (@gorshenin): fix CountryInfoGetter to return CountryFile
// instances instead of plain strings.
return m_countryInfoGetterFn().GetRegionCountryId(p);
};
auto const getMwmRectByName = [this](std::string const & countryId) -> m2::RectD {
return m_countryInfoGetterFn().GetLimitRectForLeaf(countryId);
};
m_router =
make_unique<RoutingTraffDecoder::DecoderRouter>(m_countryParentNameGetterFn,
countryFileGetter, getMwmRectByName, m_numMwmIds,
routing::MakeNumMwmTree(*m_numMwmIds, m_countryInfoGetterFn()),
m_dataSource);
return true;
}
// Copied from AsyncRouter
// static
void RoutingTraffDecoder::LogCode(routing::RouterResultCode code, double const elapsedSec)
{
switch (code)
{
case routing::RouterResultCode::StartPointNotFound:
LOG(LWARNING, ("Can't find start or end node"));
break;
case routing::RouterResultCode::EndPointNotFound:
LOG(LWARNING, ("Can't find end point node"));
break;
case routing::RouterResultCode::PointsInDifferentMWM:
LOG(LWARNING, ("Points are in different MWMs"));
break;
case routing::RouterResultCode::RouteNotFound:
LOG(LWARNING, ("Route not found"));
break;
case routing::RouterResultCode::RouteFileNotExist:
LOG(LWARNING, ("There is no routing file"));
break;
case routing::RouterResultCode::NeedMoreMaps:
LOG(LINFO,
("Routing can find a better way with additional maps, elapsed seconds:", elapsedSec));
break;
case routing::RouterResultCode::Cancelled:
LOG(LINFO, ("Route calculation cancelled, elapsed seconds:", elapsedSec));
break;
case routing::RouterResultCode::NoError:
LOG(LINFO, ("Route found, elapsed seconds:", elapsedSec));
break;
case routing::RouterResultCode::NoCurrentPosition:
LOG(LINFO, ("No current position"));
break;
case routing::RouterResultCode::InconsistentMWMandRoute:
LOG(LINFO, ("Inconsistent mwm and route"));
break;
case routing::RouterResultCode::InternalError:
LOG(LINFO, ("Internal error"));
break;
case routing::RouterResultCode::FileTooOld:
LOG(LINFO, ("File too old"));
break;
case routing::RouterResultCode::IntermediatePointNotFound:
LOG(LWARNING, ("Can't find intermediate point node"));
break;
case routing::RouterResultCode::TransitRouteNotFoundNoNetwork:
LOG(LWARNING, ("No transit route is found because there's no transit network in the mwm of "
"the route point"));
break;
case routing::RouterResultCode::TransitRouteNotFoundTooLongPedestrian:
LOG(LWARNING, ("No transit route is found because pedestrian way is too long"));
break;
case routing::RouterResultCode::RouteNotFoundRedressRouteError:
LOG(LWARNING, ("Route not found because of a redress route error"));
break;
case routing::RouterResultCode::HasWarnings:
LOG(LINFO, ("Route has warnings, elapsed seconds:", elapsedSec));
break;
}
}
void RoutingTraffDecoder::DecodeLocationDirection(traffxml::TraffMessage & message,
traffxml::MultiMwmColoring & decoded, bool backwards)
{
bool adjustToPrevRoute = false; // calculate a fresh route, no adjustments to previous one
uint64_t routeId = 0; // used in callbacks to identify the route, we might not need it at all
std::string routerName; // set later
std::vector<m2::PointD> points;
if (message.m_location.value().m_from)
points.push_back(mercator::FromLatLon(message.m_location.value().m_from.value().m_coordinates));
if (message.m_location.value().m_at)
points.push_back(mercator::FromLatLon(message.m_location.value().m_at.value().m_coordinates));
else if (message.m_location.value().m_via)
points.push_back(mercator::FromLatLon(message.m_location.value().m_via.value().m_coordinates));
if (message.m_location.value().m_to)
points.push_back(mercator::FromLatLon(message.m_location.value().m_to.value().m_coordinates));
if (backwards)
std::reverse(points.begin(), points.end());
// m_notVia is ignored as OpenLR does not support this functionality.
CHECK_GREATER(points.size(), 1, ("At least two reference points must be given"));
/*
* startDirection is the direction of travel at start. Can be m2::PointD::Zero() to ignore
* direction, or PositionAccumulator::GetDirection(), which basically returns the difference
* between the last position and an earlier one (offset between two points from which the
* direction of travel can be inferred).
*
* For our purposes, points[1] - points[0] would be as close as we could get to a start direction.
* This would be accurate on very straight roads, less accurate on not-so-straight ones. However,
* even on a near-straight road, the standard router (with the default EdgeEstimator) seemed quite
* unimpressed by the direction and insisted on starting off on the carriageway closest to the
* start point and sending us on a huge detour, instead of taking the direct route on the opposite
* carriageway.
*/
m2::PointD startDirection = m2::PointD::Zero();
routing::Checkpoints checkpoints(std::move(points));
/*
* This code is mostly lifted from:
* - AsyncRouter::CalculateRoute(Checkpoints const &, m2::PointD const &, bool, ReadyCallbackOwnership const &,
* NeedMoreMapsCallback const &, RemoveRouteCallback const &, ProgressCallback, uint32_t)
* - AsyncRouter::CalculateRoute()
*/
// AsyncRouter::CalculateRoute(with args)
/*
* AsyncRouter::CalculateRoute() has a `DelegateProxy`, which is private. We just need the return
* value of GetDelegate(), which is a `routing::RouterDelegate`, so use that instead. We dont
* nedd any of the callbacks, therefore we dont set them.
*/
routing::RouterDelegate delegate;
delegate.SetTimeout(kRouterTimeoutSec);
// AsyncRouter::CalculateRoute()
if (!m_router && !InitRouter())
return;
routerName = m_router->GetName();
// TODO is that for following a track? If so, can we use that with just 23 reference points?
//router->SetGuides(std::move(m_guides));
//m_guides.clear();
auto route = std::make_shared<routing::Route>(m_router->GetName(), routeId);
routing::RouterResultCode code;
base::Timer timer;
double elapsedSec = 0.0;
try
{
LOG(LINFO, ("Calculating the route of direct length", checkpoints.GetSummaryLengthBetweenPointsMeters(),
"m. checkpoints:", checkpoints, "startDirection:", startDirection, "router name:", m_router->GetName()));
// Run basic request.
code = m_router->CalculateRoute(checkpoints, startDirection, adjustToPrevRoute,
delegate, *route);
m_router->SetGuides({});
elapsedSec = timer.ElapsedSeconds(); // routing time
LogCode(code, elapsedSec);
LOG(LINFO, ("ETA:", route->GetTotalTimeSec(), "sec."));
}
catch (RootException const & e)
{
code = routing::RouterResultCode::InternalError;
LOG(LERROR, ("Exception happened while calculating route:", e.Msg()));
return;
}
if (code == routing::RouterResultCode::NoError)
{
LOG(LINFO, ("Decoded route:"));
for (auto rsegment : route->GetRouteSegments())
{
routing::Segment segment = rsegment.GetSegment();
if (segment.GetMwmId() == routing::kFakeNumMwmId)
{
LOG(LINFO, (" Fake segment:", segment));
continue;
}
LOG(LINFO, (" segment:", segment));
auto const countryFile = m_numMwmIds->GetFile(segment.GetMwmId());
MwmSet::MwmId mwmId = m_dataSource.GetMwmIdByCountryFile(countryFile);
auto const fid = segment.GetFeatureId();
auto const sid = segment.GetSegmentIdx();
uint8_t direction = segment.IsForward() ?
traffic::TrafficInfo::RoadSegmentId::kForwardDirection :
traffic::TrafficInfo::RoadSegmentId::kReverseDirection;
decoded[mwmId][traffic::TrafficInfo::RoadSegmentId(fid, sid, direction)] = traffic::SpeedGroup::Unknown;
}
}
}
void RoutingTraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traffxml::MultiMwmColoring & decoded)
{
ASSERT(message.m_location, ("Message has no location"));
decoded.clear();
int dirs = (message.m_location.value().m_directionality == Directionality::BothDirections) ? 2 : 1;
for (int dir = 0; dir < dirs; dir++)
DecodeLocationDirection(message, decoded, dir == 0 ? false : true /* backwards */);
}
} // namespace traffxml