[traffic] Penalize turns near endpoints

This improves decoding quality on urban multi-carriageway roads.

Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
mvglasow
2025-10-20 22:38:29 +03:00
parent c1340a9941
commit dda13b8d3d
4 changed files with 111 additions and 5 deletions

View File

@@ -0,0 +1,16 @@
<!--
From DE-realworld5.
Test case for turn penalty (eastern end).
-->
<feed>
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.29829.n.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_name="Mittlerer Ring" road_ref="B2R">
<from junction_name="Lerchenauer Straße">+48.176102 +11.558100</from>
<to junction_name="Petueltunnel">+48.178001 +11.572800</to>
</location>
<events>
<event class="CONGESTION" type="CONGESTION_QUEUE">
</event>
</events>
</message>
</feed>

View File

@@ -3,6 +3,7 @@
A combination of reference points on the opposite carriageway, high offroad cost, A combination of reference points on the opposite carriageway, high offroad cost,
allowing any road to be used and a low penalty results in a completely incorrect allowing any road to be used and a low penalty results in a completely incorrect
location (through a residential area rather than along the opposite carriageway). location (through a residential area rather than along the opposite carriageway).
Test case for turn penalty (eastern end) if the above is resolved.
--> -->
<feed> <feed>
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.22689.p.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00"> <message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.22689.p.1" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">

View File

@@ -0,0 +1,22 @@
<!--
Subset of realworld5.
Test case for turn penalty (both ends).
Junction names are the names of the crossing roads.
No segments of the crossing roads should be among the matched segments.
-->
<feed>
<message expiration_time="2025-10-09T21:37:08+03:00" id="tmc:d.1.15:d.1.46572.p.1,14" receive_time="2025-10-09T21:17:08+03:00" update_time="2025-10-09T21:17:08+03:00">
<location directionality="ONE_DIRECTION" fuzziness="LOW_RES" road_name="Leopoldstraße" road_ref="GM4">
<from junction_name="Potsdamer Straße">+48.167301 +11.586200</from>
<to junction_name="Ungererstraße">+48.164200 +11.586500</to>
</location>
<events>
<event class="INCIDENT" type="INCIDENT_ACCIDENT">
</event>
<event class="CONGESTION" type="CONGESTION_STATIONARY_TRAFFIC">
</event>
<event class="HAZARD" type="HAZARD_PASSABLE_WITH_CARE_BELOW_ELEVATION">
</event>
</events>
</message>
</feed>

View File

@@ -91,6 +91,21 @@ auto constexpr kAttributePenalty = 4;
*/ */
auto constexpr kReducedAttributePenalty = 2; auto constexpr kReducedAttributePenalty = 2;
/*
* Maximum distance in meters from location endpoint at which a turn penalty is applied
*/
auto constexpr kTurnPenaltyMaxDist = 100.0;
/*
* Minimum angle in degrees at which turn penalty is applied
*/
auto constexpr kTurnPenaltyMinAngle = 65.0;
/*
* Minimum angle in degrees at which the full turn penalty is applied
*/
auto constexpr kTurnPenaltyFullAngle = 90.0;
/* /*
* Invalid feature ID. * Invalid feature ID.
* Borrowed from indexer/feature_decl.hpp. * Borrowed from indexer/feature_decl.hpp.
@@ -563,15 +578,67 @@ double RoutingTraffDecoder::TraffEstimator::GetUTurnPenalty(Purpose /* purpose *
return 2 * 60; // seconds return 2 * 60; // seconds
} }
double RoutingTraffDecoder::TraffEstimator::GetTurnPenalty(Purpose /* purpose */, double /* angle */, double RoutingTraffDecoder::TraffEstimator::GetTurnPenalty(Purpose /* purpose */, double angle,
routing::RoadGeometry const & /* from_road */, routing::RoadGeometry const & from_road,
routing::RoadGeometry const & /* to_road */, routing::RoadGeometry const & to_road,
bool /* is_left_hand_traffic */) const bool is_left_hand_traffic) const
{ {
/* /*
* TODO determine if turn penalties make sense for the traffic decoder, else leave them out. * TODO determine if turn penalties make sense for the traffic decoder, else leave them out.
* `angle` seems to be in degrees, right is negative
* Turn is at the first or last point of the roads involved, compare points to find out.
*/ */
return 0.0; // Flip sign for left-hand traffic, so a positive angle always means a turn across traffic
if (is_left_hand_traffic)
angle *= -1;
// We only penalize sharp turns (above kTurnPenaltyMinAngle) across traffic
if (angle < kTurnPenaltyMinAngle)
return 0.0;
/*
* Identify coordinates of location endpoints and of the turn, and establish distance between the
* turn and the nearest endpoint.
*/
ms::LatLon from = m_decoder.m_message.value().m_location.value().m_from
? m_decoder.m_message.value().m_location.value().m_from.value().m_coordinates
: m_decoder.m_message.value().m_location.value().m_at.value().m_coordinates;
ms::LatLon to = m_decoder.m_message.value().m_location.value().m_to
? m_decoder.m_message.value().m_location.value().m_to.value().m_coordinates
: m_decoder.m_message.value().m_location.value().m_at.value().m_coordinates;
// Upper boundary for distance (approximately earth circumference)
double dist = 4.0e+7;
for (auto & fromPoint : { from_road.GetPoint(0), from_road.GetPoint(from_road.GetPointsCount() - 1) })
for (auto & toPoint : { to_road.GetPoint(0), to_road.GetPoint(to_road.GetPointsCount() - 1) })
if (fromPoint == toPoint)
for (auto & endpoint : { from, to })
{
auto newdist = ms::DistanceOnEarth(fromPoint, endpoint);
if (newdist < dist)
dist = newdist;
}
// We only penalize turns close to an endpoint
if (dist > kTurnPenaltyMaxDist)
return 0.0;
/*
* The penalty depends on the distance between the turn point and the nearest endpoint: the
* shorter the distance, the greater the penalty. This is obtained by subtracting the distance
* from `kTurnPenaltyMaxDist`.
*
* Above `kTurnPenaltyFullAngle`, the full turn penalty applies, i.e. the distance-based value is
* multiplied with `kAttributePenalty`.
*
* Between `kTurnPenaltyMinAngle` and `kTurnPenaltyFullAngle`, the penalty proportionally
* increases from 0 to the full value.
*/
double result = (kTurnPenaltyMaxDist - dist) * kAttributePenalty;
if (angle < kTurnPenaltyFullAngle)
result *= (angle - kTurnPenaltyMinAngle) / (kTurnPenaltyFullAngle - kTurnPenaltyMinAngle);
return result;
} }
double RoutingTraffDecoder::TraffEstimator::GetFerryLandingPenalty(Purpose /* purpose */) const double RoutingTraffDecoder::TraffEstimator::GetFerryLandingPenalty(Purpose /* purpose */) const