In series of Bring data to life, today I will demonstrate how you can use Google Map API to display SAP data.
At high level, I want to display the location of SAP Entity i.e. Plant, Distribution Center, Warehouse, Customer, Vendor etc location on Google Map. Get Latitude and Longitude (geo coordinates) information for the given entities. Pass these geo coordinates to the Google Map API in an HTML page and you would have given life to your data. For this demo, I would pass the Route as the parameter in my URL. I will retrieve associated customer’s geo coordinates and display them on Google Map.
Google Map API
When using Google Map API, you can pass locations as address or geo coordinates. You need to specify the start of the address and end of the address. If you have different stopover, you can also define them as Way Points. Pass all these details to the API and API will do the rest.
In order to use any Google Product API, you need to get a API key. This API key is free up to certain usage limit. If you go beyond, you need to pay for licensing. You can object an API key for yourself from Google Maps API – Obtaining an API key
Learn more about using Google Map API at Google Maps JavaScript API.
RESTFul WebService
We would use RESTful web service to expose our data. We would build up the response message in HTML containing the JavaScript. This JavaScript code would call the Google Maps API with selected geo coordinates.
Within the handler method for the RESTful WS,
- Extract the parameter from URL
- Get the Data or Prepare the data
- Build up Javascript and HTML code
- Send back the reponse
For this demo, I have create a service zgmap_demo in transaction code SICF and assigned a handler class ZCL_TEST_ROUTE_MAP within it.
Learn more about RESTFul Web Service at RESTful WebService Step-by-Step guide
Code Snippet
For this demo, I haven’t loaded the template file in SMW0 but rather I prepared the HTML within code. I used simple concatenate to build up JavaScript code but leveraged my HTMLinABAP utility to generate entire HTML body.
Method code for IF_HTTP_EXTENSION~HANDLE_REQUEST
METHOD if_http_extension~handle_request. DATA: lv_path TYPE string, lv_cdata TYPE string, lv_param TYPE string. DATA: lt_request TYPE STANDARD TABLE OF string. DATA: lv_route TYPE char10. * get the request attributes lv_path = server->request->get_header_field( name = '~path_info' ). SHIFT lv_path LEFT BY 1 PLACES. SPLIT lv_path AT '/' INTO TABLE lt_request. READ TABLE lt_request INTO lv_param INDEX 1. * Get Customer associated to ROute * Get Geo Codes for those customers TYPES: BEGIN OF lty_geo, kunnr TYPE kna1-kunnr, lon TYPE string, lat TYPE string, END OF lty_geo. DATA: li_geo TYPE STANDARD TABLE OF lty_geo. DATA: lwa_geo LIKE LINE OF li_geo. DEFINE append_data. lwa_geo-kunnr = &1. lwa_geo-lon = &2. lwa_geo-lat = &3. append lwa_geo to li_geo. END-OF-DEFINITION. append_data: '1001' '33.042342' '-96.768866', '1002' '33.057954' '-96.772127', '1003' '33.060364' '-96.796782', '1004' '33.030794' '-96.795516', '1005' '33.027699' '-96.790152', '1006' '33.027771' '-96.768866'. * Build up JavaScript DATA: lv_tot TYPE i. DATA: lv_from TYPE i. DATA: lv_to TYPE i. DATA: lv_start TYPE string. DATA: lv_end TYPE string. DATA: lv_wp TYPE string, lv_pair TYPE string, lv_iw TYPE string. lv_tot = LINES( li_geo ). IF lv_tot GT 2. lv_from = 2. lv_to = lv_tot - 1. ENDIF. * start / end point READ TABLE li_geo INTO lwa_geo INDEX 1. CONCATENATE 'var startpoint = new google.maps.LatLng(' lwa_geo-lon ',' lwa_geo-lat ');' INTO lv_start. * Info window lv_iw = 'var infoWindow;'. CONCATENATE ' infoWindow = new google.maps.InfoWindow();' ' infoWindow.setOptions({' ' content: "<div>First Customer to Visit :' lwa_geo-kunnr '</div>",' ' position: startpoint,' ' });' ' infoWindow.open(map); ' INTO lv_iw. READ TABLE li_geo INTO lwa_geo INDEX lv_tot. CONCATENATE 'var endpoint = new google.maps.LatLng(' lwa_geo-lon ',' lwa_geo-lat ');' INTO lv_end. CONCATENATE lv_iw ' infoWindow = new google.maps.InfoWindow();' ' infoWindow.setOptions({' ' content: "<div>Last Customer : ' lwa_geo-kunnr '</div>",' ' position: endpoint,' ' });' ' infoWindow.open(map); ' INTO lv_iw. * Way points LOOP AT li_geo INTO lwa_geo FROM lv_from TO lv_to. CONCATENATE `location:"` lwa_geo-lon `,` lwa_geo-lat `",` INTO lv_pair. CONCATENATE lv_wp ' waypts.push({' * ' location:"33.077842,-96.820428″,' lv_pair ' stopover:true' ' });' INTO lv_wp SEPARATED BY cl_abap_char_utilities=>cr_lf. ENDLOOP. DATA: lv_js TYPE string. CONCATENATE '<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />' '<style type="text/css">' ' html { height: 100% }' ' body { height: 100%; margin: 0; padding: 0 }' ' #map_canvas { height: 100% }' '</style>' '<script type="text/javascript"' ' src="http://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=false">' '</script>' '<script type="text/javascript">' 'var directionsDisplay;' 'var directionsService = new google.maps.DirectionsService();' 'var map;' lv_start lv_end 'function initialize() {' ' directionsDisplay = new google.maps.DirectionsRenderer();' ' var myOptions = {' ' zoom: 16,' ' mapTypeId: google.maps.MapTypeId.ROADMAP,' ' center: startpoint' ' }' ' map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);' ' directionsDisplay.setMap(map); ' ' var waypts = [];' * ' waypts.push({' * ' location:"33.077842,-96.820428",' * ' stopover:true' * ' });' lv_wp ' console.log(waypts);' ' var request = {' ' origin:startpoint,' ' destination:endpoint,' ' waypoints: waypts,' ' optimizeWaypoints: true,' ' travelMode: google.maps.TravelMode.DRIVING' ' }; ' ' directionsService.route(request, function(result, status) {' ' if (status == google.maps.DirectionsStatus.OK) {' ' directionsDisplay.setDirections(result);' ' }' ' }); ' lv_iw * ' var infoWindow = new google.maps.InfoWindow();' * ' infoWindow.setOptions({' * ' content: "<div></div>",' * ' position: startpoint,' * ' });' * ' infoWindow.open(map); ' '}' '</script>' INTO lv_js SEPARATED BY cl_abap_char_utilities=>cr_lf. * Body HTML DATA: lv_body TYPE string. DATA: lv_html TYPE string. DATA: lo_body TYPE REF TO lcl_html. DATA: lo_table TYPE REF TO lcl_html. DATA: lo_thead TYPE REF TO lcl_html. DATA: lo_tbody TYPE REF TO lcl_html. DATA: lo_tr TYPE REF TO lcl_html. DATA: lo_td TYPE REF TO lcl_html. DATA: lo_tag TYPE REF TO lcl_html. DATA: lv_val TYPE string. CONCATENATE '<html>' '<head><title>Route Map</title>' '<style>body{ font: normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif; }</style>' INTO lv_html. CREATE OBJECT lo_body. lo_body->create_element( 'body' ). lo_body->add_attribute( attr = 'onload' val = 'initialize()' ). CREATE OBJECT lo_tag. lo_tag->create_element( 'h3' ). lo_tag->add_inner_html( 'Generated Route Map from SAP' ). lo_body->append_child( lo_tag ). CONCATENATE 'Route:' lv_route INTO lv_val SEPARATED BY space. CREATE OBJECT lo_tag. lo_tag->create_element( 'p' ). lo_tag->add_inner_html( lv_val ). lo_body->append_child( lo_tag ). CREATE OBJECT lo_table. lo_table->create_element( 'table' ). lo_table->add_attribute( attr = 'style' val = 'float:left' ). lo_body->append_child( lo_table ). CREATE OBJECT lo_thead. lo_thead->create_element( 'thead' ). lo_table->append_child( lo_thead ). CREATE OBJECT lo_tr. lo_tr->create_element( 'tr' ). lo_thead->append_child( lo_tr ). CREATE OBJECT lo_td. lo_td->create_element( 'td' ). lo_td->add_inner_html( 'Customer' ). lo_tr->append_child( lo_td ). CREATE OBJECT lo_td. lo_td->create_element( 'td' ). lo_td->add_inner_html( 'Long' ). lo_tr->append_child( lo_td ). CREATE OBJECT lo_td. lo_td->create_element( 'td' ). lo_td->add_inner_html( 'Lat' ). lo_tr->append_child( lo_td ). CREATE OBJECT lo_tbody. lo_tbody->create_element( 'tbody' ). lo_table->append_child( lo_tbody ). LOOP AT li_geo INTO lwa_geo. CREATE OBJECT lo_tr. lo_tr->create_element( 'tr' ). lo_tbody->append_child( lo_tr ). lv_val = lwa_geo-kunnr. CREATE OBJECT lo_td. lo_td->create_element( 'td' ). lo_td->add_inner_html( lv_val ). lo_tr->append_child( lo_td ). lv_val = lwa_geo-lon. CREATE OBJECT lo_td. lo_td->create_element( 'td' ). lo_td->add_inner_html( lv_val ). lo_tr->append_child( lo_td ). lv_val = lwa_geo-lat. CREATE OBJECT lo_td. lo_td->create_element( 'td' ). lo_td->add_inner_html( lv_val ). lo_tr->append_child( lo_td ). ENDLOOP. CREATE OBJECT lo_tag. lo_tag->create_element( 'div' ). lo_tag->add_attribute( attr = 'id' val = 'map_canvas' ). lo_tag->add_attribute( attr = 'style' val = 'width: 100%, height: 100%' ). lo_body->append_child( lo_tag ). lv_body = lo_body->get_html( ). CONCATENATE lv_html lv_js '</head>' lv_body INTO lv_html. lv_cdata = lv_html. * Send the response back server->response->set_cdata( data = lv_cdata ). ENDMETHOD.
Output
When you execute your Service URL, it would generate this type of nice representation on Google Map. You feel like you “bring data to life”
More from Bring Data to Life series
Various different ways to give life to your data:
- Bring Data to Life – Integrating D3.js in SAP via RESTful Web Service – Sneak Pick at d3.js (Part I)
- Bring Data to Life – Integrating D3.js in SAP via RESTful Web Service – RESTful WS Step-by-Step (Part II)
- Bring Data to Life – Integrating D3.js in SAP via RESTful Web Service – Full Working Solution (Part III)
- Google Map integration in SAP using RESTful Web Service – Bring Data to Life