Geospatial Extensions with Cloud Application Programing (CAP)

The recommendation from SAP for extension development is to use cloud application programming (CAP). However, geospatial data is often consumed through a specialized web service provider such as geoserver or Esri ArcGIS.
When a customer has a use for CAP and wants the geospatial use cases to follow the same pattern, including support for modifications and functional validations, we recommend to consider the following:

CAP architecture

Architecture with CAP

The geospatial data resides in HANA tables which can be local, a cached replica or remote, depending on the business needs. Stored Procedures and views are useful artifacts that are typically used in conjunction with spatial data types.

Functions/API represented on the right orange box perform various business logic:

  • records containing any form of address field will benefit from geocoding to add coordinates and normalized fields such as country, state, county, city…
  • A polygon representing a harvesting block is checked for an intersection with a powerlines.
  • A polygon representing a harvesting block along with a harvest date range is checked for overlapping with wildlife sanctuary zones and seasons.

The purpose of using functions outside the CAP main module is to simplify developments, releases, even improve scalability if using serverless functions and micro services APIs.

Lastly, the CAP service layer is the main component. It holds 3 important artifacts:

In the service extension file, we handle the geospatial format conversions: the value in and out of the HANA table is in binary format and we prefer the service layer to use a human readable format such as geojson or Well Known Text (WKT).

Example of web service requests are in the samples folder. For instance, adding a warehouse is done by posting to <host>/forest/WareHouse this body:

{"ID": 1, "label": "Warehouse 1", "address": "okipii, Finland" }

Geocoding is performed before the record is inserted, to check the result, you can simply fetch the record with a get request to <host>/forest/Warehouse?$filter=label%20eq%20’Warehouse 1′

The column geo_score with a value of 0.99 indicates that the geocoding is accurate.

HTTP/1.1 200 OK
OData-Version: 4.0
content-type: application/json;odata.metadata=minimal
Date: Tue, 07 Sep 2021 13:41:27 GMT
Connection: close
Content-Length: 297 { "@odata.context": "$metadata#Warehouse/$entity", "ID": 1, "label": "Warehouse 1", "surface": null, "address": "okipii, Finland", "geo_score": 0.99, "geo_point": { "type": "Point", "coordinates": [ 22.59858989715576, 62.495699882507324 ] }, "geo_city": "Kurikka", "geo_county": "Etelä-Pohjanmaa", "geo_country_iso3": "FIN"

A more fun example is to have a look at the wild life sanctuaries defined and post a harvesting block inside a sanctuary during the protected season:

### Harvesting block in a protected area during a specific season.
POST http://localhost:4004/forest/HarvestingBlock
Content-Type: application/json {"ID":2,"dt":"2022-10-10","geom_as_wkt":"POLYGON ((23.77344846725464 61.42467889322534, 23.77394199371338 61.425407593025284, 23.77162456512451 61.426126012789695, 23.77134561538696 61.42581812063022, 23.771259784698486 61.425161273869925, 23.77145290374756 61.42508943041672, 23.77344846725464 61.42467889322534))"} 

The response returns the id of the first rule that was infringed:

HTTP/1.1 500 Internal Server Error
OData-Version: 4.0
content-type: application/json;odata.metadata=minimal
Date: Tue, 07 Sep 2021 14:11:37 GMT
Connection: close
Content-Length: 138 { "error": { "code": "500", "message": "Harvesting in this location and date infringes the sanctuary \"Little Trolls of the woods in Autumn\"" }

The sample project with full code and example data is available on github. I’d be happy to take this further and build a UI on top unfortunately I have zero UI5 skills so I’d be happy to get some help!