Appendix B: Implementing geolocation

patterns & practices Developer Center

On this page: Download:
Geolocation in HTML5 - The API in detail | Browsers with no geolocation support | Online services: Microsoft Bing Maps | Geolocation in the real world: Finding a nearby gas station in Mileage Stats Download code samples

Geolocation has been become a fundamental feature in many apps over the last few years.

Many of the devices on the market today have been fitted with GPS hardware that can produce reliable geographical data. In addition, the HTML5 specification has recently included geolocation as one of the main features, which means any web browser supporting that specification will be able to retrieve geographic positioning information with the use of JavaScript.

Many online services and apps (such as Microsoft Bing Maps) provide valuable use of such geolocation information. Displaying the user location on a map or providing directions are some of the more useful services these capabilities can enable.

Geolocation in HTML5

HTML5 introduced a new set of JavaScript APIs that can be used to determine the user's location. There are multiple techniques that a device may use to determine this data and provide it to the browser. These include the use of GPS, IP address, and cell tower triangulation.

The actual technique used by the device is hidden from the app consuming the API. Not knowing how the location information was obtained might not be a problem, but it's important for developers to understand that the accuracy of the information might vary considerably, depending on the technique used.

Another important aspect of the geolocation API is that it runs completely asynchronously on the browser. Any attempt to access geolocation data will typically result in a dialogue being presented to the user asking them for their permission to allow the geolocation lookup. You must handle the case in which the user does not grant permission. If permission is granted by the user, the API will return the data via a callback when the operation is complete.

The API in detail

Any web browser with support for geolocation will provide access to the intrinsic global object navigator available in the windows object. The privacy settings on the browser for disabling geolocation will not affect the availability of this object.

We will start by discussing the first and most important method available in this object, getCurrentPosition.

void getCurrentPosition(successCallback, errorCallback, options);

The getCurrentPosition method asynchronously attempts to acquire a new Position object. If successful, it will invoke the success callback by passing the Position object as an argument. If the attempt fails, it will try to invoke the error callback by passing an object with the error details. If the user intentionally disabled geolocation in the browser privacy settings, the error callback will be invoked as well. The following example illustrates how this method is called by a script block in the browser.

function get_geolocation() {  
  navigator.geolocation.getCurrentPosition(function(position) {
     alert('Lat: ' + position.coords.latitude + ' ' + 'Lon: ' + position.coords.latitude);
  });  
}  

The code above does not handle any error condition that might happen while trying to get the user's location. The error callback must be used in that case for addressing the error.

function get_geolocation() {  
  navigator.geolocation.getCurrentPosition(function(position) {
     alert('Lat: ' + position.coords.latitude + ' ' + 'Lon: ' + position.coords.latitude);
  }, function(error) {
    switch(error.code)  
    {  
      case error.PERMISSION_DENIED: 
        alert("user did not share geolocation data");  
        break;  
      case error.POSITION_UNAVAILABLE: 
        alert("could not detect current position");  
        break;  
      case error.TIMEOUT: 
        alert("retrieving position timed out");  
        break;  
      default: 
        alert("unknown error");  
        break;  
    }  
  });  
}  

As part of the position object returned by the getCurrentPosition method call, two pieces of information are available, the geographic coordinates and a timestamp. The timestamp simply denotes the time at which the instance of geolocation data was created.

The geographic coordinates also include their associated accuracy, as well as a set of other optional attributes, such as altitude and speed.

The other two available methods, watchPosition and clearPosition, work pretty much together. As the name states, the watchPosition method asynchronously starts watching the device location and invoking a callback with the new position. This allows the app to get real-time data about the device location. On the other hand, the clearPosition method stops acquiring any new position fixes and invoking any callbacks.

long watchPosition(success, error, options);
void clearWatch(long watchId);

The app can create multiple watchers, which are correlated and identified by the number returned when the function is invoked. The same identifier can also be used as the argument for destroying that watcher with the clearWatch method.

Browsers with no geolocation support

You might run into scenarios in which geolocation is not available as a native feature on the device you are targeting. In those cases, the obvious question concerns finding the best possible solution to provide a similar user experience.

An external geolocation service is a well-known solution to this problem. These services typically map the IP address of a device to geographic locations using geolocation databases. Usually they do a good job, but at times they may suffer from the following issues:

  • The IP address cannot be mapped to any record in the database, so the location cannot be determined. This typically happens for IP addresses not commonly used on the Internet.
  • The IP address is associated with a very large geographic area such as a big city or state, which means the exact location in terms of latitude and longitude, cannot be determined.
  • The IP address is associated with a wrong location.

The rule of thumb in this scenario is that an external geolocation service is not as reliable as the native geolocation support in the device, and in some cases it might be completely inaccurate. In addition, these services do not provide additional information such as altitude, speed, or heading of the device. Nevertheless, IP address-based location is still a good solution when GPS or triangulation are not supported.

Please note that this may raise some privacy concerns if the geolocation capability was intentionally disabled by the user, so it is always a good idea to get the user’s consent before performing any location-aware operation.

Online services: Microsoft Bing Maps

Bing Maps provides a set of online services for integrating rich geographic capabilities into any existing web app. In a nutshell, it enables users to search, discover, explore, plan, and share information about specific locations.

The Bing Maps platform includes a map control for web-based apps as well as a set of REST and SOAP-based services for incorporating both location and local search features into a web app.

The REST and SOAP services differ in the way they are consumed and in some of the functionality they provide. Some of the features they provide include:

  • A locations API for finding a location based on a point, address, or query. This service basically matches addresses, places, and geographic entities to latitude and longitude coordinates on the map, and return location information for a specified latitude and longitude set of coordinates. The SOAP API also provides a way to search for points of interests, which is not available in the REST version, and can be used for example to search all the nearby gas stations.
  • An imagery API for getting a static map which might contain a route, or for getting imagery metadata.
  • A routes API for finding a walking, driving, or transit route, or more specific information about routes (such as the route for going to a specific location).
  • A traffic API for getting traffic information for a geographical area. (Only available in the REST API)

The following code illustrates how the SOAP service for searching locations can be used to find any nearby gas station.

public IEnumerable<string> SearchKeywordLocation(string keyword, double latitude, double longitude)
{
    var results = new List<string>();

    var searchRequest = new SearchRequest();

    // Set the credentials using a valid Bing Maps key
    searchRequest.Credentials = new SearchService.Credentials();
    searchRequest.Credentials.ApplicationId = GetBingMapsApplicationKey();

    //Create the search query
    var ssQuery = new StructuredSearchQuery();
    ssQuery.Keyword = keyword;
    ssQuery.Location = string.Format("{0}, {1}", latitude, longitude);
    searchRequest.StructuredQuery = ssQuery;

    //Make the search request 
    SearchResponse searchResponse;
    using (var searchService = new SearchServiceClient("BasicHttpBinding_ISearchService"))
    {
        searchResponse = searchService.Search(searchRequest);
    }

    foreach (var searchResult in searchResponse.ResultSets[0].Results)
    {
        results.Add(string.Format("{0} ({1})", searchResult.Name, searchResult.Distance));
    }
    return results;
}

Both the SOAP and REST services require an application key for authenticating the caller application. A new application key can be obtained by following the instructions on Getting a Bing Maps Key on MSDN.

Geolocation in the real world: Finding a nearby gas station in Mileage Stats

The Mileage Stats app uses geolocation for finding all nearby gas stations based on the device location.

In terms of implementation, this feature combines the geolocation API available in the browser for determining the device location and an online service like Bing Maps for finding the nearest gas stations. The Bing Maps REST service did not provide an API for locating points of interest, so we used the SOAP service instead.

Both the REST and the SOAP services require the use of an API key for authentication. The API key should be kept secret. If you embed this key in the JavaScript code, it will become visible to anyone.

In Mileage Stats, the functionality for consuming the Bing Maps API is encapsulated on the server side, and exposed to the browser through a façade using REST services. In that way, the key does not need to be exposed on the client side. Another advantage is that the REST service can hide many of the complexities involved in dealing with the Bing Map APIs or even make it possible to use the SOAP services on the server side as well.

A specific controller, GeoLocationController, was created for exposing this façade to the browser.

public class GeoLocationController : Controller
{
    private readonly IMapService mapService;

    public GeoLocationController(IMapService mapService)
    {
        this.mapService = mapService;
    }

    public JsonResult GetFillupStations(double latitude, double longitude)
    {
        return Json(this.mapService.SearchKeywordLocation("Gas Stations", latitude, longitude), JsonRequestBehavior.AllowGet);
    }        
}

GetFillupStations is an action that can be invoked on the browser by using regular Ajax calls. This method receives the device latitude and longitude (which can be obtained from the geolocation API), and returns a JSON message with the expected response.

IMapService is another abstraction on top of Bing Maps that hides many of the implementation details for simplifying unit testing and allowing a possible replacement by another online service in the future.

The MileageStats app is configured to use a MockMapService that provides sample data. If you wish to use the BingMapService instead, you must update the UnityContainerFactory.cs class to register the BingMapService instead of the MockMapService.

…
public IUnityContainer CreateConfiguredContainer()
{
    var container = new UnityContainer();

    // other registrations omitted for brevity

    // remove the following line:
    // RegisterPerRequest<IMapService, MockMapService>(container);
    // and uncomment this line:
    RegisterPerRequest<IMapService, BingMapService>(container);

    return container;
}
…

In addition, you will need to provide a valid Bing Maps key in the web.config appSettings “BingMapsApplicationKey”.

Next Topic | Previous Topic | Home | Community

Last built: June 5, 2012