MaxMind is great for tracking visitors – It gives you all kind of information based on the IP address.
Today when more and more users/visitors are using handheld devices with GPS for browsing the internet, I wanted to get the actual location for the visitor using HTML5 Geolocation API
Note: Since it can compromise user privacy, the position is not available unless the user approves it.
In order to get the location/coordinates, following javascript is used (window.coordinates is a global variable):
if (navigator)
navigator.geolocation.getCurrentPosition(geoSuccess, geoError);
function geoSuccess(p) {
window.coordinates = p.coords;
}
function geoError(data) {
//Do some error handling
}
I added the geolocation to my previous post, Client side tracking with Sitecore DMS.
var Sandbox = Sandbox || {};
jQuery(document).ready(function () {
Sandbox.Alert.DomReady();
});
Sandbox.Alert = {
DomReady: function () {
Sandbox.Alert.InitSandbox();
jQuery("#closeAlert").click(function () {
Sandbox.Alert.TrackCloseEvent();
});
},
InitSandbox: function () {
if (navigator)
navigator.geolocation.getCurrentPosition(geoSuccess, geoError);
function geoSuccess(p) {
window.coordinates = p.coords;
}
function geoError(data) {
//Some error handling
}
},
TrackCloseEvent: function () {
var dataContainer = jQuery(".alert alert-info fade in");
var requestParamAndValues = {};
requestParamAndValues["PageEventId"] = dataContainer.data("goal");
requestParamAndValues["PageUrl"] = dataContainer.data("pageurl");
if (window.coordinates)
requestParamAndValues["Coordinates"] = Sandbox.Alert.StringFormat("{0},{1}", window.coordinates.latitude, window.coordinates.longitude, '1');
var jsonObject = {};
jsonObject["requestParamAndValues"] = requestParamAndValues;
var analyticsEvent = new AnalyticsPageEvent(jsonObject);
analyticsEvent.trigger();
},
StringFormat : function () {
var s = arguments[0];
for (var i = 0; i < arguments.length - 1; i++) {
var reg = new RegExp("\\{" + i + "\\}", "gm");
s = s.replace(reg, arguments[i + 1]);
}
return s;
}
};
In the aspx page, ClientEventTracker, we need to add some lines of code (TrackerService):
namespace Sandbox.Tracking.Presentation
{
public partial class ClientEventTracker : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
Context.Response.ContentType = "application/javascript";
TriggerEvent(this.Context);
}
private static void TriggerEvent(HttpContext context)
{
string jsonData = context.Request["jsonData"];
InputData inputData = ConvertJsonToObjectService.Convert<InputData>(jsonData);
if (!inputData.ContainsParamkey(InputDataKeys.PageUrl))
return;
if (!inputData.ContainsParamkey(InputDataKeys.PageEventId))
return;
Tracker.StartTracking();
Tracker.CurrentPage.Cancel();
TrackerService.SetCurrentVisitorCoordinates(inputData.GetValueByKey(InputDataKeys.Coordinates));
TrackerService.RegisterEventToAPage(inputData.GetValueByKey(InputDataKeys.PageEventId), inputData.GetValueByKey(InputDataKeys.PageUrl));
Tracker.Submit();
}
}
}
TrackerService is defined here:
namespace Sandbox.Tracking.Infrastructure
{
public class TrackerService
{
public static void RegisterEventToAPage(string eventId, string url)
{
if (string.IsNullOrWhiteSpace(eventId))
return;
if (string.IsNullOrWhiteSpace(url))
return;
if (!Tracker.IsActive)
return;
PageEventItem pageEvent = new PageEventItem(Sandbox.Framework.SitecoreEnhancements.SitecoreItemRepository.Get(eventId));
VisitorDataSet.PagesRow pagesRow = Tracker.CurrentVisit.GetPages().LastOrDefault(p => p.Url.Contains(url));
if (pagesRow == null)
return;
pagesRow.Register(pageEvent);
}
public static void SetCurrentVisitorCoordinates(string coordinates)
{
GeoCoordinate? geoCoordinate = GeoCoordinateRepository.Get(coordinates);
if (!geoCoordinate.HasValue)
return;
if (!Tracker.IsActive)
return;
Tracker.CurrentVisit.Latitude = geoCoordinate.Value.Latitude;
Tracker.CurrentVisit.Longitude = geoCoordinate.Value.Longitude;
}
}
}
GeoCoordinate object:
namespace Sandbox.Tracking.Model
{
public struct GeoCoordinate
{
private readonly double _latitude;
private readonly double _longitude;
public double Latitude { get { return _latitude; } }
public double Longitude { get { return _longitude; } }
public GeoCoordinate(double latitude, double longitude)
{
_latitude = latitude;
_longitude = longitude;
}
public override string ToString()
{
return string.Format("{0},{1}", Latitude, Longitude);
}
public override bool Equals(Object other)
{
return other is GeoCoordinate && Equals((GeoCoordinate)other);
}
public bool Equals(GeoCoordinate other)
{
return Math.Abs(Latitude - other.Latitude) < double.Epsilon && Math.Abs(Longitude - other.Longitude) < double.Epsilon;
}
public override int GetHashCode()
{
return Latitude.GetHashCode() ^ Longitude.GetHashCode();
}
}
}
GeoCoordinateRepository:
namespace Sandbox.Tracking.Model.Repositories
{
public class GeoCoordinateRepository
{
public static GeoCoordinate? Get(string latitude, string longitude)
{
if (string.IsNullOrWhiteSpace(latitude) || string.IsNullOrWhiteSpace(longitude))
return null;
string coordinates = string.Concat(latitude, ",", longitude);
return GeoCoordinateFactory.Create(coordinates);
}
public static GeoCoordinate? Get(string coordinates)
{
if (string.IsNullOrWhiteSpace(coordinates))
return null;
return GeoCoordinateFactory.Create(coordinates);
}
public static IEnumerable<GeoCoordinate?> Get(IEnumerable<string> manyCoordinates)
{
return manyCoordinates.Select(Get);
}
}
}
Finally the GeoCoordinateFactory:
namespace Sandbox.Tracking.Model.Factories
{
internal class GeoCoordinateFactory
{
internal static GeoCoordinate? Create(string coordinates)
{
string[] coordinateArray = coordinates.Split(',');
if (coordinateArray.Length == 0)
return null;
if (string.IsNullOrWhiteSpace(coordinateArray[0]) || string.IsNullOrWhiteSpace(coordinateArray[1]))
return null;
return new GeoCoordinate(double.Parse(coordinateArray[0], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture), double.Parse(coordinateArray[1], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture));
}
}
}
That’s all for now folks 🙂
Very cool!
LikeLike
Thanks I really appreciate it
LikeLike
Client Tracker is now released on Sitecore Marketplace 🙂 Check out blog post https://visionsincode.wordpress.com/2014/04/10/client-tracker-with-sitecore-dms
LikeLike
Nice Article. Do you think this could be moved into a Standard Sitecore package, and released on the Marketplace?
LikeLike
Thank you. I haven’t thought about it but yes it’s possible. It’s a really good idea. I will work with it and update you how it’s developing.
LikeLike
It is now released on Sitecore Marketplace 🙂 Check out blog post https://visionsincode.wordpress.com/2014/04/10/client-tracker-with-sitecore-dms
LikeLike
Thank you David(@angrymrt), for fixing my code 🙂
LikeLike