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)

Getting the user's location (Intermediate)


In this section, we will detect geolocation support, use the Geolocation API to obtain the user's coordinates (if we can), save the coordinates to MySQL via our PHP callback, and handle any errors that might occur.

Getting ready

Make sure you've set up the required files in the previous section: index.php to serve the main page, callback.php to serve as our callback, and feed.php to serve as our KML feed. In this section, we will be concentrating on index.php and callback.php.

How to do it...

Perform the following steps for getting the user's location:

  1. First, set up lib.php as discussed here. This will be the file that handles connections to the database for all components in the system.

  2. Fill in the blanks at the top of the page with your own database details. Note that for simplicity, I have used the built-in MySQL functions. For a fully fledged PHP application, I recommend using the built-in PDO library.

    <?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
    
    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;
    }
  3. Then, set up callback.php. This will accept longitude and latitude data from our location detection page via an HTTP POST request, and save it to our database:

    <?php
    // Load our common library file, and fail if it isn't present
    require_once('lib.php');
    
    // Check for the existence of longitude and latitude in our POST request
    // variables; if they're present, continue attempting to save
    if (isset($_POST['longitude']) && isset($_POST['latitude'])) {
      // Cast variables to float 
      (never accept unsanitized input!)
      $longitude = (float) $_POST['longitude'];
      $latitude = (float) $_POST['latitude'];
      // For now, let's hard-code the user identifier to "1" - 
      we can
      // use PHP sessions and authentication to set this 
      differently later
      // on
      $user = 1;
      // Set the timestamp from the current system time
      $time = time();
      // Put our query together:
      $query = "insert into points set 'longitude' = 
      {$longitude}, 
      'latitude' = {$latitude},
      'user_id' = {$user},
      'time' = {$time}";
      // Run the query, and return an error if it fails
      if (!($result = mysql_query($query))) {
        header($_SERVER['SERVER_PROTOCOL'] . 
        ' 500 Internal Server Error', true, 500);
        echo "Could not save point.";
        exit;
      }
    
    }
  4. Finally, set up index.php. This is the page that users will access directly:

    <!doctype html>
    <html>
      <head>
        <title>
          Location detector
        </title>
    
        <!-- We're using jQuery to simplify our JavaScript DOM-
        handling code -->
        <script src="//code.jquery.com/
        jquery-1.9.1.min.js"></script>
    
        <script language="javascript">
    
          // This function is called when the Geolocation API 
          successfully
          // retrieves the user's location
          function savePosition(point) {
    
            // 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: point.coords.latitude,
                longitude: point.coords.longitude
              },
              statusCode: {
                500: function() {
                  $('#locationpane').html
                  ('<p>We couldn\'t save your location.</p>');
                }
              }
            }).done(function() {
              $('#locationpane').html
              ('<p>Location saved.</p>');
            }).fail(function() {
              $('#locationpane').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: $('#locationpane').html
            ('<p>No location was retrieved.</p>');
            break;
    
            // Error code 2: the user's location could not be 
            determined
            case 2: $('#locationpane').html
            ('<p>We couldn\'t find you.</p>');
            break;
    
            // Error code 3: the Geolocation API timed out
            case 3: $('#locationpane').html
            ('<p>We took too long 
            trying to find your location.</p>');
            break;
    
          }
        }
    
        </script>
    
      </head>
      <body>
    
        <div id="locationpane">
    
          <p>
            Waiting for location ...
          </p>
    
        </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">
    
          // 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, errorPosition);
    
          } else {
    
            // If the browser doesn't support the Geolocation 
            API, tell the user.
            $('#locationpane').html
            ('<p>No geolocation support is available.</p>');
    
          }
    
        </script>
    
      </body>
    </html>

How it works...

There are two large JavaScript blocks in index.php, which together interact with the Geolocation API. In the body of the page, we've included a simple div element with ID locationpane, which we'll use to give feedback to the user. Every time we give feedback, we do so by changing the HTML contents of locationpane to contain a paragraph with a different message.

In the header of the page, there are two functions: savePosition and errorPosition. savePosition will be called by the Geolocation API when a location is determined and errorPosition will be called when there has been an error determining the location.

savePosition takes a single Position object as its first parameter. This has the following properties:

  • coords: An object encapsulating the location's coordinates, which in turn contains the following attributes:

    • latitude: This is the user's latitude in degrees. This is a double value.

    • longitude: This is the user's longitude in degrees. This is a double value.

    • accuracy: This is the margin of error, in meters. This can be a double or a null value.

    • altitude: This is the number of meters above the mathematically defined surface of the Earth. This can be a double or a null value.

    • altitudeAccuracy: This is the margin of error for the altitude, in meters. This can be double or a null value.

    • heading: This is specified in degrees, clockwise relative to true north. This can be a double or a null value.

    • speed: This is meters per second. This can be a double or a null value.

  • timestamp (DOMTimeStamp): This is the timestamp that the location was retrieved.

Note that on some systems in certain contexts, the location won't be determined at the time of request; instead, a cached version will be returned. This is why the timestamp is important. However, we will discard it here.

savePosition uses jQuery's AJAX function to take the latitude and longitude from the coords object and sends it to callback.php. It then checks the HTTP response code; if callback.php has returned an error 500, it tells the user that his/her location could not be saved. (More on this in a moment.)

Meanwhile, if there was an error determining the user's location with the Geolocation API, errorPosition is called. This takes a PositionError object as its parameter, which has the following properties:

  • code (short): A numeric error code

  • message (DOMstring): An internal error message

Rather than output message, which isn't intended for end users, errorPosition looks at the error code to determine what kind of feedback to provide to the user:

  • Error code 1: The user denied the application's request to track his/her location

  • Error code 2: The user's location could not be determined

  • Error code 3: The Geolocation API timed out

At the bottom of the page is the code that actually runs the Geolocation API.

Before accessing the JavaScript API functions, it's important to check to make sure that the Geolocation API is supported in the current browser. To do this, you can simply check to make sure the navigator.geolocation object exists:

if (navigator.geolocation) { /* The Geolocation API is supported */ }

If it doesn't, we should give the user feedback to explain that his/her location cannot be determined. We could also attempt to retrieve the user's location using server-side technologies such as IP geolocation, but this is much less accurate and out of the scope of this book.

Once we're sure, we can use the Geolocation API, we can call navigator.geolocation.getCurrentPosition, with references to the success and failure functions as its parameters:

navigator.geolocation.getCurrentPosition(savePosition, errorPosition);

It's worth mentioning here that a third parameter is available, which takes a PositionOptions object. This may contain the following properties:

  • enableHighAccuracy: This is a Boolean value. It enables high accuracy mode (default: off).

  • timeout: This is a Boolean value. This is the threshold beyond which the API times out (in milliseconds; the default is no limit).

  • maximumAge: This is a long value. The maximum age of a cached location that we'll accept, in milliseconds (default: 0).

If we enable high accuracy mode, mobile devices with GPS units will attempt to use it to get the best possible location information (if their owner has allowed it); otherwise, they may default to using trilateration to determine the location. However, because not all devices have these units and because GPS signals are not always available, requesting the current position with high accuracy is more likely to fail.

While high accuracy, location detection will not automatically fall back to the standard method, you can achieve this yourself, if you like. First, call getCurrentPosition with highAccuracy set to true and with a reference to a new error handling function:

navigator.geolocation.getCurrentPosition(savePosition, highAccuracyErrorPosition, {enableHighAccuracy: true});

All this new error handler, highAccuracyErrorPosition, does is call getCurrentPosition with highAccuracy set to false:

function highAccuracyErrorPosition(error) {

navigator.geolocation.getCurrentPosition(savePosition, errorPosition, {enableHighAccuracy: false});

}

The result is that the browser attempts to use high-accuracy location detection, and falls back to the standard method if it is not available due to some reason. Should the user decline authorization for location information, this continues to be respected down the chain.

The callback script, callback.php, first loads the database functionality from lib.php and ensures that it can connect. If connection fails for some reason, it returns an HTTP error 500 (Internal Server Error), which tells index.php to display an error to the user, as previously described.

If callback.php is connected to the database successfully, it then sanitizes the input variables, latitude and longitude. It's important to make sure both are cast to floating point variables, to minimize the risk of SQL injection attacks. The script also retrieves the current UNIX epoch timestamp (represented as the number of seconds since 00:00 on January 1, 1970).

The script makes it possible to store location information for an unlimited number of users. However, because authentication and user handling are not within the scope for this book, we've hardcoded the user's unique ID to 1. If you had a separate MySQL user table, for example, you would set this value to the ID of the currently logged-in user. This ID would be saved in the current browser session at the point of login. callback.php would use the version saved in the session rather than sent to it explicitly via a GET or POST variable, to prevent third parties from maliciously saving location information to a user's account.

Finally, callback.php attempts to save this data to the MySQL table we created in the previous section, using a standard MySQL insert call:

$query =    "insert into points set 'longitude' = {$longitude}, 
    'latitude' = {$latitude},
    'user_id' = {$user},
    'time' = {$time}";

Once again, if an error occurs, the script returns an HTTP 500 error so that the JavaScript on index.php can let the user know in a friendly way.

Otherwise, we can reasonably assume that the data was saved in our MySQL table. Because we saved it with timestamp information, and because we are also saving the user's unique ID in the same table row, we will be able to easily retrieve any individual user's locations in chronological order later on.

PHP's default HTTP response code is 200: OK. This tells the jQuery call in index.php that the positioning data was saved without any problems. In turn, index.php lets the user know that his/her location was saved.