Plotting yourself on the map

Using interactive maps to find places and routes has become a common task for web and mobile users: online maps have doubtless changed the way people travel. This article presents the Geolocation API and explores using web map services such as OpenStreetMap and Google Maps to build a geolocator web application.

Mapping technologies

There are plenty of online maps available these days. To get an idea of the global and local map services available, have a look at this list. This article will focus on the most used one (Google Maps) and on the most open one (OpenStreetMap).

OpenStreetMap

OpenStreetMap is a open and free service, built by a community of mappers that contribute and maintain data about roads and much more all over the world. OpenStreetMap provides dynamic and interactive maps to be included in any web page. OpenLayers is the tool for displaying OpenStreetMap data in most modern web browsers, and it has no server-side dependencies. OpenLayers implements a Javascript API for building rich web-based geographic applications.

Google Maps

Google Maps is the most popular online map service, which also offers a JavaScript API for building rich web-based geographic applications. A notable difference is that OpenStreetMap/OpenLayers are free Software, developed for and by the open source software community, while Google Maps is a proprietary solution, provided and owned by Google.

Geolocation API

The Geolocation API is a W3C Recommendation on which both OpenStreepMap and Google Maps rely to access geographical information associated with the hosting device. The Geolocation API is very well explained at a basic level in our Using Geolocation article.

Demo: Geo

Geo is a geolocation web application. It lets users choose among OpenStreetMap, Google Maps, and a hybrid map that is a Google Map with OpenStreetMap tiles placed on it.

This is a screenshot taken in Firefox:

The red marker is the current position or a search result, while green markers represent interesting places around, randomly positioned for demo purposes.

Geo uses the Geolocation API to get the user's current position and provides UI controls to manipulate various options (enableHighAccuracy, timeout, maximumAge). The demo also provides a search tool that uses the Google Searchbox in the case of Google and Hybrid map searches, and pure XmlHttpRequests to Nominatim in the case of OpenStreetMap searches.

Note that OpenStreetMap data is free for everyone to use, but OpenStreetMap tile servers are not: heavy use of OpenStreetMap tiles is forbidden without prior permission from OpenStreetMap's System Administrators: please read the Tile Usage Policy and this article on blog.openstreetmap.org if you want to distribute an app that gets use of tiles from openstreetmap.org

Some HTML & CSS to start

Google Maps, OpenStreetMap and Geolocation are Javascript APIs, so showing the HTML and CSS might be considered out of scope here, however it’s important to understand the elements composing the page on which JavaScript operates. Feel free to skip to the JavaScript and Geolocation section if you are only interested in the JavaScript layer.

Below we will briefly examine the main HTML elements of the web app:

Action Box

Maps

Action box

The action box consists of tabs, a geolocation button, geolocation options and a search box:

Tabs

Using the tabs you can switch between different kind of maps and search engines:

sensor indicates whether the web app uses a sensor (such as a GPS sensor) to determine the user's location. It is set to false because in this case Geo retrieves the user's location through the Geolocation API.

libraries. When loading the JavaScript Maps API via the URL you may optionally load additional libraries. Geo loads places for the search features.

Switching among maps

The MapSwitcher is responsible for the tabs status and interaction. It essentially creates a GeoMap to the window object based on the target element specified in the URL (#openstreetmap is the default) , calls methods of SearchBox to switch between search engines and uses the GeolocationManager to get the current position and show an interactive map.

Calling OpenLayers.Layer.OSM() with no arguments means that the layer enables accessing OpenStreetMap tiles. Other layers can be specified: OpenCycleMap for example. Check the documentation for further details.

In GoogleMap the initMap method uses the Google Maps library to create a new google.maps.Map based on options passed as parameter, and center the map to the default position.

In HybridMap the initMap method uses the Google Maps library to create a new google.maps.Map based on options in which it’s specified to use OSM tiles instead of Google tiles, to keep using Google controls but turning off StreetView.

/*
* showPosition
* Show the specified position on the map
* @param {Position} position
*/
showPosition: function(position) {
/* Retrieve longitude and latitude from Position */
var plon = position.coords.longitude;
var plat = position.coords.latitude;
/* Calculate the OpenStreetMap position */
var osmPosition = new OpenLayers.LonLat(plon, plat).transform(this.fromProjection, this.toProjection);
/* Set the center of the map */
this.map.setCenter(osmPosition, this.defaultZoom);
if (this.currentPosition === null) { // if this is the first time this method is invoked
/* Add a marker to the center */
this.markers.addMarker(new OpenLayers.Marker(osmPosition));
/* Show POIs only the first time this method is called */
this.showPOIs(new OpenLayers.LonLat(plon, plat));
/* Keep track of the current position */
this.currentPosition = osmPosition;
}
}

/*
* showPosition
* Show the specified position on the map
* @param {Position} position
*/
showPosition: function(position) {
Map.prototype.showPosition.call(this, position);
/* Retrieve latitude and longitude from Position */
var plat = position.coords.latitude;
var plon = position.coords.longitude;
/* Calculate the Google Maps position */
var gmPosition = new google.maps.LatLng(plat, plon);
/* Set the center of the map */
this.map.setCenter(gmPosition);
if (this.currentPosition === null) { // if this is the first time this method is invoked
/* Add a marker to the center */
new google.maps.Marker({
position: gmPosition,
map: this.map,
title: 'Current position'
});
/* Show POIs only the first time this method is called */
this.showPOIs(gmPosition);
/* Keep track of the current position */
this.currentPosition = gmPosition;
}
}

The HybridMap relies completely on the the superclass method.

The handleGeolocationErrors method

The handleGeolocationErrors method of each subclass relies on the superclass implementation.

The GeolocationManager is called to retrieve the user position: a callback function, that is the Map’s showPosition method, is passed in as a parameter to get it called if the device position is successfully retrieved.

For demo purposes you can try the following use cases:

Leave the enableHighAccuracy box checked, set the timeout to 8000ms and maximumAge to 0ms, and click the currentPositionButton.

Uncheck the enableHighAccuracy box and click the currentPositionButton: this way the web app should be faster in retrieving the device position but might be less precise.

Set maximumAge to a value greater than zero milliseconds: if a cached position not older than maximumAge is available, the current position will be retrieved from the cache and you should notice an improvement in terms of performance.

Set the timeout and maximumumAge values to zero or less: you should experiance a time out error message.

Retrieving current position

The GeolocationManager is the object that uses the Geolocation API to get the current position of the device, through the getCurrentPosition method. It takes 3 arguments:

A successCallback, set to GeoMap.showMap by the MapSwitcher, or to GeoMap.showPosition by the GeolocationBox.

An errorCallback, set to GeoMap.handleGeolocationErrors.

options, a PositionOptions object built by the GeolocationBox and explained in the previous section.

Note that, for security reasons, when a web page tries to access location information the user is notified and asked to grant permission:

If the user allows access to their location info, the successCallback is invoked passing a Position object providing coords and timestamp. If the user denies access to their location info, the errorCallback is invoked passing an PositionError object providing an id and a message, such as "user denied Geolocation" or "timeout expired".

If the Geolocation API is not supported by the browser (i.e. navigator.geolocation is undefined), the errorCallback is called with a customized PositionError object as its argument.

Regarding the Geolocation API, this article explained how the navigator.geolocation.getCurrentPosition() can be applied in some detail. The navigator.geolocation object provides a further method however — watchPosition() — that we didn't discuss here. watchPosition() is pretty much the same as getCurrentPosition(), including signature, with the difference that the callback functions are called automatically each time the position of the device changes. Have a look at this MDN page to learn more about how to use this method.