Creating a Custom CMaps Analytics Plugin and Property Sheet

This article is for developers who would like to embed a configurable map component inside of another development environment (IDE) or cloud app. The goal in a custom extension is to grant end users with the ability to configure data or basic visualization properties for one or multiple layers.

Need help? Don’t hesitate to contact our team for additional guidance, training, or code examples.

Overview

extension3

The goal of this workflow is to utilize CMaps Analytics Designer as the primary tool for creating maps experience (learn about CMaps Designer). However, in your application / reporting tool we will create a simple property sheet for data binding which is the most common scenario.

CMaps Analytics Designer is a cloud solution that proides hundreds of configurable map visualizaiton and analysis properties. The output of CMaps Analytics Designer is an XML configuration file. Learn More

CMaps Configuration File (XML) Inside of this configuration file is all of the properties needed to generate your map. With only a couple of lines of code you can embed and initialize your map inside of an application.

Your Platform is presumed to have an SDK or APIs that allow you to create custom extensions or property sheets, where your end users or developers can quickly define data binding properties.

CMaps JS API should be loaded in your custom extension’s property sheet, and of course when you load your complete map view to render the map. Calling the CMaps Analytics JS API is as simple as entering a URL and appending your authentication key.

Property Sheet Basics

[ezcol_1half]

In most modern web and IDE tools, you have a property sheet to configure how a component will behave, where it will retrieve data, and other properties that are important for the end user to modify without having to re-import the CMaps Configuration File. The bare minimum properties that you will want in your solution are the following:

CMaps Analytics Designer XML

Somewhere in your property sheet you will need to load the CMaps Analytics Configuration File (XML) into the property sheet and component. Generally the XML is loaded as a URL or embedded via copy/paste. This XML file contains hundreds of properties that indicate the behaviors, and appearance of the map.

In future versions of CMaps Designer we will offer webhooks so you can stream XML files directly to your web application. Please contact support to learn more and ensure our webhook approach will meet your needs.

[/ezcol_1half] [ezcol_1half_end]

propertysheet

In this particular example for SAP BusinessObjects Webi, there is no opportunity to automate the import of XML, so the property sheet was configured for the end user to copy/paste the XML into an input text box. This input box is bound to the XML Configuration property. Additionally, the property sheet communicates with an API that allows a user to click on bind to select and map a specific column to each map property. All of the properties are stored inside of a native API inside of SAP BusinessObjects.

[/ezcol_1half_end]

 

Data Properties

It is presumed that the reason you have decided to create a CMaps Analytics extension vs a single map view is the ability to grant non-CMaps Analytics Designer users wit the ability to modify data or  basic map visualization properties.

There are two common scenarios:

  1. Your end user will chose what data fields go to what properties through a property sheet interface (see example above)
  2. You already have defined metadata for the map data properties to bind locations, labels, values, etc. Regardless, you need a mechanism to bind arrays to the following properties:
  • Address / Location (required)
  • Label (required)
  • Value (required)
  • Color (optional)- Color could come from the Designer XML. If you provide color in the data it will over-ride the configuration XML
  • Drill IDs (Optional) -This is only needed if you have more than 1 layer and want to drill between layers

Learn more about all CMaps Analytics Data Properties

Persisting your Property Sheet Properties

It is presumed that as you define your properties, the application framework will already have a mechanism to save and reload your properties for future editing, and that the properties will be available to the map component.

Step 1 Loading the CMaps Configuration File (XML)

The first step in creating your map component is loading the XML Configuration file and then initializing the map. This is the foundation for your map in addition to the CMaps Analytics JS API. Generally in a custom extension there is one of 2 ways to load the XML. Either through an AJAX call to a URL, or copy and pasting the XML into a property sheet. In either case you need to set “mapCfg” similar the example below.

Simple 1 Layer XML Configuration

<script type="text/javascript">
var mapCfg = '<mapView createdBy="name:cMapsAnalyticsDesigner;version:1.2" createdOn="1/28/2015"><viewName>My View</viewName><viewDescription></viewDescription><tags></tags><key></key><googlePremierKey></googlePremierKey><dynamicZoomEnabled>true</dynamicZoomEnabled>...</mapView>';
</script>

var cMap;

function buildMap(){

//creates the map instance

cMap = new centigon.locationIntelligence.MapView();

cMap.onMapReady = function(){console.log('map is ready');}

//applies the config xml

cMap.parseConfigXml(mapCfg);

};

</script>

 

 

Step 2 Setting a Key

Depending on your license for CMaps Analytics will likely influence how you apply keys to CMaps Anlytics extension. All requests for CMaps Analytics require a valid authentication key which is provided by Centigon Solutions. An API key can be loaded through the XML configuration.

Free Google Maps API: CMaps Analytics Designer does not support using free Google Maps API keys. If you are building a free, public facing app please contact Centigon Solutions support.

Using an Existing Google Maps for Work Key: CMaps Analytics supports existing Google Maps for Work licenses keys which may have been purchased as a part of an OEM license from Google. Where to insert my Google Maps for Work Key

If you need to manually set a key through the property sheet, here is the JavaScript Function:

cMap.key("yourkey");

Step 3 Bind Data

When creating a plugin, the Map behaves like any other data visualization like a bubble chart. As such, the goal of creating a CMaps Plugin component for your IDE or cloud application is to re-use data connectivity and security from your existing platform.

The following sample code demonstrates how you would take each column as an array and feed it into a map layer.

propertysheet

When we get data from the application framework via (via javascript function, Ajax request, etc), the data needs to be parsed and formatted as arrays. Each property for the map is a two dimensional array. If we have a one layer map with points, here is an example of how the data would be formatted:


var locations =[["37.7693911,-122.41750","37.7480245,-122.4208806","37.7512649,-122.4201445","37.789675,-122.417049","37.7565985,-122.4045723","37.7956765,-122.4092976","37.7630855,-122.4578733","37.7739893,-122.4544912","37.7473074,-122.4592076","37.780416,-122.403231"]];

var labels = [["one","two","three","four","five","six","seven","eight","nine","ten"]];

var values = [[20,30,40,50,60,70,80,90,100,110]];

var colors = [["#0000ff","#c0c0c0","#ff0000","#124568","#f05264","#00ff00"]];

var defaultLayerColors = ["#FFFF00"];

Then with these variables defined as two dimensional arrays, you can feed it directly into the map layer properties like this:



function buildMap(){

//creates the map instance
cMap = new centigon.locationIntelligence.MapView();

cMap.onMapReady = function(){
}
cMap.onLayersReady = function(){
cMap.locations(locations);
cMap.labels(labels);
cMap.values(values);
cMap.colors(colors);
cMap.defaultLayerColors(defaultLayerColors);
}

//applies the config xml
cMap.parseConfigXml(mapCfg);
};

Below is the complete code for this simple example if you would like to test.. Note, that you will need a valid key for this to work:



<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<script src="http://api.cmapsanalytics.net/mapview?version=1.2&authkey=ENTER_YOUR_AUTH_KEY"></script>
<!--END JS LIBS-->

<script type="text/javascript">

var mapCfg = '<mapView createdBy="name:cMapsAnalyticsDesigner;version:1.2" createdOn="1/28/2015"><viewName>Single Points Layer View</viewName><viewDescription></viewDescription><tags></tags><key>ENTER_YOUR_AUTH_KEY</key><googlePremierKey></googlePremierKey><dynamicZoomEnabled>true</dynamicZoomEnabled><panCoords></panCoords><zoomLevel></zoomLevel><dynamicZoomOnShapeSelect>false</dynamicZoomOnShapeSelect><dynamicZoomOnClusterSelect>false</dynamicZoomOnClusterSelect><dynamicZoomOnPointSelect>true</dynamicZoomOnPointSelect><showBreadcrumbsOnDrill>true</showBreadcrumbsOnDrill><dynamicZoomOnDrill>true</dynamicZoomOnDrill><multiSelectEnabled>false</multiSelectEnabled><adhocPanelVisibility>false</adhocPanelVisibility><radiusMeasureUnit>km</radiusMeasureUnit><indoorEnabled>true</indoorEnabled><sslEnabled>false</sslEnabled><infoWindowFontFamily>Arial</infoWindowFontFamily><infoWindowFontSize>12</infoWindowFontSize><infoWindowFontColor>000000</infoWindowFontColor><infoWindowFontAlign>left</infoWindowFontAlign><infoWindowUseBoldFont>false</infoWindowUseBoldFont><infoWindowFontUnderlineEnabled>false</infoWindowFontUnderlineEnabled><infWinItalicFontEnabled>false</infWinItalicFontEnabled><infoWindowVisibility>true</infoWindowVisibility><infoWindowValueVisibility>true</infoWindowValueVisibility><infoWindowLayerNameVisibility>true</infoWindowLayerNameVisibility><useDynamicSizing>false</useDynamicSizing><clusterMarkersEnabled>false</clusterMarkersEnabled><glowVisibility>true</glowVisibility><glowColor>0000ff</glowColor><baseMapImageryStyleType>styleTypeRoadmap</baseMapImageryStyleType><showTrafficLayer>false</showTrafficLayer><showTransitLayer>false</showTransitLayer><showBikelayer>false</showBikelayer><legendVisibility>false</legendVisibility><navigationControlVisibility>true</navigationControlVisibility><mapReady>true</mapReady><layerSelected>false</layerSelected><zoomChanged>false</zoomChanged><centerChanged>false</centerChanged><reverseGeocodeResponse>false</reverseGeocodeResponse><latLngToReverseGeocode></latLngToReverseGeocode><geocodeResponse>false</geocodeResponse><addressToGeocode></addressToGeocode><visible>true</visible><indoorMapVisibility>false</indoorMapVisibility><showPinsForIndoorMapLocations>false</showPinsForIndoorMapLocations><directionPanelVisibility>false</directionPanelVisibility><directionWaypoints></directionWaypoints><currentLocation>false</currentLocation><useFreeApi>false</useFreeApi><layers><layer><name>My Layer</name><description></description><type>points</type><indoorMapUrl></indoorMapUrl><shapeFileType>customUrl</shapeFileType><shapeFileUrl></shapeFileUrl><dataKeysUrl></dataKeysUrl><dataSourceType>none</dataSourceType><dataSource><![CDATA[]]></dataSource><dataSourceColumnOrder>dsLocationColumn,dsLabelColumn,dsValueColumn,dsColorColumn,dsCategoryColumn,dsDrillIdColumn</dataSourceColumnOrder><transparency>100</transparency><enabled>true</enabled><iconSize>20</iconSize><iconType>pin</iconType><useLogScale>false</useLogScale><clusterStyle>count</clusterStyle><visibility>true</visibility><drillLevel></drillLevel><drillEventTrigger>layerClicked</drillEventTrigger><drillParent>none</drillParent><useCurrentLocationAsStartPoint>false</useCurrentLocationAsStartPoint><travelMode>Driving</travelMode><travelUnits>IMPERIAL</travelUnits><useCacheDriveTimePolyService>on</useCacheDriveTimePolyService><driveTimePolyMarkerLayerBindings></driveTimePolyMarkerLayerBindings><wmsURL></wmsURL><wmsStyleString></wmsStyleString><wmsLayerString></wmsLayerString><layerColor>1b8dce</layerColor><alerts><alertIcons>pin,pin</alertIcons><alertType>type-none</alertType><alertValues>50,100</alertValues><numAlertLevels>2</numAlertLevels><alertHeatmapVals>50,100</alertHeatmapVals><alertDefinition>high-values-good</alertDefinition><alertColors>c0c0c0,008800</alertColors></alerts></layer></layers></mapView>';

var cMap;

var locations = [["37.7693911,-122.41750","37.7480245,-122.4208806","37.7512649,-122.4201445","37.789675,-122.417049","37.7565985,-122.4045723",
"37.7956765,-122.4092976","37.7630855,-122.4578733","37.7739893,-122.4544912","37.7473074,-122.4592076","37.780416,-122.403231"]];
var labels = [["one","two","three","four","five","six","seven","eight","nine","ten"]];
var values = [[20,30,40,50,60,70,80,90,100,110]];
var colors = [["#0000ff","#c0c0c0","#ff0000","#124568","#f05264","#00ff00"]];
var defaultLayerColors = ["#FFFF00"];

function buildMap(){

//creates the map instance

cMap = new centigon.locationIntelligence.MapView();

cMap.onMapReady = function(){
}

cMap.onLayersReady = function(){
cMap.locations(locations);
cMap.labels(labels);
cMap.values(values);
cMap.colors(colors);
cMap.defaultLayerColors(defaultLayerColors);
}

//applies the config xml
cMap.parseConfigXml(mapCfg);

};

</script>

</head>

<body onload="buildMap();">

<div id="mapDiv" style="height:400px;width:400px;position:absolute;top:0px;left:0px"></div>

</body>