
Hello All,
In this Blog I would like Exhibit a Scenario Where the RESTful webservice is used to Display SAP Data using MAPS API. In this Blog, Displaying the Location of SAP Entity like Plant, Distribution Center, Warehouse, Vendor Location on Google Maps.
We need to Fetch Latitude and Longitude (Geo Coordinates) information for the given entities and then further we need to pass Geo Coordinates to the Google Map API in an HTML Block, Here I would pass Route as parameter in the URL then will retrieve customer location and display them on Google Map.
API
While working with Google Maps API, I’ll be passing locations as Addresses or Co-Ordinates. I need to specify the start of address and end of address. If you have Stopover in between you can also define them as Waypoints. I will pass all the details to the API, and it will process all the rest.
To Use Google Product API, I need to get an API Key. This API key is free up to certain usage Limit. If we go Beyond, we need to pay for licensing. We can Object an API Key ourselves Obtaining Google Maps API Key
To Learn More about Google Maps API and Geo Coordinates, we can look up Google Maps JS API
RESTful WS
I Would use RESTful Web Service to Expose our data. I Will Build up the response message in HTML containing the JavaScript. The JavaScript code will call Google Maps API with Selected Geo Co-Ordinates.
Within the Handler Method for the RESTful WS,
For this Scenario I will create a Service ZAL_DEMO in Transaction code SICF and assigned a Handler Class ZCL_AL_DEMO_HANDLER within it and a Standard Interface IF_HTTP_EXTENSION to Handle the HTTP.
First Step I will Navigate to Transaction SICF
After Navigating to Transaction SICF we can see the screen Define Service as below, then we need to click on Execute Button
We can Create a New Node whenever we are creating RESTful WS as we can create different services under that Node. We shouldn’t Use default_host/sap/bc/srt as it required to have SOAMANAGER Configuration, which I will be not using for our Service.
So, I will place the cursor on desired node and Click on Drop Down
Default_host--->sap--->bc--->ZAL_DEMO
I will Right click on “bc” and create a Sub-Element, Further I will Enter the name of the Service in Next Popup. In the subsequent screen I’ll provide the Description.
Next, I’ll Provide the Necessary Credentials in Logon Data Tab like Description, Client which I’ll be working on, User ID and Password.
In the Handler Tab I’ll assign a custom class which would be used to handle the request coming from the web. This class should Implement the standard Interface IF_HTTP_EXTENSION and to gain access of the request and also required method.
Now I’ll add the Interface IF_HTTP_EXTENSION in my Handler class and Implement the Method.
HTML Build
For HTML Body we can load our template in Transaction SMW0
But I Have chose to Manually Build Utility HTML class within ABAP. This utility wraps the logic for building HTML Tags and string manipulation within itself for simple client access.
---> Template with Static Data = like Main CSS, Headers, Table Headers
---> Place Holders for Dynamic Data = Table Details
I Will create 3 Private Attributes which can be accessed through above methods.
Now I’ll Create a Global Class with the Above Methods and Attributes.
METHODS
ATTRIBUTES
METHOD IMPLEMENTATION
Preference in Local Class
CLASS lcl_html DEFINITION.
PUBLIC SECTION.
METHODS : create_elemnt IMPORTING tag TYPE string,
add_att IMPORTING attr TYPE string
val TYPE string,
add_inner_html IMPORTING val TYPE string,
append_child IMPORTING io_tag TYPE REF TO lcl_html,
get_html RETURNING VALUE(rv_string) TYPE string.
PRIVATE SECTION.
DATA : tag TYPE string.
DATA : inner_html TYPE string.
TYPES : BEGIN OF ty_attr,
attr TYPE string,
val TYPE string,
END OF ty_attr.
DATA : attrs TYPE STANDARD TABLE OF ty_attr.
DATA : children TYPE STANDARD TABLE OF REF TO lcl_html.
ENDCLASS.
CLASS lcl_html IMPLEMENTATION.
METHOD create_elemnt.
me->tag = tag.
ENDMETHOD.
METHOD append_child.
APPEND io_tag TO me->children.
ENDMETHOD.
METHOD add_att.
DATA : ls_attr LIKE LINE OF me->attrs.
ls_attr-attr = attr.
ls_attr-val = val.
APPEND ls_attr TO me->attrs.
ENDMETHOD.
METHOD add_inner_html.
me->inner_html = val.
ENDMETHOD.
METHOD get_html.
DATA : lv_string TYPE string.
DATA : lv_attr_val TYPE string.
DATA : ls_attr LIKE LINE OF me->attrs.
DATA : lo_child TYPE REF TO lcl_html.
DATA : lv_child_html TYPE string.
*opening bracket
lv_string = |<{ me->tag }|.
LOOP AT me->attrs INTO ls_attr.
lv_attr_val = |{ ls_attr-attr }='{ ls_attr-val }'|.
lv_string = |{ lv_string } { lv_attr_val }|.
ENDLOOP.
lv_string = |{ lv_string }>|.
*Inner HTML
lv_string = |{ lv_string }{ me->inner_html }|.
*Child
LOOP AT me->children INTO lo_child.
lv_child_html = lo_child->get_html( ).
lv_string = |{ lv_string }{ lv_child_html }|.
CLEAR lv_child_html.
ENDLOOP.
*Closing
lv_string = |{ lv_string }</{ me->tag }>|.
*Back the HTML
rv_string = lv_string.
ENDMETHOD.
ENDCLASS.
IMPLEMENTATION
In this Implementation part I'll Use the HTML Builder Class in IF_HTTP_EXTENSION~HANDLE_REQUEST Method of my Handler Class ZCL_AL_DEMO_HANDLER.
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'.
li_geo = value #( ( kunnr = '1001' lon = '33.042342' lat = '-96.768866' )
( kunnr = '1002' lon = '33.057954' lat = '-96.772127' )
( kunnr = '1003' lon = '33.060364' lat = '-96.796782' )
( kunnr = '1004' lon = '33.030794' lat = '-96.795516' )
( kunnr = '1005' lon = '33.027699' lat = '-96.790152' )
( kunnr = '1006' lon = '33.027771' lat = '-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 src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>'
'<script'
' src="http://maps.googleapis.com/maps/api/js?key=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 zcl_html.
DATA: lo_table TYPE REF TO zcl_html.
DATA: lo_thead TYPE REF TO zcl_html.
DATA: lo_tbody TYPE REF TO zcl_html.
DATA: lo_tr TYPE REF TO zcl_html.
DATA: lo_td TYPE REF TO zcl_html.
DATA: lo_tag TYPE REF TO zcl_html.
data: lo_button TYPE REF TO zcl_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_elemnt( 'body' ).
lo_body->add_att( attr = 'onload' val = 'initialize()' ).
CREATE OBJECT lo_tag.
lo_tag->create_elemnt( '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_elemnt( 'p' ).
lo_tag->add_inner_html( lv_val ).
lo_body->append_child( lo_tag ).
CREATE OBJECT lo_table.
lo_table->create_elemnt( 'table' ).
lo_table->add_att( attr = 'style' val = 'float:left' ).
lo_body->append_child( lo_table ).
CREATE OBJECT lo_thead.
lo_thead->create_elemnt( 'thead' ).
lo_table->append_child( lo_thead ).
CREATE OBJECT lo_tr.
lo_tr->create_elemnt( 'tr' ).
lo_thead->append_child( lo_tr ).
CREATE OBJECT lo_td.
lo_td->create_elemnt( 'td' ).
lo_td->add_inner_html( 'Customer' ).
lo_tr->append_child( lo_td ).
CREATE OBJECT lo_td.
lo_td->create_elemnt( 'td' ).
lo_td->add_inner_html( 'Long' ).
lo_tr->append_child( lo_td ).
CREATE OBJECT lo_td.
lo_td->create_elemnt( 'td' ).
lo_td->add_inner_html( 'Lat' ).
lo_tr->append_child( lo_td ).
CREATE OBJECT lo_tbody.
lo_tbody->create_elemnt( 'tbody' ).
lo_table->append_child( lo_tbody ).
LOOP AT li_geo INTO lwa_geo.
CREATE OBJECT lo_tr.
lo_tr->create_elemnt( 'tr' ).
lo_tbody->append_child( lo_tr ).
lv_val = lwa_geo-kunnr.
CREATE OBJECT lo_td.
lo_td->create_elemnt( '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_elemnt( '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_elemnt( 'td' ).
lo_td->add_inner_html( lv_val ).
lo_tr->append_child( lo_td ).
ENDLOOP.
CREATE OBJECT lo_tag.
lo_tag->create_elemnt( 'div' ).
lo_tag->add_att( attr = 'id' val = 'map_canvas' ).
lo_tag->add_att( 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 ).
Now I’ll Use this ZCL_AL_DEMO_HANDLER Class in my Service in the Tab Handler List which was created in Transaction SICF.
OUTPUT
For the Output Firstly, we should activate the service and then we can Execute URL or Test the Service which generates Nice Representation on Google Map.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
3 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |