diff --git a/drape_frontend/route_builder.cpp b/drape_frontend/route_builder.cpp index 3bf75e567d..faddcc4a1e 100644 --- a/drape_frontend/route_builder.cpp +++ b/drape_frontend/route_builder.cpp @@ -4,27 +4,25 @@ namespace { -std::vector> SplitSubroute(df::SubrouteConstPtr subroute) +std::map> SplitSubroute(df::SubrouteConstPtr subroute) { ASSERT(subroute != nullptr, ()); - std::vector> result; + std::map> result; if (subroute->m_styleType == df::SubrouteStyleType::Single) { ASSERT(!subroute->m_style.empty(), ()); - result.emplace_back(std::make_pair(0, subroute->m_polyline.GetSize() - 1)); + result[0] = std::make_pair(0, subroute->m_polyline.GetSize() - 1); return result; } - ASSERT_EQUAL(subroute->m_style.size() + 1, subroute->m_polyline.GetSize(), ()); - - size_t startIndex = 0; + size_t startStyleIndex = 0; for (size_t i = 1; i <= subroute->m_style.size(); ++i) { - if (i == subroute->m_style.size() || subroute->m_style[i] != subroute->m_style[startIndex]) + if (i == subroute->m_style.size() || subroute->m_style[i] != subroute->m_style[i - 1]) { - result.emplace_back(std::make_pair(startIndex, i)); - startIndex = i; + result[i - 1] = std::make_pair(subroute->m_style[startStyleIndex].m_startIndex, subroute->m_style[i - 1].m_endIndex); + startStyleIndex = i; } } return result; @@ -53,8 +51,8 @@ void RouteBuilder::Build(dp::DrapeID subrouteId, SubrouteConstPtr subroute, subrouteData.reserve(subrouteIndices.size()); for (auto const & indices : subrouteIndices) { - subrouteData.push_back(RouteShape::CacheRoute(subrouteId, subroute, indices.first, - indices.second, recacheId, textures)); + subrouteData.push_back(RouteShape::CacheRoute(subrouteId, subroute, indices.second.first, + indices.second.second, indices.first, recacheId, textures)); } auto markersData = RouteShape::CacheMarkers(subrouteId, subroute, recacheId, textures); diff --git a/drape_frontend/route_renderer.cpp b/drape_frontend/route_renderer.cpp index 179af5d09c..0b95b80aa0 100644 --- a/drape_frontend/route_renderer.cpp +++ b/drape_frontend/route_renderer.cpp @@ -28,24 +28,12 @@ std::string const kRouteMaskBicycle = "RouteMaskBicycle"; std::string const kRouteArrowsMaskBicycle = "RouteArrowsMaskBicycle"; std::string const kRouteMaskPedestrian = "RouteMaskPedestrian"; +// TODO(@darina) Use separate colors. +std::string const kGateColor = "Route"; +std::string const kTransitOutlineColor = "RouteMarkPrimaryTextOutline"; + namespace { -std::vector const kHalfWidthInPixelCar = -{ - // 1 2 3 4 5 6 7 8 9 10 - 1.0f, 1.0f, 1.5f, 1.5f, 1.5f, 2.0f, 2.0f, 2.0f, 2.5f, 2.5f, - //11 12 13 14 15 16 17 18 19 20 - 3.0f, 3.0f, 4.0f, 5.0f, 6.0, 8.0f, 10.0f, 10.0f, 18.0f, 27.0f -}; - -std::vector const kHalfWidthInPixelOthers = -{ - // 1 2 3 4 5 6 7 8 9 10 - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.2f, 1.2f, - //11 12 13 14 15 16 17 18 19 20 - 1.5f, 1.5f, 2.0f, 2.5f, 3.0, 4.0f, 5.0f, 5.0f, 9.0f, 13.0f -}; - std::vector const kPreviewPointRadiusInPixel = { // 1 2 3 4 5 6 7 8 9 10 @@ -68,9 +56,9 @@ void InterpolateByZoom(SubrouteConstPtr const & subroute, ScreenBase const & scr float lerpCoef = 0.0f; ExtractZoomFactors(screen, zoom, index, lerpCoef); - std::vector const * halfWidthInPixel = &kHalfWidthInPixelOthers; + std::vector const * halfWidthInPixel = &kRouteHalfWidthInPixelOthers; if (subroute->m_routeType == RouteType::Car || subroute->m_routeType == RouteType::Taxi) - halfWidthInPixel = &kHalfWidthInPixelCar; + halfWidthInPixel = &kRouteHalfWidthInPixelCar; halfWidth = InterpolateByZoomLevels(index, lerpCoef, *halfWidthInPixel); halfWidth *= static_cast(df::VisualParams::Instance().GetVisualScale()); @@ -407,7 +395,7 @@ void RouteRenderer::RenderSubroute(SubrouteInfo const & subrouteInfo, size_t sub } dp::GLState const & state = subrouteData->m_renderProperty.m_state; - size_t const styleIndex = subrouteData->m_startPointIndex; + size_t const styleIndex = subrouteData->m_styleIndex; ASSERT_LESS(styleIndex, subrouteInfo.m_subroute->m_style.size(), ()); auto const & style = subrouteInfo.m_subroute->m_style[styleIndex]; diff --git a/drape_frontend/route_renderer.hpp b/drape_frontend/route_renderer.hpp index 718cb2ace3..f197e92a3d 100644 --- a/drape_frontend/route_renderer.hpp +++ b/drape_frontend/route_renderer.hpp @@ -22,6 +22,8 @@ extern std::string const kRouteColor; extern std::string const kRouteOutlineColor; extern std::string const kRoutePedestrian; extern std::string const kRouteBicycle; +extern std::string const kGateColor; +extern std::string const kTransitOutlineColor; class RouteRenderer final { diff --git a/drape_frontend/route_shape.cpp b/drape_frontend/route_shape.cpp index fb2e08f10b..93d93db456 100644 --- a/drape_frontend/route_shape.cpp +++ b/drape_frontend/route_shape.cpp @@ -15,6 +15,22 @@ namespace df { +std::vector const kRouteHalfWidthInPixelCar = +{ + // 1 2 3 4 5 6 7 8 9 10 + 1.0f, 1.0f, 1.5f, 1.5f, 1.5f, 2.0f, 2.0f, 2.0f, 2.5f, 2.5f, + //11 12 13 14 15 16 17 18 19 20 + 3.0f, 3.0f, 4.0f, 5.0f, 6.0, 8.0f, 10.0f, 10.0f, 18.0f, 27.0f +}; + +std::vector const kRouteHalfWidthInPixelOthers = +{ + // 1 2 3 4 5 6 7 8 9 10 + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.2f, 1.2f, + //11 12 13 14 15 16 17 18 19 20 + 1.5f, 1.5f, 2.0f, 2.5f, 3.0, 4.0f, 5.0f, 5.0f, 9.0f, 13.0f +}; + namespace { float const kLeftSide = 1.0f; @@ -474,7 +490,7 @@ void RouteShape::CacheRouteArrows(ref_ptr mng, m2::PolylineD } drape_ptr RouteShape::CacheRoute(dp::DrapeID subrouteId, SubrouteConstPtr subroute, - size_t startIndex, size_t endIndex, int recacheId, + size_t startIndex, size_t endIndex, size_t styleIndex, int recacheId, ref_ptr textures) { ASSERT_LESS(startIndex, endIndex, ()); @@ -504,6 +520,7 @@ drape_ptr RouteShape::CacheRoute(dp::DrapeID subrouteId, Subro subrouteData->m_subroute = subroute; subrouteData->m_startPointIndex = startIndex; subrouteData->m_endPointIndex = endIndex; + subrouteData->m_styleIndex = styleIndex; subrouteData->m_pivot = subroute->m_polyline.GetLimitRect().Center(); subrouteData->m_recacheId = recacheId; subrouteData->m_distanceOffset = subroute->m_polyline.GetLength(startIndex); @@ -514,7 +531,7 @@ drape_ptr RouteShape::CacheRoute(dp::DrapeID subrouteId, Subro static_cast(subroute->m_baseDepthIndex * kDepthPerSubroute), geometry, joinsGeometry); - auto state = CreateGLState(subroute->m_style[startIndex].m_pattern.m_isDashed ? + auto state = CreateGLState(subroute->m_style[styleIndex].m_pattern.m_isDashed ? gpu::ROUTE_DASH_PROGRAM : gpu::ROUTE_PROGRAM, RenderState::GeometryLayer); state.SetColorTexture(textures->GetSymbolsTexture()); diff --git a/drape_frontend/route_shape.hpp b/drape_frontend/route_shape.hpp index 51c228459f..1bd3e595a2 100644 --- a/drape_frontend/route_shape.hpp +++ b/drape_frontend/route_shape.hpp @@ -35,6 +35,9 @@ float const kArrowTailFactor = static_cast(2.0 * kArrowTailTextureWidth / double const kArrowHeightFactor = kArrowTextureHeight / kArrowBodyHeight; double const kArrowAspect = kArrowTextureWidth / kArrowTextureHeight; +extern std::vector const kRouteHalfWidthInPixelCar; +extern std::vector const kRouteHalfWidthInPixelOthers; + enum class RouteType : uint8_t { Car, @@ -78,6 +81,8 @@ struct SubrouteStyle df::ColorConstant m_color; df::ColorConstant m_outlineColor; df::RoutePattern m_pattern; + size_t m_startIndex = 0; + size_t m_endIndex = 0; SubrouteStyle() = default; SubrouteStyle(df::ColorConstant const & color) @@ -168,6 +173,7 @@ struct SubrouteData : public BaseSubrouteData SubrouteConstPtr m_subroute; size_t m_startPointIndex = 0; size_t m_endPointIndex = 0; + size_t m_styleIndex = 0; double m_distanceOffset = 0.0; }; @@ -193,7 +199,7 @@ public: using TMarkersGeometryBuffer = buffer_vector; static drape_ptr CacheRoute(dp::DrapeID subrouteId, SubrouteConstPtr subroute, - size_t startIndex, size_t endIndex, int recacheId, + size_t startIndex, size_t endIndex, size_t styleIndex, int recacheId, ref_ptr textures); static drape_ptr CacheMarkers(dp::DrapeID subrouteId, diff --git a/geometry/polyline2d.hpp b/geometry/polyline2d.hpp index 673df5cd88..2bc82d64b5 100644 --- a/geometry/polyline2d.hpp +++ b/geometry/polyline2d.hpp @@ -90,6 +90,12 @@ public: m_points.insert(m_points.end(), poly.m_points.cbegin(), poly.m_points.cend()); } + template + void Append(Iter beg, Iter end) + { + m_points.insert(m_points.end(), beg, end); + } + void PopBack() { ASSERT(!m_points.empty(), ()); diff --git a/map/routing_manager.cpp b/map/routing_manager.cpp index 2adf40c7b2..8bdc8a1454 100644 --- a/map/routing_manager.cpp +++ b/map/routing_manager.cpp @@ -17,6 +17,7 @@ #include "routing_common/num_mwm_id.hpp" +#include "drape_frontend/color_constants.hpp" #include "drape_frontend/drape_engine.hpp" #include "indexer/map_style_reader.hpp" @@ -52,6 +53,8 @@ namespace char const kRouterTypeKey[] = "router"; double const kRouteScaleMultiplier = 1.5; +float const kStopMarkerScale = 2.0f; +float const kTransferMarkerScale = 4.0f; string const kRoutePointsFile = "route_points.dat"; @@ -87,16 +90,313 @@ void FillTrafficForRendering(vector const & segments, traffic.push_back(s.GetTraffic()); } -void FillTransitStyleForRendering(vector const & segments, - vector & subrouteStyles) +struct TransitTitle +{ + TransitTitle() = default; + TransitTitle(string const & text, df::ColorConstant const & color) : m_text(text), m_color(color) {} + + string m_text; + df::ColorConstant m_color; +}; + +struct TransitMarkInfo +{ + m2::PointD m_point; + vector m_titles; +}; + +using GetMwmIdFn = std::function; +void CollectTransitDisplayInfo(vector const & segments, GetMwmIdFn const & getMwmIdFn, + TransitDisplayInfos & transitDisplayInfos) { - subrouteStyles.clear(); - subrouteStyles.reserve(segments.size()); for (auto const & s : segments) { - // TODO: AddTransitType. - // TODO: Use transit type to determine color and pattern. - subrouteStyles.emplace_back(df::kRoutePedestrian); + if (!s.HasTransitInfo()) + continue; + + auto const mwmId = getMwmIdFn(s.GetSegment().GetMwmId()); + + auto & mwmTransit = transitDisplayInfos[mwmId]; + if (mwmTransit == nullptr) + mwmTransit = my::make_unique(); + + TransitInfo const & transitInfo = s.GetTransitInfo(); + switch (transitInfo.GetType()) + { + case TransitInfo::Type::Edge: + { + auto const & edge = transitInfo.GetEdge(); + + mwmTransit->m_stops[edge.m_stop1Id] = {}; + mwmTransit->m_stops[edge.m_stop2Id] ={}; + mwmTransit->m_lines[edge.m_lineId] = {}; + for (auto const & shapeId : edge.m_shapeIds) + mwmTransit->m_shapes[shapeId] = {}; + } + break; + case TransitInfo::Type::Transfer: + { + auto const & transfer = transitInfo.GetTransfer(); + mwmTransit->m_stops[transfer.m_stop1Id] = {}; + mwmTransit->m_stops[transfer.m_stop2Id] = {}; + } + break; + case TransitInfo::Type::Gate: + { + auto const & gate = transitInfo.GetGate(); + auto const featureId = FeatureID(mwmId, gate.m_featureId); + mwmTransit->m_features[featureId] = {}; + } + break; + } + } +} + +void AddTransitGateSegment(m2::PointD const & destPoint, df::Subroute & subroute) +{ + df::SubrouteStyle style(df::kGateColor, df::RoutePattern(1.0, 1.0)); + style.m_startIndex = subroute.m_polyline.GetSize() - 1; + auto const vec = destPoint - subroute.m_polyline.Back(); + subroute.m_polyline.Add(destPoint); + style.m_endIndex = subroute.m_polyline.GetSize() - 1; + subroute.m_style.push_back(style); +} + +void AddTransitPedestrianSegment(m2::PointD const & destPoint, df::Subroute & subroute) +{ + df::SubrouteStyle style(df::kRoutePedestrian, df::RoutePattern(4.0, 2.0)); + style.m_startIndex = subroute.m_polyline.GetSize() - 1; + subroute.m_polyline.Add(destPoint); + style.m_endIndex = subroute.m_polyline.GetSize() - 1; + subroute.m_style.push_back(style); +} + +void AddTransitShapes(std::vector const & shapeIds, TransitShapesInfo const & shapes, + df::ColorConstant const & color, bool isInverted, df::Subroute & subroute) +{ + df::SubrouteStyle style(color); + style.m_startIndex = subroute.m_polyline.GetSize() - 1; + + for (auto it = shapeIds.crbegin(); it != shapeIds.crend(); ++it) + { + auto const & shapePolyline = shapes.at(*it).GetPolyline(); + if (isInverted) + subroute.m_polyline.Append(shapePolyline.crbegin(), shapePolyline.crend()); + else + subroute.m_polyline.Append(shapePolyline.cbegin(), shapePolyline.cend()); + } + + style.m_endIndex = subroute.m_polyline.GetSize() - 1; + subroute.m_style.emplace_back(move(style)); +} + +void FillTransitStyleForRendering(vector const & segments, TransitReadManager & transitReadManager, + GetMwmIdFn const & getMwmIdFn, df::Subroute & subroute, + vector & transitMarks) +{ + TransitDisplayInfos transitDisplayInfos; + CollectTransitDisplayInfo(segments, getMwmIdFn, transitDisplayInfos); + + // Read transit display info. + transitReadManager.GetTransitDisplayInfo(transitDisplayInfos); + + subroute.m_styleType = df::SubrouteStyleType::Multiple; + subroute.m_style.clear(); + subroute.m_style.reserve(segments.size()); + + df::ColorConstant lastColor; + m2::PointD lastDir; + + df::SubrouteMarker marker; + TransitMarkInfo transitMarkInfo; + + double distance = 0; + bool pendingEntrance = false; + + for (auto const & s : segments) + { + if (!s.HasTransitInfo()) + { + AddTransitPedestrianSegment(s.GetJunction().GetPoint(), subroute); + lastColor = ""; + continue; + } + + auto const mwmId = getMwmIdFn(s.GetSegment().GetMwmId()); + + auto const & transitInfo = s.GetTransitInfo(); + auto const & displayInfo = *transitDisplayInfos.at(mwmId).get(); + + if (transitInfo.GetType() == TransitInfo::Type::Edge) + { + auto const & edge = transitInfo.GetEdge(); + + auto const & line = displayInfo.m_lines.at(edge.m_lineId); + auto const currentColor = df::GetTransitColorName(line.GetColor()); + + auto const & stop1 = displayInfo.m_stops.at(edge.m_stop1Id); + auto const & stop2 = displayInfo.m_stops.at(edge.m_stop2Id); + bool const isTransfer1 = stop1.GetTransferId() != transit::kInvalidTransferId; + bool const isTransfer2 = stop2.GetTransferId() != transit::kInvalidTransferId; + + marker.m_distance = distance; + marker.m_scale = kStopMarkerScale; + marker.m_innerColor = currentColor; + if (isTransfer1) + { + auto const & transferData = displayInfo.m_transfers.at(stop1.GetTransferId()); + marker.m_position = transferData.GetPoint(); + } + else + { + marker.m_position = stop1.GetPoint(); + } + transitMarkInfo.m_point = marker.m_position; + + if (pendingEntrance) + { + AddTransitGateSegment(marker.m_position, subroute); + pendingEntrance = false; + } + + auto const id1 = isTransfer1 ? stop1.GetTransferId() : stop1.GetId(); + auto const id2 = isTransfer2 ? stop2.GetTransferId() : stop2.GetId(); + bool const isInverted = id1 > id2; + + AddTransitShapes(edge.m_shapeIds, displayInfo.m_shapes, currentColor, isInverted, subroute); + + auto const & p1 = *(subroute.m_polyline.End() - 2); + auto const & p2 = *(subroute.m_polyline.End() - 1); + m2::PointD currentDir = (p2 - p1).Normalize(); + + if (lastColor != currentColor) + { + if (!lastColor.empty()) + marker.m_scale = kTransferMarkerScale; + marker.m_colors.push_back(currentColor); + + auto const fid = FeatureID(mwmId, stop1.GetFeatureId()); + transitMarkInfo.m_titles.push_back(TransitTitle(displayInfo.m_features.at(fid).m_title, + df::GetTransitTextColorName(line.GetColor()))); + } + lastColor = currentColor; + + if (marker.m_colors.size() > 1) + { + marker.m_innerColor = df::kTransitOutlineColor; + marker.m_up = (currentDir - lastDir).Normalize(); + if (m2::CrossProduct(marker.m_up, -lastDir) < 0) + marker.m_up = -marker.m_up; + } + + subroute.m_markers.push_back(marker); + marker = df::SubrouteMarker(); + + transitMarks.push_back(transitMarkInfo); + transitMarkInfo = TransitMarkInfo(); + + lastDir = currentDir; + distance += stop1.GetPoint().Length(stop2.GetPoint()); + + marker.m_distance = distance; + marker.m_scale = kStopMarkerScale; + marker.m_innerColor = currentColor; + marker.m_colors.push_back(currentColor); + + if (isTransfer2) + { + auto const & transferData = displayInfo.m_transfers.at(stop2.GetTransferId()); + marker.m_position = transferData.GetPoint(); + } + else + { + marker.m_position = stop2.GetPoint(); + } + + transitMarkInfo.m_point = marker.m_position; + auto const fid = FeatureID(mwmId, stop2.GetFeatureId()); + transitMarkInfo.m_titles.push_back(TransitTitle(displayInfo.m_features.at(fid).m_title, + df::GetTransitTextColorName(line.GetColor()))); + } + else if (transitInfo.GetType() == TransitInfo::Type::Gate) + { + auto const & gate = transitInfo.GetGate(); + if (!lastColor.empty()) + { + AddTransitGateSegment(s.GetJunction().GetPoint(), subroute); + + subroute.m_markers.push_back(marker); + marker = df::SubrouteMarker(); + + transitMarks.push_back(transitMarkInfo); + transitMarkInfo = TransitMarkInfo(); + } + else + { + pendingEntrance = true; + } + } + } + + if (subroute.m_markers.size() > 1) + { + subroute.m_markers.front().m_innerColor = df::kTransitOutlineColor; + subroute.m_markers.back().m_innerColor = df::kTransitOutlineColor; + } +} + +vector GetTransitMarkerSizes(float markerScale) +{ + vector markerSizes; + markerSizes.reserve(df::kRouteHalfWidthInPixelOthers.size()); + for (auto const halfWidth : df::kRouteHalfWidthInPixelOthers) + { + float const d = 2 * halfWidth * markerScale; + markerSizes.push_back(m2::PointF(d, d)); + } + return markerSizes; +} + +void CreateTransitMarks(vector const & transitMarks, UserMarksController & marksController) +{ + static vector const kTrasferMarkerSizes = GetTransitMarkerSizes(kTransferMarkerScale); + static vector const kStopMarkerSizes = GetTransitMarkerSizes(kStopMarkerScale); + + for (size_t i = 0; i < transitMarks.size(); ++i) + { + auto const & mark = transitMarks[i]; + if (!mark.m_titles.empty()) + { + auto userMark = marksController.CreateUserMark(mark.m_point); + ASSERT(dynamic_cast(userMark) != nullptr, ()); + auto transitMark = static_cast(userMark); + + transitMark->SetPrimaryText(mark.m_titles.front().m_text); + transitMark->SetPrimaryTextColor(df::GetColorConstant(mark.m_titles.front().m_color)); + + if (mark.m_titles.size() > 1) + { + transitMark->SetTextPosition(dp::Bottom); + transitMark->SetSymbolSizes(kTrasferMarkerSizes); + + userMark = marksController.CreateUserMark(mark.m_point); + ASSERT(dynamic_cast(userMark) != nullptr, ()); + auto transitMark2 = static_cast(userMark); + + transitMark2->SetPrimaryText(mark.m_titles.back().m_text); + transitMark2->SetPrimaryTextColor(df::GetColorConstant(mark.m_titles.back().m_color)); + transitMark2->SetTextPosition(dp::Top); + transitMark2->SetSymbolSizes(kTrasferMarkerSizes); + } + else + { + transitMark->SetTextPosition(dp::Left); + transitMark->SetSymbolSizes(kStopMarkerSizes); + int const kMinZoomSimpleStopLabel = 16; + if (i > 0 && i + 1 < transitMarks.size()) + transitMark->SetMinZoom(kMinZoomSimpleStopLabel); + } + } } } @@ -415,6 +715,10 @@ void RoutingManager::SetRouterImpl(RouterType type) void RoutingManager::RemoveRoute(bool deactivateFollowing) { + auto & marksController = m_bmManager->GetUserMarksController(UserMark::Type::TRANSIT); + marksController.Clear(); + marksController.NotifyChanges(); + if (deactivateFollowing) SetPointsFollowingMode(false /* enabled */); @@ -459,22 +763,30 @@ void RoutingManager::InsertRoute(Route const & route) // Fill points. double const currentBaseDistance = distance; - points.clear(); - points.reserve(segments.size() + 1); - points.push_back(route.GetSubrouteAttrs(subrouteIndex).GetStart().GetPoint()); - for (auto const & s : segments) - points.push_back(s.GetJunction().GetPoint()); - if (points.size() < 2) + auto subroute = make_unique_dp(); + subroute->m_baseDistance = currentBaseDistance; + subroute->m_baseDepthIndex = static_cast(subroutesCount - subrouteIndex - 1); + auto const startPt = route.GetSubrouteAttrs(subrouteIndex).GetStart().GetPoint(); + if (m_currentRouterType != RouterType::Transit) { - LOG(LWARNING, ("Invalid subroute. Points number =", points.size())); - continue; + points.clear(); + points.reserve(segments.size() + 1); + points.push_back(startPt); + for (auto const &s : segments) + points.push_back(s.GetJunction().GetPoint()); + if (points.size() < 2) + { + LOG(LWARNING, ("Invalid subroute. Points number =", points.size())); + continue; + } + subroute->m_polyline = m2::PolylineD(points); + } + else + { + subroute->m_polyline.Add(startPt); } distance = segments.back().GetDistFromBeginningMerc(); - auto subroute = make_unique_dp(); - subroute->m_polyline = m2::PolylineD(points); - subroute->m_baseDistance = currentBaseDistance; - subroute->m_baseDepthIndex = static_cast(subroutesCount - subrouteIndex - 1); switch (m_currentRouterType) { case RouterType::Vehicle: @@ -487,9 +799,23 @@ void RoutingManager::InsertRoute(Route const & route) subroute->m_turns); break; case RouterType::Transit: - subroute->m_routeType = df::RouteType::Transit; - subroute->m_styleType = df::SubrouteStyleType::Multiple; - FillTransitStyleForRendering(segments, subroute->m_style); + { + subroute->m_routeType = df::RouteType::Transit; + + auto numMwmIds = make_shared(); + m_delegate.RegisterCountryFilesOnRoute(numMwmIds); + auto getMwmIdFn = [this, &numMwmIds](routing::NumMwmId numMwmId) + { + return m_callbacks.m_indexGetter().GetMwmIdByCountryFile(numMwmIds->GetFile(numMwmId)); + }; + + vector transitMarks; + FillTransitStyleForRendering(segments, m_transitReadManager, getMwmIdFn, *subroute.get(), transitMarks); + + auto & marksController = m_bmManager->GetUserMarksController(UserMark::Type::TRANSIT); + CreateTransitMarks(transitMarks, marksController); + marksController.NotifyChanges(); + } break; case RouterType::Pedestrian: subroute->m_routeType = df::RouteType::Pedestrian; @@ -747,6 +1073,10 @@ void RoutingManager::BuildRoute(uint32_t timeoutSec) { ASSERT_THREAD_CHECKER(m_threadChecker, ("BuildRoute")); + auto & marksController = m_bmManager->GetUserMarksController(UserMark::Type::TRANSIT); + marksController.Clear(); + marksController.NotifyChanges(); + auto routePoints = GetRoutePoints(); if (routePoints.size() < 2) {