Microsoft Dynamics 365 Blog

I’d like to introduce Marco Amoedo Martinez, a CRM MVP from Spain and our guest blogger today.

Mapping technologies can be very useful for CRM tools. We can position customers on a map and obtain valuable information about where they are, about where we have more market share, and so on.

Microsoft provides several mapping technologies that could help us add these functionalities to our CRM. We have MapPoint, MapPoint Web services and Live Maps. While the first ones have a license cost or service fee, because they are more powerful and have more value-add functionalities, the last one is free and “simple” to use. So, Live Maps looks like a good starting point for adding maps to our CRM.

A good place to start using maps could be the account form. We will try to add a new tab with a map indicating us the location of our customer. To achieve this we will use an IFrame based customization of Microsoft Dynamics CRM.

Step One: Creating a Web page with the map

The first step is creating a new Web page with all the things needed in order to show a map using Live Maps: importing the jscript control of Live Maps, setting the div tag which will contain the map, and adding jscript code to load the map. The html looks like this:


<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

<html>

<head>

<title></title>

<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″/>

<script type=”text/jscript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=5″ mce_src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=5″></script>

<script type=”text/jscript”>

var map = null;

function GetMap()

{

map = new VEMap(‘myMap’);

map.LoadMap();

}

</script>

</head>

<body onload=”GetMap();”>

<div id=’myMap’ style=”position:relative; width:400px; height:400px;”></div>

</body>

</html>


More detailed information on the Live Maps SDK.

This Web page will return a generic map, not a map showing up our customer location which is our goal. So, suppose that we have a variable with the customer address. Now we can center the map on the customer address using functions provided by the map. The script code will look like this.


var map = null;

function GetMap()

{

var address = “Emilio Gonzalez Lopez, 15011 A Coruña (A Coruña), Spain”;

map = new VEMap(‘myMap’);

map.LoadMap();

map.Find(null,address);

}


Now we have a map centered on customer’s address. But this map doesn’t reflect the exact customer location. Therefore, we will add some more code to place a pushpin in the map over the customer location using an overload of the Find function. This overload will make a call to other function when the find operation completes, and this new function will add the pushpin to the map based on the results of the find call.


var map = null;

function GetMap()

{

var address = “Emilio Gonzalez Lopez, 15011 A Coruña (A Coruña), Spain”;

map = new VEMap(‘myMap’);

map.LoadMap();

map.Find(null,address,null,null,0,10,true,true,true,true,FindCallBack)

}

function FindCallBack(shapeLayer, results, positions, moreResults, e)

{

if(positions != null && positions.length > 0)

{

latlong = new VELatLong(positions[0].LatLong.Latitude,positions[0].LatLong.Longitude);

customerPushPin = new VEShape(VEShapeType.Pushpin,latlong);

map.AddShape(customerPushPin);

}

}



Well, we now have a map that shows up the customer location based on the customer address. It would be fine if we could store the latitude and longitude of customer position instead of always try to find it using the address. As we do before, suppose that we have a latitude and longitude variables for storing customer position. Now, the jscript code will check if we already have customer’s latitude and longitude, and place the pushpin, or if it has to find them using customer’s address as we do previously. Also, we will add some code to store the latitude and longitude if we did not already have them. The script now looks like this:


var map = null;

var latitude = 43.37490;

var longitude = -8.43178;

var address = “Emilio Gonzalez Lopez, 15011 A Coruña (A Coruña), Spain”;

function GetMap()

{

map = new VEMap(‘myMap’);

map.LoadMap();

if (latitude != null && longitude != null)

{

PlacePushPin(latitude,longitude);

}

else

{

map.Find(null,address,null,null,0,10,true,true,true,true,FindCallBack);

}

}

function FindCallBack(shapeLayer, results, positions, moreResults, e)

{

if(positions != null && positions.length > 0)

{

PlacePushPin(positions[0].LatLong.Latitude,positions[0].LatLong.Longitude);

latitude = positions[0].LatLong.Latitude;

longitude = positions[0].LatLong.Longitude;

}

}

function PlacePushPin(lat, lon)

{

latlong = new VELatLong(lat,lon);

customerPushPin = new VEShape(VEShapeType.Pushpin,latlong);

map.AddShape(customerPushPin);

map.SetCenterAndZoom(latlong,15);

}


Now we have a web page that can show up the location of a customer based on his address or his latitude and longitude. We could add more functionality to this page, like for instance to allow the user to move the pushpin, give driving directions, etc. But before doing this lets proceed to integrate this Web page into Microsoft Dynamics CRM 3.0.

Step Two: Integrate Maps on CRM

As I said before, we will add the Web page to CRM’s customer form using an IFrame. But before doing this, we have to “wire” the script of the map with the CRM data. It’s quite a simple work; we only have to get/set values from/to CRM fields using jscript for latitude, longitude and address (see the CRM SDK page about this). The code will look like this:


var map = null;

var latitude = null;

var longitude = null;

var address = “”;

function GetMap()

{

// Try to get lat&long from the CRM

latitude = parent.document.forms[0].all.address1_latitude.DataValue;

longitude = parent.document.forms[0].all.address1_longitude.DataValue;

//Get adress from CRM

var addressLine1 = parent.document.forms[0].all.address1_line1.DataValue;

var postalCode = parent.document.forms[0].all.address1_postalcode.DataValue;

var city = parent.document.forms[0].all.address1_city.DataValue;

var stateOrProvince = parent.document.forms[0].all.address1_stateorprovince.DataValue;

var country = parent.document.forms[0].all.address1_country.DataValue;

//Compose customer address

address = addressLine1+”, “+postalCode+” “+city+” (“+stateOrProvince+”), “+country;

map = new VEMap(‘myMap’);

map.LoadMap();

if (latitude != null && longitude != null)

{

PlacePushPin(latitude,longitude);

}

else

{

map.Find(null,address,null,null,0,10,true,true,true,true,FindCallBack);

}

}

function FindCallBack(shapeLayer, results, positions, moreResults, e)

{

if(positions != null && positions.length > 0)

{

PlacePushPin(positions[0].LatLong.Latitude,positions[0].LatLong.Longitude);

latitude = positions[0].LatLong.Latitude;

longitude = positions[0].LatLong.Longitude;

parent.document.forms[0].all.address1_latitude.DataValue = latitude;

parent.document.forms[0].all.address1_longitude.DataValue = longitude;

}

}

function PlacePushPin(lat, lon)

{

latlong = new VELatLong(lat,lon);

customerPushPin = new VEShape(VEShapeType.Pushpin,latlong);

map.AddShape(customerPushPin);

map.SetCenterAndZoom(latlong,15);

}


Notice that the GetMap method now retrieves the value of CRM fields for address, latitude and longitude. Also, in the FindCallBack method we update CRM fields for storing latitude and longitude.

Before continuing, we must place the Web page file in a virtual directory within the IIS web server that contains CRM. The best option is to create a new virtual directory under CRM’s site and place the Web page on it (i. e.: crm.plainconcepts.com/livemaps/), since adding it directly to CRM’s Web site is not supported.

Now we have the Web page ready to be integrated with CRM. So let’s go customize the account form with the IFrame and fields to storing latitude and longitude.


  • Open your CRM web client.

  • Go to Settings | Customization | Customize entities and open account entity.

  • Select Forms and Views, select the entity form and open it.

  • Add a new tab, click on “Add a Tab” at the right side. Set a name (i. e.: Map) and click Ok.

  • Create a new Section, click on “Add a Section” at the right side. Set a name (i. e.: Customer’s Location), in the tab option select the new tab and press Ok.

  • Next, we have to add address1_latitude and address1_longitude to the form (these system fields are intended to store latitude and longitude). Click “Add fields”, select the fields, and select the tab and the section that you have just created.

  • Now we have to add an IFrame, select “Add an IFrame”. On the dialog enter the name of the IFrame, select the tab and the section that you have just created and uncheck the option to restrict cross-frame scripting. On the URL field, type the location of the Web that we have created to show maps (i. e.: crm.plainconcepts.com/livemaps/AccountMap.htm).

  • At the Formatting tab set the options that you want for the appearance of the IFrame.

  • At the Dependencies tab select the account fields used on the jscript code: address1_line1, address1_city, address1_postalcode, address1_stateorprovince, address1_country, address1_latitude and address1_longitude.

  • Save and close the form, and then publish the customizations (select Actions -> Publish).


Only one more customization is left for our solution to works correctly. The attributes address1_latitude and addres1_longitude must be modified to accept the value range used for storing coordinates at Live Maps (Latitude: -90 to 90, Longitude: -180 to 180).


  • Go to the account customization form select attributes, and open the attribute address1_latitude.

  • Change the precision to 5 and the range value from -90 to 90.

  • Repeat the same for address1_longitude but with the range value from -180 to 180.

A precision of 5 is not very accurate. Live maps works with 14 decimals (a double precision floating point), but for our purpose is sufficient since we will lose only some meters. This lost of precision could be mitigated if we make some kind of transformation between CRM and Live Map data.

Conclusion

As we have shown here, adding a map to the CRM is not so complex. The possibilities of this kind of integration are enormous and it could add a great value to our CRM. I leave you a more complete example of this integration with Live Maps. This example also supports moving pushpins and giving driving directions, and it could be illustrative on how to customize live maps. I hope it be useful to you.



Marco Amoedo Martinez


Some answers to the comments below:


——-


Mike:  Make sure that your are using map.find() method passing the correct address as an argument. Also, try to put the same address string in maps.live.com and see what happens. Virtual Earth is still in enhancement for address outside the US, in Spain there are also some address that are not find correctly.


Serge: I am trying to figure what is failing but I cannot find the problem. Give more details of the error, and I will try to help you. About showing several accounts on the same map, you could achieve this using a multi-selection button on account’s grid. Take a look to this example on the SDK http://msdn2.microsoft.com/en-us/library/bb267367.aspx


Andrew: The Web Page is the responsible for getting the data from CRM Form when is loaded. If you give a look to the Onload event of the HTML page showed on the IFrame you will see that there is some jscript code that retrieves the data from the CRM Form.


Paul:  There is a line of Jscript code on the HTML page that composes the string of the address to search on the map. Try to modify this line to only use the postal code and the country.

We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!