19. Geolocation

The Geolocation API allows an application to leverage the GPS capabilities of a mobile device and determine the device’s location on the surface of the earth. Using this API, either an application can manually check the device’s current position or it can create a location watch that will cause the application to be periodically notified of the device’s physical location.

To use this API, the device running the application must provide geolocation capabilities (by utilizing either a GPS radio and associated software or some alternate mechanism for determining the device’s location). While geolocation capabilities in smartphones are regularly enhanced in newer models, an application cannot guarantee that even though the device has geolocation capabilities, it will be able to determine its location. There are many geographical (such as being in a canyon) or mechanical issues (such as being inside a building) that can affect the device’s ability to report its location to a PhoneGap application.

Since most HTML 5–compatible mobile browsers provide support for the W3C Geolocation API Specification already (www.w3.org/TR/geolocation-API), PhoneGap applications running on a compatible device can use this API directly today (this is the default behavior for PhoneGap applications). For devices that don’t provide a Geolocation API such as BlackBerry devices running Device Software 5 or Android 1.x devices, the PhoneGap development team has included a compatible Geolocation API in the standard PhoneGap JavaScript library. For these devices, to enable a PhoneGap application to use PhoneGap’s implementation of the Geolocation API, simply invoke Geolocation.usePhoneGap() once the PhoneGap deviceready event has fired.

The implementation of this API is structured very similarly to how the Accelerometer (Chapter 10) and Compass (Chapter 13) APIs work. You’ll find that the examples provided in this chapter are very similar to what has already been shown in those chapters.

Getting a Device’s Current Location

To manually determine the location of a smartphone, an application should execute the following code:

navigator.geolocation.getCurrentPosition(onGeolocationSuccess,
  ongeolocationError);

The call to getCurrentPosition includes the callback functions onLocationSuccess and onLocationError. The onLocationSuccess is executed when the device’s location has been successfully measured, and the onLocationError function is executed if there is an error measuring the device’s location.

An application can also configure options that control the way in which the API measures location, as shown in the following example:

var geolocationOptions = {
  timeout : 3000,
  enableHighAccuracy : true
};
navigator.geolocation.getCurrentPosition(onGeolocationSuccess,
  ongeolocationError, geolocationOptions);

In this example, a geolocationOptions object is passed to the call to getCurrentPosition. The available properties for geolocationOptions are as follows:

enableHighAccuracy: Boolean value indicating whether the application would like to measure the device’s location with a higher degree of accuracy. When enabling this option, the application may obtain more accurate results, but at the same time, it will place a higher load on the device, which may decrease performance and battery life while the application runs.

frequency: Defines in milliseconds how often the location is measured. This option applies only when implementing a location watch (described later in this chapter). This is a PhoneGap-specific setting, and as PhoneGap more closely implements the W3C specification, this property will be removed. Developers should use the maximumAge property instead.

maximumAge: Defines the maximum age (in milliseconds) of a cached location value that will be accepted by the application. A smaller value for this property should force the API to only deliver more recent location updates.

timeout: The maximum amount of time (in milliseconds) that is allowed to pass between the call to either geolocation.getCurrentPosition or geolocation.watchPosition until the onGeolocationSuccess callback is executed.

After the API measures the device’s location, it executes the onGeolocationSuccess callback function. Passed to the function is a location object that exposes coords properties an application can use to understand the device’s location. The following function illustrates the properties that are exposed through the location object:

function onGeolocationSuccess(loc) {
  //We received something from the API, so first get the
  // timestamp in a date object so we can work with it
  var d = new Date(loc.timestamp);
  //Then replace the page's content with the current
  // location retrieved from the API
  lc.innerHTML = '<b>Current Location</b><hr />' +
    '<b>Latitude</b>: ' + loc.coords.latitude +
    '<br /><b>Longitude</b>: ' + loc.coords.longitude +
    '<br /><b>Altitude</b>: ' + loc.coords.altitude +
    '<br /><b>Accuracy</b>: ' + loc.coords.accuracy +
    '<br /><b>Altitude Accuracy</b>: ' +
    loc.coords.altitudeAccuracy +
    '<br /><b>Heading</b>: ' + loc.coords.heading +
    '<br /><b>Speed</b>: ' + loc.coords.speed +
    '<br /><b>Timestamp</b>: ' + d.toLocaleString();
}

When an error occurs while measuring the device’s location, the onGeolocation Error callback function is executed. Passed to the function is an error object that exposes the code and message properties, as shown in the following example:

function onGeolocationError(e) {
  var msgText = "Geolocation error: #" + e.code + " " +
    e.message;
  console.log(msgText);
  alert(msgText);
}

Example 19-1 illustrates how to use the getCurrentPosition method in an application. The application exposes a single button that, when clicked, calls getCurrentPosition and updates the screen with the device’s current location.


Example 19-1

<!DOCTYPE html>
<html>
  <head>
    <title>Example 19-1</title>
    <meta name="viewport" content="width=device-width,
      height=device-height initial-scale=1.0,
      maximum-scale=1.0, user-scalable=no;" />
    <meta http-equiv="Content-type" content="text/html;
      charset=utf-8">
    <script type="text/javascript" charset="utf-8"
      src="phonegap-1.2.0.js"></script>
    <script type="text/javascript">

      //Location content
      var lc;
      //PhoneGap Ready variable
      var pgr = false;

      function onBodyLoad() {
        //During testing, Let me know we got this far
        alert("onBodyLoad");
        //Add the PhoneGap deviceready event listener
        document.addEventListener("deviceready", onDeviceReady,
          false);
      }

      function onDeviceReady() {
        //During testing, Let me know PhoneGap actually
        // initialized
        alert("onDeviceReady");
        //Get a handle we'll use to adjust the accelerometer
        //content
        lc = document.getElementById("locationInfo");
        //Set the variable that lets other parts of the program
        //know that PhoneGap is initialized
        pgr = true;
      }

      function getLocation() {
        alert("getLocation");
        if(pgr == true) {
          var locOptions = {
            timeout : 5000,
            enableHighAccuracy : true
          };
          //get the current location
          navigator.geolocation.getCurrentPosition(
          onGeolocationSuccess, onGeolocationError,
            locOptions);
        //Clear the current location while we wait for a
        //reading
        lc.innerHTML = "Reading location...";
      } else {
        alert("Please wait, PhoneGap is not ready.");
      }
    }

    function onGeolocationSuccess(loc) {
      alert("onLocationSuccess");
      //We received something from the API, so first get the
      // timestamp in a date object so we can work with it
      var d = new Date(loc.timestamp);
      //Then replace the page's content with the current
      // location retrieved from the API
      lc.innerHTML = '<b>Current Location</b><hr />' +
        '<b>Latitude</b>: ' + loc.coords.latitude +
        '<br /><b>Longitude</b>: ' + loc.coords.longitude +
        '<br /><b>Altitude</b>: ' + loc.coords.altitude +
        '<br /><b>Accuracy</b>: ' + loc.coords.accuracy +
        '<br /><b>Altitude Accuracy</b>: ' +
        loc.coords.altitudeAccuracy +
        '<br /><b>Heading</b>: ' + loc.coords.heading +
        '<br /><b>Speed</b>: ' + loc.coords.speed +
        '<br /><b>Timestamp</b>: ' + d.toLocaleString();
    }

    function onGeolocationError(e) {
      alert("Geolocation error: #" + e.code + " " +
        e.message);
    }
  </script>
</head>
<body onload="onBodyLoad()">
  <h1>Geolocation API Demo #1</h1>
  <p>
    Click the button to determine the current location.
  </p>
  <input type="button" value="Refresh Location"
    onclick="getLocation();">
  <hr />
  <p id="locationInfo"></p>
 </body>
</html>


Figure 19-1 shows Example 19-1 running on a BlackBerry Torch 9800 simulator.

Image

Figure 19-1 Example 19-1 running on a BlackBerry Torch 9800 simulator

Watching a Device’s Location

Instead of checking the device’s location manually, an application can define a geolocation watch that causes the device’s location to be passed to the program periodically.

Setting a Watch

To create a location watch, an application should execute the following code:

watchID = navigator.geolocation.watchPosition(
  onGeolocationSuccess, onGeolocationError);

In this example, a watchID variable is assigned to the result of the operation; an application can use the watchID at a later time to cancel the watch. As with other PhoneGap APIs, the method is passed the names of two functions, the onGeolocationSuccess and onGeolocationError functions, which are executed after the device’s location has been successfully measured and when there is an error measuring the device’s location.

As with the call to getCurrentLocation, the watchPosition method can accept a geolocationOptions object to control configuration parameters for the watch. In the following example, the geolocationOptions object is configured to ignore cached values older than 10 seconds (10,000 milliseconds), to time out if a response isn’t received within 5 seconds (5,000 milliseconds), and to try to measure with higher accuracy when determining the device’s location.

var geolocationOptions = {
  maximumAge : 10000,
  timeout : 5000,
  enableHighAccuracy : true
};
//get the current location
watchID = navigator.geolocation.watchPosition(
  onGeolocationSuccess, onGeolocationError, geolocationOptions);

The onGeolocationSuccess function is passed a geolocation object, loc in this example, which exposes the same geolocation properties described in the previous section.

function onGeolocationSuccess(loc) {
  //We have a new location, so get the timestamp in a date
  // object so we can work with it
  var d = new Date(loc.timestamp);
  //Replace the page's content with the current
  //location retrieved from the API
  $('#locationInfo').html('<b>Latitude</b>: ' +
    loc.coords.latitude + '<br /><b>Longitude</b>: ' +
    loc.coords.longitude + '<br /><b>Altitude</b>: ' +
    loc.coords.altitude);
  $('#timestampInfo').prepend(d.toLocaleString() +
    '<br />'),
}

In this example, a subset of the location information is built into some HTML markup for cleaner rendering on the application screen and then added to the locationInfo division of the page using the $() function from jQuery (www.jquery.com). The application also maintains a reverse chronological (newest at the top) history of timestamp information in the timestampInfo division of the page (you can see the HTML markup for the page and the complete application code in the listing for Exercise 19-2 later in the chapter). When the application runs, it will display a screen similar to the one shown in Figure 19-2.

Image

Figure 19-2 Exercise 19-2 running on a BlackBerry Torch 9800 simulator

As you can see from the figure, the onGeolocationSuccess function is being executed every two seconds, even if the device hasn’t moved. When I first started working with this API, I expected that some sort of trigger could be defined that could be used to minimize the number of times the onGeolocationSuccess function would be fired, but that turned out to not be the case. No matter what values I entered for maximumAge and timeout, the watch returns geolocation information every two seconds.

If you think about it, querying for geolocation every two seconds will likely put a big load on the device and likely affect both application and overall device performance plus reduce battery life. To help reduce the impact on performance, I rewrote the function so it captures the previous longitude and latitude values and updates the location information on the screen only when there’s been a change, as shown in the following example:

function onGeolocationSuccess(loc)
  //We have a new location, so get the timestamp in a date
  // object so we can work with it
  var d = new Date(loc.timestamp);
  //Has anything changed since the last time?
  if(lastLat != loc.coords.latitude ||
    lastLong != loc.coords.longitude) {
    //Then replace the page's content with the current
    // location retrieved from the API
    $('#locationInfo').html('<b>Latitude</b>: ' +
      loc.coords.latitude + '<br /><b>Longitude</b>: ' +
      loc.coords.longitude + '<br /><b>Altitude</b>: ' +
      loc.coords.altitude);
    $('#timestampInfo').prepend(d.toLocaleString() +
      '<br />'),
    lastLat = loc.coords.latitude;
    lastLong = loc.coords.longitude;
  } else {
    $('#timestampInfo').prepend('Skipping: ' +
      d.toLocaleTimeString() + '<br />'),
  }
}

Depending on your application, you could easily modify this approach so it takes into consideration how much the location has changed before updating the screen or doing something time-consuming or processor-consuming within the application. Figure 19-3 shows the same application with that fix implemented; in other words, it shows the geolocation information being updated only when the values actually change.

Image

Figure 19-3 Updated Exercise 19-2 running on a BlackBerry Torch 9800 simulator

The onGeolocationError function is the same as the one used in the previous section:

function onGeolocationError(e) {
  var msgText = "Geolocation error: #" + e.code + " " +
    e.message;
  console.log(msgText);
  alert(msgText);
}

Canceling a Watch

Once a watch has been created, it can be canceled using the saved watchID variable and the following code:

navigator.geolocation.clearWatch(watchID);

There’s no callback function required, but you could do a little extra work to help out your user, as shown in the following function example:

function cancelWatch() {
  //Clear the watch
  navigator.geolocation.clearWatch(watchID);
  //Clear the watch ID (just because)
  watchID = null;
  //Let the user know we cleared the watch
  alert("Location Watch Cancelled");
}

In the example, I clear the watch and then null out the watchID variable just to make sure it’s not available in case the program accidently tries to clear the same watch later. We don’t have an onGeolocationError function to execute if this fails, so it might even be better if you wrap the call into a try/catch block and deal with any errors that might occur directly.

Example 19-2 lists the complete HTML markup and application code for the application illustrated in Figure 19-3.


Example 19-2

<!DOCTYPE html>
<html>
  <head>
    <title>Example 19-1</title>
    <meta name="viewport" content="width=device-width,
      height=device-height initial-scale=1.0,
      maximum-scale=1.0, user-scalable=no;" />
    <meta http-equiv="Content-type" content="text/html;
      charset=utf-8">
    <script type="text/javascript" charset="utf-8"
      src="jquery-1.7.1.js"></script>
    <script type="text/javascript" charset="utf-8"
      src="phonegap.js"></script>
    <script type="text/javascript">

      var watchID, lastLong, lastLat;

      function onBodyLoad() {
        //Add the PhoneGap deviceready event listener
        document.addEventListener("deviceready", onDeviceReady,
          false);
      }

      function onDeviceReady() {
        //Create the watch
        startWatch();
      }

      function startWatch() {
        //Clear out the previous content on the page
        $('#locationInfo').empty();
        $('#timestampInfo').empty();
        //Show and hide the appropriate buttons
        $('#btnStart').hide();
        $('#btnCancel').show();
        //Geolocation Options
        var locOptions = {
          maximumAge : 10000,
          timeout : 5000,
          enableHighAccuracy : true
        };
        //get the current location
        watchID = navigator.geolocation.watchPosition(
          onLocationSuccess, onLocationError, locOptions);
      }

      function onLocationSuccess(loc) {
        //We have a new location, so get the timestamp in a date
        // object so we can work with it
        var d = new Date(loc.timestamp);
        //Has anything changed since the last time?
        if(lastLat != loc.coords.latitude ||
          lastLong != loc.coords.longitude) {
          //Then replace the page's content with the current
          // location retrieved from the API
          $('#locationInfo').html('<b>Latitude</b>: ' +
            loc.coords.latitude + '<br /><b>Longitude</b>: ' +
            loc.coords.longitude + '<br /><b>Altitude</b>: ' +
            loc.coords.altitude);
          $('#timestampInfo').prepend(d.toLocaleString() +
            '<br />'),
          lastLat = loc.coords.latitude;
          lastLong = loc.coords.longitude;
        } else {
          $('#timestampInfo').prepend('Skipping: ' +
            d.toLocaleTimeString() + '<br />'),
        }
      }
      function onLocationError(e) {
        alert("Geolocation error: #" + e.code + " " +
          e.message);
      }

      function cancelWatch() {
        //Clear the watch
        navigator.geolocation.clearWatch(watchID);
        //Clear the watch ID (just because)
        watchID = null;
        //Hide the cancel button so they can't cancel it again.
        $('#btnCancel').hide();
        $('#btnStart').show();
        //Let the user know we cleared the watch
        alert("Watch Cancelled");
      }
    </script>
  </head>
  <body onload="onBodyLoad()">
    <h1>Geolocation API Demo #2</h1>
    <input type="button" value="Cancel"
      onclick="cancelWatch();" id="btnCancel">
    <input type="button" value="Start"
      onclick="startWatch();" id="btnStart">
    <br />
    <b>Location</b>
    <hr />
    <div id="locationInfo"></div>
    <br />
    <b>Timestamp</b>
    <hr />
    <div id="timestampInfo"></div>
  </body>
</html>


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset