/wp-content/uploads/2014/11/ui5_576243.jpgInjecting CMaps Analytics into an SAPUI5 powered application is fortunately as easy as leveraging just about any other SAP UI5 component. Much like an SAPUI5 component, a developer can create anywhere from basic to advanced mapping functionality with only a few lines of code. In this example we will focus on a basic mapping implementation.

2016 UPDATE:

Since we jumped in head first, we completely overhauled our approach to fit common design patterns for SAP UI5. The result is a new Library, allowing you to design maps code free and embed Google Maps into your SAPUI5 powered upp:

http://cmapsanalytics.com/designer/sapui5.html


Application Requirements

The goal of our application was simple… We want to evaluate the viability of replacing an SAP Dashboards sample with SAP UI5 and CMaps Analytics for the purposes of connecting to HANA XS / OData. The original business case was from an operational dashboard that enables delivery dispatchers to more effectively manage customer satisfaction by identifying customers awaiting product delivery in the San Francisco area who may be at risk for a late delivery.

After these requirements, we’ll jump straight into the code that makes this all happen. While this sample example, is powered with static data, you could just as easily consume OData from HANA XS as OData.


Why CMaps Analytics over Google Maps API?

What SAPUI5 to JQuery, CMaps Analytics is to Google Maps, providing a powerful library of visualization and user experience features that can be assembled through a point and click cloud designer, resulting in only a few lines of code. While simply displaying locations on a map is extremely simple through the standard Google Maps JS API, creating maps experiences with multiple layers, alerts, regions, and other analysis can be time consuming and code intensive. CMaps Analytics bundles Google Maps and CMaps Analytics JS APIs together for rapid maps design and management.

The Results

With very little code or effort, we were able to convert our SAP Dashboards (Xcelsius) content with SAPUI5. Thanks to flexibility and complete programmatic control over the user experience and design, we were even able to improve on design limitations from Xcelsius, including BI-directional communication between data grid and map and inline filtering from the data grid.


Development Components


For this example, we need 3 primary UI controls

  1. CMaps Analytics Google-powered Map
    • The map will be used to display an interactive color coded layer of pin icons that represent customers awaiting delivery as well as their current estimated delivery time as compared to their originally promised delivery time. We want our icons colored as follows: green for good (on time), yellow for at-risk (projected over 5 minutes late) and red for anyone projected to receive a late delivery (over 15 minutes late).
  2. sap.ui.table.Table with the following columns
    • Location (Latitude and Longitude of customer)
    • Delivery Address (postal address of customer)
    • Scheduled Delivery Time (original promised delivery time)
    • Minutes from Now (minutes until original promised delivery time)
    • Projected Delivery (current estimated delivery time)
    • Minutes from Projected (minutes until current estimated delivery time)
    • Status (Shows “On Time”, “Delinquent” or “At Risk”)
  3. sap.viz.ui5.Pie
    • Shows the overall count of On Time, At Risk and Delinquent deliveries


Our UI controls need to perform these tasks

  • The map needs to display the business data and further, let the user click on a given pin icon to see the customer’s Address, Scheduled Delivery Time and Projected Delivery Time.
  • The table needs to display textual information and to allow the user to click on any given row in the table, at which point the map will be triggered to automatically pan to and select the corresponding pin icon. The table also needs to allow filtering on the “Status” column so we can narrow down customers by delivery status (“On Time”, “Delinquent” or “At Risk”). Upon table filtering, the map needs to update its data accordingly and the pie chart needs to recalculate itself as well.
  • The pie chart is simply a display.

Sample Source Code


Scripts to include


     1. SAP UI5



<script id='sap-ui-bootstrap'
src='https://sapui5.netweaver.ondemand.com/resources/sap-ui-core.js'
type="text/javascript"
data-sap-ui-libs="sap.ui.commons, sap.m, sap.me, sap.ui.table, sap.viz"
data-sap-ui-modules="sap.ui.core.plugin.DeclarativeSupport"
data-sap-ui-theme="sap_bluecrystal"></script>


     2. CMaps Analytics



<script src="URL FOR CMAPS ANALYTICS JS API" type="text/javascript"
></script>


HTML Elements



<html>
<head>
<meta name="description" content="Map Data" />
<meta charset=utf-8 />
<title>SAPUI5 Demo</title>
</head>
<body>
  <div id="appHeader"> </div>
  <div id='map'></div> 
  <div id="listAndPieHolder"> </div>
</body>
</html>


Javascript



*The business data (var deliveryData) is JSON, where a record looks like:
"Index": 1,
"Label":"<b>Location</b><br>920-9402 Pretium Avenue<br><b>Scheduled Delivery:</b>18:30<br><b>Projected Delivery:</b> 18:25<br>32",
"Location":"37.780416,-122.403231",
"Delivery Address":"920-9402 Pretium Avenue",
"Scheduled Delivery Time":"6:30:00 PM",
"Minutes from Now":50,
"Projected Delivery":"6:25:00 PM",
"Minutes  from Projected":5,
"Status":"On Time"
}

     1. Create the map and once it’s ready, feed it the delivery data.



var cMap;
function buildMap(){
                    
//creates the map instance
cMap = new centigon.locationIntelligence.integrations.CMapAnalyticsMobile();
      
/*once the map is ready, configure alerts for the first layer
and set the default layer labels, values and locations*/
cMap.onMapReady = function(){
                          
       var mapLimits = [];
       var mapTargets = [];
       var mapLocations = [];
       var mapLabels = [];
       var mapValues = [];
       var rec;
       for(var d=0;d<deliveryData.length;d++){
       rec = deliveryData[d];
       mapLocations.push(rec.Location);
       mapLabels.push(rec.Label);
       mapLimits.push(rec["Minutes  from Projected"] * Math.random());
mapTargets.push(rec["Minutes  from Projected"] *          Math.floor((Math.random() * 1.5) + .2));
       mapValues.push(rec["Minutes  from Projected"]);
              }            
                                 
cMap.labels([mapLabels]);
cMap.values([mapValues]);
cMap.locations([mapLocations]);
              }
//apply map configuration generated by CMapsAnalytics Designer
              cMap.parseConfigXml(mapCfg);
}

     2. Create the SAP UI5 Table and Pie Chart



// create a simple matrix layout
var oOuterLayout = new sap.ui.commons.layout.MatrixLayout({
                     id : "matrix",
                     columns : 2,
                     widths : [ "60%", "40%" ],
                     layoutFixed : false});
              
//create the UI5 table and table columns         
var oTable = new sap.ui.table.Table({
                     title: "Map Data",
                     visibleRowCount: 25,
                     firstVisibleRow: 3,
                     selectionMode: sap.ui.table.SelectionMode.Single,
                     filter: [updatePieChartAndMap, oTable],
                     rowSelectionChange: panToAndSelectMapIcon,});
              
                       
     oTable.addColumn(new sap.ui.table.Column({
                     label: new sap.ui.commons.Label({text: "Location"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Location", type: new sap.ui.model.type.String()}),
                     width: "100px"
              }));
     oTable.addColumn( new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: "Delivery Address"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Delivery Address", type: new sap.ui.model.type.String()}),
                     width: "100px"
              }));
              
     oTable.addColumn( new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: "Scheduled Delivery Time"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Scheduled Delivery Time", type: new sap.ui.model.type.String()}),
                     width: "100px"
              }));
            
     oTable.addColumn( new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: "Minutes from Now"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Minutes from Now", type: new sap.ui.model.type.Float()}),
                     sortProperty: "Minutes from Now",
                     width: "100px"
              })); 
              
     oTable.addColumn( new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: "Projected Delivery"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Projected  Delivery", type: new sap.ui.model.type.String()}),
                     sortProperty: "Projected  Delivery",
                     width: "100px"
              }));
              
     oTable.addColumn( new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: "Minutes from Projected"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Minutes  from Projected", type: new sap.ui.model.type.Float()}),
                     sortProperty: "Minutes  from Projected",
                     width: "100px"
              }));  
              
     oTable.addColumn( new sap.ui.table.Column({
label: new sap.ui.commons.Label({text: "Status"}),
template: new sap.ui.commons.TextView().bindProperty("text", {path: "Status", type: new sap.ui.model.type.String()}),
                     sortProperty: "Status",
                     filterProperty: "Status",
                     width: "100px"
              }));  
              
//Create a JSON model and bind the table rows to this model
var oModel = new sap.ui.model.json.JSONModel();
     oModel.setData({modelData: deliveryData});
     Table.setModel(oModel);
     oTable.bindRows("/modelData");
//Configure the UI5 Table layout 
var oTableCell = new sap.ui.commons.layout.MatrixLayoutCell({
backgroundDesign : sap.ui.commons.layout.BackgroundDesign.Fill1,
                     rowSpan : 1 });
     oTableCell.addContent(oTable);
//Configure the UI5 Pie Chart
var pieJson = [];
pieJson.push({'status': 'On Time', 'count': 0});          
pieJson.push({'status': 'Deliquent', 'count': 0}); 
pieJson.push({'status': 'At Risk', 'count': 0});              
          
       for (var i = 0; i < deliveryData.length; i++) {
              if (aData[i].Status == 'On Time'){
                     pieJson[0].count = pieJson[0].count + 1;
              }
              else if (aData[i].Status == 'Delinquent'){
                     pieJson[1].count = pieJson[1].count + 1;
              }
              else if (aData[i].Status == 'At Risk'){
                     pieJson[2].count = pieJson[2].count + 1;
              }                
       };
          
var dataset = new sap.viz.ui5.data.FlattenedDataset({
            dimensions : [ {
              axis : 1,
              name : 'Status',
              value : "{status}"
            } ],
            measures : [
{name : 'Count',
              value : { path : 'count', formatter : function($) { return $; }}}
],
            data : {path : "/businessData",factory : function() {}
}});
var pie = new sap.viz.ui5.Pie({id:"pie", width : "100%",dataset : dataset});
pie.setModel(new sap.ui.model.json.JSONModel().setData({businessData: pieJson})); 
           
     oOuterLayout.createRow(oTableCell, pie);
     oOuterLayout.placeAt("listAndPieHolder");  


3. Update the Pie Chart and Map whenever the SAP UI5 Table is filtered



function updatePieChartAndMap(table){
                    
              // Get latest status filter
              var latestFilterValue = table.getParameter('value');
                   
                        // Filter the json
              if (latestFilterValue != ''){
                     var newData = aData.filter(function(a) {
                     return a['Status'] == latestFilterValue });
              }
              else{
                     newData = aData;
              }
                    
              pieJson = [];
             
     pieJson.push({'status': 'On Time', 'count': 0});             
     pieJson.push({'status': 'Deliquent', 'count': 0});    
     pieJson.push({'status': 'At Risk', 'count': 0});                          
// create new pie json
for (var i = 0; i < newData.length; i++) {
              if (newData[i].Status == 'On Time'){
                     pieJson[0].count = pieJson[0].count + 1;
              }
              else if (newData[i].Status == 'Delinquent'){
                     pieJson[1].count = pieJson[1].count + 1;
              }
              else if (newData[i].Status == 'At Risk'){
                     pieJson[2].count = pieJson[2].count + 1;
              }                   
       }     
       oPieModel.setData({businessData: pieJson});                  
    
       //refresh map labels, values and locations
       var mapLocations = [];
       var mapLabels = [];
       var mapValues = [];
       var rec;
       for(var d=0;d<newData.length;d++){
              rec = newData[d];
              mapLocations.push(rec.Location);
              mapLabels.push(rec.Label);
              mapValues.push(rec["Minutes  from Projected"]);
       }
                    
       cMap.labels([mapLabels]);
       cMap.values([mapValues]);
       cMap.locations([mapLocations]);
}

4. Handle table row selection



function panToAndSelectMapIcon (item){
              cMap.selectedItem("1," + item.mParameters.rowIndex + 1);
       }        

   

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. Ryan Goodman Post author

    Interestingly enough someone emailed me, inquiring that they didn’t understand why we would charge money for something that could be accessed and integrated for free.. I asked his permission to reply publicly after providing further explanation offline…

    Unfortunately the Google Maps APIs are not free for enterprise use, and never have been. You will require a Google Maps for Work (Google Maps Enterprise) license to publish content that is not publicly available. All of the cloud map vendors operate this way licensing anything from page views, to transactions, credits, etc…

    For customers who want to use Google Maps with SAP, you can procure a license through Google or get everything you need from us through our bundles. Essentially a CMaps Analytics Bundle will pay for itself in your first application with saved man hours and licensing costs..Bold statement, but something we have proven hundreds of times since we started building premium extensions with Google Maps in 2008.

    Excited to connect with anyone who we can help along the way on the Google Maps front. The SAPUI5 doors have only opened for us but the possibilities out of the gates are limitless 🙂

    (0) 

Leave a Reply