Routing (v2.1.x – 2.2.x)

The core component of Navmii SDK responsible for route calculation is NMRoutingService. This class provides the API for calculating new routes and snapping external routes to the map.

CREATING ROUTING SERVICE

NMSdk class contains routingService property which allows Navmii SDK user to access routing service instance. The routing service instance can only be created after the SDK was started.

It's the SDK user's responsibility to make sure that routing service instance exists during the route calculation process, therefore you should NOT release the routing service instance until all the desired requests finish.

Here is an example of routing service creation.

Sample: creating routing service
/// RoutingServiceContainer.h
#import <Foundation/Foundation.h>
#import <NavmiiSDK/NMSdk.h>
#import <NavmiiSDK/NMRoutingService.h>
 
@interface RoutingServiceContainer : NSObject
 
@property (nonatomic, readonly, strong, nullable) NMRoutingService *routingService;

- (nullable instancetype)initWithSdk:(nonnull NMSdk *)sdk;

- (nullable instancetype)init NS_UNAVAILABLE;

+ (nullable instancetype)new NS_UNAVAILABLE;

@end


/// RoutingServiceContainer.m
#import "RoutingServiceContainer.h"

@implementation RoutingServiceContainer {
    
    NMSdk *sdk;
}

@synthesize routingService;

- (nullable NMRoutingService *)routingService {
 
    if (routingService == nil) {
 
        routingService = sdk.routingService;
    }
 
    return routingService;
}

- (nullable instancetype)initWithSdk:(nonnull NMSdk *)sdk {
    
    if (self = [super init]) {
        
        self->sdk = sdk;
    }
    
    return self;
}
 
@end

ROUTING SERVICE BASICS

Routing service can be used to calculate new routes or to snap external routes to the map. Routing service handles both processes in the same way.

Successful route calculation or snapping result is the route or the list of routes each of which is represented by NMRoute class instance. Those routes can be used by other SDK modules: they can be rendered on map or used for navigation.

Once routing service is requested to calculate or snap a route, it returns a route calculation session represented by NMRouteCalculationSession class instance. Route calculation session is the routing service request handler which is used to control the request.

Since session is the only way to control the associated calculation request, the user of the routing service should NOT release the session object until the request finishes. If the session object is released while the request is in progress, the request is canceled and the user gets no notification on its progress anymore.

NMRouteCalculationSession class instances are passive objects which you can use to request the current status and successful or failure info. You can also use it to cancel the request. In order to get the request status change notifications you can specify an object conforming to the NMRouteCalculationListener protocol withing the request to the routing service.

Basically the route calculation request can be in two states: in progress or finished. Route calculation session's hasFinished property indicates the state.

Once you requested a new route calculation or snapping by calling the corresponding routing service method and got the session instance back, route calculation starts. At this moment session's hasFinished property, as well as wasCanceled, hasSucceeded and hasFailed, returns NO.

The request can become finished due to the following reasons:

  • route calculation has succeeded. Session's hasFinished property, as well as hasSucceeded, returns YES. wasCanceled and hasFailed properties return NO. result property contains valid non-null request result. Route calculation listener's onRouteCalculationSucceededWithResult: is called while session transits to this state.
  • route calculation has failed. Session's hasFinished property, as well as hasFailed, returns YES. wasCanceled and hasSucceeded properties return NO. failureInfo property contains valid non-null request failure info. Route calculation listener's onRouteCalculationFailedWithInfo: is called while session transits to this state.
  • route calculation was canceled by user by calling session's cancel method. Session's hasFinished property, as well as wasCanceled, returns YES. hasSucceeded and hasFailed properties return NO. Please note that request can only be canceled if it hasn't finished by the moment cancel method is called. Canceling the finished session doesn't affect the request state.

As mentioned before, routing service uses NMRouteCalculationListener protocol to notify user about route calculation status changes. NMRouteCalculationListener contains three optional methods:

  • onRouteCalculationSucceededWithResult: is called once route calculation succeeded. result parameter contains valid non-null route calculation results. The fact that the method was called means the route calculation process had completely finished, therefore user won't receive any other notifications on its status changes.
  • onRouteCalculationFailedWithInfo: is called once route calculation failed. info parameter contains valid non-null route failure info. The fact that the method was called means the route calculation process had completely finished, therefore user won't receive any other notifications on its status changes.
  • onNonFatalRouteCalculationErrorOccurred: is called once a non-fatal route error occurs during the route calculation. A non-fatal error is an error which does not result in route calculation process interruption. An example of such an error is a network error during an attempt to access routing or map tile server. error parameter contains an error code describing the reason the method was called. The call is just a notification to the SDK user that something went wrong during the route calculation process. Since the fact that the method was called does NOT mean the route calculation process had completely finished, user should expect to see other notifications on route calculation status update.

Here is an example of route calculation request handling.

Sample: routing service request handling
/// SimpleRouteCalculator.h
#import <Foundation/Foundation.h>
#import <NavmiiSDK/NMSdk.h>
#import <NavmiiSDK/NMRoutePlanPoint.h>
#import <NavmiiSDK/NMRouteCalculationOptions.h>
 
@interface SimpleRouteCalculator : NSObject
 
- (void)calculateRouteUsingRoutePlanPoints:(nonnull NSArray<NMRoutePlanPoint *> *)routePlanPoints
                                   options:(nullable NMRouteCalculationOptions *)options;
 
- (void)cancelRouteCalculation;
 
- (nullable instancetype)initWithSdk:(nonnull NMSdk *)sdk;

- (nullable instancetype)init NS_UNAVAILABLE;

+ (nullable instancetype)new NS_UNAVAILABLE;
 
@end

/// SimpleRouteCalculator.m
#import "SimpleRouteCalculator.h"
#import "RoutingServiceContainer.h"
#import <NavmiiSDK/NMRouteCalculationSession.h>
#import <NavmiiSDK/NMRouteCalculationListener.h>
 
@interface SessionListener: NSObject<NMRouteCalculationListener>
 
- (nullable instancetype)init NS_UNAVAILABLE;
 
- (nullable instancetype)initWithWeakCalculator:(__weak SimpleRouteCalculator *)weakCalculator;
 
@end
 
@implementation SimpleRouteCalculator {
 
    RoutingServiceContainer *routingServiceContainer;
    NMRouteCalculationSession *session;
    SessionListener *sessionListener;
}
 
- (void)calculateRouteUsingRoutePlanPoints:(nonnull NSArray<NMRoutePlanPoint *> *)routePlanPoints
                                   options:(nullable NMRouteCalculationOptions *)options {
 
    NMRoutingService *routingService = routingServiceContainer.routingService;
    if (routingService == nil) {
 
        // Routing service is not available.
        return;
    }
 
    [self cancelRouteCalculation];
 
    SessionListener *sessionListener = [[SessionListener alloc] initWithWeakCalculator:self];
    NMRouteCalculationSession *session = [routingService calculateRouteUsingRoutePlan:routePlanPoints
                                                                              options:options
                                                                 statusReportListener:sessionListener];
    if (session == nil) {
         
        // Couldn't start route calculation.
        return;
    }
 
    // Store route calculation session and its listeners.
    self->session = session;
    self->sessionListener = sessionListener;
}
 
- (void)cancelRouteCalculation {
     
    // Optional: you may not do this if you don't pass session elsewhere.
    if (session != nil) {
         
        [session cancel];
    }
 
    [self onSessionFinished];
}
 
- (void)onSessionFinished {
 
    session = nil;
    sessionListener = nil;
}
 
- (nullable instancetype)initWithSdk:(nonnull NMSdk *)sdk {
     
    if (self = [super init]) {
 
        routingServiceContainer = [[RoutingServiceContainer alloc] initWithSdk:sdk];
    }
 
    return self;
}
 
@end
 
@implementation SessionListener {
 
    __weak SimpleRouteCalculator *weakCalculator;
}
 
- (void)onRouteCalculationSucceededWithResult:(nonnull NMRouteCalculationResult *)result {
 
    // Route calculation succeeded: handling result.
     
    // Releasing session and listeners.
    SimpleRouteCalculator *calculator = weakCalculator;
    if (calculator != nil) {
 
        [calculator onSessionFinished];
    }
}
 
- (void)onRouteCalculationFailedWithInfo:(nonnull NMRouteCalculationFailureInfo *)failureInfo {
 
    // Route calculation failed: handling failure info.
     
    // Releasing session and listeners.
    SimpleRouteCalculator *calculator = weakCalculator;
    if (calculator != nil) {
 
        [calculator onSessionFinished];
    }
}
 
- (void)onNonFatalRouteCalculationErrorOccurred:(NMRouteCalculationError)error {
 
    // Non-fatal route calculation error occurred: handling it. Route calculation continues.
}
 
- (nullable instancetype)initWithWeakCalculator:(__weak SimpleRouteCalculator *)weakCalculator {
 
    if (self = [super init]) {
 
        self->weakCalculator = weakCalculator;
    }
 
    return self;
}
 
@end

CALCULATING ROUTES

CALCULATING A ROUTE USING ROUTE PLAN

Routing service allows to calculate new routes using the specified route plan. A route plan is a list of waypoints represented by instances of NMRoutePlanPoint class. A route plan should contain at least two points.

A route plan point contains geo coordinates and optional course. Course is used by the routing engine as a hint to calculate direction to move from the point. Usually SDK users don't have such information so you don't have to specify it.

Another route plan point property is called snapped. It indicates whether the route plan point was snapped to the map by Navmii SDK. SDK users do NOT have a way to construct the snapped route plan point other then to construct it from a snapped position returned by another SDK module. The SDK returns positions as NMPosition class instances. The class has the snapped property as well. A route plan point will be considered as snapped if only constructed from a snapped position. In this case snapped route plan point will have course set as well.

All that means that its enough to have geo coordinates for the second and further route plan points. Nevertheless there are options for the first point. If you need to calculate a route from the current position, the preferred way to do this is to use the snapped current position to construct the first route plan point. If however you need to calculate a route starting from some random geo location, geo coordinates would be enough to construct the first route plan point.

Use snapped current position provided by Navmii SDK to construct the first route plan point if you need to calculate a route from current position. Use just coordinates to construct the first route plan point if you need to calculate route starting from some random location.

Here are example methods for both cases.

Sample: calculating route using route plan
// ExtendedRouteCalculator.h
#import <Foundation/Foundation.h>
#import "SimpleRouteCalculator.h"
#import <NavmiiSDK/NMMapCoordinates.h>
#import <NavmiiSDK/NMPosition.h>
 
@interface ExtendedRouteCalculator : SimpleRouteCalculator
 
- (void)calculateRouteUsingRoutePlanCoords:(nonnull NSArray<NMMapCoordinates *> *)routePlanCoords
                                   options:(nullable NMRouteCalculationOptions *)options;
 
- (void)calculateRouteFromCurrentPosition:(nonnull NMPosition *)currentPosition
                     usingRoutePlanCoords:(nonnull NSArray<NMMapCoordinates *> *)routePlanCoords
                                  options:(nullable NMRouteCalculationOptions *)options;

@end

// ExtendedRouteCalculator.m
#import "ExtendedRouteCalculator.h"
 
@implementation ExtendedRouteCalculator
 
- (void)calculateRouteUsingRoutePlanCoords:(nonnull NSArray<NMMapCoordinates *> *)routePlanCoords
                                   options:(nullable NMRouteCalculationOptions *)options {
 
    NSArray<NMRoutePlanPoint *> *routePlanPoints = [ExtendedRouteCalculator pointsFromCoords:routePlanCoords];
 
    [self calculateRouteUsingRoutePlanPoints:routePlanPoints options:options];
}
 
- (void)calculateRouteFromCurrentPosition:(nonnull NMPosition *)currentPosition
                     usingRoutePlanCoords:(nonnull NSArray<NMMapCoordinates *> *)routePlanCoords
                                  options:(nullable NMRouteCalculationOptions *)options {
     
    NSArray<NMRoutePlanPoint *> *routePlanPoints = [NSArray arrayWithObject:[[NMRoutePlanPoint alloc] initWithPosition:currentPosition]];
    routePlanPoints = [routePlanPoints arrayByAddingObjectsFromArray:[ExtendedRouteCalculator pointsFromCoords:routePlanCoords]];
 
    [self calculateRouteUsingRoutePlanPoints:routePlanPoints options:options];
}
 
+ (nonnull NSArray<NMRoutePlanPoint *> *)pointsFromCoords:(nonnull NSArray<NMMapCoordinates *> *)routePlanCoords {
 
    NSMutableArray<NMRoutePlanPoint *> *routePlanPoints =
        [[NSMutableArray<NMRoutePlanPoint *> alloc] initWithCapacity:routePlanCoords.count]; 
 
    for (NMMapCoordinates *coords in routePlanCoords) {
 
        [routePlanPoints addObject:[[NMRoutePlanPoint alloc] initWithCoordinates:coords]];
    }
 
    return routePlanPoints;
}
 
@end

ROUTING SERVICE SETTINGS

NMRouteCalculationOptions instance is a convenient way to tailor your route calculation request.

Here's how you can create an instance: most typical example of the last three methods usage:

// Common initializer:
NMRouteCalculationOptions *options = [NMRouteCalculationOptions new];

// Or with default values except the isWalkingMode property, which is initialized with YES:
NMRouteCalculationOptions *options = [[NMRouteCalculationOptions alloc] initInWalkingMode];

// Or with a custom NMTrafficSnapshot object:
NMRouteCalculationOptions *options = [[NMRouteCalculationOptions alloc] initWithTraffic: trafficSnapshot];


Following parameters can be set:

  • isWalkingMode specifies whether walking mode is used. Default value is 'NO'.
  • optimization specifies a route optimization. The property is ignored in walking mode. Default value is NMRoutingOptimization_Fastest.
  • vehicleType specifies vehicle type. The property is ignored in walking mode. Default value is NMVehicleType_Car.
  • avoidTollRoads specifies whether toll roads should be avoided. The property is ignored in walking mode. Default value is 'NO'.
  • buildsAllDirections specifies whether to build all directions before the route calculation. Default value is 'NO'.
  • considerTraffic specifies whether traffic data should be considered during route calculation. Default value is 'YES'.
  • useServerTimesToCalculateEta specifies whether the ETA of a route should be calculated using time data received from routing server (if available).  If 'NO' is specified, the ETA is based on time data obtained from maps locally on the device. Default value is 'YES'.
  • maxAlternativeRoutesCount limits maximum number of alternative routes being calculated in route calculation session. Default value is 3.
  • computeEstimatedTimeIgnoringTrafficDelay informs routing engine that it should compute the estimated route time not considering delays caused by traffic along with just estimated route time. The computed value is available via the estimatedTimeIgnoringTrafficDelay property ofNMRoute classDefault value is 'NO'.
  • routingType specifies routing type. Default value is NMRoutingType_Combined.
  • maxOnboardRoutingDistanceInMeters specifies whether on-board or server routing will be used if the routingType property is set. Default value is 30,000 meters.
  • maxServerRoutesSnappingDistanceInMeters limits the distance of the route being snapped to map immediately after obtaining the route. Setting this property to -1 indicates that snapping distance is unlimited and the whole route will be snapped immediately after obtaining the route. Please note that setting large values or -1 to the property may slow down the whole calculation process, because the snapping process requires all the map tiles along the snapping route part being downloaded from web. This property is taken into account in case of server routing only. Default value is 100,000 meters.
  • trafficSnapshot a traffic snapshot which is used in route calculation and visualisation of the route polyline. The source of the traffic snapshot is another SDK module. If you want to consider traffic in route calculation and display it on the route polyline after the route is calculated you need to set valid nonnull value to this property. Default value is 'nil'.
  • externalRouteProvider route provider to be used to fetch routes from external routing server. If not specified default server is used. Default value is 'nil'.