Skip to Content
Technical Articles

Google Distance Matrix API in SAP Cloud Application Studio – C4C/ByD

Introduction:

Google’s Distance Matrix API(DMA) can be used to calculate distance and duration between Origins and Destinations. With release of new reuse-library functions for JSON parsing in version-2005, this blog will help you in rapid development for consuming Google Distance Matrix service with easy JSON parsing.

Use Case:

Service team want to calculate distance between Customer and Technician(employees) based on supplied address for the customer and technicians.

Solution:

Steps overview:

Google Maps Platform Step-1 Create Google developer account
Step-2 Create a billing account
Step-3 Create a project
Step-4 Enable Distance Matrix API
Step-5 Generate API key
SAP Cloud Applications Studio Step-6 Create External Web Service Integration
Step-7 Create Communication System
Step-8 Create Communication Scenario
Step-9 Communication Arrangement
Step-10 Create Business object nodes for Reuse-Library Function parameters
Step-11 Define Reuse-library function to fetch Distance Matrix
Step-12 Implement Distance Matrix Library Function:

  • Prepare Origin and Destination address
  • Invoke Google Distance Matrix Web Service
  • JSON Parsing: Check JSON validity
  • JSON Parsing: Find JSON Array Length
  • JSON Parsing: Prepare Key collection to read JSON object
  • JSON Parsing: Fetch JSON object for the supplied Key collection
  • JSON Parsing: Read JSON attribute values and fill returning collection
Step-13 Create event handler to invoke reuse-library function
Step-14 Output of Google Distance Matrix

  • JSON Output
  • Output In UI
Step-15 Complete Code for Reuse-Library Function

Step by Step:

Step 1: Create Google developer account:To use Google Map Services we have to create a google developer account and click Maps Platform.

 

Step-2: Create a Billing account: Create a billing account which will be used as you payment wallet for the Google Map Platform Services.

 

Step-3: Create a Project: We must have a project to use Google Map Platform. The project is the basis for managing services, credentials, billing, APIs, and SDKs. Usage costs associated with the project will be charged to the billing account linked to the project.

 

Step-4: Enable Distance Matrix API: To calculate distance and duration between places, Google is providing Distance Matrix API. After creating Billing account and Project, we will have to enable Distance Matrix API for the created project.

 

Step-5: Generate API key: API key will be used to access Google Map services. API Key is associated with project and billing, every access using an API key will be charged under selected project from the linked billing account.

 

Step-6: Create External Web Service: As we have enabled Google Distance Matrix API in Google Map Platform and generated API Key. Now we will create External Web Service Integration(“GoogleDistanceMatrixApiWS”) in SAP Cloud Application Studio for the Distance Matrix API.

 

Step-7: Create Communication System: Create a communication System to connect with Google Map Platform.

 

Step-8: Create Communication Scenario: For the created External Web Service Interface(“GoogleDistanceMatrixApiWS”), we will create Communication Scenario

Step-9: Communication Arrangement:  Create Communication Arrangement for the created Communication Scenario(“GoogleDistanceMatrixCS”) and fill communication system(“GOOGLE_DISTANCE_MATRIX_RAHUL”).

 

Step-10: Create Business object nodes for Reuse-Library Function parameters. Create business object nodes which will be used as importing and returning parameters for reuse-library functions

import AP.Common.GDT as apCommonGDT;
import AP.FO.BusinessPartner.Global;
import AP.CRM.Global;
import AP.FO.ProductDataMaintenance.Global as prd;

businessobject SeminarMgmt {

        [AlternativeKey][Label("Seminar ID")] element SeminarID:ID;
	[Label("Name")] element Name :LANGUAGEINDEPENDENT_LONG_Text;
	

	node DMAResults [0,n]{
		element slno : IntegerValue;
		element EmployeeID : LANGUAGEINDEPENDENT_EXTENDED_Name;
		element destination_addresses : LANGUAGEINDEPENDENT_EXTENDED_Name;
		element origin_addresses : LANGUAGEINDEPENDENT_EXTENDED_Name;
		element distance : IntegerValue;
		element distance_text : LANGUAGEINDEPENDENT_EXTENDED_Name;
		element duration : IntegerValue;
		element duration_text : LANGUAGEINDEPENDENT_EXTENDED_Name;
		element status : LANGUAGEINDEPENDENT_EXTENDED_Name;
		element error_text : LANGUAGEINDEPENDENT_LONG_Text;
	}

	node DMAAddresses [0,n] {
		element ObjectID : LANGUAGEINDEPENDENT_EXTENDED_Name; //Customer or Employee ID
		element SkillID : LANGUAGEINDEPENDENT_EXTENDED_Name;  //Skill
		element Address : LANGUAGEINDEPENDENT_LONG_Text;     //Customer or Employee Address
	}
}

 

Step-11: Define Reuse-library function to get Distance Matrix by supplying customer & employee addresses and fetch distances and duration.

 

Step-12: Implement Distance Matrix Library Function: Implement Reuse-library function to get Distance Matrix of Customer and employee addresses

  • Prepare Origin and Destination address: Prepare and fill parameters for the supplied importing addresses in “IM_ADDRESSES”.
foreach (var contact_addr in IM_ADDRESSES)
{
	//Customer address will always be at index 1 and 2..N is employee address
	if (contact_index == 1)
	{
		//Customer & its Address 1:1
		//&destinations=" + contact_addr.Address;  //Ittina Mahaveer
		URLParameter.Name = "destinations";
		URLParameter.Value = contact_addr.Address;
		URLParameter_coll.Add(URLParameter);
	}
	else 
	{		
		//Employee and its address 1:N
		//"&origins=Starbuck Coffee|Coffee Cafe Day"
		origins_addresses_str = origins_addresses_str + "|" + contact_addr.Address;			
	}
	contact_index = contact_index + 1;
}

//Trim extra bar on the left 
origins_addresses_str = origins_addresses_str.TrimLeft("|");

//Add Origin Parameters
//origins=Starbuck Coffee|Coffee Cafe Day
URLParameter.Name = "origins";
URLParameter.Value = origins_addresses_str;
URLParameter_coll.Add(URLParameter);

//units=imperial
URLParameter.Name = "units";
URLParameter.Value = "imperial";  
URLParameter_coll.Add(URLParameter);

//key=AIzaSyAg....................2SkRUZPPxc
URLParameter.Name = "key";
URLParameter.Value = "AIzaSyAg....................2SkRUZPPxc";  
URLParameter_coll.Add(URLParameter);
  • Invoke Google Distance Matrix Web Service: Call the External Web service created in Step-6 and supply required parameters to fetch distance and duration for the supplied addresses.
// Call Google DMA Service
DMA_Response = WebServiceUtilities.ExecuteRESTService("GoogleDistanceMatrixCS", "GoogleDistanceMatrixApiWS", HttpMethod, HttpResource, URLParameter_coll, HeaderParameter, ContentType, Body);
if(DMA_Response.Code != "200 " || DMA_Response.Content == "" )
{
	//Raise Error, Google Service Failed, so add all employees and mark error	
	error_text = "Google Distance Matrix service call failed";
	error_flag = "X";
}

———————————————————————————

  • JSON Parsing: Check JSON validity– Check the JSON response received from the API is a valid JSON
if (Json.IsValidJson(DMA_Response_JSON) == true)
  • JSON Parsing: Find JSON Array Length– Check if searched addresses are found in the JSON object
//Find Array Origin address Length
Keys_coll.Add("origin_addresses");

//Fetch Origin address count
array_Length = Json.GetArrayLength(Keys_coll, DMA_Response_JSON);
  • JSON Parsing: Prepare Key collection to read JSON object
  • //Add all elements that we want to read from the JSON object
    		while ((exec_index <= array_Length.Arraylength.GetFirst().Length) 
    				&& (array_Length.Arraylength.GetFirst().Length >= 1))
    		{
     			Keys_coll.Add("origin_addresses[" + exec_index.ToString() + "]");    // Multiple Employee Address
    
    			Keys_coll.Add("destination_addresses[1]");                           //Always 1 Customer 
    
    			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].status");
    
    			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].distance.text");
    
    			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].distance.value");
    
    			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].duration.text");
    
    			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].duration.value");
    
    			//Counter
    			exec_index = exec_index + 1;
    		}
  • JSON Parsing: Parse JSON object for the supplied Key collection
  • //Fetch Parsed Key & value from the collection
    ParsedKeyValueColl = Json.ParseKeyValues(Keys_coll, DMA_Response_JSON);
    
  • JSON Parsing: Read JSON attribute values and fill returning collection
  •      exec_index = 0;
    		while (exec_index <= ParsedKeyValueColl.KeyValue.Count())
    		{
    			ParsedKeyValueElement.Clear();
    			ParsedKeyValueElement = ParsedKeyValueColl.KeyValue.GetByIndex(exec_index);
    			if (ParsedKeyValueElement.IsInitial() == false)
    			{
    
    				//Origin Address
    				if (exec_index % 7 == 1)
    				{				
    					DMAResultElements.origin_addresses = ParsedKeyValueElement.Value;
    				}
    
    				//Destination Address
    				if (exec_index % 7 == 2)
    				{
    					DMAResultElements.destination_addresses = ParsedKeyValueElement.Value;
    				}
    
    				//Status
    				if (exec_index % 7 == 3)
    				{
    					
    					DMAResultElements.status = ParsedKeyValueElement.Value;
    					if(DMAResultElements.status != "OK")
    					{
    						DMAResultElements.error_text = "Invalid address, Address not found using google distance matrix API";
    					}
    				}
    
    				//Distance Text
    				if (exec_index % 7 == 4)
    				{
    					DMAResultElements.distance_text = ParsedKeyValueElement.Value;
    				}
    
    				//Distance Value
    				if (exec_index % 7 == 5)
    				{
    					DMAResultElements.distance = ParsedKeyValueElement.Value;
    				}
    
    				//Duration Text
    				if (exec_index % 7 == 6)
    				{
    					DMAResultElements.duration_text = ParsedKeyValueElement.Value;
    				}
    		
    				//Duration Value
    				//Enter Node to collection
    				if (exec_index % 7 == 0)
    				{
    					DMAResultElements.duration = ParsedKeyValueElement.Value;
    					DMAResultsColl.Add(DMAResultElements);			
    				}
    			}
    			exec_index = exec_index + 1;
    		}

 

Step-13: Create event handler to invoke reuse-library function: For this example I have created AfterModify event and invoked Distance Matrix Function.

Note: According to scenario which Technician(Employees) is closest to customer address, so Multiple Employee Address(1:N) and 1 Customer Address(1:1) will be supplied

import ABSL;
import AP.FO.BusinessPartner.Global;
import AP.FO.ProductDataMaintenance.Global;
import AP.PDI.Utilities;
import AP.Common.GDT as apCommonGDT;
import AP.CRM.Global;

var Address_Coll : collectionof elementsof SeminarMgmt.DMAAddresses;
var Address      : elementsof SeminarMgmt.DMAAddresses;

//Smaple parameter and values
//&origins=Ramakrishna Smart Hospitals|Ajmera Infinity|Blossom Multi Speciality Hospital|Gati Limited
//&destinations=Ittina Mahaveer

//Customer Address
Address.ObjectID = "80";
Address.Address = "Ittina Mahaveer";
Address_Coll.Add(Address);


//Employees Addresses
Address.ObjectID = "90";
Address.Address = "Ramakrishna Smart Hospitals";
Address_Coll.Add(Address);

Address.ObjectID = "100";
Address.Address = "Ajmera Infinity";
Address_Coll.Add(Address);

Address.ObjectID = "110";
Address.Address = "Blossom Multi Speciality Hospital, hosa road, bangalore";
Address_Coll.Add(Address);

Address.ObjectID = "120";
Address.Address = "Gati Limited, hosur main road, bangalore";
Address_Coll.Add(Address);

Address.ObjectID = "130";
Address.Address = "India post, electronic city, bangalore";
Address_Coll.Add(Address);

var DistanceMatrixResultCol = GoogleAPI.GetDistanceMatrix(Address_Coll);

 

Step-14:Output of Google Distance Matrix: After calling Distance Matrix API service the result is fetched and shown in the UI for the bind business object node.

Showing distance matrix in UI:

JSON Output: 

Sample URL:

https://maps.googleapis.com/maps/api/distancematrix/json?origins=Ramakrishna Smart Hospitals|Ajmera Infinity|Blossom Multi Speciality Hospital, hosa road, bangalore|Gati Limited, hosur main road, bangalore|India post, electronic city, bangalore&destinations=Ittina Mahaveer&key=AIzaSyAg...............Pxc&region=de&units=MeTRIC

JSON Response:

{
    "destination_addresses": [
        "12th Cross Road, Neeladri Nagar, Electronics City Phase 1, Electronic City, Bengaluru, Karnataka 560100, India"
    ],
    "origin_addresses": [
        "Survey no 92/1 B, HP Avenue, Konappana Agrahara Village Begur Hubli, Konappana Agrahara, Electronic City, Bengaluru, Karnataka 560100, India",
        "Ground Floor, Electronic City Phase I, A Block, Karuna Nagar, Electronics City Phase 1, Electronic City, Bengaluru, Karnataka 560100, India",
        "3, Hosa Rd, Naganathapura, Rayasandra, Bengaluru, Karnataka 560100, India",
        "SHOP # 1, NEAR, BUS STOP, Hosur Rd, ROPENA AGARA, Electronic City, Bengaluru, Karnataka 560068, India",
        "2nd Cross Road, Electronics City Phase 1, Electronic City, Indra Nagar, Electronics City Phase 1, Electronic City, Bengaluru, Karnataka 560100, India"
    ],
    "rows": [
        {
            "elements": [
                {
                    "distance": {
                        "text": "4.0 km",
                        "value": 4028
                    },
                    "duration": {
                        "text": "15 mins",
                        "value": 897
                    },
                    "status": "OK"
                }
            ]
        },
        {
            "elements": [
                {
                    "distance": {
                        "text": "0.7 km",
                        "value": 671
                    },
                    "duration": {
                        "text": "5 mins",
                        "value": 286
                    },
                    "status": "OK"
                }
            ]
        },
        {
            "elements": [
                {
                    "distance": {
                        "text": "5.9 km",
                        "value": 5925
                    },
                    "duration": {
                        "text": "21 mins",
                        "value": 1234
                    },
                    "status": "OK"
                }
            ]
        },
        {
            "elements": [
                {
                    "distance": {
                        "text": "4.4 km",
                        "value": 4354
                    },
                    "duration": {
                        "text": "14 mins",
                        "value": 862
                    },
                    "status": "OK"
                }
            ]
        },
        {
            "elements": [
                {
                    "distance": {
                        "text": "2.1 km",
                        "value": 2147
                    },
                    "duration": {
                        "text": "9 mins",
                        "value": 554
                    },
                    "status": "OK"
                }
            ]
        }
    ],
    "status": "OK"
}

 

Step-15: Complete Code for Reuse-Library Function

/*
Objective: Call Google Distance Matrix API(DMA) to fetch distance and duration between supplied orgin and destination addresses.
Steps:
	1. Build Source & Destination parameters based on supplied incoming parameters
	2. Build URL parameters collections for DMA URL
	3. Invoke Google DMA Webservice with URL parameters - WebServiceUtilities.ExecuteRESTService()
	4. Fetch Google DMA JSON response 
	5. Check Response JSON validity
	6. Fetch array length for orgin address - Json.GetArrayLength()
	7. Build  key collection for the below attributes we want to read from JSON object:
		a. Origin Address
		b. destination address
		c. Status
		d. Distance - Integer
		e. Duration - Integer
	8. Parse DMA JSON for the supplied key attributes - Json.ParseKeyValues();
	9. Iterate Parsed collection to retrive attribute values and fill result collection
	10. Sort DMA result using "duration" field - DMAResultsColl.OrderBy()
Created by: Rahul Sharma
Created on: 15.05.2020 09:50:00 AM
*/

import ABSL;
import AP.Common.GDT;
import AP.PDI.Utilities;
import AP.CRM.Global;

var contact_index : IntegerValue=1;
var origins_addresses_str : LANGUAGEINDEPENDENT_LONG_Text;

// Webservice Variables 
var HttpMethod = "GET";
var HttpResource = "";                                 // not required
var ContentType = "";                                  // not required
var Body = "";                                         // not required
var HeaderParameter : collectionof NameAndValue;  // not required
var URLParameter_coll : collectionof NameAndValue;
var URLParameter : NameAndValue;


var DMA_Response_JSON; 
var Keys_coll : collectionof LANGUAGEINDEPENDENT_Text;
var key_element;
var array_Length : JsonArrayLength;
var exec_index : IntegerValue=0;
var ParsedKeyValueColl : JsonResult;
var ParsedKeyValueElement : KeyValueResult;
var DMAResultElements : elementsof SeminarMgmt.DMAResults;
var DMAResultsColl : collectionof elementsof SeminarMgmt.DMAResults;
var DMA_Response;
var error_text;
var error_flag;


//Sample URL to be build
//https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=Ramakrishna Smart Hospitals|Ajmera&destinations=Ittina Mahaveer&key=AIzaSyAg8............C2SkRUZPPxc
foreach (var contact_addr in IM_ADDRESSES)
{
	//Customer address will always be at index 1 and 2..N is employee address
	if (contact_index == 1)
	{
		//Customer & its Address 1:1
		//&destinations=" + contact_addr.Address;  //Ittina Mahaveer
		URLParameter.Name = "destinations";
		URLParameter.Value = contact_addr.Address;
		URLParameter_coll.Add(URLParameter);
	}
	else 
	{		
		//Employee and its address 1:N
		//"&origins=Starbuck Coffee|Coffee Cafe Day"
		origins_addresses_str = origins_addresses_str + "|" + contact_addr.Address;			
	}
	contact_index = contact_index + 1;
}

//Trim extra bar on the left 
origins_addresses_str = origins_addresses_str.TrimLeft("|");

//Add Origin Parameters
//origins=Starbuck Coffee|Coffee Cafe Day
URLParameter.Name = "origins";
URLParameter.Value = origins_addresses_str;
URLParameter_coll.Add(URLParameter);

//units=imperial
URLParameter.Name = "units";
URLParameter.Value = "imperial";  
URLParameter_coll.Add(URLParameter);

//key=AIzaSyAg...................RUZPPxc
URLParameter.Name = "key";
URLParameter.Value = "AIzaSyAg...................RUZPPxc";  
URLParameter_coll.Add(URLParameter);


// Call Google DMA Service
DMA_Response = WebServiceUtilities.ExecuteRESTService("GoogleDistanceMatrixCS", "GoogleDistanceMatrixApiWS", HttpMethod, HttpResource, URLParameter_coll, HeaderParameter, ContentType, Body);
if(DMA_Response.Code != "200 " || DMA_Response.Content == "" )
{
	//Raise Error, Google Service Failed, so add all employees and mark error	
	error_text = "Google Distance Matrix service call failed";
	error_flag = "X";
}
else
{			
	//JSON Payload for searched distance matrix
	DMA_Response_JSON = DMA_Response.Content;

	//Check JSON Validity
	if (Json.IsValidJson(DMA_Response_JSON) == true)
	{
		
		//Find Array Origin address Length
		Keys_coll.Add("origin_addresses");

		//Fetch Origin address count
		array_Length = Json.GetArrayLength(Keys_coll, DMA_Response_JSON);

		//Clear existing keys from the collection
		Keys_coll.Clear();
		
		
		exec_index = 1; 
		//NOTE: Index starts from 1 for reading elements in JSON object

		//Add all elements that we want to read from the JSON object
		while ((exec_index <= array_Length.Arraylength.GetFirst().Length) 
				&& (array_Length.Arraylength.GetFirst().Length >= 1))
		{
 			Keys_coll.Add("origin_addresses[" + exec_index.ToString() + "]");    // Multiple Employee Address

			Keys_coll.Add("destination_addresses[1]");                           //Always 1 Customer 

			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].status");

			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].distance.text");

			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].distance.value");

			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].duration.text");

			Keys_coll.Add("rows[" + exec_index.ToString() + "].elements[1].duration.value");

			//Counter
			exec_index = exec_index + 1;
		}

		
		//Fetch Parsed Key & value from the collection
		ParsedKeyValueColl = Json.ParseKeyValues(Keys_coll, DMA_Response_JSON);

		exec_index = 0;
		while (exec_index <= ParsedKeyValueColl.KeyValue.Count())
		{
			ParsedKeyValueElement.Clear();
			ParsedKeyValueElement = ParsedKeyValueColl.KeyValue.GetByIndex(exec_index);
			if (ParsedKeyValueElement.IsInitial() == false)
			{

				//Origin Address
				if (exec_index % 7 == 1)
				{				
					DMAResultElements.origin_addresses = ParsedKeyValueElement.Value;
				}

				//Destination Address
				if (exec_index % 7 == 2)
				{
					DMAResultElements.destination_addresses = ParsedKeyValueElement.Value;
				}

				//Status
				if (exec_index % 7 == 3)
				{
					
					DMAResultElements.status = ParsedKeyValueElement.Value;
					if(DMAResultElements.status != "OK")
					{
						DMAResultElements.error_text = "Invalid address, Address not found using google distance matrix API";
					}
				}

				//Distance Text
				if (exec_index % 7 == 4)
				{
					DMAResultElements.distance_text = ParsedKeyValueElement.Value;
				}

				//Distance Value
				if (exec_index % 7 == 5)
				{
					DMAResultElements.distance = ParsedKeyValueElement.Value;
				}

				//Duration Text
				if (exec_index % 7 == 6)
				{
					DMAResultElements.duration_text = ParsedKeyValueElement.Value;
				}
		
				//Duration Value
				//Enter Node to collection
				if (exec_index % 7 == 0)
				{
					DMAResultElements.duration = ParsedKeyValueElement.Value;
					DMAResultsColl.Add(DMAResultElements);			
				}
			}
			exec_index = exec_index + 1;
		}

		//Sort Ascending by Duration
		DMAResultsColl = DMAResultsColl.OrderBy(map => map.duration);
	}
	else
	{
		//Error handling JSON not valid
		error_text = "Invalid JSON received from Google Distance Matrix API";
		error_flag = "X";
	}
}

if(error_flag == "X")
{
		exec_index = 1;
		foreach (var address in IM_ADDRESSES)
		{
			DMAResultElements.Clear();

			if(exec_index == 1)
			{
				//Customer Address
				//Ignore Customer as we are seeking Employee address
			}
			else
			{
				//Employee Address
				DMAResultElements.EmployeeID = address.ObjectID;
				DMAResultElements.origin_addresses = address.Address;
				DMAResultElements.error_text = error_text;
				DMAResultsColl.Add(DMAResultElements);
			}		
			exec_index = exec_index + 1;
		}
}

return DMAResultsColl; //============>>>>>>>>>>>>

Happy Learning!

 

10 Comments
You must be Logged on to comment or reply to a post.