HERE iOS SDK Developer's Guide

Venue Routing

The HERE iOS SDK extends its 3D venue maps functionality to provide indoor routing. The SDK supports the following use cases:

  • Routing from store A to store B within a venue
  • Routing from an outside point to a point in a venue
  • Routing from a point in a venue to an outside point
  • Routing from a venue to another venue, with endpoints being a store or a point in a venue

Both on-line and off-line routing are supported.

Please note that if HERE has no routing information for an area between the outdoor part of the route and the venue entry point, the route visualization represents the unknown section of the route with a dotted line.

Routing Between Locations in a Venue

This section demonstrates how to calculate and display an indoor route by using a code example. The example is based on a scenario where a device user wants to find out how to reach another location in the same venue. In real life, the user would select the starting point and destination for the route by tapping on the map of the venue. However, for the sake of simplicity, the code below calculates a route from the first space (shop or Point of Interest) in the venue and to a destination which is the last space in the venue.

Note: See the Getting Indoor Location Based on a Tap Point section for a code example of how to handle tap events to get a location for indoor venue routes.

The following code shows the implementation and is assumed to be part of an application. The code comments explain each step.

// Get a routing controller object
NMAVenue3dRoutingController *routingController =
    _venueMapLayer.venueRoutingController;

// Set a listener for the "route calculation completed" message.
[routingController addObserver:id<NMAVenue3dRoutingControllerObserver>(self)];

// Get a pointer to a venue the user selected/tapped.
NMAVenue3dVenue* venue3d = _testVenue;
NSArray *spaces = venue3d.spaces;

// Route start is the first space in the array of spaces. (An
// alternative is to get a space from didSelectSpace event.)
NMAVenue3dSpace *startSpace = [spaces objectAtIndex:0];

// Set route start:
NMAVenue3dSpaceLocation *startLocation =
      [NMAVenue3dSpaceLocation spaceLocationWithSpace:startSpace
      inVenue:venueMapLayer.venueController];

// Get route destination - the last element from the array of spaces.
NMAVenue3dSpace *endSpace = [spaces objectAtIndex:spaces.count-1];

// Set route destination:
NMAVenue3dSpaceLocation *endLocation =
      [NMAVenue3dSpaceLocation spaceLocationWithSpace:endSpace
      inVenue:venueMapLayer.venueController];

// To initialize routing options, first set routing mode:
NMARoutingMode *routingMode;

// ... We want the shortest pedestrian route and only one result.
routingMode = [[NMARoutingMode alloc] initWithRoutingType:NMARoutingTypeShortest
      transportMode:NMATransportModePedestrian
      routingOptions:0];

// Set routing options, using the configured routing mode:
NMAVenue3dRouteOptions *routeOptions =
      [NMAVenue3dRouteOptions optionsWithRoutingMode:routingMode];

// Calculate route - this is an asynchronous call, once the calculation is done
// we receive the didCalculateRoute message.
[routingController calculateRouteFrom:startLocation to:endLocation
      withParams:routeOptions];

...
#pragma mark - NMAVenue3dRoutingControllerDelegate
// Callback invoked when the route calculation is done to display the route passed
// to it as an argument.
- (void)didCalculateRoute:(NMAVenue3dCombinedRoute *)combinedRoute
{
  // Show route
  [_venueMapLayer.venueRoutingController showRoute: combinedRoute];
}

Routing Between Venues

Routing from one venue to another is also possible. In this case, the start and end locations have different NMAVenue3dController objects, and this is demonstrated below:


// In this example startVenueController and endVenueController are assumed to contain
// proper NMAVenue3dController references to the start and destination venues.
// Set route start:
NMAVenue3dSpaceLocation *startLocation =
      [NMAVenue3dSpaceLocation spaceLocationWithSpace:startSpace
                          inVenue:startVenueController];
// Set route end:
NMAVenue3dSpaceLocation *endLocation =
      [NMAVenue3dSpaceLocation spaceLocationWithSpace:startSpace
                          inVenue:endVenueController];
// For other parts see the previous example
    

Routing to an Indoor Endpoint

The following is an example of how to implement a route calculation where the starting point is outdoors and the destination indoors.

// Set route start:
NMAGeoCoordinates *startPosition =
      [NMAGeoCoordinates geoCoordinatesWithLatitude:52.517072 longitude:13.411232];
NMAVenue3dOutdoorLocation *startLocation =
      [NMAVenue3dOutdoorLocation outdoorLocationWithGeoCoordinates:startPosition];

// Set route end, using the first object inside a venue:
NMAVenue3dVenue* venue3d = _venueMapLayer.venueController.venue;
NSArray *spaces = venue3d.spaces;
NMAVenue3dSpace *space = [spaces objectAtIndex:0];
NMAVenue3dSpaceLocation *endLocation =
      [NMAVenue3dSpaceLocation spaceLocationWithSpace:space
      inVenue:_venueMapLayer.venueController];

// Set routing options. We want the shortest car route.
NMARoutingMode *routingMode =
      [[NMARoutingMode alloc] initWithRoutingType:NMARoutingTypeShortest
      transportMode:NMATransportModeCar
      routingOptions:0];

// Calculate the route:
[routingController calculateRouteFrom:startLocation
      to:endLocation
      withParams:[NMAVenue3dRouteOptions optionsWithRoutingMode:routingMode]];

Routing Using an Arbitrary Indoor Location

It is also possible to use an arbitrary indoor location that is not at a store or designated space as a route endpoint. An example of this kind of location is a point in a corridor. The next code snippet demonstrates the initialization of these points.

NMAVenue3dController* _venueController;
float _tapX;
float _tapY;
// ...

// Get a routing controller object
NMAVenue3dRoutingController *routingController =
_venueMapLayer.venueRoutingController;

// Create a free point location to be used as a start location
NMAVenue3dLevelLocation* startLocation =
  [_venueController getLocationAtX:tapX Y:tapY WithSpacePrefered:false];

// Create outdoor location based on geocoordinates
NMAVenue3dOutdoorLocation* endLocation =
  [NMAVenue3dOutdoorLocation outdoorLocationWithGeoCoordinates:_destination];

// Set routing mode. We want the shortest car route.
NMARoutingMode *routingMode =
  [[NMARoutingMode alloc] initWithRoutingType:NMARoutingTypeShortest
                  transportMode:NMATransportModeCar
                 routingOptions:0];

// Set routing options. We want to avoid stairs, and prefer corridors.
NMAVenue3dRouteOptions* options =
   [NMAVenue3dRouteOptions optionsWithRoutingMode:routingMode];
if (_routeOptionsVC)
{
  options.avoidStairs = YES;
  options.preferCorridors = YES;
}

// Calculate the route:
[routingController calculateRouteFrom:startLocation
      to:endLocation
      withParams:[NMAVenue3dRouteOptions
      optionsWithRoutingMode:routingMode]];

Venue Route Options

NMAVenue3dRouteOptions encapsulate options used in indoor routing. It is possible to set many parameters related to visualization of the route line (for example, color, line width, and whether start and end flags are visible) as well as parameters related to how the route is calculated (for example, if elevators are allowed, if stairs are allowed, or if corridors are preferred).

Getting Indoor Location Based on a Tap Point

The next code example shows how to add a route point to an indoor route using the didReceiveTapAtLocation method of NMAMapGestureDelegate.

- (void)mapView:(NMAMapView *)mapView didReceiveTapAtLocation:(CGPoint)tapLocation
{
  // convert tap point to NMAGeoGoordinate
  NMAGeoCoordinates *coords = [mapView geoCoordinatesFromPoint:tapLocation];

  // If any venue is selected, get related venue controller
  NMAVenue3dController* venueController = _venueMapLayer.venueController;

  // If no venue was selected, consider tapped location as outdoor location
  // and add it as route point.
    if (venueController == nil) {
        NMAVenue3dOutdoorLocation *loc = [NMAVenue3dOutdoorLocation  outdoorLocationWithGeoCoordinates:coords];
    [self addToRoute:loc];
    return;
    }

  // Otherwise find location inside a venue and add it as route point.
    else {
        NMAVenue3dBaseLocation* loc = [venueController getLocationAtX:tapLocation.x
                                                                     Y:tapLocation.y
                                                     WithSpacePrefered:true];
        if ([loc isValid] && ![loc isKindOfClass:[NMAVenue3dOutdoorLocation class]]) {
            [self addToRoute:loc];
        }
    }
}
-(void)addToRoute:(NMAVenue3dBaseLocation*) baseLocation
{
    // do something with the route
}

Calculating Route Length

The following code example shows how the total length of a route can be calculated:

- (void)didCalculateRoute:(NMAVenue3dCombinedRoute *)combinedRoute
{
    double distance = 0.0;
    NSArray *routeSections = combinedRoute.routeSections;

    for (id routeSection in routeSections) {
        if ([routeSection class] == [NMAVenue3dVenueRouteSection class]) {
            NMAVenue3dVenueRouteSection *section = (NMAVenue3dVenueRouteSection *)routeSection;
            NSArray *maneuvers = section.routeManeuvers;
            NMAVenue3dRouteManeuver *lastManeuver = [maneuvers lastObject];
            distance += [lastManeuver distanceFromStart];
        } else if ([routeSection class] == [NMAVenue3dLinkRouteSection class]) {
            NMAVenue3dLinkRouteSection *section = (NMAVenue3dLinkRouteSection *)routeSection;
            NMAGeoCoordinates *start = section.from;
            NMAGeoCoordinates *destination = section.to;
            distance += [start distanceTo:destination];
        } else if ([routeSection class] == [NMAVenue3dOutdoorRouteSection class]) {
            NMAVenue3dOutdoorRouteSection *section = (NMAVenue3dOutdoorRouteSection *)routeSection;
            NMARoute *route = section.route;
            distance += route.length;
        }
    }
    // do something with distance information
}

Natural Guidance for Venue Maneuvers

Venue maneuvers provide the names of the closest POIs for natural guidance purposes. For each maneuver, this is the closest POI within a natural guidance radius around the position of the maneuver. If no POI exists within this radius, an empty string is returned. The natural guidance radius is a global parameter common to all maneuvers that may be set and queried by the user.

Note: For more information about natural guidance, see Turn-by-Turn Navigation for Walking and Driving.
NMAVenue3dRouteManeuver.naturalGuidanceRadius = 10.0;
float naturalGuidanceRadius = NMAVenue3dRouteManeuver.naturalGuidanceRadius;
NSString *naturalGuidance = myManeuver.naturalGuidancePOI;

Bounding Boxes for Parts of Venue Routes

Venue and outdoor route sections provide axis-aligned bounding boxes for the route. Axis-aligned bounding boxes are provided for individual route segments for each level. These methods return NIL if a route has no segment on the given level.

NMAGeoBoundingBox *obb = outdoorRouteSection.boundingBox;
NMAGeoBoundingBox *vbb = venueRouteSection.boundingBox;
NMAGeoBoundingBox *lbb = [venueRouteSection boundingBox:level];

The bounding box for the venue route (vbb in the example) also provides altitude information that may be extracted using the topLeftFront and bottomRightBack properties of NMAGeoBoundingBox.