diff --git a/data/test_data/traff/DE-RoundaboutEndpoint.xml b/data/test_data/traff/DE-RoundaboutEndpoint.xml new file mode 100755 index 000000000..616834699 --- /dev/null +++ b/data/test_data/traff/DE-RoundaboutEndpoint.xml @@ -0,0 +1,58 @@ + + + + + +48.661098 +7.936800 + +48.683701 +7.916600 + + + + + + + + + + + +49.822948 +7.906900 + +49.803650 +7.901450 + + + + + + + + + + + +49.239750 +8.222300 + +49.253448 +8.268100 + + + + + + + + + + + +50.372398 +8.038500 + +50.370850 +8.004050 + + + + + + + + + + diff --git a/libs/routing/directions_engine.cpp b/libs/routing/directions_engine.cpp index 95555632f..d5bc87a24 100644 --- a/libs/routing/directions_engine.cpp +++ b/libs/routing/directions_engine.cpp @@ -85,6 +85,7 @@ void DirectionsEngine::LoadPathAttributes(FeatureID const & featureId, LoadedPat pathSegment.m_isOneWay = m_onewayChecker(types); 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_destination_ref = ft->GetMetadata(feature::Metadata::FMD_DESTINATION_REF); pathSegment.m_roadNameInfo.m_destination = ft->GetMetadata(feature::Metadata::FMD_DESTINATION); diff --git a/libs/routing/route.hpp b/libs/routing/route.hpp index 1aa35d3dc..44d9cd17e 100644 --- a/libs/routing/route.hpp +++ b/libs/routing/route.hpp @@ -121,6 +121,10 @@ public: * @brief Whether the road is of a link type. */ bool m_isLink = false; + /** + * @brief Whether the road is part of a roundabout. + */ + bool m_onRoundabout = false; RoadNameInfo() = default; RoadNameInfo(std::string name) : m_name(std::move(name)) {} diff --git a/libs/traffxml/traff_decoder.cpp b/libs/traffxml/traff_decoder.cpp index c30693733..add330f9e 100644 --- a/libs/traffxml/traff_decoder.cpp +++ b/libs/traffxml/traff_decoder.cpp @@ -1030,6 +1030,32 @@ void RoutingTraffDecoder::DecodeLocationDirection(traffxml::TraffMessage & messa 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) // from–at in forward direction, add last segment AddDecodedSegment(decoded, rsegments.back().GetSegment()); @@ -1070,6 +1096,19 @@ void RoutingTraffDecoder::DecodeLocationDirection(traffxml::TraffMessage & messa { 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 (segment.GetMwmId() == routing::kFakeNumMwmId) continue;