Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,913 @@
#include "transit_display.hpp"
#include "drape_frontend/route_renderer.hpp"
#include "drape_frontend/visual_params.hpp"
#include "routing/routing_session.hpp"
#include <memory>
using namespace std;
map<TransitType, string> const kTransitSymbols = {{TransitType::Subway, "transit_subway"},
{TransitType::LightRail, "transit_light_rail"},
{TransitType::Monorail, "transit_monorail"},
{TransitType::Train, "transit_train"},
{TransitType::Tram, "transit_tram"},
{TransitType::Bus, "transit_bus"},
{TransitType::Ferry, "transit_ferry"},
{TransitType::CableTram, "transit_cable_tram"},
{TransitType::AerialLift, "transit_aerial_lift"},
{TransitType::Funicular, "transit_funicular"},
{TransitType::Trolleybus, "transit_trolleybus"},
{TransitType::AirService, "transit_air_service"},
{TransitType::WaterService, "transit_water_service"}};
namespace
{
float constexpr kStopMarkerScale = 2.2f;
float constexpr kTransferMarkerScale = 4.0f;
float constexpr kGateBgScale = 1.2f;
int constexpr kSmallIconZoom = 1;
int constexpr kMediumIconZoom = 10;
int constexpr kMinStopTitleZoom = 13;
int constexpr kTransferTitleOffset = 2;
int constexpr kStopTitleOffset = 0;
int constexpr kGateTitleOffset = 0;
std::string const kZeroIcon = "zero-icon";
TransitType GetTransitType(string const & type)
{
if (type == "subway")
return TransitType::Subway;
if (type == "light_rail")
return TransitType::LightRail;
if (type == "monorail")
return TransitType::Monorail;
if (type == "train" || type == "rail")
return TransitType::Train;
if (type == "tram")
return TransitType::Tram;
if (type == "bus")
return TransitType::Bus;
if (type == "ferry")
return TransitType::Ferry;
if (type == "cabletram")
return TransitType::CableTram;
if (type == "aerial_lift")
return TransitType::AerialLift;
if (type == "funicular")
return TransitType::Funicular;
if (type == "trolleybus")
return TransitType::Trolleybus;
if (type == "air_service")
return TransitType::AirService;
if (type == "water_service")
return TransitType::WaterService;
LOG(LERROR, (type));
UNREACHABLE();
}
uint32_t ColorToARGB(df::ColorConstant const & colorConstant)
{
auto const color = df::GetColorConstant(colorConstant);
return color.GetAlpha() << 24 | color.GetRed() << 16 | color.GetGreen() << 8 | color.GetBlue();
}
vector<m2::PointF> GetTransitMarkerSizes(float markerScale, float maxRouteWidth)
{
auto const vs = static_cast<float>(df::VisualParams::Instance().GetVisualScale());
vector<m2::PointF> markerSizes;
markerSizes.reserve(df::kRouteHalfWidthInPixelTransit.size());
for (auto const halfWidth : df::kRouteHalfWidthInPixelTransit)
{
float const d = 2.0f * min(halfWidth * vs, maxRouteWidth * 0.5f) * markerScale;
markerSizes.push_back(m2::PointF(d, d));
}
return markerSizes;
}
} // namespace
TransitStepInfo::TransitStepInfo(TransitType type, double distance, int time, string const & number, uint32_t color,
int intermediateIndex)
: m_type(type)
, m_distanceInMeters(distance)
, m_timeInSec(time)
, m_number(number)
, m_colorARGB(color)
, m_intermediateIndex(intermediateIndex)
{}
bool TransitStepInfo::IsEqualType(TransitStepInfo const & ts) const
{
if (m_type != ts.m_type)
return false;
if (m_type != TransitType::Pedestrian && m_type != TransitType::IntermediatePoint)
return m_number == ts.m_number && m_colorARGB == ts.m_colorARGB;
return true;
}
void TransitRouteInfo::AddStep(TransitStepInfo const & step)
{
if (!m_steps.empty() && m_steps.back().IsEqualType(step))
{
m_steps.back().m_distanceInMeters += step.m_distanceInMeters;
m_steps.back().m_timeInSec += step.m_timeInSec;
}
else
{
m_steps.push_back(step);
}
if (step.m_type == TransitType::Pedestrian)
{
m_totalPedestrianDistInMeters += step.m_distanceInMeters;
m_totalPedestrianTimeInSec += step.m_timeInSec;
}
}
void TransitRouteInfo::UpdateDistanceStrings()
{
if (m_steps.empty())
return;
for (auto & step : m_steps)
routing::FormatDistance(step.m_distanceInMeters, step.m_distanceStr, step.m_distanceUnitsSuffix);
routing::FormatDistance(m_totalDistInMeters, m_totalDistanceStr, m_totalDistanceUnitsSuffix);
routing::FormatDistance(m_totalPedestrianDistInMeters, m_totalPedestrianDistanceStr, m_totalPedestrianUnitsSuffix);
}
void AddTransitGateSegment(m2::PointD const & destPoint, df::ColorConstant const & color, df::Subroute & subroute)
{
ASSERT_GREATER(subroute.m_polyline.GetSize(), 0, ());
df::SubrouteStyle style(color, 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.AddStyle(style);
}
void AddTransitPedestrianSegment(m2::PointD const & destPoint, df::Subroute & subroute)
{
ASSERT_GREATER(subroute.m_polyline.GetSize(), 0, ());
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.AddStyle(style);
}
void AddTransitShapes(vector<routing::transit::ShapeId> const & shapeIds, TransitShapesInfo const & shapes,
df::ColorConstant const & color, bool isInverted, df::Subroute & subroute)
{
ASSERT_GREATER(subroute.m_polyline.GetSize(), 0, ());
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.AddStyle(style);
}
void AddTransitShapes(::transit::ShapeLink shapeLink, TransitShapesInfoPT const & shapesInfo,
df::ColorConstant const & color, df::Subroute & subroute)
{
ASSERT_GREATER(subroute.m_polyline.GetSize(), 0, ());
df::SubrouteStyle style(color);
style.m_startIndex = subroute.m_polyline.GetSize() - 1;
bool const isInverted = shapeLink.m_startIndex > shapeLink.m_endIndex;
auto const it = shapesInfo.find(shapeLink.m_shapeId);
CHECK(it != shapesInfo.end(), (shapeLink.m_shapeId));
size_t const startIdx = isInverted ? shapeLink.m_endIndex : shapeLink.m_startIndex;
size_t const endIdx = isInverted ? shapeLink.m_startIndex : shapeLink.m_endIndex;
auto const & edgePolyline =
vector<m2::PointD>(it->second.GetPolyline().begin() + startIdx, it->second.GetPolyline().begin() + endIdx + 1);
if (isInverted)
subroute.m_polyline.Append(edgePolyline.crbegin(), edgePolyline.crend());
else
subroute.m_polyline.Append(edgePolyline.cbegin(), edgePolyline.cend());
style.m_endIndex = subroute.m_polyline.GetSize() - 1;
subroute.AddStyle(style);
}
TransitRouteDisplay::TransitRouteDisplay(TransitReadManager & transitReadManager, GetMwmIdFn const & getMwmIdFn,
GetStringsBundleFn const & getStringsBundleFn, BookmarkManager * bmManager,
map<string, m2::PointF> const & transitSymbolSizes)
: m_transitReadManager(transitReadManager)
, m_getMwmIdFn(getMwmIdFn)
, m_getStringsBundleFn(getStringsBundleFn)
, m_bmManager(bmManager)
, m_symbolSizes(transitSymbolSizes)
{
float maxSymbolSize = -1.0f;
for (auto const & symbolSize : m_symbolSizes)
{
if (maxSymbolSize < symbolSize.second.x)
maxSymbolSize = symbolSize.second.x;
if (maxSymbolSize < symbolSize.second.y)
maxSymbolSize = symbolSize.second.y;
}
m_maxSubrouteWidth = maxSymbolSize * kGateBgScale / kStopMarkerScale;
}
TransitRouteInfo const & TransitRouteDisplay::GetRouteInfo()
{
m_routeInfo.UpdateDistanceStrings();
return m_routeInfo;
}
void TransitRouteDisplay::AddEdgeSubwayForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute,
SubrouteParams & sp, SubrouteSegmentParams & ssp)
{
CHECK_EQUAL(ssp.m_displayInfo.m_transitVersion, ::transit::TransitVersion::OnlySubway, ());
auto const & edge = ssp.m_transitInfo.GetEdgeSubway();
auto const currentLineId = edge.m_lineId;
auto const & line = ssp.m_displayInfo.m_linesSubway.at(currentLineId);
auto const currentColor = df::GetTransitColorName(line.GetColor());
sp.m_transitType = GetTransitType(line.GetType());
m_routeInfo.AddStep(
TransitStepInfo(sp.m_transitType, ssp.m_distance, ssp.m_time, line.GetNumber(), ColorToARGB(currentColor)));
auto const & stop1 = ssp.m_displayInfo.m_stopsSubway.at(edge.m_stop1Id);
auto const & stop2 = ssp.m_displayInfo.m_stopsSubway.at(edge.m_stop2Id);
bool const isTransfer1 = stop1.GetTransferId() != routing::transit::kInvalidTransferId;
bool const isTransfer2 = stop2.GetTransferId() != routing::transit::kInvalidTransferId;
sp.m_marker.m_distance = sp.m_prevDistance;
sp.m_marker.m_scale = kStopMarkerScale;
sp.m_marker.m_innerColor = currentColor;
if (isTransfer1)
{
auto const & transferData = ssp.m_displayInfo.m_transfersSubway.at(stop1.GetTransferId());
sp.m_marker.m_position = transferData.GetPoint();
}
else
{
sp.m_marker.m_position = stop1.GetPoint();
}
sp.m_transitMarkInfo.m_point = sp.m_marker.m_position;
if (sp.m_pendingEntrance)
{
sp.m_transitMarkInfo.m_type = TransitMarkInfo::Type::KeyStop;
sp.m_transitMarkInfo.m_symbolName = kTransitSymbols.at(sp.m_transitType);
sp.m_transitMarkInfo.m_color = currentColor;
AddTransitGateSegment(sp.m_marker.m_position, currentColor, subroute);
sp.m_pendingEntrance = false;
}
auto const id1 = isTransfer1 ? stop1.GetTransferId() : stop1.GetId();
auto const id2 = isTransfer2 ? stop2.GetTransferId() : stop2.GetId();
if (id1 != id2)
{
bool const isInverted = id1 > id2;
AddTransitShapes(edge.m_shapeIds, ssp.m_displayInfo.m_shapesSubway, currentColor, isInverted, subroute);
}
CHECK_GREATER(subroute.m_polyline.GetSize(), 1, ());
auto const & p1 = *(subroute.m_polyline.End() - 2);
auto const & p2 = *(subroute.m_polyline.End() - 1);
m2::PointD currentDir = (p2 - p1).Normalize();
if (sp.m_lastLineId != currentLineId)
{
if (sp.m_lastLineId != routing::transit::kInvalidLineId)
{
sp.m_marker.m_scale = kTransferMarkerScale;
sp.m_transitMarkInfo.m_type = TransitMarkInfo::Type::Transfer;
}
sp.m_marker.m_colors.push_back(currentColor);
if (stop1.GetFeatureId() != kInvalidFeatureId)
{
auto const fid = FeatureID(ssp.m_mwmId, stop1.GetFeatureId());
sp.m_transitMarkInfo.m_featureId = fid;
sp.m_transitMarkInfo.m_titles.emplace_back(ssp.m_displayInfo.m_features.at(fid).m_title,
df::GetTransitTextColorName(line.GetColor()));
}
}
sp.m_lastColor = currentColor;
sp.m_lastLineId = currentLineId;
if (sp.m_marker.m_colors.size() > 1)
{
sp.m_marker.m_innerColor = df::kTransitStopInnerMarkerColor;
sp.m_marker.m_up = (currentDir - sp.m_lastDir).Normalize();
if (m2::CrossProduct(sp.m_marker.m_up, -sp.m_lastDir) < 0)
sp.m_marker.m_up = -sp.m_marker.m_up;
}
subroute.m_markers.push_back(sp.m_marker);
sp.m_marker = df::SubrouteMarker();
m_transitMarks.push_back(sp.m_transitMarkInfo);
sp.m_transitMarkInfo = TransitMarkInfo();
sp.m_lastDir = currentDir;
sp.m_marker.m_distance = segment.GetDistFromBeginningMeters();
sp.m_marker.m_scale = kStopMarkerScale;
sp.m_marker.m_innerColor = currentColor;
sp.m_marker.m_colors.push_back(currentColor);
if (isTransfer2)
{
auto const & transferData = ssp.m_displayInfo.m_transfersSubway.at(stop2.GetTransferId());
sp.m_marker.m_position = transferData.GetPoint();
}
else
{
sp.m_marker.m_position = stop2.GetPoint();
}
sp.m_transitMarkInfo.m_point = sp.m_marker.m_position;
if (stop2.GetFeatureId() != kInvalidFeatureId)
{
auto const fid = FeatureID(ssp.m_mwmId, stop2.GetFeatureId());
sp.m_transitMarkInfo.m_featureId = fid;
sp.m_transitMarkInfo.m_titles.push_back(
TransitTitle(ssp.m_displayInfo.m_features.at(fid).m_title, df::GetTransitTextColorName(line.GetColor())));
}
}
void TransitRouteDisplay::AddEdgePTForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute,
SubrouteParams & sp, SubrouteSegmentParams & ssp)
{
CHECK_EQUAL(ssp.m_displayInfo.m_transitVersion, ::transit::TransitVersion::AllPublicTransport,
(segment.GetSegment()));
auto const & edge = ssp.m_transitInfo.GetEdgePT();
auto const currentLineId = edge.m_lineId;
auto const & line = ssp.m_displayInfo.m_linesPT.at(currentLineId);
auto const it = ssp.m_displayInfo.m_routesPT.find(line.GetRouteId());
CHECK(it != ssp.m_displayInfo.m_routesPT.end(), (line.GetRouteId()));
auto const & route = it->second;
auto const currentColor = df::GetTransitColorName(route.GetColor());
sp.m_transitType = GetTransitType(route.GetType());
m_routeInfo.AddStep(
TransitStepInfo(sp.m_transitType, ssp.m_distance, ssp.m_time, route.GetTitle(), ColorToARGB(currentColor)));
auto const & stop1 = ssp.m_displayInfo.m_stopsPT.at(edge.m_stop1Id);
auto const & stop2 = ssp.m_displayInfo.m_stopsPT.at(edge.m_stop2Id);
bool const isTransfer1 = !stop1.GetTransferIds().empty();
bool const isTransfer2 = !stop2.GetTransferIds().empty();
sp.m_marker.m_distance = sp.m_prevDistance;
sp.m_marker.m_scale = kStopMarkerScale;
sp.m_marker.m_innerColor = currentColor;
if (isTransfer1)
{
auto itTransfer = ssp.m_displayInfo.m_transfersPT.find(stop1.GetTransferIds().front());
ASSERT(itTransfer != ssp.m_displayInfo.m_transfersPT.end(), ());
sp.m_marker.m_position = itTransfer->second.GetPoint();
}
else
{
sp.m_marker.m_position = stop1.GetPoint();
}
sp.m_transitMarkInfo.m_point = sp.m_marker.m_position;
if (sp.m_pendingEntrance)
{
sp.m_transitMarkInfo.m_type = TransitMarkInfo::Type::KeyStop;
sp.m_transitMarkInfo.m_symbolName = kTransitSymbols.at(sp.m_transitType);
sp.m_transitMarkInfo.m_color = currentColor;
AddTransitGateSegment(sp.m_marker.m_position, currentColor, subroute);
sp.m_pendingEntrance = false;
}
auto const id1 = isTransfer1 ? stop1.GetTransferIds().front() : stop1.GetId();
auto const id2 = isTransfer2 ? stop2.GetTransferIds().front() : stop2.GetId();
if (id1 != id2)
AddTransitShapes(edge.m_shapeLink, ssp.m_displayInfo.m_shapesPT, currentColor, subroute);
CHECK_GREATER(subroute.m_polyline.GetSize(), 1, ());
auto const & p1 = *(subroute.m_polyline.End() - 2);
auto const & p2 = *(subroute.m_polyline.End() - 1);
m2::PointD currentDir = (p2 - p1).Normalize();
if (sp.m_lastLineId != currentLineId)
{
if (sp.m_lastLineId != routing::transit::kInvalidLineId)
{
sp.m_marker.m_scale = kTransferMarkerScale;
sp.m_transitMarkInfo.m_type = TransitMarkInfo::Type::Transfer;
}
sp.m_marker.m_colors.push_back(currentColor);
if (stop1.GetFeatureId() != kInvalidFeatureId)
{
auto const fid = FeatureID(ssp.m_mwmId, stop1.GetFeatureId());
sp.m_transitMarkInfo.m_featureId = fid;
sp.m_transitMarkInfo.m_titles.emplace_back(ssp.m_displayInfo.m_features.at(fid).m_title,
df::GetTransitTextColorName(route.GetColor()));
}
}
sp.m_lastColor = currentColor;
sp.m_lastLineId = currentLineId;
if (sp.m_marker.m_colors.size() > 1)
{
sp.m_marker.m_innerColor = df::kTransitStopInnerMarkerColor;
sp.m_marker.m_up = (currentDir - sp.m_lastDir).Normalize();
if (m2::CrossProduct(sp.m_marker.m_up, -sp.m_lastDir) < 0)
sp.m_marker.m_up = -sp.m_marker.m_up;
}
subroute.m_markers.push_back(sp.m_marker);
sp.m_marker = df::SubrouteMarker();
m_transitMarks.push_back(sp.m_transitMarkInfo);
sp.m_transitMarkInfo = TransitMarkInfo();
sp.m_lastDir = currentDir;
sp.m_marker.m_distance = segment.GetDistFromBeginningMeters();
sp.m_marker.m_scale = kStopMarkerScale;
sp.m_marker.m_innerColor = currentColor;
sp.m_marker.m_colors.push_back(currentColor);
if (isTransfer2)
{
auto itTransfer = ssp.m_displayInfo.m_transfersPT.find(stop2.GetTransferIds().front());
ASSERT(itTransfer != ssp.m_displayInfo.m_transfersPT.end(), ());
sp.m_marker.m_position = itTransfer->second.GetPoint();
}
else
{
sp.m_marker.m_position = stop2.GetPoint();
}
sp.m_transitMarkInfo.m_point = sp.m_marker.m_position;
if (stop2.GetFeatureId() != kInvalidFeatureId)
{
auto const fid = FeatureID(ssp.m_mwmId, stop2.GetFeatureId());
sp.m_transitMarkInfo.m_featureId = fid;
sp.m_transitMarkInfo.m_titles.emplace_back(ssp.m_displayInfo.m_features.at(fid).m_title,
df::GetTransitTextColorName(route.GetColor()));
}
}
void TransitRouteDisplay::AddGateSubwayForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute,
SubrouteParams & sp, SubrouteSegmentParams & ssp)
{
auto const & gate = ssp.m_transitInfo.GetGateSubway();
if (sp.m_lastLineId != routing::transit::kInvalidLineId)
{
m_routeInfo.AddStep(TransitStepInfo(TransitType::Pedestrian, ssp.m_distance, ssp.m_time));
AddTransitGateSegment(segment.GetJunction().GetPoint(), sp.m_lastColor, subroute);
subroute.m_markers.push_back(sp.m_marker);
sp.m_marker = df::SubrouteMarker();
sp.m_transitMarkInfo.m_type = TransitMarkInfo::Type::KeyStop;
sp.m_transitMarkInfo.m_symbolName = kTransitSymbols.at(sp.m_transitType);
sp.m_transitMarkInfo.m_color = sp.m_lastColor;
m_transitMarks.push_back(sp.m_transitMarkInfo);
sp.m_transitMarkInfo = TransitMarkInfo();
}
else
{
sp.m_pendingEntrance = true;
}
auto gateMarkInfo = TransitMarkInfo();
gateMarkInfo.m_point = sp.m_pendingEntrance ? subroute.m_polyline.Back() : segment.GetJunction().GetPoint();
gateMarkInfo.m_type = TransitMarkInfo::Type::Gate;
gateMarkInfo.m_symbolName = kZeroIcon;
if (gate.m_featureId != kInvalidFeatureId)
{
auto const fid = FeatureID(ssp.m_mwmId, gate.m_featureId);
auto const & featureInfo = ssp.m_displayInfo.m_features.at(fid);
auto symbolName = featureInfo.m_gateSymbolName;
if (symbolName.ends_with("-s") || symbolName.ends_with("-m") || symbolName.ends_with("-l"))
symbolName = symbolName.substr(0, symbolName.rfind('-'));
gateMarkInfo.m_featureId = fid;
if (!symbolName.empty())
gateMarkInfo.m_symbolName = symbolName;
auto const title = m_getStringsBundleFn().GetString(sp.m_pendingEntrance ? "core_entrance" : "core_exit");
gateMarkInfo.m_titles.emplace_back(title, df::GetTransitTextColorName("default"));
}
m_transitMarks.push_back(gateMarkInfo);
}
void TransitRouteDisplay::AddGatePTForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute,
SubrouteParams & sp, SubrouteSegmentParams & ssp)
{
auto const & gate = ssp.m_transitInfo.GetGatePT();
if (sp.m_lastLineId != ::transit::kInvalidTransitId)
{
m_routeInfo.AddStep(TransitStepInfo(TransitType::Pedestrian, ssp.m_distance, ssp.m_time));
AddTransitGateSegment(segment.GetJunction().GetPoint(), sp.m_lastColor, subroute);
subroute.m_markers.push_back(sp.m_marker);
sp.m_marker = df::SubrouteMarker();
sp.m_transitMarkInfo.m_type = TransitMarkInfo::Type::KeyStop;
sp.m_transitMarkInfo.m_symbolName = kTransitSymbols.at(sp.m_transitType);
sp.m_transitMarkInfo.m_color = sp.m_lastColor;
m_transitMarks.push_back(sp.m_transitMarkInfo);
sp.m_transitMarkInfo = TransitMarkInfo();
}
else
{
sp.m_pendingEntrance = true;
}
auto gateMarkInfo = TransitMarkInfo();
gateMarkInfo.m_point = sp.m_pendingEntrance ? subroute.m_polyline.Back() : segment.GetJunction().GetPoint();
gateMarkInfo.m_type = TransitMarkInfo::Type::Gate;
gateMarkInfo.m_symbolName = kZeroIcon;
if (gate.m_featureId != kInvalidFeatureId)
{
auto const fid = FeatureID(ssp.m_mwmId, gate.m_featureId);
auto const & featureInfo = ssp.m_displayInfo.m_features.at(fid);
auto symbolName = featureInfo.m_gateSymbolName;
if (symbolName.ends_with("-s") || symbolName.ends_with("-m") || symbolName.ends_with("-l"))
symbolName = symbolName.substr(0, symbolName.rfind('-'));
gateMarkInfo.m_featureId = fid;
if (!symbolName.empty())
gateMarkInfo.m_symbolName = symbolName;
auto const title = m_getStringsBundleFn().GetString(sp.m_pendingEntrance ? "core_entrance" : "core_exit");
gateMarkInfo.m_titles.emplace_back(title, df::GetTransitTextColorName("default"));
}
m_transitMarks.push_back(gateMarkInfo);
}
bool TransitRouteDisplay::ProcessSubroute(vector<routing::RouteSegment> const & segments, df::Subroute & subroute)
{
if (m_subrouteIndex > 0)
{
TransitStepInfo step;
step.m_type = TransitType::IntermediatePoint;
step.m_intermediateIndex = m_subrouteIndex - 1;
m_routeInfo.AddStep(step);
}
++m_subrouteIndex;
TransitDisplayInfos transitDisplayInfos;
CollectTransitDisplayInfo(segments, transitDisplayInfos);
// Read transit display info.
if (!m_transitReadManager.GetTransitDisplayInfo(transitDisplayInfos))
return false;
subroute.m_maxPixelWidth = m_maxSubrouteWidth;
subroute.m_styleType = df::SubrouteStyleType::Multiple;
subroute.m_style.clear();
subroute.m_style.reserve(segments.size());
SubrouteParams sp;
sp.m_prevDistance = m_routeInfo.m_totalDistInMeters;
sp.m_prevTime = m_routeInfo.m_totalTimeInSec;
for (auto const & s : segments)
{
auto const time = static_cast<int>(ceil(s.GetTimeFromBeginningSec() - sp.m_prevTime));
auto const distance = s.GetDistFromBeginningMeters() - sp.m_prevDistance;
sp.m_prevDistance = s.GetDistFromBeginningMeters();
sp.m_prevTime = s.GetTimeFromBeginningSec();
if (!s.HasTransitInfo())
{
m_routeInfo.AddStep(TransitStepInfo(TransitType::Pedestrian, distance, time));
AddTransitPedestrianSegment(s.GetJunction().GetPoint(), subroute);
sp.m_lastColor = "";
sp.m_lastLineId = routing::transit::kInvalidLineId;
sp.m_transitType = TransitType::Pedestrian;
continue;
}
SubrouteSegmentParams ssp(s.GetTransitInfo());
ssp.m_time = time;
ssp.m_distance = distance;
ssp.m_mwmId = m_getMwmIdFn(s.GetSegment().GetMwmId());
ssp.m_displayInfo = *transitDisplayInfos.at(ssp.m_mwmId).get();
if (ssp.m_transitInfo.GetVersion() == ::transit::TransitVersion::OnlySubway)
{
if (ssp.m_transitInfo.GetType() == routing::TransitInfo::Type::Edge)
AddEdgeSubwayForSubroute(s, subroute, sp, ssp);
else if (ssp.m_transitInfo.GetType() == routing::TransitInfo::Type::Gate)
AddGateSubwayForSubroute(s, subroute, sp, ssp);
}
else if (ssp.m_transitInfo.GetVersion() == ::transit::TransitVersion::AllPublicTransport)
{
if (ssp.m_transitInfo.GetType() == routing::TransitInfo::Type::Edge)
AddEdgePTForSubroute(s, subroute, sp, ssp);
else if (ssp.m_transitInfo.GetType() == routing::TransitInfo::Type::Gate)
AddGatePTForSubroute(s, subroute, sp, ssp);
}
else
{
CHECK(false, (ssp.m_transitInfo.GetVersion()));
}
}
m_routeInfo.m_totalDistInMeters = sp.m_prevDistance;
m_routeInfo.m_totalTimeInSec = static_cast<int>(ceil(sp.m_prevTime));
bool const isValidSubroute = subroute.m_polyline.GetSize() > 1;
if (!isValidSubroute)
LOG(LWARNING, ("Invalid subroute. Points number =", subroute.m_polyline.GetSize()));
return isValidSubroute;
}
template <class T>
void FillMwmTransitSubway(unique_ptr<TransitDisplayInfo> & mwmTransit, routing::TransitInfo const & transitInfo,
T mwmId)
{
switch (transitInfo.GetType())
{
case routing::TransitInfo::Type::Edge:
{
auto const & edge = transitInfo.GetEdgeSubway();
mwmTransit->m_stopsSubway[edge.m_stop1Id] = {};
mwmTransit->m_stopsSubway[edge.m_stop2Id] = {};
mwmTransit->m_linesSubway[edge.m_lineId] = {};
for (auto const & shapeId : edge.m_shapeIds)
mwmTransit->m_shapesSubway[shapeId] = {};
break;
}
case routing::TransitInfo::Type::Transfer:
{
auto const & transfer = transitInfo.GetTransferSubway();
mwmTransit->m_stopsSubway[transfer.m_stop1Id] = {};
mwmTransit->m_stopsSubway[transfer.m_stop2Id] = {};
break;
}
case routing::TransitInfo::Type::Gate:
{
auto const & gate = transitInfo.GetGateSubway();
if (gate.m_featureId != kInvalidFeatureId)
{
auto const featureId = FeatureID(mwmId, gate.m_featureId);
TransitFeatureInfo featureInfo;
featureInfo.m_isGate = true;
mwmTransit->m_features[featureId] = featureInfo;
}
break;
}
}
}
template <class T>
void FillMwmTransitPT(unique_ptr<TransitDisplayInfo> & mwmTransit, routing::TransitInfo const & transitInfo, T mwmId)
{
switch (transitInfo.GetType())
{
case routing::TransitInfo::Type::Edge:
{
auto const & edge = transitInfo.GetEdgePT();
mwmTransit->m_stopsPT[edge.m_stop1Id] = {};
mwmTransit->m_stopsPT[edge.m_stop2Id] = {};
mwmTransit->m_linesPT[edge.m_lineId] = {};
mwmTransit->m_shapesPT[edge.m_shapeLink.m_shapeId] = {};
break;
}
case routing::TransitInfo::Type::Transfer:
{
auto const & transfer = transitInfo.GetTransferPT();
mwmTransit->m_stopsPT[transfer.m_stop1Id] = {};
mwmTransit->m_stopsPT[transfer.m_stop2Id] = {};
break;
}
case routing::TransitInfo::Type::Gate:
{
auto const & gate = transitInfo.GetGatePT();
if (gate.m_featureId != kInvalidFeatureId)
{
auto const featureId = FeatureID(mwmId, gate.m_featureId);
TransitFeatureInfo featureInfo;
featureInfo.m_isGate = true;
mwmTransit->m_features[featureId] = featureInfo;
}
break;
}
}
}
void TransitRouteDisplay::CollectTransitDisplayInfo(vector<routing::RouteSegment> const & segments,
TransitDisplayInfos & transitDisplayInfos)
{
for (auto const & s : segments)
{
if (!s.HasTransitInfo())
continue;
auto const mwmId = m_getMwmIdFn(s.GetSegment().GetMwmId());
auto & mwmTransit = transitDisplayInfos[mwmId];
if (mwmTransit == nullptr)
mwmTransit = make_unique<TransitDisplayInfo>();
routing::TransitInfo const & transitInfo = s.GetTransitInfo();
mwmTransit->m_transitVersion = transitInfo.GetVersion();
if (transitInfo.GetVersion() == ::transit::TransitVersion::OnlySubway)
FillMwmTransitSubway(mwmTransit, transitInfo, mwmId);
else if (transitInfo.GetVersion() == ::transit::TransitVersion::AllPublicTransport)
FillMwmTransitPT(mwmTransit, transitInfo, mwmId);
}
}
TransitMark * TransitRouteDisplay::CreateMark(m2::PointD const & pt, FeatureID const & fid)
{
uint32_t const nextIndex = static_cast<uint32_t>(m_bmManager->GetUserMarkIds(UserMark::Type::TRANSIT).size());
auto editSession = m_bmManager->GetEditSession();
auto * transitMark = editSession.CreateUserMark<TransitMark>(pt);
transitMark->SetFeatureId(fid);
transitMark->SetIndex(nextIndex);
return transitMark;
}
void TransitRouteDisplay::CreateTransitMarks()
{
if (m_transitMarks.empty())
return;
vector<m2::PointF> const transferMarkerSizes = GetTransitMarkerSizes(kTransferMarkerScale, m_maxSubrouteWidth);
vector<m2::PointF> const stopMarkerSizes = GetTransitMarkerSizes(kStopMarkerScale, m_maxSubrouteWidth);
vector<m2::PointF> transferArrowOffsets;
transferArrowOffsets.reserve(transferMarkerSizes.size());
for (auto const & size : transferMarkerSizes)
transferArrowOffsets.emplace_back(0.0f, size.y * 0.5f);
auto const vs = static_cast<float>(df::VisualParams::Instance().GetVisualScale());
auto editSession = m_bmManager->GetEditSession();
for (auto const & mark : m_transitMarks)
{
auto transitMark = CreateMark(mark.m_point, mark.m_featureId);
dp::TitleDecl titleDecl;
if (mark.m_type == TransitMarkInfo::Type::Gate)
{
if (!mark.m_titles.empty())
{
TransitMark::GetDefaultTransitTitle(titleDecl);
titleDecl.m_primaryText = mark.m_titles.front().m_text;
titleDecl.m_primaryOffset.y = kGateTitleOffset;
titleDecl.m_anchor = dp::Anchor::Top;
titleDecl.m_primaryOptional = true;
transitMark->AddTitle(titleDecl);
}
auto const GetSymbolName = [](std::string const & name, char const * suffix)
{ return name != kZeroIcon ? name + suffix : name; };
df::UserPointMark::SymbolNameZoomInfo symbolNames;
symbolNames[kSmallIconZoom] = GetSymbolName(mark.m_symbolName, "-s");
symbolNames[kMediumIconZoom] = GetSymbolName(mark.m_symbolName, "-m");
transitMark->SetSymbolNames(symbolNames);
transitMark->SetPriority(UserMark::Priority::TransitGate);
}
else if (mark.m_type == TransitMarkInfo::Type::Transfer)
{
if (mark.m_titles.size() > 1)
{
auto titleTransitMark = CreateMark(mark.m_point, mark.m_featureId);
TransitMark::GetDefaultTransitTitle(titleDecl);
titleDecl.m_primaryText = mark.m_titles.front().m_text;
titleDecl.m_primaryTextFont.m_color = df::GetColorConstant(mark.m_titles.front().m_color);
titleDecl.m_primaryOffset.x = -kTransferTitleOffset * vs;
titleDecl.m_anchor = dp::Anchor::Right;
titleDecl.m_primaryOptional = false;
titleTransitMark->AddTitle(titleDecl);
titleDecl.m_primaryText = mark.m_titles.back().m_text;
titleDecl.m_primaryTextFont.m_color = df::GetColorConstant(mark.m_titles.back().m_color);
titleDecl.m_primaryOffset.x = kTransferTitleOffset * vs;
titleDecl.m_anchor = dp::Anchor::Left;
titleDecl.m_primaryOptional = false;
titleTransitMark->AddTitle(titleDecl);
titleTransitMark->SetAnchor(dp::Top);
titleTransitMark->SetSymbolNames({{1 /* minZoom */, "transfer_arrow"}});
titleTransitMark->SetSymbolOffsets(transferArrowOffsets);
titleTransitMark->SetPriority(UserMark::Priority::TransitTransfer);
}
df::UserPointMark::ColoredSymbolZoomInfo coloredSymbol;
for (size_t sizeIndex = 0; sizeIndex < transferMarkerSizes.size(); ++sizeIndex)
{
auto const zoomLevel = sizeIndex + 1;
auto const & sz = transferMarkerSizes[sizeIndex];
df::ColoredSymbolViewParams params;
params.m_radiusInPixels = max(sz.x, sz.y) * 0.5f;
params.m_color = dp::Color::Transparent();
if (coloredSymbol.m_zoomInfo.empty() ||
coloredSymbol.m_zoomInfo.rbegin()->second.m_radiusInPixels != params.m_radiusInPixels)
{
coloredSymbol.m_zoomInfo.insert(make_pair(zoomLevel, params));
}
}
transitMark->SetColoredSymbols(coloredSymbol);
transitMark->SetPriority(UserMark::Priority::TransitTransfer);
}
else
{
if (!mark.m_titles.empty())
{
TransitMark::GetDefaultTransitTitle(titleDecl);
titleDecl.m_primaryText = mark.m_titles.front().m_text;
titleDecl.m_primaryTextFont.m_color = df::GetColorConstant(mark.m_titles.front().m_color);
titleDecl.m_primaryOffset.y = kStopTitleOffset;
titleDecl.m_anchor = dp::Anchor::Top;
titleDecl.m_primaryOptional = true;
transitMark->AddTitle(titleDecl);
}
if (mark.m_type == TransitMarkInfo::Type::KeyStop)
{
df::UserPointMark::SymbolNameZoomInfo symbolNames;
symbolNames[kSmallIconZoom] = mark.m_symbolName + "-s";
symbolNames[kMediumIconZoom] = mark.m_symbolName + "-m";
transitMark->SetSymbolNames(symbolNames);
df::UserPointMark::ColoredSymbolZoomInfo coloredSymbol;
df::ColoredSymbolViewParams params;
params.m_color = df::GetColorConstant(mark.m_color);
auto sz = m_symbolSizes.at(symbolNames[kSmallIconZoom]);
params.m_radiusInPixels = max(sz.x, sz.y) * kGateBgScale * 0.5f;
coloredSymbol.m_zoomInfo[kSmallIconZoom] = params;
sz = m_symbolSizes.at(symbolNames[kMediumIconZoom]);
params.m_radiusInPixels = max(sz.x, sz.y) * kGateBgScale * 0.5f;
coloredSymbol.m_zoomInfo[kMediumIconZoom] = params;
transitMark->SetColoredSymbols(coloredSymbol);
transitMark->SetPriority(UserMark::Priority::TransitKeyStop);
}
else
{
df::UserPointMark::ColoredSymbolZoomInfo coloredSymbol;
for (size_t sizeIndex = 0; sizeIndex < stopMarkerSizes.size(); ++sizeIndex)
{
auto const zoomLevel = sizeIndex + 1;
auto const & sz = stopMarkerSizes[sizeIndex];
df::ColoredSymbolViewParams params;
params.m_radiusInPixels = max(sz.x, sz.y) * 0.5f;
params.m_color = dp::Color::Transparent();
if (coloredSymbol.m_zoomInfo.empty() ||
coloredSymbol.m_zoomInfo.rbegin()->second.m_radiusInPixels != params.m_radiusInPixels)
{
coloredSymbol.m_zoomInfo.insert(make_pair(zoomLevel, params));
}
}
transitMark->SetSymbolSizes(stopMarkerSizes);
transitMark->SetColoredSymbols(coloredSymbol);
transitMark->SetPriority(UserMark::Priority::TransitStop);
transitMark->SetMinTitleZoom(kMinStopTitleZoom);
}
}
}
}

View file

@ -0,0 +1,174 @@
#pragma once
#include "map/transit/transit_reader.hpp"
#include "map/bookmark_manager.hpp"
#include "map/routing_mark.hpp"
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/route_shape.hpp"
#include "routing/route.hpp"
#include <functional>
#include <map>
#include <string>
#include <vector>
enum class TransitType : uint32_t
{
// Do not change the order!
IntermediatePoint,
Pedestrian,
Subway,
Train,
LightRail,
Monorail,
Tram,
Bus,
Ferry,
CableTram,
AerialLift,
Funicular,
Trolleybus,
AirService,
WaterService
};
extern std::map<TransitType, std::string> const kTransitSymbols;
struct TransitStepInfo
{
TransitStepInfo() = default;
TransitStepInfo(TransitType type, double distance, int time, std::string const & number = "", uint32_t color = 0,
int intermediateIndex = 0);
bool IsEqualType(TransitStepInfo const & ts) const;
TransitType m_type = TransitType::Pedestrian;
double m_distanceInMeters = 0.0;
int m_timeInSec = 0;
std::string m_distanceStr;
std::string m_distanceUnitsSuffix;
// Is valid for all types except TransitType::IntermediatePoint and TransitType::Pedestrian
std::string m_number;
uint32_t m_colorARGB = 0;
// Is valid for TransitType::IntermediatePoint
int m_intermediateIndex = 0;
};
struct TransitRouteInfo
{
void AddStep(TransitStepInfo const & step);
void UpdateDistanceStrings();
double m_totalDistInMeters = 0.0;
double m_totalPedestrianDistInMeters = 0.0;
int m_totalTimeInSec = 0;
int m_totalPedestrianTimeInSec = 0;
std::string m_totalDistanceStr;
std::string m_totalDistanceUnitsSuffix;
std::string m_totalPedestrianDistanceStr;
std::string m_totalPedestrianUnitsSuffix;
std::vector<TransitStepInfo> m_steps;
};
struct TransitTitle
{
TransitTitle() = default;
TransitTitle(std::string const & text, df::ColorConstant const & color) : m_text(text), m_color(color) {}
std::string m_text;
df::ColorConstant m_color;
};
struct TransitMarkInfo
{
enum class Type
{
Stop,
KeyStop,
Transfer,
Gate
};
Type m_type = Type::Stop;
m2::PointD m_point;
std::vector<TransitTitle> m_titles;
std::string m_symbolName;
df::ColorConstant m_color;
FeatureID m_featureId;
};
struct SubrouteParams
{
df::ColorConstant m_lastColor;
m2::PointD m_lastDir;
::transit::TransitId m_lastLineId = ::transit::kInvalidTransitId;
df::SubrouteMarker m_marker;
TransitMarkInfo m_transitMarkInfo;
TransitType m_transitType = TransitType::Pedestrian;
double m_prevDistance = 0.0;
double m_prevTime = 0.0;
bool m_pendingEntrance = false;
};
struct SubrouteSegmentParams
{
SubrouteSegmentParams(routing::TransitInfo const & transitInfo) : m_transitInfo(transitInfo) {}
int m_time = 0;
double m_distance = 0.0;
routing::TransitInfo m_transitInfo;
TransitDisplayInfo m_displayInfo;
MwmSet::MwmId m_mwmId;
};
class TransitRouteDisplay
{
public:
using GetMwmIdFn = std::function<MwmSet::MwmId(routing::NumMwmId numMwmId)>;
using GetStringsBundleFn = std::function<StringsBundle const &()>;
TransitRouteDisplay(TransitReadManager & transitReadManager, GetMwmIdFn const & getMwmIdFn,
GetStringsBundleFn const & getStringsBundleFn, BookmarkManager * bmManager,
std::map<std::string, m2::PointF> const & transitSymbolSizes);
bool ProcessSubroute(std::vector<routing::RouteSegment> const & segments, df::Subroute & subroute);
void CreateTransitMarks();
TransitRouteInfo const & GetRouteInfo();
private:
void AddEdgeSubwayForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute, SubrouteParams & sp,
SubrouteSegmentParams & ssp);
void AddEdgePTForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute, SubrouteParams & sp,
SubrouteSegmentParams & ssp);
void AddGateSubwayForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute, SubrouteParams & sp,
SubrouteSegmentParams & ssp);
void AddGatePTForSubroute(routing::RouteSegment const & segment, df::Subroute & subroute, SubrouteParams & sp,
SubrouteSegmentParams & ssp);
void CollectTransitDisplayInfo(std::vector<routing::RouteSegment> const & segments,
TransitDisplayInfos & transitDisplayInfos);
TransitMark * CreateMark(m2::PointD const & pt, FeatureID const & fid);
TransitReadManager & m_transitReadManager;
GetMwmIdFn m_getMwmIdFn;
GetStringsBundleFn m_getStringsBundleFn;
BookmarkManager * m_bmManager;
std::map<std::string, m2::PointF> const & m_symbolSizes;
TransitRouteInfo m_routeInfo;
std::vector<TransitMarkInfo> m_transitMarks;
int m_subrouteIndex = 0;
float m_maxSubrouteWidth = -1.0f;
};

View file

@ -0,0 +1,508 @@
#include "map/transit/transit_reader.hpp"
#include "drape_frontend/drape_engine.hpp"
#include "drape_frontend/stylist.hpp"
#include "drape_frontend/visual_params.hpp"
#include "transit/transit_graph_data.hpp"
#include "transit/transit_version.hpp"
#include "indexer/data_source.hpp"
#include "indexer/drawing_rules.hpp"
#include "indexer/drules_include.hpp" // needed despite of IDE warning
#include "indexer/feature_algo.hpp"
#include "coding/reader.hpp"
#include <algorithm>
#include <chrono>
#include <memory>
using namespace std;
using namespace std::chrono;
namespace
{
int constexpr kMinSchemeZoomLevel = 10;
size_t constexpr kMaxTransitCacheSizeBytes = 5 /* Mb */ * 1024 * 1024;
size_t CalculateCacheSize(TransitDisplayInfo const & transitInfo)
{
size_t constexpr kSegmentSize = 72;
size_t cacheSize = 0;
for (auto const & shape : transitInfo.m_shapesSubway)
cacheSize += shape.second.GetPolyline().size() * kSegmentSize;
return cacheSize;
}
} // namespace
// ReadTransitTask --------------------------------------------------------------------------------
void ReadTransitTask::Init(uint64_t id, MwmSet::MwmId const & mwmId, unique_ptr<TransitDisplayInfo> transitInfo)
{
m_id = id;
m_mwmId = mwmId;
if (transitInfo == nullptr)
{
m_loadSubset = false;
m_transitInfo = make_unique<TransitDisplayInfo>();
}
else
{
m_loadSubset = true;
m_transitInfo = std::move(transitInfo);
}
m_success = false;
}
void ReadTransitTask::Do()
{
MwmSet::MwmHandle handle = m_dataSource.GetMwmHandleById(m_mwmId);
if (!handle.IsAlive())
{
// It's possible that mwm handle is not alive because mwm may be removed after
// transit route is built but before this task is executed.
LOG(LWARNING, ("Can't get mwm handle for", m_mwmId));
m_success = false;
return;
}
MwmValue const & mwmValue = *handle.GetValue();
if (!m_loadSubset && !mwmValue.m_cont.IsExist(TRANSIT_FILE_TAG))
{
m_success = true;
return;
}
CHECK(mwmValue.m_cont.IsExist(TRANSIT_FILE_TAG),
("No transit section in mwm, but transit route was built with it. mwmId:", m_mwmId));
auto reader = mwmValue.m_cont.GetReader(TRANSIT_FILE_TAG);
CHECK(reader.GetPtr() != nullptr, ());
auto const transitHeaderVersion = ::transit::GetVersion(*reader.GetPtr());
if (transitHeaderVersion == ::transit::TransitVersion::OnlySubway)
{
m_transitInfo->m_transitVersion = ::transit::TransitVersion::OnlySubway;
routing::transit::GraphData graphData;
graphData.DeserializeForRendering(*reader.GetPtr());
FillItemsByIdMap(graphData.GetStops(), m_transitInfo->m_stopsSubway);
for (auto const & stop : m_transitInfo->m_stopsSubway)
{
if (stop.second.GetFeatureId() != kInvalidFeatureId)
{
auto const featureId = FeatureID(m_mwmId, stop.second.GetFeatureId());
m_transitInfo->m_features[featureId] = {};
}
if (m_loadSubset && (stop.second.GetTransferId() != routing::transit::kInvalidTransferId))
m_transitInfo->m_transfersSubway[stop.second.GetTransferId()] = {};
}
FillItemsByIdMap(graphData.GetTransfers(), m_transitInfo->m_transfersSubway);
FillItemsByIdMap(graphData.GetLines(), m_transitInfo->m_linesSubway);
FillItemsByIdMap(graphData.GetShapes(), m_transitInfo->m_shapesSubway);
}
else if (transitHeaderVersion == ::transit::TransitVersion::AllPublicTransport)
{
m_transitInfo->m_transitVersion = ::transit::TransitVersion::AllPublicTransport;
::transit::experimental::TransitData transitData;
transitData.DeserializeForRendering(*reader.GetPtr());
FillItemsByIdMap(transitData.GetStops(), m_transitInfo->m_stopsPT);
for (auto const & edge : transitData.GetEdges())
{
::transit::EdgeId const edgeId(edge.GetStop1Id(), edge.GetStop2Id(), edge.GetLineId());
::transit::EdgeData const edgeData(edge.GetShapeLink(), edge.GetWeight());
if (!m_loadSubset)
{
m_transitInfo->m_edgesPT.emplace(edgeId, edgeData);
}
else
{
auto it = m_transitInfo->m_edgesPT.find(edgeId);
if (it != m_transitInfo->m_edgesPT.end())
it->second = edgeData;
}
}
for (auto const & stop : m_transitInfo->m_stopsPT)
{
if (stop.second.GetFeatureId() != kInvalidFeatureId)
{
auto const featureId = FeatureID(m_mwmId, stop.second.GetFeatureId());
m_transitInfo->m_features[featureId] = {};
}
if (m_loadSubset && !stop.second.GetTransferIds().empty())
for (auto const transferId : stop.second.GetTransferIds())
m_transitInfo->m_transfersPT[transferId] = {};
}
FillItemsByIdMap(transitData.GetTransfers(), m_transitInfo->m_transfersPT);
FillItemsByIdMap(transitData.GetShapes(), m_transitInfo->m_shapesPT);
FillLinesAndRoutes(transitData);
}
else
{
CHECK(false, (transitHeaderVersion));
}
vector<FeatureID> features;
for (auto & id : m_transitInfo->m_features)
features.push_back(id.first);
sort(features.begin(), features.end());
m_readFeaturesFn([this](FeatureType & ft)
{
auto & featureInfo = m_transitInfo->m_features[ft.GetID()];
featureInfo.m_title = ft.GetReadableName();
if (featureInfo.m_isGate)
{
// TODO(pastk): there should be a simpler way to just get a symbol name.
df::Stylist stylist(ft, 19, 0);
if (stylist.m_symbolRule != nullptr)
featureInfo.m_gateSymbolName = stylist.m_symbolRule->name();
}
featureInfo.m_point = feature::GetCenter(ft);
}, features);
m_success = true;
}
void ReadTransitTask::Reset()
{
m_transitInfo.reset();
m_id = 0;
IRoutine::Reset();
}
unique_ptr<TransitDisplayInfo> && ReadTransitTask::GetTransitInfo()
{
return std::move(m_transitInfo);
}
void ReadTransitTask::FillLinesAndRoutes(::transit::experimental::TransitData const & transitData)
{
auto const & routes = transitData.GetRoutes();
auto const & linesMeta = transitData.GetLinesMetadata();
for (auto const & line : transitData.GetLines())
{
::transit::TransitId const routeId = line.GetRouteId();
auto const itRoute = ::transit::FindById(routes, routeId);
auto const itLineMetadata = ::transit::FindById(linesMeta, line.GetId(), false /*exists*/);
if (m_loadSubset)
{
auto it = m_transitInfo->m_linesPT.find(line.GetId());
if (it != m_transitInfo->m_linesPT.end())
{
it->second = line;
m_transitInfo->m_routesPT.emplace(routeId, *itRoute);
if (itLineMetadata != linesMeta.end())
m_transitInfo->m_linesMetadataPT.emplace(itLineMetadata->GetId(), *itLineMetadata);
}
}
else
{
m_transitInfo->m_linesPT.emplace(line.GetId(), line);
m_transitInfo->m_routesPT.emplace(routeId, *itRoute);
if (itLineMetadata != linesMeta.end())
m_transitInfo->m_linesMetadataPT.emplace(itLineMetadata->GetId(), *itLineMetadata);
}
}
}
TransitReadManager::TransitReadManager(DataSource & dataSource, TReadFeaturesFn const & readFeaturesFn,
GetMwmsByRectFn const & getMwmsByRectFn)
: m_dataSource(dataSource)
, m_readFeaturesFn(readFeaturesFn)
, m_getMwmsByRectFn(getMwmsByRectFn)
{
Start();
}
TransitReadManager::~TransitReadManager()
{
Stop();
}
void TransitReadManager::Start()
{
if (m_threadsPool != nullptr)
return;
using namespace placeholders;
uint8_t constexpr kThreadsCount = 1;
m_threadsPool = make_unique<base::ThreadPool>(kThreadsCount, bind(&TransitReadManager::OnTaskCompleted, this, _1));
}
void TransitReadManager::Stop()
{
if (m_threadsPool != nullptr)
m_threadsPool->Stop();
m_threadsPool.reset();
}
void TransitReadManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
{
m_drapeEngine.Set(engine);
}
void TransitReadManager::EnableTransitSchemeMode(bool enable)
{
ChangeState(enable ? TransitSchemeState::Enabled : TransitSchemeState::Disabled);
if (m_isSchemeMode == enable)
return;
m_isSchemeMode = enable;
m_drapeEngine.SafeCall(&df::DrapeEngine::EnableTransitScheme, enable);
if (m_isSchemeModeBlocked)
return;
if (!m_isSchemeMode)
{
m_lastActiveMwms.clear();
m_mwmCache.clear();
m_cacheSize = 0;
}
else
{
Invalidate();
}
}
void TransitReadManager::BlockTransitSchemeMode(bool isBlocked)
{
if (m_isSchemeModeBlocked == isBlocked)
return;
m_isSchemeModeBlocked = isBlocked;
if (!m_isSchemeMode)
return;
if (m_isSchemeModeBlocked)
{
m_drapeEngine.SafeCall(&df::DrapeEngine::ClearAllTransitSchemeCache);
m_lastActiveMwms.clear();
m_mwmCache.clear();
m_cacheSize = 0;
}
else
{
Invalidate();
}
}
void TransitReadManager::UpdateViewport(ScreenBase const & screen)
{
m_currentModelView = {screen, true /* initialized */};
if (!m_isSchemeMode || m_isSchemeModeBlocked)
return;
if (df::GetDrawTileScale(screen) < kMinSchemeZoomLevel)
{
ChangeState(TransitSchemeState::Enabled);
return;
}
auto mwms = m_getMwmsByRectFn(screen.ClipRect());
m_lastActiveMwms.clear();
auto const currentTime = steady_clock::now();
TransitDisplayInfos newTransitData;
for (auto const & mwmId : mwms)
{
if (!mwmId.IsAlive())
continue;
m_lastActiveMwms.insert(mwmId);
auto it = m_mwmCache.find(mwmId);
if (it == m_mwmCache.end())
{
newTransitData[mwmId] = {};
m_mwmCache.insert(make_pair(mwmId, CacheEntry(currentTime)));
}
else
{
it->second.m_lastActiveTime = currentTime;
}
}
if (!newTransitData.empty())
{
GetTransitDisplayInfo(newTransitData);
TransitDisplayInfos validTransitData;
for (auto & [mwmId, transitInfo] : newTransitData)
{
if (!transitInfo)
continue;
if (transitInfo->m_transitVersion == ::transit::TransitVersion::OnlySubway && transitInfo->m_linesSubway.empty())
continue;
if (transitInfo->m_transitVersion == ::transit::TransitVersion::AllPublicTransport &&
transitInfo->m_linesPT.empty())
continue;
auto it = m_mwmCache.find(mwmId);
it->second.m_isLoaded = true;
auto const dataSize = CalculateCacheSize(*transitInfo);
it->second.m_dataSize = dataSize;
m_cacheSize += dataSize;
validTransitData[mwmId] = std::move(transitInfo);
}
if (!validTransitData.empty())
{
ShrinkCacheToAllowableSize();
m_drapeEngine.SafeCall(&df::DrapeEngine::UpdateTransitScheme, std::move(validTransitData));
}
}
bool hasData = m_lastActiveMwms.empty();
for (auto const & mwmId : m_lastActiveMwms)
{
if (m_mwmCache.at(mwmId).m_isLoaded)
{
hasData = true;
break;
}
}
ChangeState(hasData ? TransitSchemeState::Enabled : TransitSchemeState::NoData);
}
void TransitReadManager::ClearCache(MwmSet::MwmId const & mwmId)
{
auto it = m_mwmCache.find(mwmId);
if (it == m_mwmCache.end())
return;
m_cacheSize -= it->second.m_dataSize;
m_mwmCache.erase(it);
m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTransitSchemeCache, mwmId);
}
void TransitReadManager::OnMwmDeregistered(platform::LocalCountryFile const & countryFile)
{
MwmSet::MwmId mwmId;
for (auto const & cacheEntry : m_mwmCache)
{
if (cacheEntry.first.IsDeregistered(countryFile))
{
mwmId = cacheEntry.first;
break;
}
}
ClearCache(mwmId);
}
void TransitReadManager::Invalidate()
{
if (!m_isSchemeMode)
return;
if (m_currentModelView.second)
UpdateViewport(m_currentModelView.first);
}
void TransitReadManager::ShrinkCacheToAllowableSize()
{
using namespace std::chrono;
if (m_cacheSize > kMaxTransitCacheSizeBytes)
{
std::multimap<time_point<steady_clock>, MwmSet::MwmId> seenTimings;
for (auto const & entry : m_mwmCache)
if (entry.second.m_isLoaded && m_lastActiveMwms.count(entry.first) == 0)
seenTimings.insert(make_pair(entry.second.m_lastActiveTime, entry.first));
while (m_cacheSize > kMaxTransitCacheSizeBytes && !seenTimings.empty())
{
ClearCache(seenTimings.begin()->second);
seenTimings.erase(seenTimings.begin());
}
}
}
bool TransitReadManager::GetTransitDisplayInfo(TransitDisplayInfos & transitDisplayInfos)
{
unique_lock<mutex> lock(m_mutex);
auto const groupId = ++m_nextTasksGroupId;
lock.unlock();
map<MwmSet::MwmId, unique_ptr<ReadTransitTask>> transitTasks;
for (auto & mwmTransitPair : transitDisplayInfos)
{
auto const & mwmId = mwmTransitPair.first;
auto task = make_unique<ReadTransitTask>(m_dataSource, m_readFeaturesFn);
task->Init(groupId, mwmId, std::move(mwmTransitPair.second));
transitTasks[mwmId] = std::move(task);
}
lock.lock();
m_tasksGroups[groupId] = transitTasks.size();
lock.unlock();
for (auto const & task : transitTasks)
m_threadsPool->PushBack(task.second.get());
lock.lock();
m_event.wait(lock, [&]() { return m_tasksGroups[groupId] == 0; });
m_tasksGroups.erase(groupId);
lock.unlock();
bool result = true;
for (auto const & transitTask : transitTasks)
{
if (!transitTask.second->GetSuccess())
{
result = false;
continue;
}
transitDisplayInfos[transitTask.first] = transitTask.second->GetTransitInfo();
}
return result;
}
void TransitReadManager::OnTaskCompleted(threads::IRoutine * task)
{
ASSERT(dynamic_cast<ReadTransitTask *>(task) != nullptr, ());
ReadTransitTask * t = static_cast<ReadTransitTask *>(task);
lock_guard<mutex> lock(m_mutex);
if (--m_tasksGroups[t->GetId()] == 0)
m_event.notify_all();
}
TransitReadManager::TransitSchemeState TransitReadManager::GetState() const
{
return m_state;
}
void TransitReadManager::SetStateListener(TransitStateChangedFn const & onStateChangedFn)
{
m_onStateChangedFn = onStateChangedFn;
}
void TransitReadManager::ChangeState(TransitSchemeState newState)
{
if (m_state == newState)
return;
m_state = newState;
if (m_onStateChangedFn)
m_onStateChangedFn(newState);
}

View file

@ -0,0 +1,154 @@
#pragma once
#include "drape_frontend/drape_engine_safe_ptr.hpp"
#include "transit/experimental/transit_data.hpp"
#include "transit/transit_display_info.hpp"
#include "indexer/data_source.hpp"
#include "indexer/feature_decl.hpp"
#include "geometry/screenbase.hpp"
#include "base/thread.hpp"
#include "base/thread_pool.hpp"
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <vector>
class DataSource;
using FeatureCallback = std::function<void(FeatureType &)>;
using TReadFeaturesFn = std::function<void(FeatureCallback const &, std::vector<FeatureID> const &)>;
class ReadTransitTask : public threads::IRoutine
{
public:
ReadTransitTask(DataSource & dataSource, TReadFeaturesFn const & readFeaturesFn)
: m_dataSource(dataSource)
, m_readFeaturesFn(readFeaturesFn)
{}
void Init(uint64_t id, MwmSet::MwmId const & mwmId, std::unique_ptr<TransitDisplayInfo> transitInfo = nullptr);
uint64_t GetId() const { return m_id; }
bool GetSuccess() const { return m_success; }
void Do() override;
void Reset() override;
std::unique_ptr<TransitDisplayInfo> && GetTransitInfo();
private:
template <typename T, typename TID>
void FillItemsByIdMap(std::vector<T> const & items, std::map<TID, T> & itemsById)
{
for (auto const & item : items)
{
if (!m_loadSubset)
{
itemsById.emplace(item.GetId(), item);
}
else
{
auto it = itemsById.find(item.GetId());
if (it != itemsById.end())
it->second = item;
}
}
}
void FillLinesAndRoutes(::transit::experimental::TransitData const & transitData);
DataSource & m_dataSource;
TReadFeaturesFn m_readFeaturesFn;
uint64_t m_id = 0;
MwmSet::MwmId m_mwmId;
std::unique_ptr<TransitDisplayInfo> m_transitInfo;
bool m_loadSubset = false;
// Sets to true if Do() method was executed successfully.
bool m_success = false;
};
class TransitReadManager
{
public:
enum class TransitSchemeState
{
Disabled,
Enabled,
NoData,
};
using GetMwmsByRectFn = std::function<std::vector<MwmSet::MwmId>(m2::RectD const &)>;
using TransitStateChangedFn = std::function<void(TransitSchemeState)>;
TransitReadManager(DataSource & dataSource, TReadFeaturesFn const & readFeaturesFn,
GetMwmsByRectFn const & getMwmsByRectFn);
~TransitReadManager();
void Start();
void Stop();
void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine);
TransitSchemeState GetState() const;
void SetStateListener(TransitStateChangedFn const & onStateChangedFn);
bool GetTransitDisplayInfo(TransitDisplayInfos & transitDisplayInfos);
void EnableTransitSchemeMode(bool enable);
void BlockTransitSchemeMode(bool isBlocked);
void UpdateViewport(ScreenBase const & screen);
void OnMwmDeregistered(platform::LocalCountryFile const & countryFile);
void Invalidate();
private:
void OnTaskCompleted(threads::IRoutine * task);
void ChangeState(TransitSchemeState newState);
void ShrinkCacheToAllowableSize();
void ClearCache(MwmSet::MwmId const & mwmId);
std::unique_ptr<base::ThreadPool> m_threadsPool;
std::mutex m_mutex;
std::condition_variable m_event;
uint64_t m_nextTasksGroupId = 0;
std::map<uint64_t, size_t> m_tasksGroups;
DataSource & m_dataSource;
TReadFeaturesFn m_readFeaturesFn;
df::DrapeEngineSafePtr m_drapeEngine;
TransitSchemeState m_state = TransitSchemeState::Disabled;
TransitStateChangedFn m_onStateChangedFn;
struct CacheEntry
{
CacheEntry(std::chrono::time_point<std::chrono::steady_clock> const & activeTime) : m_lastActiveTime(activeTime) {}
bool m_isLoaded = false;
size_t m_dataSize = 0;
std::chrono::time_point<std::chrono::steady_clock> m_lastActiveTime;
};
GetMwmsByRectFn m_getMwmsByRectFn;
std::set<MwmSet::MwmId> m_lastActiveMwms;
std::map<MwmSet::MwmId, CacheEntry> m_mwmCache;
size_t m_cacheSize = 0;
bool m_isSchemeMode = false;
bool m_isSchemeModeBlocked = false;
std::pair<ScreenBase, bool> m_currentModelView = {ScreenBase(), false /* initialized */};
};