Supply Chain Management Blogs by SAP
Expand your SAP SCM knowledge and stay informed about supply chain management technology and solutions with blog posts by SAP. Follow and stay connected.
cancel
Showing results for 
Search instead for 
Did you mean: 
Rohit_Mahajan
Product and Topic Expert
Product and Topic Expert
1,321

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.

Premium Hub CoE – DSC Knowledge Bits – “A simple setup guide to integrate SAP TM with Hana Spatial S...

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

Rohit_Mahajan_0-1718788773092.png

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 

Rohit_Mahajan_1-1718789007945.png

 

  • Enable the “Set Usage of GIS Tool” Flag in customizing: Master Data ->Transportation Lane -> Distance and Duration Determination -> Set Usage of GIS Tool

Rohit_Mahajan_0-1718809997801.png

  • The relevant Means of Transport requires an enabled “GIS Quality” flag.

Rohit_Mahajan_0-1718813760053.png

  • The speed profile number range object /SAPAPO/SP requires the creation of an interval as a prerequisite for the buffering of distances and duration.

Rohit_Mahajan_1-1718813785296.png

 

  • Define Strategy for Distance and Duration Determination and Geo-routing

Rohit_Mahajan_1-1718810299989.png

Rohit_Mahajan_2-1718810509181.png

Process Controller settings

  • Define Strategy and Method and Assign it.

Rohit_Mahajan_2-1718789290042.png

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.

https://community.sap.com/t5/supply-chain-management-blogs-by-sap/premium-hub-coe-workflow-in-sap-tr...

Do follow @gunther_sanchez for Premium Hub CoE topics on TM and EWM. 

https://community.sap.com/t5/supply-chain-management-blogs-by-sap/premium-hub-coe-dsc-knowledge-bits...

5 Comments