INTRODUCTION
This chapter presents the new Geolocation API and illustrates its use with several examples.
The Geolocation HTML5 JavaScript API is implemented by most modern Web browsers, and uses different means to get the current location: GPS, GSM/3G triangulation, Wifi, IP address, etc.
It is possible to prompt the user to activate the GPS (this is what most GPS navigation software does on mobile phones), or ask for a particular mean among those available. It is also possible to track the current position when it changes. This is useful for writing a navigation application or for tracking in real time the position of different participants in the case of an application that involves several persons at the same time (using WebSockets, for example).
CURRENT SUPPORT IS EXCELLENT
As at June 2016, support for this API is excellent, both on mobile and on desktop devices.
To get an updated table, check caniuse.com
TYPICAL USE
- navigator.geolocation.getCurrentPosition(showPosition, onError);
- function showPosition(position) {
- console.log(“latitude is: “ + position.coords.latitude);
- console.log(“longitude is: “ + position.coords.longitude);
- }
- function onError(err) {
- console.log(“Could not get the position”);
- }
This online example at JS Bin shows how to get the current longitude and latitude and display them in an HTML page. Try it below in your browser:
Note that the first time you execute this example, for privacy reasons, the browser will ask if you agree to share your position with the application.
Source code of this typical example:
- <!DOCTYPE html>
- <html lang=“en”>
- <head>
- <title>Basic example of use of the geolocation API</title>
- </head>
- <body>
- <p id=“msg”>Click the button to get your coordinates:</p>
- <button onclick=“getLocation()“>Where am I ?</button>
- var displayCoords=document.getElementById(“msg”);
- function getLocation() {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(showPosition);
- } else {
- displayCoords.innerHTML=“Geolocation API not supported by your browser.”;
- }
- }
- function showPosition(position) {
- displayCoords.innerHTML=“Latitude: “ + position.coords.latitude +
- “
Longitude: “ + position.coords.longitude; - }
- </body>
- </html>
Explanations:
- Line 14 checks if the Web browser supports the geolocation API by testing the variable navigator.geolocation. If not null, then the geolocation API is supported.
- Line 15 callsnavigator.geolocation.getCurrentPosition(showPosition) passing a callback function as a parameter (in this example we did not specify a callback in case of error). When a current position is available, the callback function will be called asynchronously, and the input parameter of this callback function will be the current position, like in the functionshowPosition(position) of the example.
- Line 22: the position objects has a coords property that is the object that holds the longitude and the latitude.
EXTERNAL RESOURCES:
- The W3C specification about the geolocation API (in Recommendation status, aka, Web standard)
- Good article from the Opera dev Web site
- Excellent tutorial on the Google Maps API. Some examples in this part of the course are illustrated using Google Maps.
The different properties of the coords object
In the previous example, we used the coords property of the position passed as an input parameter to the callback function. This coords object has many properties:
Properties of the coords object | |
latitude | The latitude of the position |
longitude | The longitude of the position |
altitude | The altitude of the position |
accuracy | The accuracy of the measure of the longitude and latitude (in meters) |
altitudeAccuracy | The accuracy of the measure of the altitude (in meters) |
heading | gives the orientation relative to north, in degrees |
speed | current speed in meters/second |
Not all these values may be available in all Web browsers. When one of these properties is null, it means that it is not available.
Geolocation error codes
In the last example, we used the navigator.geolocation.getCurrentPosition(showPosition) with only one callback function (in the case of success), but it is also possible to pass a second parameter that is another callback function called in the case of error.
This example on JS Bin shows how to properly check against the different possible errors, it’s just a slightly different version of the previous example. Try it, then turn your WiFi off or unplug your Ethernet cable (or turn off GPS and 3G/4G on a mobile phone). You should see an error message.
Source code of the example:
- <!DOCTYPE html>
- <html lang=“en”>
- <head>
- <title>Basic example of use of the geolocation API</title>
- </head>
- <body>
- <p id=“msg”>Click the button to get your coordinates:</p>
- <button onclick=“getLocation()“>Where am I ?</button>
- var displayCoords=document.getElementById(“msg”);
- function getLocation() {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(showPosition, errorPosition);
- } else {
- displayCoords.innerHTML=“Geolocation API not supported by your browser.”;
- }
- }
- function showPosition(position) {
- displayCoords.innerHTML=“Latitude: “ + position.coords.latitude +
- “
Longitude: “ + position.coords.longitude; - }
- function errorPosition(error) {
- var info = “Error during geolocation: “;
- switch(error.code) {
- case error.TIMEOUT:
- info += “Timeout !”;
- break;
- case error.PERMISSION_DENIED:
- info += “Permission denied, geolocation could not be obtained…”;
- break;
- case error.POSITION_UNAVAILABLE:
- info += “Location could not be obtained though the available means…”;
- break;
- case error.UNKNOWN_ERROR:
- info += “Unknown error”;
- break;
- }
- displayCoords.innerHTML = info;
- }
- </body>
- </html>
Tracking a position in real time
INTRODUCTION
In order to track the current position, the geolocation API provides a method similar to the getCurrentPosition(onSuccess, onError) named watchPosition(onSuccess, onError).
When getCurrentPosition gives a position when called, watchPosition does the following:
- It gets the callback function only when the current position changes. If you stay in the same location, the callback function won’t be called regularly.
- It returns an id so that you can use the clearWatch(id) method to stop the current tracking.
TYPICAL USE
- // get an id of the current tracking, the showPosition callback is like the one we saw in earlier examples.
- var watchPosId = navigator.geolocation.watchPosition(showPosition);
- …
- // stop the tracking
- navigator.geolocation.clearWatch(watchPosId);
As an exercise, you may just try to change getCurrentPosition to watchPosition in the previous examples, and try this code using a mobile phone or tablet, walk for 20 meters and see the position changing.
External resource
- An article on html5rocks.com that shows how to write a simple trip meter using the geolocation API: in particular, you will find a JavaScript function that computes the distance (in meters) between two positions defined by their longitude and latitude.
OPTIONS AVAILABLE WHEN USING THE GEOLOCATION API, IN PARTICULAR REAL TIME TRACKING
Several options are available when using HTML5 geolocation. We can pass a third parameter to the getCurrentPosition and watchPosition methods, that will hold one or several of the following options:
Properties of the coords object | |
enableHighAccuracy | A boolean (true/false) which indicates to the device that you wish to obtain its most accurate readings. in other words: use the GPS please! (However, this parameter may or may not make a difference, depending on your hardware, GPS availability, etc.) |
maximumAge | The maximum amount of time (in milliseconds) the position may remain in the cache (this is appropriate as the device may cache readings to save power and/or bandwidth). |
timeout | The maximum time (in milliseconds) for which you are prepared to allow the device to try to obtain a Geo location. After this timeout value has elapsed, the onError callback is called. |
Example of use (see the explanations in the lines of comment):
- // Just ask to turn GPS on, if available
- navigator.geolocation.getCurrentPosition(onSuccess, onError,
- {enableHighAccuracy:true});
- // maximumAge = 10 mins, the position can be cached for 10 mins,
- // useful when in tunnels…When the device tries to get
- // a position, if it does not succeed, then go on error
- // immediately
- navigator.geolocation.getCurrentPosition(onSuccess, onError,
- {maximumAge:600000, timeout:0});
- // Position will never come from the cache (maximumAge: 0), and
- // if after 0.1s the position could not be computed, then go on
- // error
- navigator.geolocation.getCurrentPosition(onSuccess, onError,
- {maximumAge:0, timeout:100});
- // Ask for GPS, cache for 30s, 27s before going on error…
- watchId=navigator.geolocation.watchPosition(onSuccess, onError,
- {enableHighAccuracy:true, maximumAge:30000, timeout:27000});
Practical examples: use the geolocation API together with Google Maps
This section presents some examples of how to get a static map (a picture), using the Google Static Map API, how to display an interactive map using the Google Map JavaScript API and even how to get an estimation of a physical address from the longitude and latitude, using the Google Reverse Geocoding JavaScript API.
The following three examples increase in complexity, but most of the code is reused and adapted without even reading the Google documentation about the different APIs.
EXAMPLE 1 (EASY): HOW TO GET A STATIC IMAGE MAP CENTERED ON YOUR LONGITUDE AND LATITUDE
Online example available on JS Bin, or try it here in your browser:
It also illustrates the use of the error callback from the previous section. The Google Map API is used to get an image centered at the longitude and latitude collected with the HTML5 Geolocation API.
Source code extract:
- <!DOCTYPE html>
- <html>
- <body>
- <p id=“demo”>Click the button to get your position:</p>
- <button onclick=“getLocation()“>Try It</button>
-
id=“mapholder”>
- var x=document.getElementById(“demo”);
- function getLocation() {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(showPosition,showError);
- } else{
- x.innerHTML=“Geolocation is not supported by this browser.”;
- }
- }
- function showPosition(position) {
- // Google map API needs the latitude and longitude separated by a comma
- var latlon=position.coords.latitude+“,”+position.coords.longitude;
- // Google map API URL that returns an image centered on the longitude and latitude
- var img_url=“https://maps.googleapis.com/maps/api/staticmap?center=”
- +latlon+“&zoom=14&size=400×300&sensor=false”;
- document.getElementById(“mapholder”).innerHTML=“+img_url+“‘ />”;
- }
- function showError(error) {
- …
- }
- </body>
- </html>
The magic occurs at line 23, where we use the Google Static Map API.
EXAMPLE 2 (A BIT MORE COMPLICATED…) THAT SHOWS HOW TO DISPLAY AN INTERACTIVE GOOGLE MAP CENTERED ON THE CURRENT POSITION
This example is just given “as is”, as there are so many possibilities for rendering a map with the Google Map API. However, we think having such a basic example might be useful.
Source code of the example:
- <!doctype html>
- <html>
- <head>
- </head>
- <body>
- <!– for position display –>
-
id=“myposition”>
- <!– for gmap display –>
-
id=“map” style=“width:640px;height:480px“>
- <!– get gmap API –>
- src=“https://maps.google.com/maps/api/js?sensor=false”>
- // Default position
- var centerpos = new google.maps.LatLng(48.579400,7.7519);
- // default options for the google map
- var optionsGmaps = {
- center:centerpos,
- navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
- mapTypeId: google.maps.MapTypeId.ROADMAP,
- zoom: 15
- };
- // Init map object
- var map = new google.maps.Map(document.getElementById(“map”), optionsGmaps);
- if(navigator.geolocation) {
- // callback function, called by getCurrentPosition() in case of success
- function drawPosition(position) {
- var infopos = “Got position :
“; - infopos += “Latitude : “+position.coords.latitude +“
“; - infopos += “Longitude: “+position.coords.longitude+“
“; - infopos += “Altitude : “+position.coords.altitude +“
“; - document.getElementById(“myposition”).innerHTML = infopos;
- // Make new object LatLng for Google Maps
- var latlng = new google.maps.LatLng(position.coords.latitude,
- position.coords.longitude);
- // Add a marker at position
- var marker = new google.maps.Marker({
- position: latlng,
- map: map,
- title:“You are here”
- });
- // center map on longitude and latitude
- map.panTo(latlng);
- }
- // callback function, called by getCurrentPosition() in case of error
- function errorPosition(error) {
- …
- }
- navigator.geolocation.getCurrentPosition(drawPosition,errorPosition);
- } else {
- alert(“Geolocation API not supported by your browser”);
- }
- </body>
- </html>
EXAMPLE 3 (ADVANCED) SHOWS HOW TO GET A PHYSICAL ADDRESS FROM THE LONGITUDE AND LATITUDE
This is another example that obtains an address from longitude and latitude. It usesthe Google Reverse Geocoding JavaScript API. For those of you who are really interested to know how this API works, please read the Google documentation and tutorials.
Without going into detail, the below example might be useful to copy/paste/adapt for trying to pre-fill a form where one is asked for an address. Geolocation is useful for guessing the country, city, zip code, street, etc. Some examples that use this feature will be given in the next section of the course.
Source code of the example:
- <!DOCTYPE html>
- <html lang=“en”>
- <head>
- src=“https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false”>
- // p elements for displaying lat / long and address
- var displayCoords, myAddress;
- // used with the google apis
- var geocoder;
- var map;
- var infowindow = new google.maps.InfoWindow();
- var marker;
- // Called when the page is loaded
- function init() {
- displayCoords=document.getElementById(“msg”);
- myAddress = document.getElementById(“address”);
- geocoder = new google.maps.Geocoder();
- // In order to show something even before a user clicks on the button
- var latlng = new google.maps.LatLng(34.0144, –6.83);
- var mapOptions = {
- zoom: 8,
- center: latlng,
- mapTypeId: ‘roadmap’
- }
- map = new google.maps.Map(document.getElementById(‘map_canvas’), mapOptions);
- } // end of init()
- // Called when the button is clicked
- function getLocation() {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(showPosition);
- } else {
- displayCoords.innerHTML=“Geolocation API not supported by your browser.”;
- }
- }
- // Called when a position is available
- function showPosition(position) {
- displayCoords.innerHTML=“Latitude: “ + position.coords.latitude +
- “
Longitude: “ + position.coords.longitude; - // Display the map
- showOnGoogleMap(new google.maps.LatLng(position.coords.latitude,
- position.coords.longitude));
- }
- function showOnGoogleMap(latlng) {
- // Ask google geocoder for an address once we get a longitude and
- // a latitude. In fact, the reverse geocoder sends back an array of “guesses”
- // i.e. not just one address object, but several. Each entry in this array
- // has several properties such as street, city, etc. We use the “formatted_address”
- // one here, but it might be interesting to get the detailed properties in other
- // applications like a form with street, city, zip code etc.
- geocoder.geocode({‘latLng’: latlng},reverseGeocoderSuccess);
- function reverseGeocoderSuccess(results, status) {
- if (status == google.maps.GeocoderStatus.OK) {
- if (results[1]) {
- map.setZoom(11);
- marker = new google.maps.Marker({
- position: latlng,
- map: map
- });
- infowindow.setContent(results[1].formatted_address);
- infowindow.open(map, marker);
- // Display address as text in the page
- myAddress.innerHTML=“Adress: “ + results[0].formatted_address;
- } else {
- alert(‘No surface address found’);
- }
- } else {
- alert(‘Geocoder failed due to: ‘ + status);
- }
- } // end of reverseGeocoderSuccess
- } // end of showOnGoogleMap
- </head>
- <body onload=“init()“>
- <title>HTML5 + Geolocalisation + Google Maps API Reverse Geocoding</title>
- <p id=“msg”>Click the button to get your coordinates:</p>
- <p id=“address”></p>
- <button onclick=“getLocation()“>Where am I ?</button>
-
id=“map_canvas” style=“width: 500px; height: 300px“>
- </body>
- </html>
Example: fill a form’s address fields automatically
INTRODUCTION
In the previous example, we used the results returned by the Google reverse geocoding service, without going into detail.
In this section, we will see how we can get the different parts of the responses (city, street, zip code, country, etc.) The reverse geocoding service tries to guess what is the “best” address that matches the longitude and latitude, but sometimes the first guess is not the best one.
HOW TO PARSE THE GOOGLE REVERSE GEOCODING RESULTS?
What are the Google reverse geocoding results exactly?
A common question is: how to have a robust code for parsing the Google reverse geocoding, to properly get the city, street, country, etc.
Depending on your location/country and on the geolocation method used by your browser (GPS on phone, IP, WiFi, 3G, etc.), some of the data might not be available (i.e., no street). So, there is no guarantee that all candidate addresses will get the same defined properties. For example, the first result may give a defined city, but the third result may not.
Look at this line of code from the last example from the previous page – the example that showed the address when you clicked on the button:
- // Display address as text in the page
- myAddress.innerHTML=“Adress: “ + results[0].formatted_address;
At line 2, we get the first address returned by the Google reverse geocoding service, and use the formatted_address property. Let’s suppose that it contained the best address, formatted as a string. We chose to use it and showed it in the page by setting myAddress.innerHTML with its value (myAddress pointed to the <p id=”address”></p> element in the page).
Let’s examine the detailed results
We add a console.dir(results) in the code, to see a structured view of the results in dev. tools console.
Once we get the results, we can get the different parts:
Here is an example of how we can parse such a field. Notice that each field is tested to see if it exists. The results are stored in the variables defined at line 1.
- var country, postalCode, state, route, streetNumber, locality, areaLvl1, areaLvl2;
- function parseResult(result) {
- for(i in result){
- console.log(“type = “ + result[i].types[0] + ” long_name = “ +
- result[i].long_name);
- if(result[i].types[0] == ‘postal_code’)
- postalCode = result[i].long_name;
- if(result[i].types[0] == ‘country’)
- country= result[i].long_name;
- if(result[i].types[0] == ‘street_number’)
- streetNumber= result[i].long_name;
- if(result[i].types[0] == ‘route’)
- route= result[i].long_name;
- if(result[i].types[0] == ‘locality’)
- locality= result[i].long_name;
- if(result[i].types[0] == ‘state’)
- state= result[i].long_name;
- if(result[i].types[0] ==‘administrative_area_level_2’)
- arealLvl2= result[i].long_name;
- if(result[i].types[0] ==‘administrative_area_level_1’)
- areaLvl1= result[i].long_name;
- }
- // added this for debugging in the console
- console.log(“postalCode = “ + postalCode);
- console.log(“country = “ + country);
- console.log(“streetNumber = “ + streetNumber);
- console.log(“route = “ + route);
- console.log(“locality = “ + locality);
- console.log(“Administrative area level 1 “ + areaLvl2);
- console.log(“Administrative area level 2 “ + areaLvl1);
- }
A FORM THAT AUTO FILLS THE ADDRESS INPUT FIELDS
It’s very hard to create a single code that will work in all situations and in all countries, since postal addresses are formatted differently depending on the country. A decoder that works well 99% of the time in the UK may be wrong for Australia, for instance. So, it’s just a “guess system”, and in real life, if you create a Web site and would like to help the user with completing a form, just fill in the country, city, postal code, and suggest the rest, propose a small icon for deleting the street input field content, etc. You could also add a drop down menu that offers not only the first guess but the second and third, etc.
Source code extract:
- function showOnGoogleMap(latlng) {
- …
- // Display address as text in the page
- myAddress.innerHTML=“Adress: “ + results[0].formatted_address;
- // Call the function that parses the results and fills
- // the input fields
- parseResult(results[0].address_components);
- …
- }
- var country, postalCode, state, route, streetNumber, locality, areaLvl1, areaLvl2;
- function parseResult(result) {
- for(i in result){
- // Let’s print all the data we can collect from the reverse geocoder,
- // Look at the debug console to see what we get…
- console.log(“type = “ + result[i].types[0] + ” long_name = “ +
- result[i].long_name);
- if(result[i].types[0] == ‘postal_code’)
- postalCode = result[i].long_name;
- …
- // fill input fields now, check if variables are undefined
- if((route != undefined) && (streetNumber !=undefined)) {
- console.log(“let’s fill the street”);
- document.querySelector(“#address1”).value = streetNumber + ” “ + route;
- }
- if(locality != undefined) {
- console.log(“let’s fill the city”);
- document.querySelector(“#address2”).value = locality;
- }
- if(country != undefined) {
- console.log(“let’s fill the country”);
- document.querySelector(“#country”).value = country;
- }
- …
- }
- }
- </script>
This example is rather long and we have only shown an extract of the source code. Take your time and look at the online example.