Google Map integration in SAP using RESTful Web Service – Bring Data to Life

By | February 4, 2013 | Data to Life, Tricks, Tutorials | 5,482 | 0

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.

Google Map demo SAP RESTFul Service

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&#8243;,'
      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>'
              '<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:

Like It? Share!!

Don't miss an Update

Naimesh Patel{245 articles}

I'm SAP ABAP Consultant for more than a decade. I like to experiment with ABAP especially OO. I have been SDN Top Contributor.
Follow :

Explore all of his 245 articles.

Load comments

Comments on this Post are now closed. If you have something important to share, you can always contact me.