Do you want to find road distance and time between 2 locations with current traffic conditions using BTP Services?

In this blog post we will walk through quick and easy low-code steps to use the capabilities provided by SAP Kyma Serverless Functions.We will tackle the common but frequently occurring problem of finding the road distance and times to go from one place to another depending on road and traffic conditions. For this SAP HANA Spatial Services provide extensive capabilities

For details on use of Kyma functions refer to SAP Help Documentation for Kyma Functions. I will use python as the programming language for all code snippets provided here. Alternatively Node.js could also be used in Kyma Functions.

The steps below describe the creation of an API to provide driving distance and time between 2 address  locations. For this purpose we will use the SAP HANA Spatial Services. For detailed reference on these services please refer to SAP Help Documentation for SAP HANA Spatial Services and other helpful details are provided here SAP Discovery Mission.

The approach described here takes away the hassle of managing high availability infrastructure for providing services and that tooo with just a few lines of code. It utilizes the cloud-based offering running on powerful infra-structure managed by SAP for spatial services with a scalable runtime.

Pre-requistites

To go through the the steps you would need to ensure you already have the following:

  1. Setup BTP Account with HANA Spatial Services as described here SAP HANA Spatial Service on BTP. Alternatively you can create the Service Instance and Service Binding in Kyma cockpit. This is advisable as then you can use the service credentials from the binding instead of coding it in the function. In this case the service is created in the same subaccount as the Kyma cluster and you need to ensure the subaccount has entitlement to create the service.
  2. Setup BTP Account with SAP Kyma runtime as described here Create Kyma Environment on BTP

Setup HANA Spatial Services in Kyma Cockpit (Optional)

If you do not already have a HANA Spatial Service instance you can create one in Kyma Cockpit.

Go to your chosen namespace in Kyma where you plan to develop the functionality, navigate to Service Management Section.

1. Add BTP Service Instance

Go to BTP Service Instances and press Create and you will get the following form

BTP%20Service%20Instance%20Creation%20on%20Kyma

BTP Service Instance Creation on Kyma

 

You can choose the Plan Name to be standard or lite depending on the entitlement of the spatial service to the BTP subaccount where the Kyma clusters is provisioned. The Offering Name has to be spatialservices. The Name is of your own choice.

Once the service is created you will see the following

HANA%20Spatial%20Services%20Instance%20on%20Kyma

HANA Spatial Services Instance on Kyma

2. Add BTP Service Binding

Next step is to add a service binding to the above spatial service instance. This step creates the required credentials needed to get OAuth token for using the HANA Spatial Services.

Under Service Management now go to BTP Service Bindings and press Create. In the dropdown of the Service Instance Name you will see the existing service instances. Choose the one created in step 1 above, in the example above we called hss-instance

Creating%20HANA%20Spatial%20Service%20Binding%20on%20Kyma

Creating HANA Spatial Service Binding on Kyma

 

This results in the following binding to be provisioned

HANA%20Spatial%20Service%20Binding%20Provisioned%20on%20Kyma

HANA Spatial Service Binding Provisioned on Kyma

 

If you click on the binding name above, you will see that this creates an associated Secret with the same name, in our example hss-binding.

HANA Spatial Service Binding

You can go look at the hss-binding secret and decode it. We will use the uaa  which has the url, clientid,clientsecret fields of this secret in the step to get OAuth token below. We will use the uri field of the secret to get HANA Spatial Service url for geocoding and route.

Kyma%20Secret%20for%20HANA%20Spatial%20Service

Kyma Secret for HANA Spatial Service

In steps below we will access the above secret properties in the Kyma function.

Create Python Functions to calculate distance between 2 locations

SAP HANA Spatial Services provide many advanced capabilities for geocoding, routing and mapping. For the example we will focus on answering the question “How to find the distance and time to go from one address to another using a car?” Also the service should gracefully return incase it is not able to map either of the origin or destination addresses.

Since the user-input is provided as addresses, first step is to have a function to get the latitude and longitude of the addresses

For this we will use the POST /geocoding/v1/geocode API followed by the POST /routing/v1/route API. Now using these APIs require the generation of OAuth token. To generate this token you would need 3 things from the HANA Spatial Service instance created in step 1 of the pre-requisites above.

1. Get OAuth Token

Option 1

If you want to use an existing HANA Spatial Services without binding to your function. However, it is advisable to follow the steps to use the binding option so you do not have hard-coded values for secret parameters as described in Option 2.

From the BTP Cockpit access your service key and note the url, clientid and clientsecret

 

Service%20Key%20on%20BTP%20Cockpit

Service Key on BTP Cockpit

 

Access%20Details%20from%20Service%20Key

Access Details from Service Key

 

Here is the code in python to use the above credentials to get the OAuth token

import requests
import json

def getToken():

    url = <url from service key>
    params = { "grant_type": "client_credentials" }

    auth = (<clientid from service key>,<clientsecret from service key>)
    response = requests.post(url, data=params,auth=auth)
 
    d = json.loads(response.text)
    return d['access_token']

Option 2

In this case we will use the secret created in step Setup HANA Spatial Services in Kyma Cockpit above. When you add a function in Kyma you also have the option to add associated Environment Variables. Since we already created a service binding and associated secret, you can choose to create an Environment variable to reference these in your code

Environment%20Variable%20for%20UAA

Environment Variable for UAA

We will create 2 environment variables using the secret hss-binding (the name of our binding created before, if you chose a different name you will see it in the drop down above for Creating Secret Value. The  variables are for uaa and uri fields from secret, here I called them hssuri and hssuaa.

Environment%20Variables%20for%20Accessing%20Secret

Environment Variables for Accessing Secret

Here is code to get the OAuth token which uses the environment variables instead of coding it in the function

import requests
import json
import os

def getToken():
  
    hssuaa = os.environ.get('hssuaa') #replace with environment variable for uaa if different name
    if hssuaa:
        hssuaa = json.loads(hssuaa)
        url = hssuaa['url'] +  '/oauth/token'
        params = { "grant_type": "client_credentials"}
        auth = (hssuaa['clientid'],hssuaa['clientsecret'])
        
        response = requests.post(url, data=params,auth=auth)

        d = json.loads(response.text)
        return d['access_token']
    else:
        raise Exception("HANA Spatial Credentials missing")

2. Get Geo-code from address

Here is python code to use the HANA Spatial Services for geocoding to get latitude and longitude. The url in the code below needs to be replaced by the uri from the service key and the token is obtained by the function above

def getCoord(address,token):
    url = <uri from serice key>/geocoding/v1/geocode #If using Option 1 Spatial Service
    url = os.environ.get('hssuri')+'/geocoding/v1/geocode' #If using Option 2 with Service Binding
    headersAPI = {
        'accept': 'application/json',
        'content-type': 'application/json',
        'Authorization': 'Bearer '+ token,
    }
    body = {
        "credentials": {
        "provider": "Here",
        "api_key": "SAP-KEY"
        },
      "addresses": [address]
    }
    response = requests.post(url, json=body,headers=headersAPI)
    return json.loads(response.text)['features'][0]['geometry']['coordinates']

The geocode service also returns useful information like matchScore and addressType which can be useful for ambiguous cases where the address match is not perfect.

3. Get the distance and other metrics from HANA Spatial Service

The routing service from HANA Spatial Services requires latitude and longitude that we get from the function above. Here is python code to use the routing service to get distance and time metrics between 2 locations.

def getHSSDistance(from_address,to_address):
    token = getToken()
    from_coord = getCoord(from_address,token)
    if not from_coord:
        raise Exception("Unidentiified from address " + from_address)

    to_coord = getCoord(to_address,token)
    if not to_coord:
        raise Exception("Unidentified to address " + to_address)

    url = <uri from service key>/routing/v1/route #If using Option 1 for Spatial Service
    url = os.environ.get('hssuri')+'/routing/v1/route' #If using Option 2 with Service Binding
  

    body = {
            "credentials": {
                "provider": "Here",
                "api_key": "SAP-KEY"
            },
            "waypoints": {
                "type": "MultiPoint",
                "coordinates": [
                    from_coord,
                    to_coord
                ]
            },
            "vehicleType": "car",
            "returnGeometry": False
        }

    headersAPI = {
            'accept': 'application/json',
            'content-type': 'application/json',
            'Authorization': 'Bearer '+ token,
    }

    response = requests.post(url, json=body,headers=headersAPI)
    data = json.loads(response.text)['properties']
    duration = data['duration']/60    ## Converting to minutes
    distance = data['distance']/1000  ## Converting to Meters to KM
    return distance,duration

As you see we simply chose vehicleType to be car because we are interested in driving distance. There are other parameters which can be useful and by default the traffic information is set to true and the departureTime to now. The detailed list of parameters is here. Also the parameters for the routes can be more complicated as a list of waypoints instead of only an origin and destination as in my example above.

Create Kyma Function to provide API to return distance and time between 2 addresses

Now that we have all the building blocks ready, we are ready to add this function to Kyma and provide an API endpoint which takes 2 addresses and returns the driving distance and time between. This is a detailed step-by-step description by following Kyma documentation for creating Serverless Function

Go to your Kyma dashboard on BTP and choose the namespace you would want to add the service to, for example below I have 3 namespaces and I choose dev

Kyma%20Dashboard%20Namespace%20Selection

Kyma Dashboard Namespace Selection

In the namespace now go to Functions and create an inline function. I chose Python as the code snippets above are for Python.

Kyma%20Inline%20Python%20Function

Kyma Inline Python Function

 

When you press create you get a “Hello World” sample code from Kyma. We will change the code to add the 3 functions above and a main function which uses it and returns the answer to the API

Kyma%20Main%20Function

Kyma Main Function

All the functionality for geocoding and routing is provided by the HANA Spatial Services so we do not need to add any other packages. Incase there are additional packages needed they can be added in the Dependencies section. The format to add packages here is same as in requirements.txt, for example python package name with desired version.

 

Now add an API endpoint so we can call the above function

Kyma API Configuration

 

Creating the API rule is straightforward, you can change the Name as you like and provide a Subdomain which adds a prefix to the host and helps to distinguish this service from others you may create on the Kyma cluster

The advanced options above provide capabilities to scale the endpoint and the underlying compute required. For this usecase we can use the XS as there is not a lot of compute needed and its off-loaded to the HANA Spatial Service. We can add more replicas depending the expected usage of the endpoint and Kyma cluster size.

Kyma%20Resource%20Configuration

Kyma Resource Configuration

Once the API is created your function would look like this

Kyma%20Function%20with%20API

Kyma Function with API

The host endpoint above can then be used to test the endpoint

Testing and Debugging the endpoint

You can test the endpoint by providing it a from and to address. To see the print messages in the code you can open the logs from the function logs in Kyma

Kyma Function Logs

 

When you call the API from curl or Postman as in example here, the logs can be seen in the Kyma dashboard

Sample Logs

 

For cases when there are errors in the code either at build time or deploy time, the logs can be checked in the Grafana logs. To see the grafana logs follow the instructions provided here at Setup Grafana for Kyma Metrics and Logs. Some other useful examples for debugging are provided in this blog post Kymas Serverless Python Functions A Short Excursion

 

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x