Mobility On-Demand Technical Solution Paper

Navigating in a Venue

You can also integrate venue maps into your passenger app to allow passengers to find their way out of large buildings to their chosen pickup locations. To do this, you can use either the Starter or Premium versions of the HERE SDKs for Android or iOS.

When you use the Starter SDK, you can integrate 2D venue maps by adding a custom tile layer that leverages the Venue Map Tile API. The Starter SDK provides an interface for implementation that generates tile URLs based on tile x and y coordinates and a zoom level (in the iOS Starter SDK, this is the NMAMapTileLayerDataSource protocol, while in the Android Starter SDK, it is the com.here.android.mpa.mapping.UrlMapRasterTileSourceBase abstract class). These x, y and zoomlevel coordinates can be translated to a quadkey, which can then be used to build a url for the Venue Map Tile API. Furthermore, a request to the Venue Map Tile API requires a signed querystring, which includes several querystring parameters as returned by the Venue Signature Service. The Signature Service provides tokens which have an expiration date, so make sure to request new tokens when they expire.

If you are using the Premium Mobile SDKs, 3D Venues are provided as part of the SDK. For more information on the Premium SDKs, see Android Premium and iOS Premium

The sample code below shows how to request for the required signature from the Venue Signature Service on iOS:
// ------------------- VenueService.h -----------------------

#import <Foundation/Foundation.h>

@interface VenueService : NSObject

+(VenueService*)sharedVenueService;

-(void)requestSignature:(dispatch_queue_t)completionQueue completionBlock:(void (^)(NSString* signedQueryString))completionBlock;

@end

// ------------------- VenueService.h -----------------------

#import "VenueService.h"
#import "Constants.h"

@implementation VenueService {
  dispatch_semaphore_t _semaphore;
  dispatch_queue_t _queue;
}

static NSString* signedQueryString;

+(VenueService*)sharedVenueService {
  static VenueService *_sharedVenueService = nil;
  static dispatch_once_t oncePredicate;
  dispatch_once(&oncePredicate, ^{
    _sharedVenueService = [[VenueService alloc] init];
  });
  return _sharedVenueService;
}

-(instancetype)init {
  self = [super init];
  if(self) {
    _semaphore = dispatch_semaphore_create(1);
    _queue = dispatch_queue_create("com.here.OnDemandPassenger.VenueApiSignatureQueue", NULL);
  }
  return self;
}

-(void)requestSignature:(dispatch_queue_t)completionQueue completionBlock:(void (^)(NSString* signedQueryString))completionBlock {
  dispatch_async(_queue, ^{
    // Only need to request signature once, then we store it locally, 
    // so using a semaphore here to wait for the single signature 
    // request to return.
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    if(!signedQueryString) {
      NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
      [request setURL:[NSURL URLWithString:@"https://signature.venue.maps.cit.api.here.com/venues/signature/v1?app_id={YOUR_APP_ID}&app_code={YOUR_APP_CODE}"]];
      [request setHTTPMethod:@"GET"];
      
      NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
      [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        signedQueryString = json[@"SignedQueryString"];
        // Signature has been stored, we can release the semaphore because
        // any waiting threads will be able to just reuse this signature 
        // and won't perform a new request
        dispatch_semaphore_signal(_semaphore);
        dispatch_async(completionQueue, ^{
          completionBlock(signedQueryString);
        });
      }] resume];
    } else {
      dispatch_semaphore_signal(_semaphore);
      dispatch_async(completionQueue, ^{
        completionBlock(signedQueryString);
      });
    }
  });
  
}

@end
The sample code below shows a sample implementation of a Venue Map Tile Layer:
// ------------------- VenueMapTileLayer.h -----------------------

#import <NMAKit/NMAKit.h>

@interface VenueMapTileLayer : NMAMapTileLayer <NMAMapTileLayerDataSource>

+(void)addVenueMapTileLayerToMapView:(NMAMapView*)mapView;

@end

// ------------------- VenueMapTileLayer.m -----------------------

#import "VenueMapTileLayer.h"
#import "Constants.h"
#import "VenueService.h"

@interface VenueMapTileLayer ()

@property (strong, nonatomic) NSString* signedQueryString;

@end

@implementation VenueMapTileLayer

+(void)addVenueMapTileLayerToMapView:(NMAMapView*)mapView
{
  VenueMapTileLayer *tileLayer = [[VenueMapTileLayer alloc] init];
  // Using the Venue API requires adding a signature to queries
  // For demo purposes, we request the signature whenever we add the Venue tile layer to the map
  // However, the signature has an expiration date, so it's possible to store it and only request a new one when it expires
  [[VenueService sharedVenueService] requestSignature:dispatch_get_main_queue() completionBlock:^(NSString *signedQueryString) {
    tileLayer.signedQueryString = signedQueryString;
    [mapView addMapTileLayer:tileLayer];
  }];
}

-(instancetype)init
{
  self = [super init];
  if(self) {
    // Set the data source
    self.dataSource = self;
    
    // Enable caching
    self.cacheTimeToLive = 60 * 60 * 24; // 24 hours
    self.cacheSizeLimit = 1024 * 1024 * 64; // 64MB
    [self setCacheEnabled:YES withIdentifier:@"VenueMapTileLayer"];
  }
  return self;
}

/**
 * This operation transforms a set of tile x and y coordinates and a zoomlevel into a quadkey for use with the Venue Tile API
 */
+(NSString*)quadKeyFromX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel {
  NSMutableString* quadKey = [NSMutableString string];
  for(NSInteger i = zoomLevel; i > 0; i--) {
    int digit = 0;
    NSUInteger mask = 1 << (i - 1);
    if ((x & mask) != 0) {
      digit++;
    }
    if ((y & mask) != 0) {
      digit++;
      digit++;
    }
    [quadKey appendFormat:@"%d", digit];
  }
  return quadKey;
}

-(NSString *)mapTileLayer:(NMAMapTileLayer *)mapTileLayer urlForTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel {
  // Distribute load between servers 1-4 based on x and y being odd or even
  int server = 1 + (x % 2) + 2 * (y % 2);
  NSString* url = [NSString stringWithFormat:@"https://static-%d.venue.maps.cit.api.here.com/0/tiles-png/L0/%@.png%@", server, [VenueMapTileLayer quadKeyFromX:x y:y zoomLevel:zoomLevel], self.signedQueryString];
  return url;
}

@end

For more information, see the API Reference.

You cannot use this account to purchase a commercial plan on Developer Portal, as it is already associated to plans with different payment methods.

To purchase a commercial plan on Developer Portal, please register for or sign in with a different HERE Account.

Something took longer than expected.

The project should be available soon under your projects page.

Sorry, our services are not available in this region.

Something seems to have gone wrong. Please try again later.

We've detected that your account is set to Australian Dollars (AUD).
Unfortunately, we do not offer checkouts in AUD anymore.
You can continue using your current plan as normal, but to subscribe to one of our new plans,
please register for a new HERE account or contact us for billing questions on selfservesupport@here.com.