Wednesday, 3 August 2016

Automatic GeoCoding Feature In Salesforce - Summer 16


Summer 16 provided an excellent feature to the force.com platform of automatic adding GeoCodes to the address Data .The Feature is currently available for addresses on existing and new accounts, contacts, and leads and in salesforce classic .

With Location aware Data one can build  very cool visualization and help business achieve its goals .

I have a very interesting business scenario and lets discuss how we are going to solve the use case with this new feature

Business Use Case

Universal Containers is planning to hire sales folks and since they are global they want to hire sales folks in countries where there are maximum no of leads located so that they can reach out in person and demo their products .

Sales VP requested a plot of leads location on the world map so that he can start his hiring strategy .

Also  marketing VP wanted to know areas where there are no leads so he can focus his campaigns in those areas .

Enable Data Clean Feature


Enabling the Automatic Geolocation mapping for address requires you to enable setting a clean rule in data.com settings .Please check below screenshot



 



 Solution

We will use a Javascript plugin to build our map and use salesforce location aware lead data to plot as noticeable points on the map

There are many Javascript plugins that can provide you ability to create maps .For this blogpost I will be using AMCharts  

AmCharts has very cool features and with simple Javascript you can get a working app .

The Finished Solution looks like below
  
                        
    The data is fed back via apex controller logic . We will create a Wrapper class so that its easier to point this map to a different object later ,if needed 

 


public with sharing class DataWrapper {
public integer zoomLevel {get;set;}
public decimal scale {get;set;}
public string title {get;set;}
public decimal latitude {get;set;}
public decimal longitude {get;set;}
public string url {get;set;}
public DataWrapper (integer zoomLevel,decimal scale,
string title,decimal latitude,decimal longitude
,string url){
this.zoomLevel = zoomLevel;
this.scale = scale;
this.title = title;
this.latitude = latitude;
this.longitude = longitude;
this.url = url;
}
}
view raw DataWrapper.cls hosted with ❤ by GitHub
<apex:page showHeader="false" sidebar="false" applyHtmlTag="false" controller="LeadHeatMapController">
<html>
<head>
<style>
#chartdiv {
width : 100%;
height : 500px;
}
.map-marker {
/* adjusting for the marker dimensions
so that it is centered on coordinates */
margin-left: -8px;
margin-top: -8px;
}
.map-marker.map-clickable {
cursor: pointer;
}
.pulse {
width: 10px;
height: 10px;
border: 5px solid #f7f14c;
-webkit-border-radius: 30px;
-moz-border-radius: 30px;
border-radius: 30px;
background-color: #716f42;
z-index: 10;
position: absolute;
}
.map-marker .dot {
border: 10px solid #fff601;
background: transparent;
-webkit-border-radius: 60px;
-moz-border-radius: 60px;
border-radius: 60px;
height: 50px;
width: 50px;
-webkit-animation: pulse 3s ease-out;
-moz-animation: pulse 3s ease-out;
animation: pulse 3s ease-out;
-webkit-animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
animation-iteration-count: infinite;
position: absolute;
top: -25px;
left: -25px;
z-index: 1;
opacity: 0;
}
@-moz-keyframes pulse {
0% {
-moz-transform: scale(0);
opacity: 0.0;
}
25% {
-moz-transform: scale(0);
opacity: 0.1;
}
50% {
-moz-transform: scale(0.1);
opacity: 0.3;
}
75% {
-moz-transform: scale(0.5);
opacity: 0.5;
}
100% {
-moz-transform: scale(1);
opacity: 0.0;
}
}
@-webkit-keyframes "pulse" {
0% {
-webkit-transform: scale(0);
opacity: 0.0;
}
25% {
-webkit-transform: scale(0);
opacity: 0.1;
}
50% {
-webkit-transform: scale(0.1);
opacity: 0.3;
}
75% {
-webkit-transform: scale(0.5);
opacity: 0.5;
}
100% {
-webkit-transform: scale(1);
opacity: 0.0;
}
}
</style>
</head>
<apex:includeScript value="{!URLFOR($Resource.amMaps,'amMaps/ammap.js')}"/>
<apex:includeScript value="{!URLFOR($Resource.amMaps,'amMaps/worldLow.js')}"/>
<body>
<div id="chartdiv" ></div>
</body>
<script>
var images = [];
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.LeadHeatMapController.getLeadData}',
handleResult
);
function handleResult(result, event) {
if(event.status){
for(var i=0;i<result.length;i++){
images.push(result[i]);
}
window.map = AmCharts.makeChart("chartdiv", {
type: "map",
"theme": "none",
"projection":"miller",
path: "http://www.amcharts.com/lib/3/",
"backgroundColor" : "#13564e",
imagesSettings: {
rollOverColor: "#089282",
rollOverScale: 3,
selectedScale: 3,
selectedColor: "#089282",
color: "#13564e"
},
dataProvider: {
map: "worldLow",
images: images
},
listeners: [ {
"event": "drawn",
"method": updateCustomMarkers
}],
areasSettings: {
"autoZoom": true,
unlistedAreasColor: "#15A892",
selectedColor: "#CC0000"
},
});
// add events to recalculate map position when the map is moved or zoomed
map.addListener("positionChanged", updateCustomMarkers);
}else{
alert(event.message);
}
}
// this function will take current images on the map and create HTML elements for them
function updateCustomMarkers (event) {
// get map object
var map = event.chart;
// go through all of the images
for( var x in map.dataProvider.images) {
// get MapImage object
var image = map.dataProvider.images[x];
// check if it has corresponding HTML element
if ('undefined' == typeof image.externalElement)
image.externalElement = createCustomMarker(image);
// reposition the element accoridng to coordinates
var xy = map.coordinatesToStageXY(image.longitude, image.latitude);
image.externalElement.style.top = xy.y + 'px';
image.externalElement.style.left = xy.x + 'px';
}
}
// this function creates and returns a new marker element
function createCustomMarker(image) {
// create holder
var holder = document.createElement('div');
holder.className = 'map-marker';
holder.title = image.title;
holder.style.position = 'absolute';
// maybe add a link to it?
if (undefined != image.url) {
holder.onclick = function() {
window.location.href = image.url;
};
holder.className += ' map-clickable';
}
// create dot
var dot = document.createElement('div');
dot.className = 'dot';
holder.appendChild(dot);
// create pulse
var pulse = document.createElement('div');
pulse.className = 'pulse';
holder.appendChild(pulse);
// append the marker to the map container
image.chart.chartDiv.appendChild(holder);
return holder;
}
</script>
</html>
</apex:page>
public with sharing class LeadHeatMapController {
@RemoteAction
public static list<DataWrapper> getLeadData(){
list<DataWrapper> lstdataWrapper = new list<DataWrapper>();
for(Lead l : [Select Id,
Name,
Latitude,
Longitude
FROM Lead
WHERE Latitude != null
AND Longitude != null
order by CreatedDate DESC limit 100]){
DataWrapper dataWrap = new DataWrapper(5,0.5,l.Name,l.Latitude,l.Longitude,'/'+l.Id);
lstdataWrapper.add(dataWrap);
}
return lstdataWrapper;
}
}
Conclusion


Clearly looks like currently  leads are located at South Easter region of United States and VP of sales needs Sales Rep in this region .Also looks like marketing needs lot of work to focus other territories apart from US .

Location aware data can clearly help your business .If you are using salesforce platform ask your VP and C level executives if visualizing data on maps help them to achieve their goals then with this feature your developers can build some cool stuff .

Introducing Lightning Base Components

Lightning Base Components are great addition to the platform and in fact revolutionary .One of the concerns around lightning component ...