javascript

Using JavaScript's Geolocation API

Using JavaScript's Geolocation API

The location of people and devices is handy if you want to display local weather, show a map, get directions, or even tweak advertisements by region. These examples of GeoLocation are just the tip of the iceberg, but you get the idea - location, location, location. Web browsers and javascript started supporting geolocation back in 2009 (Firefox v3.5). This happened with very little fanfare since very few people owned smartphones, and tracking user location wasn’t big business (yet).

Today, we assume our phone knows where we are, where we are going, where the device is, and if we are near them. Sounds complicated, but we are just tapping into an API that can ask the device for location details. With GNSS chips (AKA “GPS”) in all our phones, how easy is it to tap into this location? With JavaScript’s GeoLocation API, it’s easier than you might think.

JavaScript GeoLocation API

The JavaScript Geolocation API ’provides access to geographical location information (lat/long) associated with the hosting deviceW3C. This enables web apps to request location data - whether it be your phone, laptop, or another smart device. Even without a GNSS/GPS chip, device location is still possible via cellular triangulation, network IP, etc. The accuracy will vary and is impacted by this and a few other factors. Thankfully, we don’t need to worry about those details, we just ask the GeoLocation API for the location and the device will take care of the rest - if permission is granted.

The GeoLocation API provides a few different ways to access this information:

  • getCurrentPosition: Get the device’s current location
  • watchPosition: A handler function that is called automatically when the location of the device changes

There are three arguments with both approaches:

  1. A success callback (required)
  2. An error callback (optional)
  3. An Options object (optional)

A Simple Request for Location

Call the getCurrentPosition once, we can use: navigator.geolocation.getCurrentPosition(successCallback, errorCallback);. If the browser supports ES6, you can always use the arrow approach too:

navigator.geolocation.getCurrentPosition((pos) => {
  myFunction(pos.coords.latitude, pos.coords.longitude);
});

By default, the API answers as quickly as possible with a low accuracy result. Great if you just want to know the general area or city. If you want a more accurate result you also need to supply the Options object and enable high accuracy.

Extra options object

The options object is optional but has some important settings worth reviewing. From MDN:

  • maximumAge: A positive long value indicating the maximum age in milliseconds of a possible cached position that is acceptable to return. If set to 0, it means that the device cannot use a cached position and must attempt to retrieve the real current position. If set to Infinity the device must return a cached position regardless of its age. Default: 0.
  • timeout: A positive long value representing the maximum length of time (in milliseconds) the device is allowed to take to return a position. The default value is Infinity, meaning that getCurrentPosition() won’t return until the position is available.
  • enableHighAccuracy: A boolean value that indicates the application would like to receive the best possible results. If true and if the device can provide a more accurate position, it will do so. Note that this can result in slower response times or increased power consumption (with a GPS chip on a mobile device for example). On the other hand, if false, the device can take the liberty to save resources by responding more quickly and/or using less power. Default: false.

We can add the options by sending them with our request:

var geoOps = {
	enableHighAccuracy: true,
	timeout: 10000 //10 seconds
}
navigator.geolocation.getCurrentPosition(successCallback, errorCallback, geoOps);

Geolocation callback

With our call complete, we now wait for our results in the success callback where we can use the location to enhance the user experience.

function successCallback(pos) {
    var lat = pos.coords.latitude;
    var lng = pos.coords.longitude;
    alert("Latitude : " + lat + " Longitude: " + lng);
}

That is pretty much it. This extremely useful API can be used with your app, such as: showing location on a map, tracking, directions, or grabbing some weather data.

GeoLocation Limitations

Geolocation functionality is available in all major browsers (desktop and mobile), including IE - yeah, crazy I know. But just because it’s supported, doesn’t mean our code will always bring back a valid location. Here are a few main reasons why we might not get what we are looking for:

  1. The Geolocation API will only work on secure contexts such as HTTPS. If your site is hosted on a non-secure origin (such as HTTP) the requests to get the users’ location will no longer function in most modern browser versions.
    • An exception to the HTTPS rule is local file:// testing which still works.
  2. The Geolocation API might be blocked by the user/settings. In most desktop/mobile operating systems users can block location services from being exposed to the browser. In addition, the user will be prompted to provide permission for an individual website as per W3C specifications.
  3. A request timeout can occur if the data takes too long to return. An optional PositionOptions object has a timeout setting representing the maximum length of time (in milliseconds) the device is allowed to wait for a return a position. The default timeout value is Infinity, which is a long time to wait. It’s a good idea to set a reasonable timeout. Even slower response times should be expected if using this in combination with enableHighAccuracy set to true.

Mapping the Location

Popular mapping APIs have their own techniques to get the user location. In most cases, this is just a wrapper around the Geolocation API with some additional properties that focus on use ease of use within the map environment. For example, Leaflet’s location function uses Geolocation and also provides some shortcuts to working with their map object, providing a copy of the lat/long pre-formatted to the leaflet standard. Overall this means we can use the GeoLocation API directly, or using Leaflet’s map.locate wrapper if we want to set additional options such as the map’s setView and maxZoom. The returned object just has a few minor differences.

Leaflet with vanilla Geolocation API

A brief example of getting the user location with the Geolocation API and adding to a map.

And below is the javascript code using the Geolocation API. With the results, it grabs the date, latitude, longitude, and accuracy from the return object and uses them with the map, popup, and prints some details to the page.

// Setup Geolocation API options
const gpsOptions = {enableHighAccuracy: true, timeout: 6000, maximumAge: 0};
const gnssDiv = document.getElementById("gnssData");
// Geolocation: Success
function gpsSuccess(pos) {
    // Get the date from Geolocation return (pos)
    const dateObject = new Date(pos.timestamp);
    // Get the lat, long, accuracy from Geolocation return (pos.coords)
    const {latitude, longitude, accuracy} = pos.coords;
    // Add details to page
    gnssDiv.innerHTML = `Date: ${dateObject} 
        <br>Lat/Long: ${latitude.toFixed(5)}, ${longitude.toFixed(5)} 
        <br>Accuracy: ${accuracy} (m)`;
    const radius = accuracy / 2;
    layerGpsGroup.clearLayers();
    // Zoom to the location
    map.setView([latitude,longitude], 12);
    //Add a marker and radius based on accuracy to map 
    L.marker([latitude,longitude]).addTo(layerGpsGroup)
        .bindPopup(`Lat/Long : ${latitude.toFixed(5)}, ${longitude.toFixed(5)}`)
        .openPopup();
    L.circle([latitude,longitude], radius).addTo(layerGpsGroup);
}
// Geolocation: Error
function gpsError(err) {
    console.warn(`Error: ${err.code}, ${err.message}`);
}
// Button onClick, get the the location
function getLocation() {
    navigator.geolocation.getCurrentPosition(gpsSuccess, gpsError, gpsOptions);
}
// Setup the leaflet map
const map = L.map('map').setView([44, -79], 9); // Toronto area set as default
const osmTileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
    maxZoom: 19,
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
const layerGpsGroup = L.layerGroup().addTo(map);

If you found my writing entertaining or useful and want to say thanks, you can always buy me a coffee.
ko-fi