Book Image

Instant HTML5 Geolocation How-to

By : Benjamin Otto Werdmulle
Book Image

Instant HTML5 Geolocation How-to

By: Benjamin Otto Werdmulle

Overview of this book

We don't just surf the Web from our desktops any more – we take it with us, everywhere we go. Modern devices contain sophisticated hardware and software to determine the user's location. Apps such as Foursquare and Google Maps use this to create new kinds of functionality. Now, you can do this too with the HTML5 Geolocation API. "Instant HTML5 Geolocation How-to" is a simple guide to adding location information to your web applications. The practical, easy-to-follow recipes are designed to help you learn the ins and outs of the API. You'll learn how to use it, how it works, and how to save and display geographic information on the web. Beginning with a solid grounding in how the Geolocation API works and when to use it, you will learn how to determine, store, display, and track the user's location via a series of clear recipes. You will learn the different ways location is determined on different devices, including desktops and laptops that don't have GPS units. You'll also learn how to selectively use these different behaviours, based on the speed, accuracy, and battery life requirements of your application. You'll also get some hints about using MySQL databases to store sets of location data. "Instant HTML5 Geolocation How-to" will teach you everything you need to know about retrieving the location information your application needs, across multiple devices and platforms.
Table of Contents (7 chapters)

Displaying the user's location using the Google Maps API (Intermediate)


Now that we've written the code to save the user's location at a particular time, we need to consider how we'll display it. In this section, we'll use the Google Maps API to display the user's location right now, as well as recent locations that we've saved for this user.

The Google Maps API is the most popular online mapping API in the world, which is why we will use it for this tutorial. Other mapping APIs are available, including offerings from Microsoft Bing, Nokia, and the open source OpenStreetMap project. My decision to pick the Google Maps API does not reflect on the quality of these other APIs, and you will want to carefully choose between them based on their merits and applicability for your project. I have included links to these projects at the end of this section, as well as to the Google Maps API documentation.

Use of the Google Maps API is free for non-profit websites at the time of writing. Commercial sites are limited to 25,000 map loads per day. Beyond that, you will need to acquire a Google Maps API for Business license by contacting Google directly.

We will be using the Google Maps API v3 for the purposes of this tutorial.

How to do it...

Let's begin by retrieving all of our previously-saved points.

  1. First, alter lib.php as follows. Recall that this is the script file that connects to the database so that points can be saved. We're now expanding its role to include a helper function to retrieve all the previously saved points for a particular user.

    <?php
    
    $server = '';		    // Enter your database server here
    $username = '';		    // Enter your database username here
    $password = '';		    // Enter your database password here
    $database = '';	    // Enter your database name here
    
    // Connect to the database server, and then select $database as the database
    if (mysql_connect	(
      $server,
      $username,
      $password
    )) {
      mysql_select_db(
        $database
      );
    } else {
      header($_SERVER['SERVER_PROTOCOL'] . 
      ' 500 Internal Server Error', true, 500);
      echo "Could not connect to the database.";
      exit;
    }
    
    /**
    Retrieve all the stored locations in the database
    @return array
    */
    function getPreviousLocations($user_id) {
    
      // Initialize the array that we'll return
      $points = array();
    
      // It's always important to validate input, 
      and particularly when
      // we're using it in the context of a database query. 
      Here we make
      // sure $user_id is an integer.
      $user_id = (int) $user_id;
    
      // SQL query to simply return all points from our 
      database - over time
      // you may wish to add a limit clause
      $query = "select 'latitude', 'longitude', 'time' from 
      'points' where user_id = $user_id order by 'time' desc";
    
      // If we have points in the database, add them to the 
      $points array
      if ($result = mysql_query($query)) {
        while ($row = mysql_fetch_object($result)) {
          $points[] = $row;
        }
      } else {
      echo mysql_error();
    }
    // Finally, return the $points array
    return $points;
    
    }
  2. Then, expand index.php to load the Google Maps API, retrieve any previous points, and display both the set of previous location points and the current location on a map:

    <?php 
    
    // Load our common library file, and fail if it isn't present
    require_once('lib.php');
    
    ?>
    <!doctype html>
    <html>
      <head>
        <title>
          Location detector
        </title>
    
        <!-- We're using jQuery to simplify our JavaScript 
        DOM-handling code -->
        <script type="text/javascript" 
        src="//code.jquery.com/jquery-1.9.1.min.js"></script>
    
          <!-- We're using the Google Maps API v3; 
          note that we need to tell
          Google we're using a sensor for geolocation -->
          <script type="text/javascript" 
          src="//maps.googleapis.com/maps/api/js?v=3.
          exp&sensor=true"></script>
    
          <script type="text/javascript">
    
            // This function is called when the Geolocation API 
            successfully
            // retrieves the user's location
            function savePosition(point) {
    
              // Save the current latitude and longitude as 
              properties
              // on the window object
              window.latitude = point.coords.latitude;
              window.longitude = point.coords.longitude;
    
              // Send the retrieved coordinates to callback.php 
              via a POST
              // request, and then set the page content to 
              "Location saved"
              // once this process is complete 
              (or "We couldn't save your
              // location" if it failed for some reason)
              $.ajax({
                url: 'callback.php',
                type: 'POST',
                data:   {
                  latitude: window.latitude,
                  longitude: window.longitude
                },
                statusCode: {
                  500: function() {
                  $('#location_pane').html
                  ('<p>We couldn\'t save your location.</p>');
                }
              }
    
            }).done(function() {
              // Let the user know the location's been 
              saved to the database
              $('#location_pane').html
              ('<p>Location saved.</p>');
    
              // Center the map on the user's current location
              var currentLocation = 
              new google.maps.LatLng(window.latitude, 
              window.longitude);
              window.googleMap.setCenter(currentLocation);
    
              // Create a marker at the user's current location
              var marker = new google.maps.Marker({
                position: currentLocation,
                map: window.googleMap,
                title: 'Current location'
              });
            }).fail(function() {
            $('#location_pane').html
            ('<p>We couldn\'t save your location.</p>');
          });
    
        }
    
        // This function is called when there is a problem 
        retrieving
        // the user's location (but the Geolocation API is 
        supported in
        // his or her browser)
        function errorPosition(error) {
          switch(error.code) {
    
          // Error code 1: permission to access the user's 
          location
          // was denied
          case 1: $('#location_pane').html('<p>No location was 
          retrieved.</p>');
          break;
    
          // Error code 2: the user's location could not be 
          determined
          case 2: $('#location_pane').html
          ('<p>We couldn\'t find 
          you.</p>');
          break;
    
          // Error code 3: the Geolocation API timed out
          case 3: $('#location_pane').html
          ('<p>We took too long trying to find your 
          location.</p>');
          break;
    
        }
      }
    
      // This function is called when there is a problem 
      retrieving
      // The high-accuracy position. Instead of failing 
      outright, it
      // attempts to retrieve the low-accuracy position, 
      telling the
      // getCurrentPosition function to call errorPosition if 
      there is
      // an error this time.
      function highAccuracyErrorPosition(error) {
    
        navigator.geolocation.getCurrentPosition(savePosition, 
        errorPosition, {enableHighAccuracy: false});
    
        }
    
        </script>
    
        </head>
        <body>
    
          <div id="location_pane">
    
            <p>
              Waiting for location ...
            </p>
    
          </div>
          <div id="map_pane" style="width: 500px; 
          height: 500px"></div>
    
          <!-- We're including the Geolocation API code at the 
          bottom of the page
          so that page content will have loaded first -->
          <script language="javascript">
    
          // Set initial viewing options for the map
          var mapOptions = {
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.HYBRID
          };
    
          // Initialize the map as a googleMap property on 
          the window object
          window.googleMap = 
          new google.maps.Map
          (document.getElementById('map_pane'), mapOptions);
    
          // Load any previous points into a JSON array, 
          which itself is written
          // to the page using PHP. We're hardcoding the user 
          ID to 1, as in
          // callback.php.
          var jsonPoints = 
          <?=json_encode(getPreviousLocations(1));?>;
    
          // If jsonPoints isn't empty, 
          iterate through and create new map points
          // for each geolocation point
          if (jsonPoints.length > 0) {
            window.points = new Array();
            jsonPoints.forEach(function(point) {
              window.points.push(new google.maps.Marker({
                position: new google.maps.LatLng
                (point.latitude, point.longitude),
                map: window.googleMap
              }))
            });
          }
    
          // First, check if geolocation support is available
          if (navigator.geolocation) {
    
            // If it is, attempt to get the current position. 
            Instantiate
            // the savePosition function if the operation was 
            successful, or
            // errorPosition if it was not.
            navigator.geolocation.getCurrentPosition
            (savePosition, highAccuracyErrorPosition, 
            {enableHighAccuracy: true});
    
          } else {
    
            // If the browser doesn't support 
            the Geolocation API, tell the user.
            $('#location_pane').html
            ('<p>No geolocation support is available.</p>');
    
          }
    
        </script>
    
      </body>
    </html>

How it works...

To display our location data, we will use a MySQL function stored in lib.php to retrieve existing map points. We will also load the Google Maps API using Google's hosted JavaScript library and make use of the following Google Maps objects:

  • Map: The Google Map itself

  • Marker: An individual point on a Google Map

  • LatLng: An object representing a pair of latitude and longitude coordinates

There are three main structural changes we must make to index.php. First, we must require that lib.php is loaded. This will give us access to the database and a new function that we'll add to that library.

So far, we've only stored geolocation data; we haven't displayed it to the user at all. However, because we've been saving it to the database, we potentially have a rich history of location data that we can retrieve—organized by both user and time. A new function, getPreviousLocations($user_id), returns this data as an array in chronological order using a simple MySQL select call:

$query = "select 'latitude', 'longitude', 'time' from 'points' where user_id = $user_id order by 'time' asc";

Recall that for the purposes of this tutorial, we're always setting $user_id to 1. A more sophisticated application would substitute a user identifier from the current browser session, or another location.

By requiring lib.php at the top of index.php, we can ensure that we have reliable access to this information from the database:

<?php 
// Load our common library file, and fail if it isn't present
require_once('lib.php');
?>

Another new addition is the JavaScript library that Google provides for the Google Maps API. Note that by omitting the URI scheme (http: or https:), we can ensure that the browser will use the correct one, whether your page is accessed over a standard or secure HTTP connection. This is placed within the HTML <head> tag in index.php:

<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?v=3.exp&sensor=true"></script>

Finally, we also need a place on the page to display our map. For this, we establish a new, empty div element with a unique ID (here I've used map_pane). The Google Maps API will populate this with a complete map later.

Now that we've set up the framework of the page, we can begin configuring the map. We do this in the JavaScript block at the bottom of the page:

// Set initial viewing options for the map
var mapOptions = {
  zoom: 15,
  mapTypeId: google.maps.MapTypeId.HYBRID
};

The zoom level for Google Maps starts at 0, where you can see the entire globe. Theoretically, the zoom levels are infinite, but in practice, for most maps, the maximum level is 19. Set the zoom level at 15; it's close enough to be able to view your location with precision, but zoomed out enough to see a large amount of the surrounding neighborhood.

There are a number of different map types at your disposal:

  • google.maps.MapTypeId.ROADMAP: The street map view

  • google.maps.MapTypeId.SATELLITE: A satellite view of the Earth

  • google.maps.MapTypeId.HYBRID: Street map items overlaid on top of the satellite view

  • google.maps.MapTypeId.TERRAIN: Terrain information without road markings and so on

For now, set the mapTypeId to googlemaps.MapTypeId.HYBRID.

Next, initialize the Map object with the options you've just defined, and the map_pane DOM element. This is enough to display the map inside the map_pane div. We'll save it to the window.googleMap global variable, which will come in handy.

window.googleMap = new google.maps.Map(document.getElementById('map_pane'), mapOptions);

However, there's every chance we've already got some location information to display. Here's where our PHP function, getPreviousLocations($user_id), becomes useful. Recall that it's returning an array of database row objects containing latitude, longitude, and time.

JavaScript is a front-end language, interpreted in the web browser; PHP is a server-side language, interpreted before any HTML is received by the browser. They cannot directly interface with each other. As a result, we need a way to pre-process the array of coordinates so that it's readable by JavaScript. JSON is perfect for this task.

Luckily, PHP provides a very simple function to encode PHP variables as JSON: json_encode. We just need to use this on the result of getPreviousLocations($user_id). Remembering that we're hardcoding the value 1 in place of $user_id, our hybrid JavaScript/PHP code looks like the following:

var jsonPoints = <?=json_encode(getPreviousLocations(1));?>;

If there was a single location point in the database, this might be rendered as follows:

var jsonPoints = [{"latitude":"37.7595","longitude":"-122.463","time":"1362975429"}];

In other words, jsonPoints is seen by JavaScript as an array of JavaScript objects. We can simply check that the array is non-empty, and iterate through any elements using the Array.forEach method:

if (jsonPoints.length > 0) {
  window.points = new Array();
  jsonPoints.forEach(function(point) {
    window.points.push(new google.maps.Marker({
      position: new google.maps.LatLng(point.latitude, 
      point.longitude),
      map: window.googleMap
    }))
  });
}  

We establish window.points as a global JavaScript array of Marker objects, the objects used to represent individual geographic points in the Google Maps API. On instantiation, Marker objects are given a position in terms of a LatLng object containing latitude and longitude, and a reference to the Google Map that will display them. (We can simply supply the window.googleMap variable we created earlier for this purpose.)

Once the previously saved geographic points have been written to the map, we must ensure that the newly detected location, if it has been successfully obtained, is also added.

Previously, we had written a message to the screen—Location saved—once a location had been successfully processed. Now, we need to also draw it to the map.

First, we create a new LatLng object containing the latitude and longitude of the newly saved location:

var currentLocation = new google.maps.LatLng(window.latitude, window.longitude);

Next, we can center the map on it using the Map object's setCenter method:

window.googleMap.setCenter(currentLocation);

Finally, we create a new Marker object, containing a simple title, the newly created LatLng object, and a reference to our Map:

var marker = new google.maps.Marker({
  position: currentLocation,
  map: window.googleMap,
  title: 'Current location'
});

The location appears as a pin on the map, alongside previously saved locations.