Monday, October 11, 2010

Live visitor tracking with Sitecore OMS and Bing Maps

Real-time information delivery is the one of the most important elements of our online experience. When information being available at the moment it's produced, you can act really quickly and efficiently. That is why the words "live", "instant", "real-time" are so popular in modern web applications.
There are lot of fun apps that show people's tweets, photo galleries on the map, but how about getting similar(and useful) app for your own website?
If it's build on Sitecore, it's incredibly easy. You just need to create a UI and a service that will retrieve your data.

In this post I'll show how to create application that will show Web Forms submissions on your website using Bing Maps.


Here is the expected flow:
  1. User submits a web form
  2. Custom form action adds form fields and Sitecore OMS data to the queue 
  3. Web page retrieves the data through the web-service and adds it to the map

As a result, we'll get a map with users and submitted form data:


Let's start!

First, we need to define a data structure for our map point. Basically, we need a latitude / longitude and submitted data.

Here's the code with some additional properties(to be explained later)

public class LocationData
{
        public string Latitude;

        public string Longtitude;

        public string Description;

        public string Title;

        public int Id;

        public string IconPath;

        public LocationData(string latitude, string longtitude, string title, string description, int id, string iconPath)
        {
            Latitude = latitude;
            Longtitude = longtitude;
            Description = description;
            Id = id;
            IconPath = iconPath;
            Title = title;
        }
}

I've configured icons for all sample forms

And added a save action that will add data about each form submission to the queue. We get current user session and then IP info using AnalyticsManager, build path to the icon and pass this data(all fields) to the VisitorService.

public class AddToMap : ISaveAction
    {
        static int counter = 0;

        public virtual void Execute(ID formid, AdaptedResultList fields, object[] data)
        {
            var sessionId = AnalyticsTracker.Current.CurrentSession.SessionId;
            var ip = AnalyticsManager.GetIPBySessionId(sessionId);

            var formItem = Sitecore.Data.Database.GetDatabase("master").GetItem(formid);
            string iconPath = "/~/icon/" + formItem.Fields["__Icon"];
            var description = "<ul>";

            foreach (AdaptedControlResult result in fields)
            {
                description += "<li>" + result.Value + "</li>";
            }

            description += "</ul>";

            VisitorService.LocationsQueue.Enqueue(new LocationData(ip.Latitude, ip.Longitude, formItem.Name, description, ++counter, iconPath));
        }
 

I use Ajax WCF web service in this example, as jQuery can send request and parse JSON reponse from it out of the box, with a few lines of code. Here's the service code:

[ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class VisitorService
    {
        public static LimitedQueue LocationsQueue = new LimitedQueue(10);
       
        [OperationContract]
        [WebInvoke(Method = "GET",
                   BodyStyle = WebMessageBodyStyle.WrappedRequest,
                   ResponseFormat = WebMessageFormat.Json
        )]
        public LocationData[] GetLatestVisitors(string lastUpdated)
        {
            return Enumerable.ToArray(LocationsQueue);
        }
    }

As you can see, queue size is set to 10, you can tweak it to find the number of points suitable for you.

The latest part is the hardest one, we need some JavaScript code to add our data points to the Bing maps.
The next two methods initialize the map and add data points to it.

var map = null;
        var layer = new VEShapeLayer();

        function GetMap() {
            map = new VEMap('myMap');
            map.SetCredentials("Your Bing Maps Key");

            map.LoadMap();
            map.AddShapeLayer(layer);
            map.SetZoomLevel(2);
        }

        function AddPushpin(latitude, longtitude, id, iconPath, title, description) {
            var shape = new VEShape(VEShapeType.Pushpin, new VELatLong(latitude, longtitude));

            shape.SetTitle("<img src='" + iconPath + "' style='width:16px'></img> " + title);
            shape.Id = id;
            shape.SetDescription(description);
            shape.SetCustomIcon("<img src='" + iconPath + "' style='width:16px'></img>");
            layer.AddShape(shape);
        }

And the next one will retrieve the data and parse response. Also, it will manage which points should remain on the map and which one replaced with new ones.

window.setInterval(function (data) {
            $.getJSON('VisitorService.svc/GetLatestVisitors', function (data) {
                for (var i = 0; i < layer.GetShapeCount(); i++) {
                    var deleteShape = true;
                    shape = layer.GetShapeByIndex(i);
                    jQuery.each(data.d, function (i, app) {
                        if (app.Id == shape.Id) {
                            deleteShape = false;
                        }
                    });

                    if (deleteShape) {
                        layer.DeleteShape(shape);
                    }
                }

                jQuery.each(data.d, function (i, app) {
                    var shape = layer.GetShapeByIndex(i);

                    if (shape == null) {
                        AddPushpin(app.Latitude, app.Longtitude, app.Id, app.IconPath, app.Title, app.Description);
                    }
                });
            });
        }, 1000);    

Now, let's add our custom action to the sample forms and submit few of them


 and then see what we've got:


we can now take a  really close look at our visitor


It's just a simple example of how OMS data can used, you can visualize anything with it: new visitors, traffic sources, etc. Also, it's possible to use Google maps instead of Bing, you just need to update several lines of JavaScript code.
If you have any interesting ideas about using Sitecore OMS data - post them in comments, there is a chance I'll implement one of them in the next blog post :)