Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
913
libs/map/transit/transit_display.cpp
Normal file
913
libs/map/transit/transit_display.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
174
libs/map/transit/transit_display.hpp
Normal file
174
libs/map/transit/transit_display.hpp
Normal 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;
|
||||
};
|
||||
508
libs/map/transit/transit_reader.cpp
Normal file
508
libs/map/transit/transit_reader.cpp
Normal 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);
|
||||
}
|
||||
154
libs/map/transit/transit_reader.hpp
Normal file
154
libs/map/transit/transit_reader.hpp
Normal 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 */};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue