Mobility On-Demand Technical Solution...

45
Mobility On-Demand Technical Solution Paper Version 1.0.0

Transcript of Mobility On-Demand Technical Solution...

Page 1: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-DemandTechnical Solution PaperVersion 1.0.0

Page 2: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Notices

2

Important Information  NoticesTopics:

• Legal Notices• Document Information

This section contains document notices.

Page 3: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Notices

3

Legal Notices© 2018 HERE Global B.V. and its Affiliate(s). All rights reserved.

This material, including documentation and any related computer programs, is protected by copyrightcontrolled by HERE. All rights are reserved. Copying, including reproducing, storing, adapting or translating,any or all of this material requires the prior written consent of HERE. This material also contains confidentialinformation, which may not be disclosed to others without the prior written consent of HERE.

Trademark Acknowledgements

HERE is trademark or registered trademark of HERE Global B.V.

Other product and company names mentioned herein may be trademarks or trade names of their respectiveowners.

Disclaimer

This content is provided "as-is" and without warranties of any kind, either express or implied, including, butnot limited to, the implied warranties of merchantability, fitness for a particular purpose, satisfactory qualityand non-infringement. HERE does not warrant that the content is error free and HERE does not warrant ormake any representations regarding the quality, correctness, accuracy, or reliability of the content. Youshould therefore verify any information contained in the content before acting on it.

To the furthest extent permitted by law, under no circumstances, including without limitation the negligenceof HERE, shall HERE be liable for any damages, including, without limitation, direct, special, indirect, punitive,consequential, exemplary and/ or incidental damages that result from the use or application of this content,even if HERE or an authorized representative has been advised of the possibility of such damages.

Page 4: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Notices

4

Document Information 

 

Product

   Name: Mobility On-Demand

   Version: Version 1.0.0

   

Document

   Name: Mobility On-Demand Technical Solution Paper

   ID: 9c30866-1526295333-15ad674c

   Status: FINAL

   Date: 2018-May-14, 10:56 (GMT)

   

Page 5: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Contents

5

Contents

Chapter 1: Objective...........................................................................................................................................................................6

Chapter 2: Building Mobility On-Demand Apps.............................................................................................7Overview...................................................................................................................................................................................................................8

Authenticating Applications.......................................................................................................................................................................... 8

Ride-hailing Services.......................................................................................................................................................................................10

Filtering By Administrative Area........................................................................................................................................................... 10

The Passenger App.......................................................................................................................................................................................13

Additional Filtering of Drivers................................................................................................................................................................23

Navigating in a Venue................................................................................................................................................................................ 32

Navigating to Pickup and Dropoff Locations.................................................................................................................................35

Post Dropoff Processing............................................................................................................................................................................37

Delivery Services............................................................................................................................................................................................... 42

Determining Optimal Delivery Sequence.........................................................................................................................................42

Service Support..................................................................................................................................................................................................45

API Reference......................................................................................................................................................................................................45

Page 6: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Objective

6

Chapter 1Objective

The objective of this Technical Solution Paper is to providedevelopers with everything they need to build location-intelligent,mobility on-demand apps and services using HERE REST APIs,Platform Extensions, and Mobile SDKs. Developers can providenavigation capabilities in their apps using either our PremiumEdition HERE Mobile SDK or via a handoff to the HERE WeGo app.

This Technical Solution Paper describes how to use HERE's offering—APIs, SDKs, and the HERE WeGo app—to implement a typical mobilityon-demand app, using a ride-hailing service as an example. Whileeach particular mobility on-demand app handles specific cases,we think this paper will help you understand how to apply HERE’soffering to solve most common challenges and get started quicklyon your own app.

Page 7: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

7

Chapter 2Building Mobility On-Demand AppsTopics:

• Overview• Authenticating Application...• Ride-hailing Services• Delivery Services• Service Support• API Reference

This section contains guidance and code samples illustrating the useof HERE services.

Page 8: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

8

OverviewMobility on-demand apps using HERE services are generally composed of the following items:

• customer mobile apps created with the HERE SDK for Android and/or the HERE SDK for iOS (either theStarter or the Premium version of each)

• navigation provided via HERE WeGo for Android and/or iOS

• backend services interacting with your customer app, your driver app and other HERE services

• a special app operating on Android and/or iOS for drivers, based on the same HERE SDK for Androidand/or the HERE SDK for iOS

The image below illustrates a high-level generalized overview of the typical apps flow. Please note thatthis example shows the navigation handover to the HERE WeGo app as final step, so that the driver can useproven offline navigation to pick up and drop off passengers, i.e. your own app (On Demand Driver App) willopen the HERE WeGo app.

Figure 1: High Level Overview of Typical Components

Note: The sample code in this Technical Solution Paper only illustrates how you can use the variousHERE services; it does not illustrate how to implement your business logic. In addition, the samplecode uses a test environment to demonstrate the features. You need to switch to a productionenvironment before deployment.

Authenticating ApplicationsTo use HERE services in your apps and backend, you must request a set of credentials and use the app_idand app_code details you receive in your applications. If you do not include an authentication component inyour application, your applications are unable to successfully request the relevant information.

Everything you need to support the examples in the Technical Solution Paper is available when you sign upfor a 90-day Free Trial license on developer.here.com. Click here.

Once you’ve tried things out and you’re ready to move to a commercial implementation of your mobility on-demand app, please contact us. We have commercial licensing options based on rides, tasks, and bookings togive you the flexibility you need.

Page 9: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

9

The sections below provide additional details about how to authenticate your application in differenttechnologies.

Authenticating Android Applications

To authenticate an android application:

1. In your development environment, double-click your project's AndroidManifest.xml file and ensurethat you are viewing the file in text editor mode.

2. In the <application></application> tag block, add the following markup directly beneath the<activity></activity> tag:

<meta-data android:name="com.here.android.maps.appid"android:value="{YOUR_APP_ID}"/><meta-data android:name="com.here.android.maps.apptoken"android:value="{YOUR_APP_CODE}"/>

3. Replace {YOUR_APP_ID} and {YOUR_APP_ID} with the appropriate credentials for your application.

Authenticating iOS Applications

To authenticate an iOS application:

1. Set your app_id and app_code credentials in your app delegate:

- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ [NMAApplicationContext setAppId:@"{YOUR_APP_ID}" appCode:@"{YOUR_APP_CODE}"];

return YES;}

Authenticating JavaScript Applications

To authenticate a JavaScript application:

1. In your JavaScript implementation, add your app_id and app_code credentials to the parameters yousend to the REST API.

There are many ways to do this, but one possible implementation would look as follows (for the MatrixRouting API):

var requestParams = _({ 'mode': mode, 'summaryAttributes': 'traveltime', 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).assign(startParams.value()) .assign(destinationParams.value()) .map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&');

Note that some of the components in this structure are defined elsewhere.

2. Replace {YOUR_APP_ID} and {YOUR_APP_ID} with the appropriate credentials for your application.

Page 10: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

10

Ride-hailing ServicesThe sample code in the sections below for a ride sharing service assumes that the ride-sharing product doesthe following things:

• filters locations by administrative area in a backend as one method for determining which drivers aremost suitable for which rides

• allows passengers to use an app in either Android or iOS devices to input pickup and dropoff points, torequest rides, and to navigate in venues

• allows drivers to use an app to report their location to the system and to volunteer to give rides

• uses HERE WeGo for Android and/or iOS for determining directions on the passengers' mobile devices

• selects appropriate drivers, calculates routes and calculates possible additional costs in a backend

Note that there is sample code for several ways of selecting appropriate drivers, depending on thelevel of filtering you prefer. For more information, see The Passenger App on page 13 and AdditionalFiltering of Drivers on page 23.

• handles post dropoff processing in a backend

The sample code below uses the following HERE services:

• HERE Android Starter SDK and HERE iOS Starter SDK

• Geofencing Extension API

• Isoline Routing resource of the Routing API

• Matrix Routing resource of the Routing API

• Platform Data Extension API

• Route Match Extension API

• Toll Cost Extension API

• Venue Maps API

Filtering By Administrative AreaThis document assumes your ride-hailing service includes a backend component that filters drivers basedon which administrative area they are in. This level of filtering can be quite imprecise, as it does not takeaccount of traffic conditions or available routes. However, it does reduce the amount of time requiredto determine ETAs between drivers and passengers and can make the driver selection process moreasynchronous to improve passenger experience. Alternatively, you could use administrative areas for otherbusiness logic in your system. For example, you could charge different rates based on when the request ismade and which area the passenger is in.

A typical workflow for determining which locations are in which administrative area is as follows:

• Determine the WGS 84 compliant goecoordinates of a location. For instance, the location of variousdrivers based on the information collected from a special driver app on an Android or iOS mobile device.This assumes your driver app posts the driver's location to the backend at reasonable intervals.

Page 11: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

11

• Use the HERE Geofencing Extension API to determine which administrative areas contain those locations.Using the results, you could then implement logic in your backend that excludes drivers in particularadministrative areas from consideration for rides in other administrative areas.

• Use the HERE Platform Data Extension API to request maps (known as map tiles) of those areas in orderto allow your backend to visualize those areas.

• Present the current positions of your fleet in your backend. This helps your staff get an understanding ofwhere drivers currently are.

The figure below illustrates the elements in the workflow related to the HERE services.

Figure 2: Service Area Administration Sequence Diagram

To determine the boundary of an administrative area:

1. Send a request to the Geofencing Extension API with the following query parameters:

• layer_ids with the valuesADMIN_POLY_0,ADMIN_POLY_1,ADMIN_POLY_2,ADMIN_POLY_8,ADMIN_POLY_9

• proximity with a pair of WGS 84 compliant latitude and longitude components with valuesbetween -90 and 90 and -180 and 180 respectively. You can add an optional search radius if youwish. For example, proximity=37.870242,-122.268234, 500 specifies a request for a point inBerkeley, CA with a radius of 500 meters.

• key_attributes with the value ADMIN_PLACE_ID

The code below illustrates the process described in step 1:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the Geofencing Extension using pre-defined adminstrative area layers. * This request tests a location for the administrative areas (cities, counties, countries, and others) * it is within. * * location The location to test for */function buildGfeWithPdeLayersRequestOptions(location) { var requestParams = _({ // The ADMIN_POLY_X layers are layers that contain shapes of administrative areas. These layers are defined in the Platform Data Extension 'layer_ids': 'ADMIN_POLY_0,ADMIN_POLY_1,ADMIN_POLY_2,ADMIN_POLY_8,ADMIN_POLY_9', // The key_attributes are the attributes for each layer which uniquely identify an entry in this layer. For the ADMIN_POLY_X layers, these are always ADMIN_PLACE_ID 'key_attributes': 'ADMIN_PLACE_ID,ADMIN_PLACE_ID,ADMIN_PLACE_ID,ADMIN_PLACE_ID,ADMIN_PLACE_ID', 'proximity': location.lat + ',' + location.lon, 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).map((value, key) => {

Page 12: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

12

return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'maps.gfe.cit.api.here.com', path: ['/1/search/proximity.json', requestParams].join('?') };}

/** * Calls the Geofencing Extension API to find administrative areas a location is within. * See buildGfeWithPdeLayersRequestOptions for an explanation of the parameters. */function findAdminAreasForLocation(location) { return new Promise((fulfill, reject) => { var options = buildGfeWithPdeLayersRequestOptions(location); var areas = []; var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); var geometries = json['geometries']; if(geometries) { areas = geometries.filter((geometry) => { return geometry.distance <= 0; }).map((geometry) => { return {name: geometry.attributes.NAME, admin_layer: geometry.attributes.ADMIN_ORDER, admin_place_id: geometry.attributes.ADMIN_PLACE_ID, geometry: geometry.geometry}; }); } fulfill(areas); } }) }); req.on('error', (err) => { reject(err); });

req.end(); });}

// Coordinates for downtown Berkeley, CAvar downtownBerkeley = { lat: 37.870242, lon: -122.268234 };

findAdminAreasForLocation(downtownBerkeley) .then(console.log) .catch(console.error);/** Output: [ { name: 'United States', admin_layer: 'ADMIN_POLY_0', admin_place_id: '21000001', geometry: 'MULTIPOLYGON(((-0122.34375 37.26563,-0122.34375 37.96875,-0121.64062 37.96875,-0121.64062 37.26563,-0122.34375 37.26563)))' }, { name: 'California', admin_layer: 'ADMIN_POLY_1', admin_place_id: '21009408', geometry: 'MULTIPOLYGON(((-0122.34375 37.26563,-0122.34375 37.96876,-0121.64062 37.96876,-0121.64062 37.26564,-0122.34375 37.26563)))' }, { name: 'Alameda', admin_layer: 'ADMIN_POLY_8', admin_place_id: '21009409', geometry: 'MULTIPOLYGON(((-0122.25586 37.79297,-122.33529 37.79297, ... ,-0122.25586 37.79297)))' }, { name: 'Berkeley', admin_layer: 'ADMIN_POLY_9', admin_place_id: '21009414', geometry: 'MULTIPOLYGON(((-0122.29148 37.88086,-0122.25586 37.88086, ... ,-0122.29148

Page 13: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

13

37.88086)))' } ] */

Once you have the response containing the information about the administrative areas, you can use thatdata to help visualize the relevant areas.

To request map tiles to visualize administrative areas:

1. Send a request to the Platform Data Extension API with the following query parameters:

• layer=ADMIN_PLACE_n• attributes=ADMIN_PLACE_ID• values set to the values in the response to the first request

The result is an array of Layers that contain tiles for these values, along with the appropriate zoom leveland tileXY coordinate pairs.

2. Send a request to the Platform Data Extension API with the following query parameters:

• layer=ADMIN_POLY_n• levels set to values reflecting the zoom levels in the previous response• tilexy set to the values reflecting the tileXY values in the previous response

The response includes all administrative area polygons within those tiles. Filtering for those polygonswith the ADMIN_PLACE_IDs values returned in the first step provides you with all geometries for theseadministrative areas, which you can then use to display the complete administrative area on a map.

For more information, see the API Reference on page 45.

The Passenger AppA key component in a ride-sharing product is a passenger app that customers install on Android and iOSmobile devices.

A typical workflow for the interaction between the passenger app and the backend is as follows:

• Authenticate the customer in the app.

• Display a map to allow a customer to indicate where they are.

• Determine passenger location based on the customer selection on the map.

• Search for addresses and places for dropoff locations.

• Send a request for pickup to backend.

• Calculate ETAs of nearby drivers in backend and select an appropriate driver.

• Send options from backend to passenger app.

• Select option and request fare estimate from backend.

• Calculate route and possible toll costs in backend.

• Send fare estimate from backend to passenger app.

Page 14: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

14

The figure below illustrates the elements in the workflow related to the HERE services.

Figure 3: Request Pickup Time and Fare

Showing A Map

This section shows you how to display a map to allow customers to select their location on first an Androidmobile device and then an iOS mobile device.

The following sample illustrates the process in Android.

com.here.android.tutorial;

import android.app.Activity;import android.os.Bundle;

import com.here.android.mpa.common.GeoCoordinate;import com.here.android.mpa.common.OnEngineInitListener;import com.here.android.mpa.mapping.Map;import com.here.android.mpa.mapping.MapFragment;

public class BasicMapActivity extends Activity {

// map embedded in the map fragment private Map map = null;

// map fragment embedded in this activity

Page 15: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

15

private MapFragment mapFragment = null;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

// Search for the map fragment to finish setup by calling init(). mapFragment = (MapFragment)getFragmentManager().findFragmentById(R.id.mapfragment); mapFragment.init(new OnEngineInitListener() { @Override public void onEngineInitializationCompleted(OnEngineInitListener.Error error) { if (error == OnEngineInitListener.Error.NONE) { // retrieve a reference of the map from the map fragment map = mapFragment.getMap(); // Set the map center to the Vancouver region (no animation) map.setCenter(new GeoCoordinate(49.196261, -123.004773, 0.0), Map.Animation.NONE); // Set the zoom level to the average between min and max map.setZoomLevel((map.getMaxZoomLevel() + map.getMinZoomLevel()) / 2); } else { System.out.println("ERROR: Cannot initialize Map Fragment"); } } }); }}

The following sample illustrates the process in iOS.

When developing for iOS, first drop a View into your Storyboard, then change its class from UIView toNMAMapView, and create an outlet to the view in your ViewController. The following codeblock shows how toperform basic interaction with the NMAMapView object.

#import "HelloMapViewController.h"#import <NMAKit/NMAKit.h>

@interface HelloMapViewController ()@property (weak, nonatomic) IBOutlet NMAMapView *mapView;@end

@implementation HelloMapViewController

- (void)viewDidLoad{ [super viewDidLoad]; [NMAMapView class]; //set geo center NMAGeoCoordinates *geoCoordCenter = [[NMAGeoCoordinates alloc] initWithLatitude:49.260327 longitude:-123.115025]; [self.mapView setGeoCenter:geoCoordCenter withAnimation:NMAMapAnimationNone]; self.mapView.copyrightLogoPosition = NMALayoutPositionBottomCenter;

//set zoom level self.mapView.zoomLevel = 13.2;}

- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning];}

@end

Page 16: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

16

For more information on the basics of displaying maps in Android and iOS mobile devices, see the Androidand iOS Mobile Starter SDK Developer Guides on Android & iOS SDKs.

Determining Passenger Location

This section shows you how to turn customer geocoordinates based on their selections in a map into anaddress on first an Android mobile device and then an iOS mobile device.

The following sample illustrates the process in Android.

// Implementation of ResultListenerclass ReverseGeocodeListener implements ResultListener<Address> { @Override public void onCompleted(Address data, ErrorCode error) { if (error != ErrorCode.NONE) { // Handle error ... } else { // Process result data ... } }}

// Instantiate a GeoCoordinate objectGeoCoordinate vancouver = new GeoCoordinate(49.2849, -123.1252);

//Example code for creating ReverseGeocodeRequestResultListener<Address> listener = new ReverseGeocodeListener();ReverseGeocodeRequest request = new ReverseGeocodeRequest(vancouver);if (request.execute(listener) != ErrorCode.NONE) { // Handle request error ...}

The following sample illustrates the process in iOS.

// Implementation of NMAResultListener@interface NMAReverseGeocodeTest : NSObject<NMAResultListener> {}@implementation NMAReverseGeocodeTest

// NMAResultListener protocol callback implementation- (void)request:(NMARequest*)request didCompleteWithData:(id)data error:(NSError*)error{ if ( ( [request isKindOfClass:[NMAReverseGeocodeRequest class]]) ) && ( error.code == NMARequestErrorNone ) ) { // Process result NSArray of NMAReverseGeocodeResult objects [self processResult:(NSMutableArray *)data]; } else { // Handle error ... }}

- (void) startSearch{ // Instantiate an Address object

Page 17: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

17

NMAGeoCoordinates* vancouver = [[NMAGeoCoordinates alloc] initWithLatitude:49.2849 longitude:-123.1252];

NMAReverseGeocodeRequest* request = [[NMAGeocoder sharedGeocoder] createReverseGeocodeRequestWithGeoCoordinates:vancouver];

NSError* error = [request startWithListener:self]; if (error.code != NMARequestErrorNone) { // Handle request error ... }}

@end

For more information on working with addresses and geocoordinates, see the Android and iOS Mobile StarterSDK Developer's Guides on Android & iOS SDKs.

Searching For Addresses and Place Names

This section shows how your passenger app can allow customers to search for pickup and dropoff locationson first an Android mobile device and then an iOS mobile device.

The following sample illustrates the process in Android.

// Example Search request listenerclass SearchRequestListener implements ResultListener<DiscoveryResultPage> {@Override public void onCompleted(DiscoveryResultPage data, ErrorCode error) { if (error != ErrorCode.NONE) { // Handle error ... } else { // Process result data ... } }}

// Create a request to search for restaurants in Seattletry { GeoCoordinate seattle = new GeoCoordinate(47.592229, -122.315147);

DiscoveryRequest request = new SearchRequest("restaurant").setSearchCenter(seattle);

// limit number of items in each result page to 10 request.setCollectionSize(10);

ErrorCode error = request.execute(new SearchRequestListener()); if( error != ErrorCode.NONE ) { // Handle request error ... }} catch (IllegalArgumentException ex) { // Handle invalid create search request parameters ...}

The following sample illustrates the process in iOS.

// Sample Search request listener@interface NMASearchTest : NSObject<NMAResultListener> { NMADiscoveryPage* _result;

Page 18: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

18

}@implementation NMASearchTest

// NMAResultListener protocol callback implementation- (void)request:(NMARequest*)request didCompleteWithData:(id)data error:(NSError*)error{ if ( ( [request isKindOfClass:[NMADiscoveryRequest class]]) ) && ( error.code == NMARequestErrorNone ) ) { // Process result NMADiscoveryPage objects _result = (NMADiscoveryPage*) data; } else { // Handle error ... }}- (void) startSearch{ // Create a request to search for restaurants in Vancouver NMAGeoCoordinates* vancouver = [[NMAGeoCoordinates alloc] initWithLatitude:48.263392 longitude:-123.12203];

NMADiscoveryRequest* request = [[NMAPlaces sharedPlaces] createSearchRequestWithLocation:vancouver query:@"restaurant"]];

// optionally, you can set a bounding box to limit the results within it. NMAGeoCoordinates *boundingTopLeftCoords = [[NMAGeoCoordinates alloc] initWithLatitude:49.277484 longitude:-123.133693]; NMAGeoCoordinates *boundingBottomRightCoords = [[NMAGeoCoordinates alloc] initWithLatitude:49.257209 longitude:-123.11275]; NMAGeoBoundingBox *bounding = [[NMAGeoBoundingBox alloc] initWithTopLeft:boundingTopLeftCoords bottomRight:boundingBottomRightCoords];

request.viewport = bounding;

// limit number of items in each result page to 10 request.collectionSize = 10;

NSError* error = [request startWithListener:self]; if (error.code != NMARequestErrorNone) { // Handle request error ... }}

For more information on the search function, see the Android and iOS Mobile Starter SDK Developer's Guideson Android & iOS SDKs.

Determining Available Drivers

Once customers have selected their pickup and dropoff locations, the passenger app needs to send thosegeocoordinates to your backend (this step is not shown in the sample code below). At this point, the backendneeds to determine which drivers best fit the request from the customer. While the business logic may vary,it typically involves determining which drivers are within a predefined drive-time radius around the customerand what their ETAs are to the pickup location.

Page 19: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

19

Note: The sample code below illustrates a filtering process that only excludes drivers fromconsideration based upon their administrative area. For sample code that shows how to implementan additional level of filtering to further reduce the amount of time it takes to determine driverETAs to the pickup location by reducing the number of drivers considered, see Additional Filtering ofDrivers on page 23.

To request a list of ETAs for drivers based upon their locations and the designated pickup location:

1. Send a request to the resource calculatematrix in the Routing API with the following queryparameters:

• destination with the WGS 84 compliant latitude and longitude geocoordinates of the passengerpickup location.

• mode with the value set to fastest;car;traffic:enabled, which indicates the matrixcalculation response includes routes calculated with the fastest option for routes for cars includingthe effect of traffic information.

• start with a list of driver geocoodinates in WGS 84 compliant latitude and longitude pairs. Youcannot specify more than 100 sets of driver geocoordinates.

Since the length of time it takes to receive a matrix route response depends on the number ofdrivers for whom you request an ETA, you may wish to filter out inappropriate drivers before youconstruct this request. There can be several levels of filtering, including driver feedback (internalbusiness logic), current administrative area of the driver and pickup location. For more informationon filtering by administrative area, see Filtering By Administrative Area on page 10. For moreinformation on filtering by travel time to the pickup point, see Additional Filtering of Drivers on page23.

• summaryAttributes with the value traveltime to have the response include travel timeinformation for the routes.

These travel times are your driver ETAs.

2. Handle the response.

The following code block shows how to calculate ETAs for nearby drivers:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the Routing API matrix routing resource. * * starts An array of locations that serve as start points * destinations An array of locations that serve as destination points * mode The routing mode (e.g. 'fastest;car;traffic:enabled') */function buildEtaMatrixRoutingRequestOptions(starts, destinations, mode) { var startParams = _(starts).map((value, index) => { var key = 'start' + index; var val = 'geo!' + value.lat + ',' + value.lon; return [key, val]; }).fromPairs(); var destinationParams = _(destinations).map((value, index) => { var key = 'destination' + index; var val = 'geo!' + value.lat + ',' + value.lon; return [key, val]; }).fromPairs(); var requestParams = _({ 'mode': mode,

Page 20: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

20

'summaryAttributes': 'traveltime', 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).assign(startParams.value()) .assign(destinationParams.value()) .map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'matrix.route.cit.api.here.com', path: ['/routing/7.2/calculatematrix.json', requestParams].join('?') };}

/** * Calls the Routing API matrix routing resource to calculate an ETA Matrix * See buildEtaMatrixRoutingRequestOptions for an explanation of the parameters */function getEtaMatrix(starts, destinations, mode) { return new Promise((fulfill, reject) => { var options = buildEtaMatrixRoutingRequestOptions(starts, destinations, mode); var etaMatrix = []; var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); if(json.response && json.response.matrixEntry) { etaMatrix = json.response.matrixEntry.map((element) => { return {startIndex: element.startIndex, destinationIndex: element.destinationIndex, eta: element.summary.travelTime}; }); } fulfill(etaMatrix); } }) }); req.on('error', (err) => { reject(err); }); req.end(); });}

// Array of example driversvar exampleDrivers = [ { name: "Bob J.", location: { lat: 37.868943, lon: -122.267870 } }, { name: "Bill F.", location: { lat: 37.806119, lon: -122.270636 } }, { name: "Lisa M.", location: { lat: 37.727691, lon: -122.158144 } }];

// Coordinates for downtown Berkeley, CAvar downtownBerkeley = { lat: 37.870242, lon: -122.268234 };

// Routing mode (fastest car route accounting for traffic)var mode = 'fastest;car;traffic:enabled';

Page 21: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

21

// Extract coordinates from driversvar starts = exampleDrivers.map((driver) => { return { lat: driver.latitude, lon: driver.longitude }; });

// Only one destinationvar destinations = [ downtownBerkeley ];

getEtaMatrix(starts, destinations, mode) .then(console.log) .catch(console.error);/** Output: [ { startIndex: 0, destinationIndex: 0, eta: 83 }, { startIndex: 1, destinationIndex: 0, eta: 1100 }, { startIndex: 2, destinationIndex: 0, eta: 2100 } ] */

For more information on the resource calculatematrix in the Routing API, see API Reference on page45.

Determining Toll Costs

Once your backend has determined the ETAs for the relevant drivers, you need to determine how much toll apotential driver needs to pay to determine a reliable fare estimate.

To determine potential toll costs:

1. Send a request to the Toll Cost Extension API with the following parameters:

• currency 3 characters (ISO 4217) currency code for the currency used in the response (for instanceUSD).

• mode with the value set to fastest;car;traffic:enabled, which indicates the matrixcalculation response includes routes calculated with the fastest option for routes for car, includingthe effect of traffic information.

• start_ts Start timestamp of the route.

• vspec with the characteristics of the vehicle: tollVehicleType, trailerType,trailersCount, vehicleNumberAxles, trailerNumberAxles, hybrid, emissionType,height, trailerHeight, vehicleWeight, limitedWeight, disabledEquipped,minimalPollution, hov, passengersCount, tiresCount, commercial,shippedHazardousGoods, heightAbove1stAxle.

For instance, 2;0;0;2;0;0;5;167;0;1739;1739;0;0;0;4;0;0 with the 4 being the number ofpassengers.

• waypoint0 with the WGS 84 compliant latitude and longitude geocoordinates of the driver location.

• waypoint1 with the WGS 84 compliant latitude and longitude geocoordinates of the pickuplocation.

2. Handle the response.

The Toll Cost Extension API response includes the calculated route, along with the toll cost associatedwith the route, which you can then use in your estimation of the trip's total fare.

Page 22: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

22

The sample code below shows how to use the Toll Cost Extension API to calculate a route and its associatedtoll cost:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the calculateroute.json endpoint of the Toll Cost Extension API. * The calculateroute.json endpoint calculates a route given two (or more) waypoints, and returns a route, along with its associated toll costs * * start The start location * destination The destination location * mode The routing mode (e.g. 'fastest;car;traffic:enabled') * currency The currency in which to return toll cost information (three-letter code, e.g. 'USD') * vspec A string describing the vehicle's attributes. See the Toll Cost Extension's API Reference for more information on this parameter */function buildTollCostCalculateRouteRequestOptions(start, destination, mode, currency, vspec) { var requestParams = _({ 'waypoint0': start.lat + ',' + start.lon, 'waypoint1': destination.lat + ',' + destination.lon, 'mode': mode, 'vspec': vspec, 'currency': currency, 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'tce.cit.api.here.com', path: ['/2/calculateroute.json', requestParams].join('?') };}

/** * Calls the Toll Cost Extension API to calculate a route between waypoints and its * associated toll costs * See buildTollCostCalculateRouteRequestOptions for an explanation of the parameters. */function costForWaypoints(start, destination, mode, currency, vspec) { return new Promise((fulfill, reject) => { var options = buildTollCostCalculateRouteRequestOptions(start, destination, mode, currency, vspec); var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); var costs = json.costs; var totalCost; if(costs) { totalCost = costs.totalCost; } fulfill(totalCost); }

Page 23: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

23

}) }); req.on('error', (err) => { reject(err); });

req.end(); });}

// Coordinates for downtown Berkeley, CAvar downtownBerkeley = { lat: 37.870242, lon: -122.268234 };// Coordinates for Union Square (in San Francisco, CA)var unionSquare = { lat: 37.787526, lon: -122.407603 };// Routing mode (fastest car route accounting for traffic)var mode = 'fastest;car;traffic:enabled';// The currency to use for the Toll Cost Extensionvar currency = 'USD';// Number of passengersvar numPassengers = 2;// Vehicle specification, refer to TCE documentation for format informationvar vehicleSpec = '2;0;0;2;0;0;5;167;0;1739;1739;0;0;0;'+numPassengers+';4;0;0';// Calculate cost for waypointscostForWaypoints(downtownBerkeley, unionSquare, mode, currency, vehicleSpec) .then(console.log) .catch(console.error);/** Output: 4.0 */

For more information, see API Reference on page 45.

Additional Filtering of DriversWhile the previous section showed a basic implementation of filtering drivers before presenting them to thepassenger, this section illustrates an additional method of further reducing the amount of drivers for whomyou calculate ETAs.

The typical workflow is much the same as in the previous section, with the additional of the following steps:

• Backend receives request for ride with pickup and dropoff locations from passenger app.

• Determine which drivers are within a reasonable range of the pickup location.

• Calculate ETAs of nearby drivers in backend and select an appropriate driver - covered in the nextsection.

Page 24: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

24

The figure below illustrates the elements in the workflow related to the HERE services.

Figure 4: Assign Driver To Passenger

Determining Reasonable Travel Time Areas To Pickup Location

In this case, this flow starts with a passenger sending the pickup and dropoff locations to your backendusing your passenger app. Using that information, you can use the resource calculateisoline in theRouting API to calculate one or multiple reverse isochrone shapes around the passenger's pickup location.This allows you to determine within which polygonal shape a driver has to be in order to reach the pickuplocation within a certain amount of minutes. You use this logic to filter out which drivers you exclude fromthe calculate ETA request.

To calculate isolines around the passenger pickup location:

1. Send a request to the resource calculateisoline in the Routing API with the following queryparameters:

• destination with a value indicating the WGS 84 compliant latitude and longitude geocoordinatesof the pickup location.

• rangetype set to time to indicate you want the response to be based on how long it takes to get tothe specified location.

• range set to the desired time for the driver to arrive (or a comma-separated list of multiple desiredtimes) in seconds.

The amount of time you wish to allow drivers to reach the pickup location depends on your businessconcepts. Perhaps you may wish to have arrival times arranged in increasing steps, for example.

Page 25: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

25

The sample code below shows how to construct and perform a query for a reverse isochrone:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the Isoline Routing API. * An isoline is a shape that represents the area which can be reached from a certain point * within a given time or distance (regular isoline), or the area from which a certain * point can be reached within a given time or distance (reverse isoline) * * rangeType Indicates whether the isoline represents distance (isodistance) or time (isochrone). Possible values are 'distance' and 'time' * isReverse Indicates whether a regular or reverse isoline will be calculated. 'true' results in a reverse isoline, 'false' in a regular isoline * location The location around which to build the isoline * range The distance (in meters) or time (in seconds). The unit is determined by the rangeType parameter * mode The routing mode (e.g. 'fastest;car;traffic:enabled') */function buildIsolineRoutingRequestOptions(rangeType, isReverse, location, range, mode) { var locationParamKey = isReverse ? 'destination' : 'start'; var locationParam = {}; locationParam[locationParamKey] = 'geo!' + location.lat + ',' + location.lon; var requestParams = _({ 'rangeType': rangeType, 'range': range, 'mode': mode, 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).assign(locationParam) .map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'isoline.route.cit.api.here.com', path: ['/routing/7.2/calculateisoline.json', requestParams].join('?') };}

/** * Calls the Isoline Routing API to calculate a reverse isochrone * See buildIsolineRoutingRequestOptions for an explanation of the parameters * * Returns the isolines in WKT format (for use with other APIs, such as Geofencing Extension) */function getReverseIsochrone(location, range, mode) { return new Promise((fulfill, reject) => { var options = buildIsolineRoutingRequestOptions('time', true, location, range, mode); var wkt = ""; var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); // If the response contains one or more isolines, convert them to a // WKT MULTIPOLYGON if(json.response && json.response.isoline) {

Page 26: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

26

wkt = "MULTIPOLYGON (" + json.response.isoline.map((isoline) => { return isoline.component.map((component) => { return "((" + component.shape.map((shape) => { // Reverse order of latitude and longitude, WKT expects // them in the format X Y, where X is longitude and Y // is latitude return shape.split(',').reverse().join(' '); }).join(', ') + "))"; }).join(', '); }).join(', ') + ")"; } fulfill(wkt); } }) }); req.on('error', (err) => { reject(err); }); req.end(); });}

// Coordinates for downtown Berkeley, CAvar downtownBerkeley = { lat: 37.870242, lon: -122.268234 };

// Isochrone range in secondsvar range = 300;

// Routing mode (fastest car route accounting for traffic)var mode = 'fastest;car;traffic:enabled';

getReverseIsochrone(downtownBerkeley, range, mode) .then(console.log) .catch(console.error);/** Output: MULTIPOLYGON (((-122.2859859 37.8705597, -122.2833252 37.8705597, ... , -122.2859859 37.8705597))) */

For more information, see the API Reference on page 45.

Determiming Which Drivers Are Inside the Reverse Isochrone Shapes

Once you have determined the areas where drivers can reach the pickup location within a reasonable time,you need to load these areas to the Geofencing Extension API and use this API to check whether drivers areinside or entering the shape based on the location reported by their driver app.

To upload the shapes from the previous step:

1. Convert the polygon into a Shapefile or WKT text file.

2. Compress the result to a zip archive.

3. Upload the zip archive to the Geofencing Extension API using a POST request to the resource layersand using the parameter layer_id to assign an identifier to the shape (or shapes).

Note: Batch several shapes together into a layer, as the number of layers you can use with theGeofencing Extension is limited. Each polygon should also have a unique ID within the layer toidentify it. For example, when uploading a WKT file, add an attribute column that contains suchan ID (which can be an ID that you use in your database to keep track of the ride this shape isassociated with).

Page 27: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

27

The sample code below demonstrates how to perform the above steps, excluding the first step (for which youcan refer to the reverse isochrone calculation code sample):

const https = require('https');const _ = require('lodash');const FormData = require('form-data');const zip = require('node-zip')();

/** * Builds a POST request for uploading a layer to the Geofencing Extension. * * layerId The id of the layer being uploaded * form A FormData object containing the file to be uploaded */function buildUploadLayerRequestOptions(layerId, form) { var requestParams = _({ 'layer_id': layerId, 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'POST', hostname: 'cle.cit.api.here.com', path: ['/2/layers/upload.json', requestParams].join('?'), headers: form.getHeaders() };}

/** * Calls the Geofencing Extension API to upload a WKT file to a custom layer. * See buildUploadLayerRequestOptions for an explanation of the parameters */function uploadWkt(layerId, wkt) { return new Promise((fulfill, reject) => { // The Geofencing Extension expects the data as "multipart/form-data" MIME-type, // hence we use the FormData module to append the data var form = new FormData();

// The data must be zipped before uploading to the Geofencing Extension // The WKT filename is arbitrary, but must have the .wkt extension zip.file('wktUpload.wkt', wkt); var data = zip.generate({type: 'base64',compression:'DEFLATE'}); var buffer = new Buffer(data, 'base64');

// The file must be appended as 'zipfile' form.append('zipfile', buffer, {filename: 'wktUpload.wkt.zip', contentType: 'application/zip'});

var options = buildUploadLayerRequestOptions(layerId, form); var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { fulfill(); } }) });

Page 28: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

28

form.pipe(req); req.on('error', (err) => { reject(err); });

req.end(); });}

/** Example WKT: * ID WKT * 1 MULTIPOLYGON (((-122.2859859 37.8705597, -122.2833252 37.8705597, ... , -122.2859859 37.8705597))) */var wkt = "ID\tWKT\n1\tMULTIPOLYGON (((-122.2859859 37.8705597, -122.2833252 37.8705597, ... , -122.2859859 37.8705597)))"

// Example layer idvar layer = 1;

uploadWkt(layer, wkt) .then(() => { console.log("Uploaded WKT layer") }) .catch(console.error)/** Output: Uploaded WKT layer */

For more information, see API Reference on page 45.

Checking if a Driver Is In a Passenger Area

Once you have uploaded the isoline shapes based on the time to the pickup location to the GeofencingExtension API, you can then check whether a driver is within that area.

To check whether a driver is within range of a pickup location:

1. For currently unassigned drivers, send a request to the resource proximity in the Geofencing ExtensionAPI whenever they send a location update to your backend from your driver app. Use the following queryparameters:

• layer_ids with a comma separate list of IDs identifying the layers with shapes you have uploaded.• proximity with the WGS 84 compliant latitude and longitude of a driver you wish to check.• keyattributes with the ID of the shape you wish to check against.

The response contains a list of geometries, the distance between them and the position previouslypassed in the proximity parameter. If the position is within the geometry, this distance is negative.

The following code block demonstrates how to construct and perform a Search Proximity request:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the Geofencing Extension using custom layers uploaded by the * developer. * * location The location to test for * layerIds An array of ids of the layers to test the location for * keyAttributes An array of the attributes in each layer which uniquely identify an entry * This array must always have the same length as the layerIds array, as a * keyAttribute needs to be present for each layer */

Page 29: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

29

function buildGfeWithCustomLayersRequestOptions(location, layerIds, keyAttributes) { var requestParams = _({ 'layer_ids': layerIds.join(','), 'key_attributes': keyAttributes.join(','), 'proximity': location.lat + ',' + location.lon, 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'cle.cit.api.here.com', path: ['/2/search/proximity.json', requestParams].join('?') };}

/** * Calls the Geofencing Extension API to find custom layers a location is within. * See buildGfeWithCustomLayersRequestOptions for an explanation of the parameters */function searchCustomLayers(location, layerIds, keyAttributes) { return new Promise((fulfill, reject) => { var options = buildGfeWithCustomLayersRequestOptions(location, layerIds, keyAttributes); var rows = []; var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); var geometries = json['geometries']; if(geometries) { rows = geometries.filter((geometry) => { return geometry.distance <= 0; }).map((geometry) => { var keyAttribute; // The geometry doesn't necessarily have a layerId object if the // request contained only one layer if(geometry.layerId) { keyAttribute = keyAttributes[layerIds.indexOf(geometry.layerId)]; } else { keyAttribute = keyAttributes[0]; } return geometry.attributes[keyAttribute]; }); } fulfill(rows); } }) }); req.on('error', (err) => { reject(err); });

req.end(); });}

// Example driver

Page 30: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

30

var driver = { name: "Bob J.", location: { lat: 37.868943, lon: -122.267870 } };

// Example layer idsvar layerIds = ['ON_DEMAND_DEMO_LAYER'];

// Key attributes for layersvar keyAttributes = [ 'ID' ];

searchCustomLayers(driver.location, layerIds, keyAttributes) .then(console.log) .catch(console.error);/** Output: [ 'ON_DEMAND_DEMO_LAYER' ] */

For more information, see the API Reference on page 45.

Determining Available Drivers

Once you have filter your drivers based on whether they are within a reasonable ETA of the passenger pickuplocation, then you need to calculate their ETAs to that location – just as in the previous section.

To request a list of ETAs for drivers based upon their locations and the designated pickup location:

1. Send a request to the resource calculatematrix in the Routing API with the following queryparameters:

• destination with the WGS 84 compliant latitude and longitude geocoordinates of the passengerpickup location.

• mode with the value set to fastest;car;traffic:enabled, which indicates the matrixcalculation response includes routes calculated with the fastest option for routes for cars includingthe effect of traffic information.

• start with a list of driver geocoodinates in WGS 84 compliant latitude and longitude pairs. Youcannot specify more than 100 sets of driver geocoordinates.

• summaryAttributes with the value traveltime to have the response include travel timeinformation for the routes

These travel times are your driver ETAs.

2. Handle the response.

3. Share the driver's details and ETA with the passenger and share the passenger details and pickuplocation with the driver.

4. Remove the geometry from the layer in the Geofencing Extension API the next time you upload this layer,to prevent it from matching other drivers to this ride.

The following code block shows how to calculate ETAs for nearby drivers:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the Routing API matrix routing resource. * * starts An array of locations that serve as start points * destinations An array of locations that serve as destination points * mode The routing mode (e.g. 'fastest;car;traffic:enabled') */

Page 31: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

31

function buildEtaMatrixRoutingRequestOptions(starts, destinations, mode) { var startParams = _(starts).map((value, index) => { var key = 'start' + index; var val = 'geo!' + value.lat + ',' + value.lon; return [key, val]; }).fromPairs(); var destinationParams = _(destinations).map((value, index) => { var key = 'destination' + index; var val = 'geo!' + value.lat + ',' + value.lon; return [key, val]; }).fromPairs(); var requestParams = _({ 'mode': mode, 'summaryAttributes': 'traveltime', 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).assign(startParams.value()) .assign(destinationParams.value()) .map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'matrix.route.cit.api.here.com', path: ['/routing/7.2/calculatematrix.json', requestParams].join('?') };}

/** * Calls the Routing API matrix routing resource to calculate an ETA Matrix * See buildEtaMatrixRoutingRequestOptions for an explanation of the parameters */function getEtaMatrix(starts, destinations, mode) { return new Promise((fulfill, reject) => { var options = buildEtaMatrixRoutingRequestOptions(starts, destinations, mode); var etaMatrix = []; var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); if(json.response && json.response.matrixEntry) { etaMatrix = json.response.matrixEntry.map((element) => { return {startIndex: element.startIndex, destinationIndex: element.destinationIndex, eta: element.summary.travelTime}; }); } fulfill(etaMatrix); } }) }); req.on('error', (err) => { reject(err); }); req.end(); });}

Page 32: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

32

// Array of example driversvar exampleDrivers = [ { name: "Bob J.", location: { lat: 37.868943, lon: -122.267870 } }, { name: "Bill F.", location: { lat: 37.806119, lon: -122.270636 } }, { name: "Lisa M.", location: { lat: 37.727691, lon: -122.158144 } }];

// Coordinates for downtown Berkeley, CAvar downtownBerkeley = { lat: 37.870242, lon: -122.268234 };

// Routing mode (fastest car route accounting for traffic)var mode = 'fastest;car;traffic:enabled';

// Extract coordinates from driversvar starts = exampleDrivers.map((driver) => { return { lat: driver.latitude, lon: driver.longitude }; });

// Only one destinationvar destinations = [ downtownBerkeley ];

getEtaMatrix(starts, destinations, mode) .then(console.log) .catch(console.error);/** Output: [ { startIndex: 0, destinationIndex: 0, eta: 83 }, { startIndex: 1, destinationIndex: 0, eta: 1100 }, { startIndex: 2, destinationIndex: 0, eta: 2100 } ] */

For more information on the resource calculatematrix in the Routing API, see the API Reference on page45.

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

When you use the Starter SDK, you can integrate 2D venue maps by adding a custom tile layer thatleverages the Venue Map Tile API. The Starter SDK provides an interface for implementation thatgenerates 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 thecom.here.android.mpa.mapping.UrlMapRasterTileSourceBase abstract class). These x, y andzoomlevel coordinates can be translated to a quadkey, which can then be used to build a url for the VenueMap Tile API. Furthermore, a request to the Venue Map Tile API requires a signed querystring, which includesseveral querystring parameters as returned by the Venue Signature Service. The Signature Service providestokens 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 informationon 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 Serviceon iOS:

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

#import <Foundation/Foundation.h>

Page 33: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

33

@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 {

Page 34: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

34

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;}

Page 35: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

35

/** * 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 on page 45.

Navigating to Pickup and Dropoff LocationsAfter you have assigned a ride to a driver, the driver needs to be able to find their way to the pickup anddropoff locations, and the passenger might need help finding their way to the pickup location. To allow eachparty to get to the correct destination, you can launch the HERE WeGo application for Android and iOS fromwithin your driver and passenger apps.

The typical workflow for calculating a route using the HERE WeGo app is as follows:

• In your passenger or driver app, construct the appropriate link.

• The Android or iOS system launches the HERE WeGo app, which then calculates the route based on theprovided parameters in the link.

Page 36: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

36

The figure below illustrates the elements in the workflow related to the HERE services.

Figure 5: Request Route to Locations

To open the HERE WeGo application on Android:

• Construct an Intent with the following properties:

▫ action com.here.maps.DIRECTIONS

▫ category CATEGORY_DEFAULT

▫ data URI here.directions://v1.0/mylocation/37.870090,-122.268150,Downtown%20Berkeley?ref=<Referrer>&m=w

To open the HERE WeGo application on iOS:

• Open a URL with a custom here-route URL Scheme:

▫ //mylocation/latitude,longitude with WGS 84 compliant latitude and longitudegeocoordinates of the destination

Alternatively, you can replace the geocoordinate pair with a Place ID (as returned by a Places APIquery) to get directions to a specific POI instead of a latitude/longitude pair

▫ ,URLencoded string as the name of the destination

▫ ?ref=<Referrer>referrer (and can be something like your app or company name)

▫ &m=w to indicates the routing mode (m=w stands for walk, m=d for drive)

For instance, here-route://mylocation/37.870090,-122.268150,Downtown%20Berkeley?ref=<Referrer>&m=w requests a route by foot to a destination in downtown Berkeley by a companycalled Referrer.

Page 37: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

37

Post Dropoff ProcessingAfter the ride is complete, you can implement a number of processes, such as reviewing the driver/passenger, leaving comments, and payment, depending on your business mdoel. For the payment step, youmight want to find out exactly which route the driver took, and whether or not he incurred any toll costsalong that route.

The typical workflow for determining what route a driver took and checking for additional toll costs is asfollows:

• Match the GPS trace recorded by your driver app and sent to your backend with the route.

• Calculate toll cost for route.

The figure below illustrates the elements in the workflow related to the HERE services.

Figure 6: Request Details On Performed Drive

Determining Route Taken by Driver

To determine the route the driver took:

1. Using data retrieved from tracking the driver's location as he or she was driving, assemble those locationupdates into a trace file. Currently the supported formats are: CSV, GPX and NMEA.

2. Send a POST request to the resource matchroute in the Route Match Extension API with the followingparameters and the assembled trace:

• routemode with the value car to indicate the route was taken by a car

The response contains a list of the RouteLinks the driver used, as well as a list of Warnings that canalert you of any potentially incorrect driving behavior, such as U-turns where they are not allowed, goingin the wrong direction on a one-way street.

Page 38: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

38

The sample code below demonstrates how to transform a log of GPS position into a GPX file and uploadit to the Route Matching Extension:

const https = require('https');const _ = require('lodash');const builder = require('xmlbuilder');

/** * This function transforms a gps track into GPX format * * gpsLog Array of position updates */function buildGpx(gpsLog) { // Create <gpx version="1.1"> root element var xml = builder.create('gpx').att('version', '1.1'); // Create <trk><trkseg>...</trkseg></trk> tags var segment = xml.ele('trk').ele('trkseg'); gpsLog.forEach(element => { // Create <trkpt lat=X lon=Y><time>Z</time></trkpt> tags within trkseg element segment.ele('trkpt', { 'lat': element.lat, 'lon': element.lon }).ele('time', {}, element.timestamp); }); // Convert xml to string var xmlString = xml.doc().end(); return Promise.resolve(xmlString);}

/** * Builds a POST request for the Route Matching Extension API. * * routeMode The routing mode ('car' or 'pedestrian') */function buildMatchRouteRequestOptions(routeMode) { var requestParams = _({ 'routemode': routeMode, 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'POST', hostname: 'rme.cit.api.here.com', path: ['/2/matchroute.json', requestParams].join('?') };}

/** * Calls the Route Matching Extension API to match a GPX trace to links in the HERE data * See buildMatchRouteRequestOptions for an explanation of the parameters */function matchGpx(routeMode, gpx) { return new Promise((fulfill, reject) => { var options = buildMatchRouteRequestOptions(routeMode); var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { var json = JSON.parse(data); if(res.statusCode >= 400) { reject(new Error(data)); } else

Page 39: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

39

{ var links = []; if(json.RouteLinks) { links = json.RouteLinks.map(link => link.linkId); } fulfill(links); } }) }); req.write(gpx); req.on('error', (err) => { reject(err); });

req.end(); });}

// Example GPS log as recorded by tracking solutionconst berkeleyToSf = [ {"timestamp": "2016-05-19T00:15:39", "lat": 37.870018333, "lon": -122.270135000}, {"timestamp": "2016-05-19T00:15:45", "lat": 37.870003333, "lon": -122.270240000}, ... {"timestamp": "2016-05-19T00:48:15", "lat": 37.779980000, "lon": -122.404860000}];

buildGpx(berkeleyToSf) .then(gpx => matchGpx('car', gpx)) .then(console.log) .catch(console.error);/** Output: [ -798712120, 23665445, 1152950609, 921170882, ... ] */

3. Check the resulting list of RouteLinks the driver used to see the route followed.

For more information, see the API Reference on page 45.

Calculating Route Costs

Using the previously retrieved RouteLinks from the Route Match Extension API, calculate any toll cost thedriver paid in order to correctly compensate them for those costs.

To calculate the potential toll costs:

1. Specify a driven route using a list of link ids specified by the linkId attribute of the RouteLinkspreviously provided by the Route Match Extension API.

2. Concatenate these link ids, using a semicolon (;) as separator.

3. Send a request to the resource tollcost in the Toll Cost Extension API with the following queryparameters:

• currency 3 characters (ISO 4217) currency code for the currency used in the response (for instanceUSD).

• route Concatenated semicolon-separated string containing all link ids along the route.• start_ts Start timestamp of the route.• vspec with the characteristics of the vehicle: tollVehicleType, trailerType,

trailersCount, vehicleNumberAxles, trailerNumberAxles, hybrid, emissionType,height, trailerHeight, vehicleWeight, limitedWeight, disabledEquipped,

Page 40: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

40

minimalPollution, hov, passengersCount, tiresCount, commercial,shippedHazardousGoods, heightAbove1stAxle.

For instance, 2;0;0;2;0;0;5;167;0;1739;1739;0;0;0;4;0;0 with the 4 being the number ofpassengers.

4. Use the detailed costs associated with each toll structure the route passes through in the response tocalculate the total toll cost incurred by the driver.

The sample code below shows how to use the Toll Cost Extension API to request the toll cost:

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the tollcost.json endpoint of the Toll Cost Extension API. * The tollcost.json endpoint return detailed toll cost information associated with * given linkids * * mode The routing mode (e.g. 'fastest;car;traffic:enabled') * currency The currency in which to return toll cost information * (three-letter code, e.g. 'USD') * vspec A string describing the vehicle's attributes. See the Toll Cost Extension's * API Reference for more information on this parameter * links An array of link ids that were traversed as part of the route * (in order of traversal) */function buildTollCostLinksRequestOptions(mode, currency, vspec, links) { var requestParams = _({ 'mode': mode, 'vspec': vspec, 'currency': currency, 'route': links.join(';'), 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).map((value, key) => { return key + '=' + encodeURIComponent(value); }).join('&'); return { method: 'GET', hostname: 'tce.cit.api.here.com', path: ['/2/tollcost.json', requestParams].join('?') };}

/** * Calls the Toll Cost Extension API to calculate toll costs associated with given links * See buildTollCostLinksRequestOptions for an explanation of the parameters */function costForLinks(mode, currency, vspec, links) { return new Promise((fulfill, reject) => { var options = buildTollCostLinksRequestOptions(mode, currency, vspec, links); var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); fulfill(json);

Page 41: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

41

} }) }); req.on('error', (err) => { reject(err); });

req.end(); });}

// Routing mode (fastest car route accounting for traffic)var mode = 'fastest;car;traffic:enabled';// The currency to use for the Toll Cost Extensionvar currency = 'USD';// Number of passengersvar numPassengers = 1;// Vehicle specification, refer to TCE documentation for format informationvar vehicleSpec = '3;0;0;2;0;0;6;350;0;10000;10000;0;0;0;'+numPassengers+';8;0;0';// Array of links along the routevar route = [748873330, 68614309, 17357322, 748938713, 76719821, 17357323]

costForLinks(mode, currency, vehicleSpec, route) .then(console.log) .catch(console.error);/** Output: { "errors": [ ], "warnings": [ ], "countries": [ { "name": "FRA", "roadSectionsCosts": [ { "linksIds": [ 748938713, 76719821 ], "conditions": [ { "time": null, "pass": null, "currency": "EUR", "methodsOfPayment": 151, "daylightHours": 2, "discountAvailable": 0, "tollSystemsIds": "5016", "amount": 7.5 } ], "tollStructures": [ { "linkId1": 748938713, "linkId2": 76719821, "name": "LE TOURNEAU", "lngCode": "FRE", "latitude": 47.9931, "longitude": 2.67762, "tollStructureConditionId": 703489345 } ] } ],

Page 42: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

42

"adminAdmissionCosts": null, "usageFeeRequiredLinks": { "linksIds": [ 748873330, 68614309 ], "conditions": null, "tollStructures": [ { "linkId1": 748873330, "linkId2": 68614309, "name": "FLEURY-EN-BIERE", "lngCode": "FRE", "latitude": 48.42819, "longitude": 2.5403, "tollStructureConditionId": 55877247 } ] }, "tollSystemsNames": [ { "id": 5016, "names": [ { "languageCode": "ENG", "name": "APRR" } ] } ] } ], "onError": false }*/

For more information, see the API Reference on page 45.

Delivery ServicesThis section contains additional scenarios specific to delivery services.

Determining Optimal Delivery SequenceA user running a delivery service most likely wants to optimize the routes of the drivers so that they canmake their deliveries in the fastest and most efficient way. To do so, make use of the Waypoints SequenceExtension API. This API allows you to get the optimal sequence of delivery locations, optimized for either timeor distance.

The related request specifies the following:

• the start location as start parameter,

• the delivery locations as destinationN parameters,

• the routing mode (for example fastest;car;traffic:enabled) as mode parameter,

• and the expected start time as departure parameter.

Page 43: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

43

You can set the optional improveFor parameter to either time or distance in order to define which aspectto optimize the delivery sequence for. The response contains the array of waypoints in the optimal order.To get the full route details, use the Routing API, which allows you to calculate separate sections of thesequence, or multiple sections at once by passing multiple waypoints in the calculateroute request.

Table 1: Request Summary

Parameter Name Value

start Starting Position of the Journey

start=52.2,8.1

destinationN Intermediate destinations, at least one. If no end parameter isprovided, one of these values is selected as end of the sequence.Waypoints of destination parameters can have appointment oraccess hour constraints.

mode Specifies the routing mode.

mode=fastest;car;traffic:enabled

departure Time when travel is expected to start. Traffic speed and incidentsare taken into account when calculating the route. departure isrequired, if traffic:enabled is set or if properties are added towaypoints.

departure=2014-11-11T16:52:00+02:00

departure=2014-11-11T16:52:00Z

improveFor The parameter to optimize the sequence for (time or distance)

improveFor=time

const https = require('https');const _ = require('lodash');

/** * Builds a GET request for the Waypoint Sequence Extension API. * * mode The routing mode (e.g. 'fastest;car;traffic:enabled') * optimizeFor The parameter to optimize the sequence for ('time' or 'distance') * start The start waypoint * end The end waypoint * destinations Array of intermediate waypoints to find optimal sequence for */function buildFindSequenceRequestOptions(mode, optimizeFor, start, end, destinations) { var destinationParams = _(destinations).map((value, index) => { var key = 'destination' + (index + 1); var val = value.lat + ',' + value.lon; return [key, val]; }).fromPairs(); var requestParams = _({ 'start': [start.lat, start.lon].join(','), 'end': [end.lat, end.lon].join(','), 'mode': mode, 'improveFor': optimizeFor, 'departure': 'now', 'app_id': {YOUR_APP_ID}, 'app_code': {YOUR_APP_CODE} }).assign(destinationParams.value()) .map((value, key) => { return key + '=' + encodeURIComponent(value);

Page 44: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

44

}).join('&'); return { method: 'GET', hostname: 'wse.cit.api.here.com', path: ['/2/findsequence.json', requestParams].join('?') };}

/** * Calls the Waypoint Sequence Extension (WSE) API to find the optimal sequence of waypoints * See buildFindSequenceRequestOptions for an explanation of the parameters */function findOptimalSequence(mode, optimizeFor, start, end, destinations) { return new Promise((fulfill, reject) => { var options = buildFindSequenceRequestOptions(mode, optimizeFor, start, end, destinations); var req = https.request(options, (res) => { var data = ""; res.on('data', (d) => { data += d; }); res.on('end', () => { if(res.statusCode >= 400) { reject(new Error(data)); } else { var json = JSON.parse(data); if(json.results && json.results.length > 0) { var result = json.results[0]; // First and last items in waypoints array are start and end waypoints, we're only interested in the order of the destinations in between var destinations = result.waypoints.slice(1, -1); // Subtract 1 from index as WSE indexes destinations starting from 1 fulfill(destinations.map(waypoint => Number(waypoint.id.substr('destination'.length)) - 1)); } else { fulfill(null); } } }) }); req.on('error', (err) => { reject(err); }); req.end(); });}

// Example delivery locationsvar exampleDeliveryLocations = [ { lat: 37.93815, lon: -122.34044 }, { lat: 37.89103, lon: -122.16065 }, { lat: 37.80399, lon: -122.26692 }, { lat: 37.77562, lon: -122.44025 }, { lat: 37.8356103, lon: -122.2898201 }, { lat: 37.76375, lon: -122.24955 }, { lat: 37.76469, lon: -122.42562 }];

// Example depot location (location to both start from and end at)var exampleDepotLocation = { lat: 37.870242, lon: -122.268234 };

// Routing mode (fastest car route accounting for traffic)var mode = 'fastest;car;traffic:enabled';

// Optimize waypoint sequence for time

Page 45: Mobility On-Demand Technical Solution Paperdocumentation.developer.here.com/pdf/aggregated_platform_use_ca… · Mobility On-Demand Technical Solution Paper Building Mobility On-Demand

Mobility On-Demand Technical Solution Paper►  Building Mobility On-Demand Apps

45

var optimizeFor = 'time';

findOptimalSequence(mode, optimizeFor, exampleDepotLocation, exampleDepotLocation, exampleDeliveryLocations) .then(console.log) .catch(console.error);/** Output: [ 1, 5, 2, 4, 3, 6, 0 ] */

For more information, see the Waypoints Sequence Extension API and Routing API Developer's Guides onhttp://developer.here.com.

• Waypoint Sequence Extension API

• Routing API

Service SupportIf you need assistance with this or other HERE products, go to https://developer.here.com/contact-us.

API ReferenceThe following API References are available at https://developer.here.com:

• Android Starter SDK

• Geofencing Extension API

• iOS Starter SDK

• Isoline Routing resource of the Routing API

• Matrix Routing resource of the Routing API

• Platform Data Extension API

• Route Match Extension API

• Toll Cost Extension API

• Venue Maps API

• Waypoint Sequence Extension API

• Routing API