Posts Tagged ‘Virtual Earth’

Dynamics CRM 4.0 + Virtual Earth

I was asked by a colleague from work to integrate Virtual Earth into CRM 4.0 for a pre-sales client Demo.

The integration points are the Contact and the Service Activity entities. I’ll go through these two cases and list out the steps how I achieved them in a reasonably short period of time.

Contact – Pushpin an address

It’s required when the address tab is clicked on in the Contact main form, a virtual earth map is displayed within the same tab with a pushpin pining at the address.

1. Create a web site project to host the Virtual Earth maps.

I used Visual Studio 2008, because it allows JavaScript debugging and I’ll need to write some code behind logic for the second example.

image

2. Code up some JavaScript against the Virtual Earth API on this page to load an address.

This is the fun part – interacting with Virtual Earth API in JavaScript. And guess what? Life only gets better, the Virtual Earth folks have put together a demo site, it presents us with most common cases on how one would make use of Virtual Earth AND it provides us with the source code!!! All that I needed to do is, find the most suitable case on the demo site and copy paste the JavaScript code and tweak it from there. 🙂

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Location.aspx.cs" Inherits="Location" %>

   2: 

   3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

   4:                         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

   5: <html xmlns="http://www.w3.org/1999/xhtml">

   6: <head><title></title>

   7:     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

   8:     <script type="text/jscript"

   9:             src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=5"></script>
   1: 

   2:     <script type="text/jscript">

   3: 

   4: var map = null;

   5: var latitude = null;

   6: var longitude = null;

   7: var address = "";

   8: // Find address through virtual earth

   9: function GetMap()

  10: {

  11:     address = '51 Failkirk Ave, Seatoun, Wellington, New Zealand';

  12:     map = new VEMap('myMap');

  13:     map.LoadMap();

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

  15: }

  16: 

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

  18: {

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

  20:     {

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

  22:         latitude = positions[0].LatLong.Latitude;

  23:         longitude = positions[0].LatLong.Longitude;

  24:     }

  25: }

  26: 

  27: function PlacePushPin(lat, lon)

  28: {

  29:     latlong = new VELatLong(lat,lon);

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

  31:     customerPushPin.SetTitle(address);

  32:     map.AddShape(customerPushPin);

  33:     map.SetCenterAndZoom(latlong,15);

  34: }

  35:

</script>

  10: 

  11: </head>

  12: <body onload="GetMap();">

  13:     <div id='myMap' style="position: relative; width: 100%; height: 100%"></div>

  14: </body>

  15: </html>

Note: on line 19,

address = ’51 Failkirk Ave, Seatoun, Wellington, New Zealand’;

I’ve hard coded this address to load in the map. Then we can dynamically read the address fields from the contact form to be loaded on the map.

3. Host this Website in IIS where your CRM installation resides.

 Remember to change it to ASP.NET 2 runtime as it defaults to 1.1.

image

Then view the Location.aspx page in a web browser.

image

4. IFrame this page in Contact entity.

In CRM, Settings -> Customization -> Customize Entities -> Contact -> Forms and Views -> Form.

First add a Tab called Address then move the Address Section from General Tab to Address Tab. Finally, add an IFrame to this new section. Make sure that the “Restrict cross-frame scripting” is NOT checked, as we’ll need to access the address fields from inside of our IFRAME.

image

To test that the addition of our IFRAME worked, simply load a read-only from Preview option in Contact form.

image

You should see a map loaded with the hard coded address.

image

5. Last step, is to read the address fields from the Contact form, Address Tab and pass it on to the map to load.

   1: function GetMap()

   2: {

   3:     //Get adress from CRM

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

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

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

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

   8: 

   9:     //Compose customer address

  10:     address = addressLine1 + ", " + city + ", " + stateOrProvince + ", " + country;

  11:     //address = '51 Failkirk Ave, Seatoun, Wellington, New Zealand';

  12:     map = new VEMap('myMap');

  13:     map.LoadMap();

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

  15: }

Now, publish Contact entity and test it with an contact that has the address fields set.

image

There you go, pretty cool huh! 🙂

Service Activity – Routing from A to B

This is to work out the routes from point A to B and return the mileage of this route for reimbursement purposes. The address of the owner of the Service Activity is taken as a source address and the address of the Customer (a Contact in this case) is the destination address. Again, Virtual Earth Dev Demo site provides us with most of the source code here.

The steps are easy to follow,

1. Create a new aspx page in the same web site we just created from the Contact example in IIS, call it Routes.aspx (Or any other name you might fancy).

2. Grab some code from this demo site, hard code a source and destination addresses on the page at this stage before involving CRM at all.

3. Serve this page up in IIS to test it out.

image

4. Add this page to Service Activity as an IFrame. It’s your choice where on the form to add it, a new section on a new tab, a new section on an existing tab, doesn’t matter.

Make sure that you check the query string option and uncheck the cross-frame scripting option.

image

5. Right, we are almost there apart from the map only returns a route from two hard coded points on the page. So, we need to get source and destination addresses from CRM for this Service Activity record. This is the part that’s different from the Contact example. For Contact, the IFrame lives on the same page as the address fields that the map needs to address. So we could do with JavaScript on the page to directly read the address off the fields and feed it through Virtual Earth to serve up an addressed map in the IFrame. However for Service Activity, we don’t have all the address information on the form, so we’ll need to code up some code behind to retrieve the address via CRM WebService and feed that to Virtual Earth to work out a route for us.

   1: protected void Page_Load(object sender, EventArgs e)
   2: {
   3:     // Read the Service Activity ID from query string
   4:     String saId = this.Request.QueryString["id"];
   5:         
   6:     if (!String.IsNullOrEmpty(saId))
   7:     {
   8:         // Retrieve Service Activity instance from CRM
   9:         Guid serviceAppointmentId = new Guid(saId);
  10:         CrmAuthenticationToken token = new CrmAuthenticationToken();
  11:         token.OrganizationName = "MicrosoftCRM";
  12:         CrmService.CrmService crmService = new CrmService.CrmService();
  13:         crmService.CrmAuthenticationTokenValue = token;
  14:         crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
  15:         serviceappointment apmt = crmService.Retrieve("serviceappointment", serviceAppointmentId, new AllColumns()) as CrmService.serviceappointment;
  16:  
  17:         // Get the address for the record owner and assign it to a label on the page
  18:         systemuser user = crmService.Retrieve("systemuser", apmt.ownerid.Value, new AllColumns()) as CrmService.systemuser;
  19:         string userAddress = user.address1_line1 + ", " + user.address1_city + ", " + user.address1_stateorprovince + ", " + user.address1_country;
  20:         this.lblSource.InnerHtml = userAddress;
  21:  
  22:         // Get the address for the record customer and assign it to a label on the page
  23:         contact contact = crmService.Retrieve("contact", apmt.customers[0].partyid.Value, new AllColumns()) as CrmService.contact;
  24:         string contactAddress = contact.address1_line1 + ", " + contact.address1_city + ", " + contact.address1_stateorprovince + ", " + contact.address1_country;
  25:         this.lblDestination.InnerHtml = contactAddress;
  26:     }
  27: }
  28:  

6. Use preview form to test it, publish it.

image

There you go, away laughing 🙂

If you want a copy of the source code, here is a link.

Thanks to Microsoft Live SkyDrive.

Advertisements