[traffic] Exclude roundabouts from decoded locations (with exceptions)

Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
mvglasow
2025-10-25 15:09:28 +03:00
parent 46d363ae24
commit 9065f45b21
4 changed files with 102 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
<!--
Test cases for reference points inside a roundabout.
If the roundabout itself is included in the decoded location, the message would
affect all roads which connect to it, resulting in incorrect routing, especially
in the case of closure events. For this reason, we must truncate roundabouts if
they occur at the start or end of the decoded location.
-->
<feed>
<message id="tmc:d.1.12:d.1.13962.p.5,11" receive_time="2018-07-27T10:22:22+02:00" update_time="2018-07-27T08:43:15Z" expiration_time="2018-07-27T09:43:15Z" urgency="URGENT">
<location fuzziness="LOW_RES" directionality="BOTH_DIRECTIONS" road_class="SECONDARY" road_ref="L87">
<from junction_name="Rheinau-Freistett/B36">+48.661098 +7.936800</from>
<to junction_name="Gambsheim (F)">+48.683701 +7.916600</to>
</location>
<events>
<event class="CONSTRUCTION" type="CONSTRUCTION_CONSTRUCTION_WORK">
</event>
<event class="RESTRICTION" type="RESTRICTION_CLOSED">
</event>
</events>
</message>
<message id="tmc:d.1.12:d.1.26212.p.5,11" receive_time="2018-07-27T10:21:49+02:00" update_time="2018-07-27T08:42:41Z" expiration_time="2018-07-27T09:42:41Z" urgency="URGENT">
<location fuzziness="LOW_RES" directionality="BOTH_DIRECTIONS" road_class="PRIMARY" road_ref="B428">
<from junction_name="Hackenheim">+49.822948 +7.906900</from>
<to junction_name="Frei-Laubersheim">+49.803650 +7.901450</to>
</location>
<events>
<event class="CONSTRUCTION" type="CONSTRUCTION_LONGTERM_ROADWORKS">
</event>
<event class="RESTRICTION" type="RESTRICTION_CLOSED">
</event>
</events>
</message>
<message id="tmc:d.1.12:d.1.48638.n.9,11" receive_time="2018-07-27T10:11:06+02:00" update_time="2018-07-27T08:31:43Z" expiration_time="2018-07-27T09:31:43Z" urgency="URGENT">
<location fuzziness="LOW_RES" directionality="BOTH_DIRECTIONS" road_class="PRIMARY" road_ref="B272">
<from junction_name="Hochstadt">+49.239750 +8.222300</from>
<to junction_name="Weingarten">+49.253448 +8.268100</to>
</location>
<events>
<event class="CONSTRUCTION" type="CONSTRUCTION_ROADWORKS">
</event>
<event class="RESTRICTION" type="RESTRICTION_CLOSED_AHEAD">
</event>
</events>
</message>
<message id="tmc:d.1.12:d.1.56576.n.9,11" receive_time="2018-07-27T10:22:53+02:00" update_time="2018-07-27T08:43:45Z" expiration_time="2018-07-27T09:43:45Z" urgency="URGENT">
<location fuzziness="LOW_RES" directionality="ONE_DIRECTION" road_class="PRIMARY" road_ref="B417">
<from junction_name="Diez">+50.372398 +8.038500</from>
<to junction_name="Diez">+50.370850 +8.004050</to>
</location>
<events>
<event class="CONSTRUCTION" type="CONSTRUCTION_RESURFACING_WORK">
</event>
<event class="RESTRICTION" type="RESTRICTION_CLOSED">
<supplementary_info class="VEHICLE" type="S_VEHICLE_THROUGH_TRAFFIC"/>
</event>
</events>
</message>
</feed>

View File

@@ -85,6 +85,7 @@ void DirectionsEngine::LoadPathAttributes(FeatureID const & featureId, LoadedPat
pathSegment.m_isOneWay = m_onewayChecker(types); pathSegment.m_isOneWay = m_onewayChecker(types);
pathSegment.m_roadNameInfo.m_isLink = pathSegment.m_isLink; pathSegment.m_roadNameInfo.m_isLink = pathSegment.m_isLink;
pathSegment.m_roadNameInfo.m_onRoundabout = pathSegment.m_onRoundabout;
pathSegment.m_roadNameInfo.m_junction_ref = ft->GetMetadata(feature::Metadata::FMD_JUNCTION_REF); pathSegment.m_roadNameInfo.m_junction_ref = ft->GetMetadata(feature::Metadata::FMD_JUNCTION_REF);
pathSegment.m_roadNameInfo.m_destination_ref = ft->GetMetadata(feature::Metadata::FMD_DESTINATION_REF); pathSegment.m_roadNameInfo.m_destination_ref = ft->GetMetadata(feature::Metadata::FMD_DESTINATION_REF);
pathSegment.m_roadNameInfo.m_destination = ft->GetMetadata(feature::Metadata::FMD_DESTINATION); pathSegment.m_roadNameInfo.m_destination = ft->GetMetadata(feature::Metadata::FMD_DESTINATION);

View File

@@ -121,6 +121,10 @@ public:
* @brief Whether the road is of a link type. * @brief Whether the road is of a link type.
*/ */
bool m_isLink = false; bool m_isLink = false;
/**
* @brief Whether the road is part of a roundabout.
*/
bool m_onRoundabout = false;
RoadNameInfo() = default; RoadNameInfo() = default;
RoadNameInfo(std::string name) : m_name(std::move(name)) {} RoadNameInfo(std::string name) : m_name(std::move(name)) {}

View File

@@ -1030,6 +1030,32 @@ void RoutingTraffDecoder::DecodeLocationDirection(traffxml::TraffMessage & messa
TruncateRoute(rsegments, checkpoints); TruncateRoute(rsegments, checkpoints);
/*
* `m_onRoundabout` is set only for the first segment after the junction. In order to identify
* all roundabout segments, cache the last segment with `m_onRoundabout` set. Any subsequent
* segment with the same MWM and feature ID is also a roundabout segment.
*/
routing::Segment lastRoundaboutSegment;
/*
* We usually discard roundabouts, unless the location is a point (`at` point set) or the entire
* decoded location is a roundabout.
*/
bool keepRoundabouts = true;
if (!message.m_location.value().m_at)
for (auto & rsegment : rsegments)
{
if (rsegment.GetRoadNameInfo().m_onRoundabout)
lastRoundaboutSegment = rsegment.GetSegment();
else if ((rsegment.GetSegment().GetMwmId() != lastRoundaboutSegment.GetMwmId())
|| (rsegment.GetSegment().GetFeatureId() != lastRoundaboutSegment.GetFeatureId()))
{
keepRoundabouts = false;
break;
}
}
if (!backwards && message.m_location.value().m_at && !message.m_location.value().m_to) if (!backwards && message.m_location.value().m_at && !message.m_location.value().m_to)
// fromat in forward direction, add last segment // fromat in forward direction, add last segment
AddDecodedSegment(decoded, rsegments.back().GetSegment()); AddDecodedSegment(decoded, rsegments.back().GetSegment());
@@ -1070,6 +1096,19 @@ void RoutingTraffDecoder::DecodeLocationDirection(traffxml::TraffMessage & messa
{ {
routing::Segment & segment = rsegment.GetSegment(); routing::Segment & segment = rsegment.GetSegment();
// Skip roundabouts to avoid side effects on crossing roads
if (!keepRoundabouts)
{
if (rsegment.GetRoadNameInfo().m_onRoundabout)
{
lastRoundaboutSegment = segment;
continue;
}
else if ((segment.GetMwmId() == lastRoundaboutSegment.GetMwmId())
&& (segment.GetFeatureId() == lastRoundaboutSegment.GetFeatureId()))
continue;
}
// If we have more than two checkpoints, fake segments can occur in the middle, skip them. // If we have more than two checkpoints, fake segments can occur in the middle, skip them.
if (segment.GetMwmId() == routing::kFakeNumMwmId) if (segment.GetMwmId() == routing::kFakeNumMwmId)
continue; continue;