Many times we get this requirement, where Customers want SAP Transportation Management to Calculate exact Distance and Duration between different locations using some GIS provider and use this distance for planning and settlement.
SAP HANA Spatial Service is recommended approach for calculation of GeoCoding, Distance and Duration. Using HSS, we do not have to write any code and its only configuration steps, Details are mentioned in the below link.
When customers do not want to use SAP HANA Spatial Service and have HERE Licence for Distance and Duration Calculation, you can follow these configuration steps and code, you can copy this code and use in your projects.
Transaction: SM59 – Configuration of RFC Destinations
Test the connection to HERE API, if you get SSL error, Download the certificates from HERE API using any browser and upload it using Transaction STRUST.
In SPRO -> Transportation Management -> Transportation Lane -> Distance and Duration Determination
Process Controller settings
Code for Process Controller Class
CLASS zcl_ddd_connector DEFINITION
PUBLIC
CREATE PUBLIC .
PUBLIC SECTION.
TYPES:
BEGIN OF ty_geo_cord,
fr_x_pos TYPE c LENGTH 15,
fr_y_pos TYPE c LENGTH 15,
to_x_pos TYPE c LENGTH 15,
to_y_pos TYPE c LENGTH 15,
END OF ty_geo_cord .
TYPES ts_geo_cord TYPE ty_geo_cord .
METHODS get_duration
IMPORTING
!io_methpar TYPE REF TO /sctm/cl_meth_parameter
!it_request TYPE /sctm/tt_request .
METHODS get_api
IMPORTING
!is_geo_cord TYPE ts_geo_cord
RETURNING
VALUE(et_data) TYPE REF TO data .
METHODS cast_request
IMPORTING
!io_request TYPE REF TO /sctm/cl_request
RETURNING
VALUE(ro_request) TYPE REF TO /scmb/cl_ddd_request .
METHODS convert_seconds_to_duration
IMPORTING
!iv_seconds TYPE int4
RETURNING
VALUE(rv_duration) TYPE /sapapo/ts_durat .
METHODS extract_duration_from_route
IMPORTING
!iv_route_duration TYPE int4
RETURNING
VALUE(rv_duration) TYPE /sapapo/ts_durat .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ZCL_DDD_CONNECTOR IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_DDD_CONNECTOR->CAST_REQUEST
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_REQUEST TYPE REF TO /SCTM/CL_REQUEST
* | [<-()] RO_REQUEST TYPE REF TO /SCMB/CL_DDD_REQUEST
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD cast_request.
TRY.
ro_request ?= io_request.
CATCH cx_sy_move_cast_error.
CLEAR ro_request.
ENDTRY.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_DDD_CONNECTOR->CONVERT_SECONDS_TO_DURATION
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_SECONDS TYPE INT4
* | [<-()] RV_DURATION TYPE /SAPAPO/TS_DURAT
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CONVERT_SECONDS_TO_DURATION.
CONSTANTS: lc_seconds_minute TYPE int1 VALUE 60,
lc_seconds_hour TYPE int2 VALUE 3600.
DATA: hour(4) TYPE c,
min(2) TYPE c,
sec(2) TYPE c.
CLEAR rv_duration.
hour = iv_seconds DIV lc_seconds_hour.
min = ( iv_seconds MOD lc_seconds_hour ) DIV lc_seconds_minute.
sec = ( iv_seconds MOD lc_seconds_hour ) MOD lc_seconds_minute.
rv_duration = hour * 10000 + min * 100 + sec ##NUMBER_OK.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_DDD_CONNECTOR->EXTRACT_DURATION_FROM_ROUTE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_ROUTE_DURATION TYPE INT4
* | [<-()] RV_DURATION TYPE /SAPAPO/TS_DURAT
* +--------------------------------------------------------------------------------------</SIGNATURE>
method EXTRACT_DURATION_FROM_ROUTE.
DATA: lv_duration_in_seconds TYPE int4.
CLEAR rv_duration.
lv_duration_in_seconds = iv_route_duration.
rv_duration = convert_seconds_to_duration( iv_seconds = lv_duration_in_seconds ).
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_DDD_CONNECTOR->GET_API
* +-------------------------------------------------------------------------------------------------+
* | [--->] IS_GEO_CORD TYPE TS_GEO_CORD
* | [<-()] ET_DATA TYPE REF TO DATA
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_api.
DATA: lv_api TYPE string,
lv_destination TYPE c VALUE 'HERE_ROUTE_V7' LENGTH 15.
CONCATENATE '?transportMode=car&origin=' is_geo_cord-fr_y_pos ',' is_geo_cord-fr_x_pos '&destination='
is_geo_cord-to_y_pos ',' is_geo_cord-to_x_pos '&return=summary' INTO lv_api.
CALL FUNCTION 'Z_FM_OTC_HERE_MAP_REST_API'
EXPORTING
im_destination = lv_destination
im_api = lv_api
IMPORTING
et_data = et_data.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_DDD_CONNECTOR->GET_DURATION
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_METHPAR TYPE REF TO /SCTM/CL_METH_PARAMETER
* | [--->] IT_REQUEST TYPE /SCTM/TT_REQUEST
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_duration.
*Data Declaration
DATA: lt_data TYPE REF TO data,
ls_geo_cord TYPE ts_geo_cord,
lv_dec TYPE p LENGTH 8 DECIMALS 6,
lr_data TYPE REF TO data,
ls_ddd_result TYPE /scmb/s_ddd_request_result.
FIELD-SYMBOLS: <data> TYPE data,
<routes> TYPE any,
<sections> TYPE any,
<structure> TYPE any,
<summary> TYPE any,
<table> TYPE ANY TABLE,
<itab> TYPE ANY TABLE,
<isum> TYPE ANY TABLE,
<field> TYPE any,
<field_value> TYPE data.
*Process GeoRouting requests (just a single request object expected here containing all concrete requests)
LOOP AT it_request INTO DATA(lo_request).
*Cast
DATA(lo_ddd_request) = cast_request( lo_request ).
CHECK lo_ddd_request IS NOT INITIAL.
*Fetch geo cordinates
LOOP AT lo_ddd_request->mt_requests INTO DATA(ls_request).
IF ls_request-loc_fr_xpos <> ls_request-loc_to_xpos OR
ls_request-loc_fr_ypos <> ls_request-loc_to_ypos.
ls_geo_cord-fr_x_pos = ls_request-loc_fr_xpos.
CONDENSE ls_geo_cord-fr_x_pos.
CONCATENATE ls_geo_cord-fr_x_pos(2) '.' ls_geo_cord-fr_x_pos+2(6) INTO ls_geo_cord-fr_x_pos.
ls_geo_cord-fr_y_pos = ls_request-loc_fr_ypos.
CONDENSE ls_geo_cord-fr_y_pos .
CONCATENATE ls_geo_cord-fr_y_pos(2) '.' ls_geo_cord-fr_y_pos+2(6) INTO ls_geo_cord-fr_y_pos.
ls_geo_cord-to_x_pos = ls_request-loc_to_xpos.
CONDENSE ls_geo_cord-to_x_pos.
CONCATENATE ls_geo_cord-to_x_pos(2) '.' ls_geo_cord-to_x_pos+2(6) INTO ls_geo_cord-to_x_pos.
ls_geo_cord-to_y_pos = ls_request-loc_to_ypos..
CONDENSE ls_geo_cord-to_y_pos.
CONCATENATE ls_geo_cord-to_y_pos(2) '.' ls_geo_cord-to_y_pos+2(6) INTO ls_geo_cord-to_y_pos.
*Call method for API consumption
lt_data = get_api( ls_geo_cord ).
*If data exist
IF lt_data IS BOUND.
*Fetch routes
ASSIGN lt_data->* TO <data>.
ASSIGN COMPONENT 'ROUTES' OF STRUCTURE <data> TO <routes>.
ASSIGN <routes>->* TO <table>.
*Loop Routes data
LOOP AT <table> ASSIGNING <structure>.
*Fetch Sections
ASSIGN <structure>->* TO <data>.
ASSIGN COMPONENT 'SECTIONS' OF STRUCTURE <data> TO <sections>.
ASSIGN <sections>->* TO <itab>.
*Loop Internal data
*Append Data into Final result table
* insert INITIAL LINE TO lo_ddd_request->mt_results ASSIGNING FIELD-SYMBOL(<ls_results>).
**Pass Request data from input to output data " Mandataory to do this
* LOOP AT ls_request-requests INTO DATA(ls_req).
* APPEND INITIAL LINE TO <ls_results>-requests ASSIGNING FIELD-SYMBOL(<ls_req>).
* MOVE-CORRESPONDING ls_req TO <ls_req>.
* ENDLOOP.
*
**Precision 2 means Data is coming from 3rd party GIS provider
* <ls_results>-didu_precision = '2'.
* <ls_results>-buffered = 'X'.
LOOP AT <itab> ASSIGNING FIELD-SYMBOL(<ls_sec>).
*Fetch Summary to get distance and duration
ASSIGN <ls_sec>->* TO <data>.
ASSIGN COMPONENT 'SUMMARY' OF STRUCTURE <data> TO <summary>.
ASSIGN <summary>->* TO FIELD-SYMBOL(<wsum>).
*Pass Duration
TRY.
ASSIGN COMPONENT 'DURATION' OF STRUCTURE <wsum> TO <field>.
IF <field> IS ASSIGNED.
lr_data = <field>.
ASSIGN lr_data->* TO <field_value>.
ls_ddd_result-duration = extract_duration_from_route( <field_value> ).
ENDIF.
UNASSIGN: <field>, <field_value>.
*Pass Distance
ASSIGN COMPONENT 'LENGTH' OF STRUCTURE <wsum> TO <field>.
IF <field> IS ASSIGNED.
lr_data = <field>.
ASSIGN lr_data->* TO <field_value>.
* ls_ddd_result-distance = ls_ddd_result-distance + ( <field_value> / 1000 ).
ls_ddd_result-distance = ( <field_value> / 1000 ).
ENDIF.
UNASSIGN: <field>, <field_value>.
CATCH cx_sy_itab_line_not_found.
ENDTRY.
ENDLOOP.
IF ls_ddd_result IS NOT INITIAL.
ls_ddd_result-loc_fr_xpos = ls_request-loc_fr_xpos.
ls_ddd_result-loc_fr_ypos = ls_request-loc_fr_ypos.
ls_ddd_result-loc_to_xpos = ls_request-loc_to_xpos.
ls_ddd_result-loc_to_ypos = ls_request-loc_to_ypos.
ls_ddd_result-didu_precision = /scmb/if_dist_dur_det=>gc_didu_prec_gis.
ls_ddd_result-requests = ls_request-requests.
INSERT ls_ddd_result INTO TABLE lo_ddd_request->mt_results.
CLEAR ls_ddd_result.
ENDIF.
ENDLOOP.
ENDIF.
ELSE.
ls_ddd_result-duration = 0.
ls_ddd_result-distance = 0.
ls_ddd_result-loc_fr_xpos = ls_request-loc_fr_xpos.
ls_ddd_result-loc_fr_ypos = ls_request-loc_fr_ypos.
ls_ddd_result-loc_to_xpos = ls_request-loc_to_xpos.
ls_ddd_result-loc_to_ypos = ls_request-loc_to_ypos.
ls_ddd_result-didu_precision = /scmb/if_dist_dur_det=>gc_didu_prec_gis.
ls_ddd_result-requests = ls_request-requests.
INSERT ls_ddd_result INTO TABLE lo_ddd_request->mt_results.
CLEAR ls_ddd_result.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Create a Function Module
FUNCTION z_fm_here_map_rest_api.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(IM_DESTINATION) TYPE CHAR15
*" VALUE(IM_API) TYPE STRING
*" EXPORTING
*" REFERENCE(ET_DATA) TYPE REF TO DATA
*"----------------------------------------------------------------------
*HTTP Client Abstraction
DATA: lo_client TYPE REF TO if_http_client,
lo_rest_client TYPE REF TO cl_rest_http_client,
lo_response TYPE REF TO if_rest_entity,
lv_apikey TYPE string VALUE 'XXXXXXXXXXXXX'.
* lv_apikey TYPE string VALUE 'M-AGoC0QrkXBQqmFSnfpbsf14BFVrpmr9UNEKTVFAUQ'." Rohit on 28-08-2022
CHECK im_api IS NOT INITIAL.
*Concatenate api key and app id for authentication
CONCATENATE im_api '&apiKey=' lv_apikey INTO im_api.
*consume service from RFC Destination
cl_http_client=>create_by_destination(
EXPORTING
destination = im_destination " Logical destination (specified in function call)
IMPORTING
client = lo_client " HTTP Client Abstraction
EXCEPTIONS
argument_not_found = 1
destination_not_found = 2
destination_no_authority = 3
plugin_not_active = 4
internal_error = 5
OTHERS = 6 ).
CHECK lo_client IS BOUND.
CREATE OBJECT lo_rest_client
EXPORTING
io_http_client = lo_client.
*Get data in JSON
CALL METHOD lo_client->request->set_header_field
EXPORTING
name = 'Accept'
value = 'application/json'.
*Pass API
cl_http_utility=>set_request_uri(
EXPORTING
request = lo_client->request " HTTP Framework (iHTTP) HTTP Request
uri = im_api " URI String (in the Form of /path?query-string)
).
*Pass method as GET
lo_rest_client->if_rest_client~get( ).
CHECK lo_rest_client IS BOUND.
* HTTP response
lo_response = lo_rest_client->if_rest_client~get_response_entity( ).
CHECK lo_response IS BOUND.
* HTTP return status
DATA(http_status) = lo_response->get_header_field( '~status_code' ).
*Status code should be success
CHECK http_status = '200' OR http_status = '201'.
* HTTP JSON return string
DATA(json_response) = lo_response->get_string_data( ).
CHECK json_response IS NOT INITIAL.
*Convert JSON into Internal table
CALL METHOD /ui2/cl_json=>deserialize
EXPORTING
json = json_response
pretty_name = /ui2/cl_json=>pretty_mode-user
assoc_arrays = abap_true
CHANGING
data = et_data.
ENDFUNCTION.
Kindly let me know your feedback or if any step is missed.
Do follow @gunther_sanchez for Premium Hub CoE topics on TM and EWM.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
7 | |
2 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |