Try Now
More in this section
Blogs RSS feed

Updating the Store Locator Widget with the GeoLocation API

by Chris Eargle

When customers visit your store locations page from any mobile device, they’re at a critical juncture in their buyer decision process. Without a store locator, you could lose their business. Courtney Wilson described how to create a store locator back in March, and we have now built some of that functionality into Sitefinity 6.1’s GeoLocation API.

I am going to update Courtney’s control to give the API a test run. However, I first need to look at what functionality is available.

GeoLocation API


Represents geolocation data in Sitefinity.




Gets or sets a guid representing the content item with which this geolocation is associated.


Gets or sets a string representing the content type of the content item with which this geolocation is associated.




Gets the guid identifying this geolocation instance.


Gets or sets the latitude.


Gets or sets the longitude.


Gets or sets the data provider name.

Telerik.Sitefinity.GeoLocations.Model.IGeoLocationDistance : IDataItem

Represents the distance between two geolocation points.




A double representing the distance between two geolocation points.


Provides the ability to get, update, and delete geolocations. It is provided by the SystemManager.

IGeoLocationService geoLocationService = SystemManager.GetGeoLocationService();




Deletes a geolocation specified by its id.


Returns a geolocation based matching the arguments.


Return geolocations using by a center point and radius. Optional arguments provide sorting and further filtering.


Updates a geolocation.


Contains criteria used to filter geolocations.




Gets or sets a string representing the content type associated with the geolocation data. Using this will only retrieve content of that type.




Gets or sets the data provider name.


The default constructor.


Use this enumeration to specify how to sort geolocation data in relation to another geolocation point.




Sort with the closest first, farthest away last.


Sort with the farthest away first, closest last.


Provides additional functionality for filtering and sorting geolocation.

var manager = DynamicModuleManager.GetManager() as IGeoLocationManager;




Filters an item query by geolocation data based on radius and other criteria.


Sorts a list of geolocations based upon distance from another

Creating the Content Type

Sitefinity 6.0 included a new content type for Address, and there’s one thing you need to do to take full advantage of it.

Go to the Settings menu in Administration then click Google Maps on the left side. Follow the instructions to obtain a Google Maps v3 API Key, and enter it into the field. After saving, address fields will automatically populate the geolocation based on the provided address or map selection.

The only fields needed for this demo is Title and Address, and I recommend removing all fields covered by Address. Additionally, the Distance field is no longer necessary and may even cause confusion. I named the module Stores instead of StoreLocator.


The original Ecommerce Store Locator source code is located on GitHub. Download it and install it according to the instructions. Take note that the name of the control is now StoreLocatorCustom.

Since the control accesses fields that are no longer present, it’s not in working condition. To clean it up, I’m going to start at BindStores() in StoreLocatorCustom.ascx.cs.

var manager = DynamicModuleManager.GetManager();
Type storeType = 
var stores = manager.GetDataItems(storeType)
					.Where(s => s.Status == ContentLifecycleStatus.Live);
Figure 1.

The lines in figure 1 are pretty much the same.

var radius = double.Parse(ddlDistance.SelectedValue);
var userLocation = GetCoordinate(txtSourceZip.Text.Trim())
var itemFilter = new ItemFilter { ContentType = storeType.ToString()};
IEnumerable<IGeoLocation> geolocations;
Figure 2.

The next four lines are variables for GeoLocationManager methods. I retained the GetCoordinate method since I’m not removing the Google Maps control. I defined the geolocations variable, but left it uninitialized since I use it as an out parameter in the following line.

stores = (manager as IGeoLocationManager).FilterByGeoLocation(stores, userLocation.Latitude, userLocation.Longitude, radius, out geolocations, itemFilter: itemFilter);
var sortedStores = (manager as IGeoLocationManager).SortByDistance(stores, geolocations, 
	userLocation.Latitude, userLocation.Longitude, DistanceSorting.Asc);
Figure 3.

The code in figure 3 does most of the work. First, I am filtering by geolocation to ensure only the stores within the selected radius are returned. Then I sort them. I removed the method that measures distance since it’s built into the SortByDistance method.

DynamicContent firstStore = sortedStores.FirstOrDefault();
if (firstStore != null)
    var address = firstStore.GetAddressFields().First().Value;
    litDefaultLat.Text = String.Format("{0}", address.Latitude);
    litDefaultLong.Text = String.Format("{0}", address.Longitude);
Figure 4.

I made modified the code in Figure 4 to use the address field for the latitidude and longitude. Dynamic content can have multiple address fields, and GetAddressFields returns a dictionary. If you're working with multiple address fields, be sure to specify the name in your ItemFilter and in the indexer for GetAddressFields.

listStores.DataSource = sortedStores;
lblStoreCount.Text = sortedStores.Count().ToString();
Figure 5.

Figure 5 is identical to the old version.

User Interface

I will work lightly on the user interface, simply updating the fields in the ItemTemplate section to support the new Address type.

<div style="padding: 10px 10px 10px 10px;">
    <b><a href='javascript:showMap(<%# Eval("Address.Latitude")%>, 
		<%# Eval("Address.Longitude")%>)'><%# Eval("Title")%></a></b>
    <br />
    <%# Eval("Address.Street")%>
    <br />
    <%# Eval("Address.City")%>, <%# Eval("Address.StateCode")%> <%# Eval("Address.Zip")%>
    <br />
    <span style='display:<%# Eval("Distance").ToString() == "0.00" ? "none" : "block"%>'>
	Distance: <%# Math.Truncate((double)Eval("Distance")) %> miles</span>
Figure 6.

I suggested removing the Distance field from your content type earlier, so why did I leave it in the user interface?

Dynamic content is constructed with the IGeoLocationDistance interface, so you no longer need to store ephemeral data with real content.


I easily eliminated many lines of code by using the built-in API, and its usage was straightforward. The GeoLocation API works with any type containing an address field.

For more, visit our webinars page to sign up for the Creating Better UX with the Sitefinity Geolocation API Webinar.


Leave a comment
  1. Mark Jul 19, 2013
    Chris, do you mind posting the 3 files again? I am not getting a map to be returned (at least on localhost). When I search I get results, but no map.
  2. Chris Eargle Jul 19, 2013
    Hey Mark, I left an important piece out for showing the map. I added it to the article. Basically, to get the map to show upon load, you must set the initial lat and long. I checked in my files for you here: https://github.com/chriseargle/StoreLocatorWidget
  3. Sean Molam Mar 07, 2014

    Hi Chris,

     There is actually a problem with the call you make in Figure 3.  If you do it this way the results don't get sorted properly and the more you hit this function the more random the results that get returned are.

    What did work correctly for us was the following modification:

     var storesList = (manager as IGeoLocationManager).FilterByGeoLocation(stores, userLocation.Latitude, userLocation.Longitude, radius, out geolocations, itemFilter: itemFilter).ToList();

    storesList = (manager as IGeoLocationManager).SortByDistance(storesList , geolocations,
    userLocation.Latitude, userLocation.Longitude, DistanceSorting.Asc);

     It appears that by passing IQueryable into SortByDistance behaves very oddly indeed.


    Leave a comment