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,33 @@
#import "MWMRouterType.h"
#include "routing/router.hpp"
static inline routing::RouterType coreRouterType(MWMRouterType type)
{
switch (type)
{
case MWMRouterTypeVehicle: return routing::RouterType::Vehicle;
case MWMRouterTypePedestrian: return routing::RouterType::Pedestrian;
case MWMRouterTypePublicTransport: return routing::RouterType::Transit;
case MWMRouterTypeBicycle: return routing::RouterType::Bicycle;
case MWMRouterTypeRuler: return routing::RouterType::Ruler;
default:
ASSERT(false, ("Invalid routing type"));
return routing::RouterType::Vehicle;
}
}
static inline MWMRouterType routerType(routing::RouterType type)
{
switch (type)
{
case routing::RouterType::Vehicle: return MWMRouterTypeVehicle;
case routing::RouterType::Transit: return MWMRouterTypePublicTransport;
case routing::RouterType::Pedestrian: return MWMRouterTypePedestrian;
case routing::RouterType::Bicycle: return MWMRouterTypeBicycle;
case routing::RouterType::Ruler: return MWMRouterTypeRuler;
default:
ASSERT(false, ("Invalid routing type"));
return MWMRouterTypeVehicle;
}
}

View file

@ -0,0 +1,20 @@
#import "MWMRoutePoint.h"
#include "map/mwm_url.hpp"
#include "map/routing_mark.hpp"
@interface MWMRoutePoint (CPP)
@property(nonatomic, readonly) RouteMarkData routeMarkData;
- (instancetype)initWithURLSchemeRoutePoint:(url_scheme::RoutePoint const &)point
type:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex;
- (instancetype)initWithRouteMarkData:(RouteMarkData const &)point;
- (instancetype)initWithPoint:(m2::PointD const &)point
title:(NSString *)title
subtitle:(NSString *)subtitle
type:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex;
@end

View file

@ -0,0 +1,28 @@
typedef NS_CLOSED_ENUM(NSUInteger, MWMRoutePointType) {
MWMRoutePointTypeStart,
MWMRoutePointTypeIntermediate,
MWMRoutePointTypeFinish
};
@interface MWMRoutePoint : NSObject
- (instancetype)initWithLastLocationAndType:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex;
- (instancetype)initWithCGPoint:(CGPoint)point
title:(NSString *)title
subtitle:(NSString *)subtitle
type:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex;
@property(copy, nonatomic, readonly) NSString * title;
@property(copy, nonatomic, readonly) NSString * subtitle;
@property(copy, nonatomic, readonly) NSString * latLonString;
@property(nonatomic, readonly) BOOL isMyPosition;
@property(nonatomic) MWMRoutePointType type;
@property(nonatomic) size_t intermediateIndex;
@property(nonatomic, readonly) double latitude;
@property(nonatomic, readonly) double longitude;
@end

View file

@ -0,0 +1,167 @@
#import "MWMRoutePoint.h"
#import "CLLocation+Mercator.h"
#import "MWMLocationManager.h"
#import "MWMRoutePoint+CPP.h"
#include "geometry/mercator.hpp"
#include "platform/measurement_utils.hpp"
@interface MWMRoutePoint ()
@property(nonatomic, readonly) m2::PointD point;
@end
@implementation MWMRoutePoint
- (instancetype)initWithLastLocationAndType:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex
{
auto lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return nil;
self = [super init];
if (self)
{
_point = lastLocation.mercator;
_title = L(@"p2p_your_location");
_subtitle = @"";
_isMyPosition = YES;
_type = type;
_intermediateIndex = intermediateIndex;
[self validatePoint];
}
return self;
}
- (instancetype)initWithURLSchemeRoutePoint:(url_scheme::RoutePoint const &)point
type:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex
{
self = [super init];
if (self)
{
_point = point.m_org;
_title = @(point.m_name.c_str());
_subtitle = @"";
_isMyPosition = NO;
_type = type;
_intermediateIndex = intermediateIndex;
[self validatePoint];
}
return self;
}
- (instancetype)initWithRouteMarkData:(RouteMarkData const &)point
{
self = [super init];
if (self)
{
_point = point.m_position;
_title = @(point.m_title.c_str());
_subtitle = @(point.m_subTitle.c_str());
_isMyPosition = point.m_isMyPosition;
_intermediateIndex = point.m_intermediateIndex;
switch (point.m_pointType)
{
case RouteMarkType::Start: _type = MWMRoutePointTypeStart; break;
case RouteMarkType::Intermediate: _type = MWMRoutePointTypeIntermediate; break;
case RouteMarkType::Finish: _type = MWMRoutePointTypeFinish; break;
}
[self validatePoint];
}
return self;
}
- (instancetype)initWithCGPoint:(CGPoint)point
title:(NSString *)title
subtitle:(NSString *)subtitle
type:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex
{
auto const pointD = m2::PointD(point.x, point.y);
self = [self initWithPoint:pointD
title:title
subtitle:subtitle
type:type intermediateIndex:intermediateIndex];
return self;
}
- (instancetype)initWithPoint:(m2::PointD const &)point
title:(NSString *)title
subtitle:(NSString *)subtitle
type:(MWMRoutePointType)type
intermediateIndex:(size_t)intermediateIndex
{
self = [super init];
if (self)
{
_point = point;
_title = title;
_subtitle = subtitle ?: @"";
_isMyPosition = NO;
_type = type;
_intermediateIndex = intermediateIndex;
[self validatePoint];
}
return self;
}
- (void)validatePoint
{
// Sync with RoutePointsLayout::kMaxIntermediatePointsCount constant.
NSAssert(_intermediateIndex >= 0 && _intermediateIndex <= 100, @"Invalid intermediateIndex");
}
- (double)latitude { return mercator::YToLat(self.point.y); }
- (double)longitude { return mercator::XToLon(self.point.x); }
- (NSString *)latLonString
{
return @(measurement_utils::FormatLatLon(self.latitude, self.longitude, true).c_str());
}
- (RouteMarkData)routeMarkData
{
[self validatePoint];
RouteMarkData pt;
switch (self.type)
{
case MWMRoutePointTypeStart: pt.m_pointType = RouteMarkType::Start; break;
case MWMRoutePointTypeIntermediate: pt.m_pointType = RouteMarkType::Intermediate; break;
case MWMRoutePointTypeFinish: pt.m_pointType = RouteMarkType::Finish; break;
}
pt.m_position = self.point;
pt.m_isMyPosition = self.isMyPosition;
pt.m_title = self.title.UTF8String;
pt.m_subTitle = self.subtitle.UTF8String;
pt.m_intermediateIndex = self.intermediateIndex;
return pt;
}
- (NSString *)debugDescription
{
NSString * type = nil;
switch (_type)
{
case MWMRoutePointTypeStart: type = @"Start"; break;
case MWMRoutePointTypeIntermediate: type = @"Intermediate"; break;
case MWMRoutePointTypeFinish: type = @"Finish"; break;
}
return [NSString stringWithFormat:@"<%@: %p> Position: [%@, %@] | IsMyPosition: %@ | Type: %@ | "
@"IntermediateIndex: %@ | Title: %@ | Subtitle: %@",
[self class], self, @(_point.x), @(_point.y),
_isMyPosition ? @"true" : @"false", type, @(_intermediateIndex),
_title, _subtitle];
}
@end

View file

@ -0,0 +1,53 @@
#import "MWMRouter.h"
#include <CoreApi/Framework.h>
@interface MWMRouter ()
@property(nonatomic) uint32_t routeManagerTransactionId;
+ (MWMRouter *)router;
@end
@implementation MWMRouter (RouteManager)
+ (void)openRouteManagerTransaction
{
auto router = [MWMRouter router];
router.routeManagerTransactionId =
GetFramework().GetRoutingManager().OpenRoutePointsTransaction();
}
+ (void)applyRouteManagerTransaction
{
auto router = [MWMRouter router];
if (router.routeManagerTransactionId == RoutingManager::InvalidRoutePointsTransactionId())
return;
GetFramework().GetRoutingManager().ApplyRoutePointsTransaction(router.routeManagerTransactionId);
router.routeManagerTransactionId = RoutingManager::InvalidRoutePointsTransactionId();
}
+ (void)cancelRouteManagerTransaction
{
auto router = [MWMRouter router];
if (router.routeManagerTransactionId == RoutingManager::InvalidRoutePointsTransactionId())
return;
auto & rm = GetFramework().GetRoutingManager();
rm.CancelRoutePointsTransaction(router.routeManagerTransactionId);
router.routeManagerTransactionId = RoutingManager::InvalidRoutePointsTransactionId();
rm.CancelPreviewMode();
}
+ (void)movePointAtIndex:(NSInteger)index toIndex:(NSInteger)newIndex
{
NSAssert(index != newIndex, @"Route manager moves point to its' current position.");
GetFramework().GetRoutingManager().MoveRoutePoint(index, newIndex);
}
+ (void)updatePreviewMode
{
GetFramework().GetRoutingManager().UpdatePreviewMode();
}
@end

View file

@ -0,0 +1,94 @@
#import "MWMRoutePoint.h"
#import "MWMRouterType.h"
typedef NS_ENUM(NSInteger, MWMRoadType) {
MWMRoadTypeToll,
MWMRoadTypeDirty,
MWMRoadTypeFerry,
MWMRoadTypeMotorway,
MWMRoadTypeSteps,
MWMRoadTypePaved
};
typedef void (^MWMImageHeightBlock)(UIImage *, NSString *, NSString *);
@interface MWMRouter : NSObject
+ (void)subscribeToEvents;
+ (void)unsubscribeFromEvents;
+ (BOOL)isRoutingActive;
+ (BOOL)isRouteBuilt;
+ (BOOL)isRouteFinished;
+ (BOOL)isRouteRebuildingOnly;
+ (BOOL)isOnRoute;
+ (BOOL)isSpeedCamLimitExceeded;
+ (BOOL)canAddIntermediatePoint;
+ (void)startRouting;
+ (void)stopRouting;
+ (NSArray<MWMRoutePoint *> *)points;
+ (NSInteger)pointsCount;
+ (MWMRoutePoint *)startPoint;
+ (MWMRoutePoint *)finishPoint;
+ (void)enableAutoAddLastLocation:(BOOL)enable;
+ (void)setType:(MWMRouterType)type;
+ (MWMRouterType)type;
+ (void)disableFollowMode;
+ (void)enableTurnNotifications:(BOOL)active;
+ (BOOL)areTurnNotificationsEnabled;
+ (void)setTurnNotificationsLocale:(NSString *)locale;
+ (NSArray<NSString *> *)turnNotifications;
+ (void)addPoint:(MWMRoutePoint *)point;
+ (void)removePoint:(MWMRoutePoint *)point;
+ (void)addPointAndRebuild:(MWMRoutePoint *)point;
+ (void)removePointAndRebuild:(MWMRoutePoint *)point;
+ (void)removePoints;
+ (void)buildFromPoint:(MWMRoutePoint *)start bestRouter:(BOOL)bestRouter;
+ (void)buildToPoint:(MWMRoutePoint *)finish bestRouter:(BOOL)bestRouter;
+ (void)buildApiRouteWithType:(MWMRouterType)type
startPoint:(MWMRoutePoint *)startPoint
finishPoint:(MWMRoutePoint *)finishPoint;
+ (void)rebuildWithBestRouter:(BOOL)bestRouter;
+ (BOOL)hasRouteAltitude;
+ (void)routeAltitudeImageForSize:(CGSize)size completion:(MWMImageHeightBlock)block;
+ (void)saveRouteIfNeeded;
+ (void)restoreRouteIfNeeded;
+ (BOOL)hasSavedRoute;
+ (BOOL)isRestoreProcessCompleted;
+ (void)updateRoute;
+ (BOOL)hasActiveDrivingOptions;
+ (void)avoidRoadTypeAndRebuild:(MWMRoadType)type;
+ (void)showNavigationMapControls;
+ (void)hideNavigationMapControls;
- (instancetype)init __attribute__((unavailable("call +router instead")));
- (instancetype)copy __attribute__((unavailable("call +router instead")));
- (instancetype)copyWithZone:(NSZone *)zone __attribute__((unavailable("call +router instead")));
+ (instancetype)allocWithZone:(struct _NSZone *)zone
__attribute__((unavailable("call +router instead")));
+ (instancetype) new __attribute__((unavailable("call +router instead")));
@end
@interface MWMRouter (RouteManager)
+ (void)openRouteManagerTransaction;
+ (void)applyRouteManagerTransaction;
+ (void)cancelRouteManagerTransaction;
+ (void)movePointAtIndex:(NSInteger)index toIndex:(NSInteger)newIndex;
+ (void)updatePreviewMode;
@end

View file

@ -0,0 +1,620 @@
#import "MWMRouter.h"
#import "MWMAlertViewController+CPP.h"
#import "MWMCoreRouterType.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "MWMLocationHelpers.h"
#import "MWMLocationObserver.h"
#import "MWMMapViewControlsManager.h"
#import "MWMNavigationDashboardManager+Entity.h"
#import "MWMRoutePoint+CPP.h"
#import "MWMStorage+UI.h"
#import "MapsAppDelegate.h"
#import "SwiftBridge.h"
#import "UIImage+RGBAData.h"
#include <CoreApi/Framework.h>
#include "platform/local_country_file_utils.hpp"
#include "platform/localization.hpp"
#include "platform/distance.hpp"
using namespace routing;
@interface MWMRouter () <MWMLocationObserver, MWMFrameworkRouteBuilderObserver>
@property(nonatomic) NSMutableDictionary<NSValue *, NSData *> *altitudeImagesData;
@property(nonatomic) NSString *totalAscent;
@property(nonatomic) NSString *totalDescent;
@property(nonatomic) dispatch_queue_t renderAltitudeImagesQueue;
@property(nonatomic) uint32_t routeManagerTransactionId;
@property(nonatomic) BOOL canAutoAddLastLocation;
@property(nonatomic) BOOL isAPICall;
@property(nonatomic) BOOL isRestoreProcessCompleted;
@property(strong, nonatomic) MWMRoutingOptions *routingOptions;
+ (MWMRouter *)router;
@end
namespace {
char const *kRenderAltitudeImagesQueueLabel = "mapsme.mwmrouter.renderAltitudeImagesQueue";
} // namespace
@implementation MWMRouter
+ (MWMRouter *)router {
static MWMRouter *router;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
router = [[self alloc] initRouter];
});
return router;
}
+ (BOOL)hasRouteAltitude {
switch ([self type]) {
case MWMRouterTypeVehicle:
case MWMRouterTypePublicTransport:
case MWMRouterTypeRuler:
return NO;
case MWMRouterTypePedestrian:
case MWMRouterTypeBicycle:
return GetFramework().GetRoutingManager().HasRouteAltitude();
}
}
+ (void)startRouting {
[self start];
}
+ (void)stopRouting {
[self stop:YES];
}
+ (BOOL)isRoutingActive {
return GetFramework().GetRoutingManager().IsRoutingActive();
}
+ (BOOL)isRouteBuilt {
return GetFramework().GetRoutingManager().IsRouteBuilt();
}
+ (BOOL)isRouteFinished {
return GetFramework().GetRoutingManager().IsRouteFinished();
}
+ (BOOL)isRouteRebuildingOnly {
return GetFramework().GetRoutingManager().IsRouteRebuildingOnly();
}
+ (BOOL)isOnRoute {
return GetFramework().GetRoutingManager().IsRoutingFollowing();
}
+ (BOOL)IsRouteValid {
return GetFramework().GetRoutingManager().IsRouteValid();
}
+ (BOOL)isSpeedCamLimitExceeded
{
return GetFramework().GetRoutingManager().IsSpeedCamLimitExceeded();
}
+ (NSArray<MWMRoutePoint *> *)points {
NSMutableArray<MWMRoutePoint *> *points = [@[] mutableCopy];
auto const routePoints = GetFramework().GetRoutingManager().GetRoutePoints();
for (auto const &routePoint : routePoints)
[points addObject:[[MWMRoutePoint alloc] initWithRouteMarkData:routePoint]];
return [points copy];
}
+ (NSInteger)pointsCount {
return GetFramework().GetRoutingManager().GetRoutePointsCount();
}
+ (MWMRoutePoint *)startPoint {
auto const routePoints = GetFramework().GetRoutingManager().GetRoutePoints();
if (routePoints.empty())
return nil;
auto const &routePoint = routePoints.front();
if (routePoint.m_pointType == RouteMarkType::Start)
return [[MWMRoutePoint alloc] initWithRouteMarkData:routePoint];
return nil;
}
+ (MWMRoutePoint *)finishPoint {
auto const routePoints = GetFramework().GetRoutingManager().GetRoutePoints();
if (routePoints.empty())
return nil;
auto const &routePoint = routePoints.back();
if (routePoint.m_pointType == RouteMarkType::Finish)
return [[MWMRoutePoint alloc] initWithRouteMarkData:routePoint];
return nil;
}
+ (void)enableAutoAddLastLocation:(BOOL)enable {
[MWMRouter router].canAutoAddLastLocation = enable;
}
+ (BOOL)canAddIntermediatePoint {
return GetFramework().GetRoutingManager().CouldAddIntermediatePoint();
}
- (instancetype)initRouter {
self = [super init];
if (self) {
self.altitudeImagesData = [@{} mutableCopy];
self.renderAltitudeImagesQueue = dispatch_queue_create(kRenderAltitudeImagesQueueLabel, DISPATCH_QUEUE_SERIAL);
self.routeManagerTransactionId = RoutingManager::InvalidRoutePointsTransactionId();
[MWMLocationManager addObserver:self];
[MWMFrameworkListener addObserver:self];
_canAutoAddLastLocation = YES;
_routingOptions = [MWMRoutingOptions new];
_isRestoreProcessCompleted = NO;
[NSNotificationCenter.defaultCenter addObserverForName:@"RoutingOptionsChanged" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
[MWMRouter updateRoute];
}];
}
return self;
}
+ (void)subscribeToEvents {
[MWMFrameworkListener addObserver:[MWMRouter router]];
[MWMLocationManager addObserver:[MWMRouter router]];
}
+ (void)unsubscribeFromEvents {
[MWMFrameworkListener removeObserver:[MWMRouter router]];
[MWMLocationManager removeObserver:[MWMRouter router]];
}
+ (void)setType:(MWMRouterType)type {
if (type == self.type)
return;
[self doStop:NO];
GetFramework().GetRoutingManager().SetRouter(coreRouterType(type));
}
+ (MWMRouterType)type {
return routerType(GetFramework().GetRoutingManager().GetRouter());
}
+ (void)disableFollowMode {
GetFramework().GetRoutingManager().DisableFollowMode();
}
+ (void)enableTurnNotifications:(BOOL)active {
GetFramework().GetRoutingManager().EnableTurnNotifications(active);
}
+ (BOOL)areTurnNotificationsEnabled {
return GetFramework().GetRoutingManager().AreTurnNotificationsEnabled();
}
+ (void)setTurnNotificationsLocale:(NSString *)locale {
GetFramework().GetRoutingManager().SetTurnNotificationsLocale(locale.UTF8String);
}
+ (NSArray<NSString *> *)turnNotifications {
NSMutableArray<NSString *> *turnNotifications = [@[] mutableCopy];
std::vector<std::string> notifications;
auto announceStreets = [NSUserDefaults.standardUserDefaults boolForKey:@"UserDefaultsNeedToEnableStreetNamesTTS"];
GetFramework().GetRoutingManager().GenerateNotifications(notifications, announceStreets);
for (auto const &text : notifications)
[turnNotifications addObject:@(text.c_str())];
return [turnNotifications copy];
}
+ (void)removePoint:(MWMRoutePoint *)point {
RouteMarkData pt = point.routeMarkData;
GetFramework().GetRoutingManager().RemoveRoutePoint(pt.m_pointType, pt.m_intermediateIndex);
[[MWMNavigationDashboardManager sharedManager] onRoutePointsUpdated];
}
+ (void)removePointAndRebuild:(MWMRoutePoint *)point {
if (!point)
return;
[self removePoint:point];
[self rebuildWithBestRouter:NO];
}
+ (void)removePoints {
GetFramework().GetRoutingManager().RemoveRoutePoints();
}
+ (void)addPoint:(MWMRoutePoint *)point {
if (!point) {
NSAssert(NO, @"Point can not be nil");
return;
}
RouteMarkData pt = point.routeMarkData;
GetFramework().GetRoutingManager().AddRoutePoint(std::move(pt));
[[MWMNavigationDashboardManager sharedManager] onRoutePointsUpdated];
}
+ (void)addPointAndRebuild:(MWMRoutePoint *)point {
if (!point)
return;
[self addPoint:point];
[self rebuildWithBestRouter:NO];
}
+ (void)buildFromPoint:(MWMRoutePoint *)startPoint bestRouter:(BOOL)bestRouter {
if (!startPoint)
return;
[self addPoint:startPoint];
[self rebuildWithBestRouter:bestRouter];
}
+ (void)buildToPoint:(MWMRoutePoint *)finishPoint bestRouter:(BOOL)bestRouter {
if (!finishPoint)
return;
[self addPoint:finishPoint];
if (![self startPoint] && [MWMLocationManager lastLocation] && [MWMRouter router].canAutoAddLastLocation) {
[self addPoint:[[MWMRoutePoint alloc] initWithLastLocationAndType:MWMRoutePointTypeStart intermediateIndex:0]];
}
if ([self startPoint] && [self finishPoint])
[self rebuildWithBestRouter:bestRouter];
}
+ (void)buildApiRouteWithType:(MWMRouterType)type
startPoint:(MWMRoutePoint *)startPoint
finishPoint:(MWMRoutePoint *)finishPoint {
if (!startPoint || !finishPoint)
return;
[MWMRouter setType:type];
auto router = [MWMRouter router];
router.isAPICall = YES;
[self addPoint:startPoint];
[self addPoint:finishPoint];
router.isAPICall = NO;
[self rebuildWithBestRouter:NO];
}
+ (void)rebuildWithBestRouter:(BOOL)bestRouter {
[self clearAltitudeImagesData];
auto &rm = GetFramework().GetRoutingManager();
auto const &points = rm.GetRoutePoints();
auto const pointsCount = points.size();
if (pointsCount < 2) {
[self doStop:NO];
[[MWMMapViewControlsManager manager] onRoutePrepare];
return;
}
if (bestRouter)
self.type = routerType(rm.GetBestRouter(points.front().m_position, points.back().m_position));
[[MWMMapViewControlsManager manager] onRouteRebuild];
rm.BuildRoute();
}
+ (void)start {
[self saveRoute];
auto const doStart = ^{
auto &rm = GetFramework().GetRoutingManager();
auto const routePoints = rm.GetRoutePoints();
if (routePoints.size() >= 2)
{
auto p1 = [[MWMRoutePoint alloc] initWithRouteMarkData:routePoints.front()];
auto p2 = [[MWMRoutePoint alloc] initWithRouteMarkData:routePoints.back()];
CLLocation *lastLocation = [MWMLocationManager lastLocation];
if (p1.isMyPosition && lastLocation)
{
rm.FollowRoute();
[[MWMMapViewControlsManager manager] onRouteStart];
[MWMThemeManager setAutoUpdates:YES];
}
else
{
BOOL const needToRebuild = lastLocation && [MWMLocationManager isStarted] && !p2.isMyPosition;
[[MWMAlertViewController activeAlertController]
presentPoint2PointAlertWithOkBlock:^{
[self buildFromPoint:[[MWMRoutePoint alloc] initWithLastLocationAndType:MWMRoutePointTypeStart
intermediateIndex:0]
bestRouter:NO];
}
needToRebuild:needToRebuild];
}
}
};
if ([MWMSettings routingDisclaimerApproved]) {
doStart();
} else {
[[MWMAlertViewController activeAlertController] presentRoutingDisclaimerAlertWithOkBlock:^{
doStart();
[MWMSettings setRoutingDisclaimerApproved];
}];
}
}
+ (void)stop:(BOOL)removeRoutePoints {
[self doStop:removeRoutePoints];
[self hideNavigationMapControls];
[MWMRouter router].canAutoAddLastLocation = YES;
}
+ (void)doStop:(BOOL)removeRoutePoints {
[self clearAltitudeImagesData];
GetFramework().GetRoutingManager().CloseRouting(removeRoutePoints);
if (removeRoutePoints)
GetFramework().GetRoutingManager().DeleteSavedRoutePoints();
[MWMThemeManager setAutoUpdates:NO];
}
- (void)updateFollowingInfo {
if (![MWMRouter isRoutingActive])
return;
auto const &rm = GetFramework().GetRoutingManager();
routing::FollowingInfo info;
rm.GetRouteFollowingInfo(info);
if (!info.IsValid())
return;
auto navManager = [MWMNavigationDashboardManager sharedManager];
if ([MWMRouter type] == MWMRouterTypePublicTransport)
[navManager updateTransitInfo:rm.GetTransitRouteInfo()];
else
[navManager updateFollowingInfo:info routePoints:[MWMRouter points] type:[MWMRouter type]];
}
+ (void)routeAltitudeImageForSize:(CGSize)size completion:(MWMImageHeightBlock)block {
if (![self hasRouteAltitude])
return;
auto altitudes = std::make_shared<RoutingManager::DistanceAltitude>();
if (!GetFramework().GetRoutingManager().GetRouteAltitudesAndDistancesM(*altitudes))
return;
// |altitudes| should not be used in the method after line below.
dispatch_async(self.router.renderAltitudeImagesQueue, [=]() {
auto router = self.router;
CGFloat const screenScale = [UIScreen mainScreen].scale;
CGSize const scaledSize = {size.width * screenScale, size.height * screenScale};
CHECK_GREATER_OR_EQUAL(scaledSize.width, 0.0, ());
CHECK_GREATER_OR_EQUAL(scaledSize.height, 0.0, ());
uint32_t const width = static_cast<uint32_t>(scaledSize.width);
uint32_t const height = static_cast<uint32_t>(scaledSize.height);
if (width == 0 || height == 0)
return;
NSValue *sizeValue = [NSValue valueWithCGSize:scaledSize];
NSData *imageData = router.altitudeImagesData[sizeValue];
if (!imageData)
{
altitudes->Simplify();
std::vector<uint8_t> imageRGBAData;
if (!altitudes->GenerateRouteAltitudeChart(width, height, imageRGBAData))
return;
if (imageRGBAData.empty())
return;
imageData = [NSData dataWithBytes:imageRGBAData.data() length:imageRGBAData.size()];
router.altitudeImagesData[sizeValue] = imageData;
uint32_t totalAscentM, totalDescentM;
altitudes->CalculateAscentDescent(totalAscentM, totalDescentM);
auto const localizedUnits = platform::GetLocalizedAltitudeUnits();
router.totalAscent = @(platform::Distance::FormatAltitude(totalAscentM).c_str());
router.totalDescent = @(platform::Distance::FormatAltitude(totalDescentM).c_str());
}
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *altitudeImage = [UIImage imageWithRGBAData:imageData width:width height:height];
if (altitudeImage)
block(altitudeImage, router.totalAscent, router.totalDescent);
});
});
}
+ (void)clearAltitudeImagesData {
auto router = self.router;
dispatch_async(router.renderAltitudeImagesQueue, ^{
[router.altitudeImagesData removeAllObjects];
router.totalAscent = nil;
router.totalDescent = nil;
});
}
#pragma mark - MWMLocationObserver
- (void)onLocationUpdate:(CLLocation *)location {
if (![MWMRouter isRoutingActive])
return;
auto tts = [MWMTextToSpeech tts];
NSArray<NSString *> *turnNotifications = [MWMRouter turnNotifications];
if ([MWMRouter isOnRoute] && tts.active) {
[tts playTurnNotifications:turnNotifications];
[tts playWarningSound];
}
[self updateFollowingInfo];
}
#pragma mark - MWMFrameworkRouteBuilderObserver
- (void)onRouteReady:(BOOL)hasWarnings {
self.routingOptions = [MWMRoutingOptions new];
GetFramework().DeactivateMapSelection();
auto startPoint = [MWMRouter startPoint];
if (!startPoint || !startPoint.isMyPosition) {
dispatch_async(dispatch_get_main_queue(), ^{
[MWMRouter disableFollowMode];
});
}
[[MWMMapViewControlsManager manager] onRouteReady:hasWarnings];
[self updateFollowingInfo];
}
- (void)processRouteBuilderEvent:(routing::RouterResultCode)code
countries:(storage::CountriesSet const &)absentCountries {
MWMMapViewControlsManager *mapViewControlsManager = [MWMMapViewControlsManager manager];
switch (code) {
case routing::RouterResultCode::NoError:
[self onRouteReady:NO];
break;
case routing::RouterResultCode::HasWarnings:
[self onRouteReady:YES];
break;
case routing::RouterResultCode::RouteFileNotExist:
case routing::RouterResultCode::InconsistentMWMandRoute:
case routing::RouterResultCode::NeedMoreMaps:
case routing::RouterResultCode::FileTooOld:
case routing::RouterResultCode::RouteNotFound:
self.routingOptions = [MWMRoutingOptions new];
[self presentDownloaderAlert:code countries:absentCountries];
[[MWMNavigationDashboardManager sharedManager] onRouteError:L(@"routing_planning_error")];
break;
case routing::RouterResultCode::Cancelled:
[mapViewControlsManager onRoutePrepare];
break;
case routing::RouterResultCode::StartPointNotFound:
case routing::RouterResultCode::EndPointNotFound:
case routing::RouterResultCode::NoCurrentPosition:
case routing::RouterResultCode::PointsInDifferentMWM:
case routing::RouterResultCode::InternalError:
case routing::RouterResultCode::IntermediatePointNotFound:
case routing::RouterResultCode::TransitRouteNotFoundNoNetwork:
case routing::RouterResultCode::TransitRouteNotFoundTooLongPedestrian:
case routing::RouterResultCode::RouteNotFoundRedressRouteError:
[[MWMAlertViewController activeAlertController] presentAlert:code];
[[MWMNavigationDashboardManager sharedManager] onRouteError:L(@"routing_planning_error")];
break;
}
}
- (void)processRouteBuilderProgress:(CGFloat)progress {
[[MWMNavigationDashboardManager sharedManager] setRouteBuilderProgress:progress];
}
- (void)processRouteRecommendation:(MWMRouterRecommendation)recommendation {
switch (recommendation) {
case MWMRouterRecommendationRebuildAfterPointsLoading:
[MWMRouter addPointAndRebuild:[[MWMRoutePoint alloc] initWithLastLocationAndType:MWMRoutePointTypeStart
intermediateIndex:0]];
break;
}
}
#pragma mark - Alerts
- (void)presentDownloaderAlert:(routing::RouterResultCode)code countries:(storage::CountriesSet const &)countries {
MWMAlertViewController *activeAlertController = [MWMAlertViewController activeAlertController];
if (!countries.empty()) {
[activeAlertController presentDownloaderAlertWithCountries:countries
code:code
cancelBlock:^{
if (code != routing::RouterResultCode::NeedMoreMaps)
[MWMRouter stopRouting];
}
downloadBlock:^(storage::CountriesVec const &downloadCountries, MWMVoidBlock onSuccess) {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:downloadCountries.size()];
for (auto const &cid : downloadCountries) {
[array addObject:@(cid.c_str())];
}
[[MWMStorage sharedStorage] downloadNodes:array onSuccess:onSuccess];
}
downloadCompleteBlock:^{
[MWMRouter rebuildWithBestRouter:NO];
}];
} else if ([MWMRouter hasActiveDrivingOptions]) {
[activeAlertController presentDefaultAlertWithTitle:L(@"unable_to_calc_alert_title")
message:L(@"unable_to_calc_alert_subtitle")
rightButtonTitle:L(@"settings")
leftButtonTitle:L(@"cancel")
rightButtonAction:^{
[[MapViewController sharedController] openDrivingOptions];
}];
} else {
[activeAlertController presentAlert:code];
}
}
#pragma mark - Save / Load route points
+ (void)saveRoute {
GetFramework().GetRoutingManager().SaveRoutePoints();
}
+ (void)saveRouteIfNeeded {
if ([self isOnRoute])
[self saveRoute];
}
+ (void)restoreRouteIfNeeded {
if ([MapsAppDelegate theApp].isDrapeEngineCreated) {
auto &rm = GetFramework().GetRoutingManager();
if ([self isRoutingActive] || ![self hasSavedRoute]) {
self.router.isRestoreProcessCompleted = YES;
return;
}
rm.LoadRoutePoints([self](bool success) {
if (success)
[self rebuildWithBestRouter:YES];
self.router.isRestoreProcessCompleted = YES;
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self restoreRouteIfNeeded];
});
}
}
+ (BOOL)isRestoreProcessCompleted {
return self.router.isRestoreProcessCompleted;
}
+ (BOOL)hasSavedRoute {
return GetFramework().GetRoutingManager().HasSavedRoutePoints();
}
+ (void)updateRoute {
MWMRoutingOptions *newOptions = [MWMRoutingOptions new];
if (self.isRoutingActive && !self.isOnRoute && ![newOptions isEqual:[self router].routingOptions]) {
[self rebuildWithBestRouter:YES];
}
}
+ (BOOL)hasActiveDrivingOptions {
return [MWMRoutingOptions new].hasOptions && self.type != MWMRouterTypeRuler;
}
+ (void)avoidRoadTypeAndRebuild:(MWMRoadType)type {
MWMRoutingOptions *options = [MWMRoutingOptions new];
switch (type) {
case MWMRoadTypeToll:
options.avoidToll = YES;
break;
case MWMRoadTypeDirty:
options.avoidDirty = YES;
break;
case MWMRoadTypePaved:
options.avoidPaved = YES;
break;
case MWMRoadTypeFerry:
options.avoidFerry = YES;
break;
case MWMRoadTypeMotorway:
options.avoidMotorway = YES;
break;
case MWMRoadTypeSteps:
options.avoidSteps = YES;
break;
}
[options save];
[self rebuildWithBestRouter:YES];
}
+ (void)showNavigationMapControls {
[[MWMMapViewControlsManager manager] onRouteStart];
}
+ (void)hideNavigationMapControls {
[[MWMMapViewControlsManager manager] onRouteStop];
}
@end

View file

@ -0,0 +1,3 @@
typedef NS_ENUM(NSUInteger, MWMRouterRecommendation) {
MWMRouterRecommendationRebuildAfterPointsLoading
};

View file

@ -0,0 +1,12 @@
#import "MWMRouterTransitType.h"
@interface MWMRouterTransitStepInfo : NSObject
@property(nonatomic, readwrite) MWMRouterTransitType type;
@property(copy, nonatomic, readwrite) NSString * distance;
@property(copy, nonatomic, readwrite) NSString * distanceUnits;
@property(copy, nonatomic, readwrite) NSString * number;
@property(nonatomic, readwrite) UIColor * color;
@property(nonatomic, readwrite) NSInteger intermediateIndex;
@end

View file

@ -0,0 +1,54 @@
#import "MWMRouterTransitStepInfo.h"
#include "map/routing_manager.hpp"
namespace
{
MWMRouterTransitType convertType(TransitType type)
{
switch (type)
{
case TransitType::IntermediatePoint: return MWMRouterTransitTypeIntermediatePoint;
case TransitType::Pedestrian: return MWMRouterTransitTypePedestrian;
case TransitType::Subway: return MWMRouterTransitTypeSubway;
case TransitType::Train: return MWMRouterTransitTypeTrain;
case TransitType::LightRail: return MWMRouterTransitTypeLightRail;
case TransitType::Monorail: return MWMRouterTransitTypeMonorail;
}
// This is temporary solution for compiling iOS project after adding new
// TransitType values. When these values will be approved we'll add them
// above in switch(type) and remove this line.
// TODO(o.khlopkova) Replace this return with more cases when transit
// types are ready.
return MWMRouterTransitTypePedestrian;
}
UIColor * convertColor(uint32_t colorARGB)
{
CGFloat const alpha = CGFloat((colorARGB >> 24) & 0xFF) / 255;
CGFloat const red = CGFloat((colorARGB >> 16) & 0xFF) / 255;
CGFloat const green = CGFloat((colorARGB >> 8) & 0xFF) / 255;
CGFloat const blue = CGFloat(colorARGB & 0xFF) / 255;
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}
} // namespace
@implementation MWMRouterTransitStepInfo
- (instancetype)initWithStepInfo:(TransitStepInfo const &)info
{
self = [super init];
if (self)
{
_type = convertType(info.m_type);
_distance = @(info.m_distanceStr.c_str());
_distanceUnits = @(info.m_distanceUnitsSuffix.c_str());
_number = @(info.m_number.c_str());
_color = convertColor(info.m_colorARGB);
_intermediateIndex = info.m_intermediateIndex;
}
return self;
}
@end

View file

@ -0,0 +1,9 @@
typedef NS_CLOSED_ENUM(NSUInteger, MWMRouterTransitType) {
MWMRouterTransitTypeIntermediatePoint,
MWMRouterTransitTypePedestrian,
MWMRouterTransitTypeSubway,
MWMRouterTransitTypeTrain,
MWMRouterTransitTypeLightRail,
MWMRouterTransitTypeMonorail,
MWMRouterTransitTypeRuler
};

View file

@ -0,0 +1,7 @@
typedef NS_ENUM(NSUInteger, MWMRouterType) {
MWMRouterTypeVehicle,
MWMRouterTypePedestrian,
MWMRouterTypePublicTransport,
MWMRouterTypeBicycle,
MWMRouterTypeRuler,
};