beta
Hello developer. Login with your existing account. New to Vodafone Developer? Register your account.

+ Login or create an account

Geolocation Code Notes

Introduction

The Vodafone developer Widget Samples series demonstrates how to use the JIL 1.2.2/WAC 1.0 widget APIs. These notes document the Geolocation sample widget.

The JIL geolocation API enables widgets to request geographic position information associated with the hosting device. It is a "one-shot" API that is similar to the one-shot request defined by the W3C Geolocation API. For the relevant W3C links see Find out more, below.

The ability to request, track, and use device location gives mobile apps a unique edge compared to the desktop. The JIL geolocation API is easy to use, does exactly what it says, and makes it easy to give your apps that mobile edge.

Download the installable widget package and source files here - geolocation.zip (9.6 KB)

You can find more samples in this series at Widgets Getting started .

JIL APIs used

The Geolocation widget uses the following JIL APIs:

  • Widget.Device.PositionInfo
  • Widget.Device.DeviceStateInfo

JIL Features declared

The Geolocation widget declares the following JIL features:

Required features:
  • http://jil.org/jil/api/1.1/widget
  • http://jil.org/jil/api/1.1/device
Optional features:

To use the JIL geolocation APIs, declare the following features:

  • http://jil.org/jil/api/1.1/devicestateinfo
    • The JIL DeviceStateInfo object provides access to properties and methods relevant to the dynamic state of the device, including making location requests. DeviceStateInfo is a sub-object of Widget.Device.
  • http://jil.org/jil/api/1.1/positioninfo
    • The JIL PositionInfo object encapsulates location data including latitude, longitude, cellID, and accuracy. PositionInfo is a sub-object of Widget.Device.

Compatibility

  • Android: Yes
  • Symbian: Yes
  • Nokia N8: Yes

Overview

This widget demonstrates how to use the JIL geolocation API. The API is easy to use: it consists of a request method, a callback handler to handle the asynchronous result, and a PositionInfo object that encapsulates the returned location information.

On launch, the widget displays a "Current Position" Info box and "Get Location" and "Show On Map" buttons.

Tapping "Get Location" initiates a JIL position request. If successful, the returned location information is displayed in the Info box. Otherwise, a failure message is displayed.

Tapping "Show On Map" loads a map centred on the current device location using the Google Static Maps API and displays a location marker.

The screen shots below show the Geolocation widget running on the Sony Ericsson Xperia X10 mini.

The JIL geolocation API

The JIL geolocation API consists of a request method, a callback function, and a location properties object:

  • Call void Widget.Device.DeviceStateInfo.requestPositionInfo( <String> method) to initiate an asynchronous location request, passing a method string to specify the data source; possible values are gps, agps, and cellid.
  • Implement callback function void Widget.Device.DeviceStateInfo.onPositionRetrieved( <PositionInfo> locationinfo <String> method), passing a PositionInfo object that will be updated if the request succeeds, and a method string to specify the data source, which should match the method string of the location request.
  • Widget.Device.PositionInfo is a read-only object that encapsulates device location data including accuracy, altitude, latitude, longitude, cellid, and timestamp properties. The altitude value is in meters; altitude, latitude, and longitude values use the WGS84 datum, see Find out more below. Where data is unavailable, values are set to null.

The data sources that can be specified in the request method parameter are:

  • agps – Assisted GPS (A-GPS), augments satellite-only GPS with network data and/or network-based processing of satellite data to improve availability (indoor and other difficult locations) and time taken to acquire position information
  • gps – Satellite-only GPS, uses the GPS satellite network to calculate device position
  • cellid – Network cell identity provided by the network operator

A-GPS is faster than "pure" GPS, and is recommended unless you specifically need an alternative method (e.g. you might be using a cell ID database like OpenCellID to map a position to a named location).

Code notes

The Geolocation sample widget demonstrates how to make a location request and handle the asynchronous result, including displaying the returned position on a map tile. You can reuse the code as a starting point for creating your own location aware apps. The sample uses Google's open mapping APIs to request a map tile; the basic Google API no longer requires an API key.

Of course, there are many more ways to use location data than just displaying positions on maps, including:

  • Geotagging, i.e. tagging mobile-originated data with location information, for example photos and videos
  • Updating social network location tags from mobile apps
  • Keying local information by location to find nearby shops or services, for example bus and train routes

The sample consists of the following files:

  • config.xml
  • geolocation.html
  • geolocation.js
  • common/js/util.js
  • common/css/base.css
  • common/css/forms.css
  • common/css/layout.css

The notes below walk through the code in more detail.

config.xml

Every widget must provide a config.xml configuration file that defines the widget package and is used at install time by the runtime.

For a JIL widget, this is also where you should declare the JIL features your widget uses, including required features and optional features. Note that the widget and device declarations are required by all JIL APIs, and should always be declared as required="true". It is a design choice whether you declare additional JIL APIs your widget uses as required or optional, i.e. required="false":

  • Declaring a feature as required with required="true" will cause installation to fail if the device does not support that specific feature
  • Declaring a feature as optional with required="false" will enable installation, but cause a warning to be displayed if the device does not support that specific feature

The Geolocation sample widget declares JIL features as follows:

<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets"
   xmlns:jil="http://www.jil.org/ns/widgets1.2" ... >
<name>geolocation

	...
<feature name="http://jil.org/jil/api/1.1/widget" required="true"/>
<feature name="http://jil.org/jil/api/1.1/device" required="true"/>
<description>An example GPS/LocationInfo widget.</description>
<feature name="http://jil.org/jil/api/1.1/devicestateinfo"
   required="false"/>
<feature name="http://jil.org/jil/api/1.1/positioninfo" required="false"/>
</widget>

For more general comments on the structure and contents of config.xml for JIL widgets, see the Basic App code notes.

geolocation.html

The HTML page uses the HTML5 DOCTYPE declaration <!DOCTYPE html>. The code is straightforward: it loads page styles using style sheet <link> tags, defines all page elements, and finally loads the JavaScript code using <script> tags.

Page elements include:

  • Fixed position header and footer <div> elements
  • Container <div> elements for the two screen views, screen-main and screen-map, and body <div> elements for each
  • A modifiable info box main-info <div> used to display the location information
  • Container <p> elements used to hold the buttons for the screen-main and screen-map pages
  • <button> elements

All buttons are declared as custom buttons using the type="button" attribute/value assignment; custom buttons have no preset behaviour. See User interaction and event handling below for more about how button events are captured and handled.

Note that the "Show On Map" button's initial state is set disabled:

<button type="button" name="action" value="show-map"
   disabled class="button" id="map-button">Show On Map</button>

In XHTML this would be declared as disabled="disabled".

geolocation.js

The geolocation.js code performs the following main functions:

  • Initialises the main page and handles user input
  • When "Get Location" is pressed – makes a location request using the JIL geolocation API, handles the asynchronous result, updates the Info box with the current location
  • When "Show On Map" is pressed – launches a map with location marker to show the current location

See the Basic App sample code notes for more general discussion about structuring your widget JavaScript code, including the app encapsulation pattern, DOM handling and use of local objects to reduce the scope chain, and other aspects of initialisation.

Requesting location and handling the asynchronous result

Using the JIL geolocation API is very straightforward. The location request consists of a single statement:

Widget.Device.DeviceStateInfo.requestPositionInfo("agps");

Handling the result is straightforward too: declare a positionInfo object and implement the required callback method, which should:

  • Verify that values have been returned in the positionInfo object
  • If so, extract the values and use them

For example:

// Assume the positionInfo object posInfo has been declared
onPositionRetrieved: function(posInfo) {
    // Check to see that values were returned
    if(posInfo.latitude === null || posInfo.longitude === null){
        return app.onPositionRetrieveFail();
    }
    // Otherwise...
    // Extract the values from posInfo.latitude, posInfo.longitude,
    // and do something with them
    ...
}

In practice there is a bit more to do than this, as you can see in geolocation.js.

Firstly, positioning using GPS can have unpredictable results. To use GPS at all, the device must have a "sky view", which means that it won't work inside most buildings or in some urban environments where tall buildings can create a "canyon" effect and block GPS signals. To ensure that a GPS location request does not wait forever when a satellite signal cannot be found, the JIL API implements a timeout, the duration of which depends on the runtime implementation (and which may therefore vary between different runtimes and runtime versions). There is no cancellation method in the API to enable a location request to be cancelled; the only way it can be terminated is for the runtime timeout to expire. This means that the only way you can be sure that a request has not succeeded is to wait for it to timeout. However, this makes your app dependent on the timeout period chosen by the runtime. If instead you implement your own timeout (as the sample does), be aware that it is possible for your app timeout to expire but for the API to still return a result successfully before the runtime timeout expires.

Secondly, it is important to handle any exceptions the API might throw. For example, on a specific device the API may not be supported, or the widget may not have the necessary permissions to use the API. In both cases the runtime will raise exceptions; see the JIL 1.2.2 API Overview for exception values.

In geolocation.js the location request is wrapped in a try-catch clause so that exceptions can be handled, and a local timeout is set on the request:

try {
    Widget.Device.DeviceStateInfo.requestPositionInfo("agps");
    app.positionRequestTimeout = setTimeout(app.onPositionRetrieveFail,
      app.POSITION_TIMEOUT_MS);
} catch(e) {
    app.onPositionRetrieveFail();
}

The value of POSITION_TIMEOUT_MS is set on initialisation to 180000 milliseconds (3 minutes).

Displaying location on a map

When a location has been successfully requested, showing it on a map using the Google Static Maps API is easy. Of course, you can use an alternative map provider if you already have one.

In geolocation.js the map is loaded and displayed by the goToMap() function:

goToMap: function() {
    var posInfo = app.positionInfo;
    var latLng = posInfo.lat + "," + posInfo.lng;
    // Create an image to display the map
    var map = new Image();
    ... // Set other image attributes
    map.src = "http://maps.google.com/staticmap?" + [
        "center=" + latLng,
        "zoom=14",
        "size=500x500",
        "maptype=mobile",
        "format=jpeg", // smallest filesize, best quality: "png" or "png8"
        // "key=abcde", // ADD YOUR GOOGLE MAPS API KEY HERE
        "markers="+latLng
        ].join("&");
    ... // The image can now be displayed
}

Here, the JavaScript DOM API is used to create a new HTML image element, and its src attribute is then set to the value of a Google Maps API request i.e. to a returned map tile.

The sample code assumes that you have a Google Maps API key. If you don't have a key, you can use the alternative format of the Static Maps V2 API without a key, for example with the following code:

map.src = "http://maps.google.com/maps/api/staticmap?" + [
    "center=" + latLng,
    "zoom=14",
    "size=500x500",
    "maptype=hybrid",
    "mobile=true",
    "format=jpeg",
    "markers="+latLng,
    "sensor=true"
    ].join("&");

The base query URL changes to http://maps.google.com/maps/api/staticmap? and the maptype parameter is expanded from maptype="mobile" to "maptype=hybrid&mobile=true"; no API key is required. For more about Google Static Maps See Find out more, below.

User interaction and event handling

The event handling code in geolocation.js demonstrates some useful techniques for handling user input.

In the W3C event model, events are always propagated or "bubbled" from the DOM node i.e. HTML element on which they occur, up through the node hierarchy. The custom buttons declared in geolocation.html generate events via the parent <p> elements in which they are declared:

<p class="buttons" id="main-controls">
    <button type="button" name="action" value="get-location"
       class="button button-main" id="location-button">Get Location</button>
    <button type="button" name="action" value="show-map" disabled
       class="button" id="map-button">Show On Map</button>
</p>

and:

<p class="buttons">
    <button type="button" id="back-to-main" class="button">Back to main screen</button>
</p>

In geolocation.js the button event handlers are attached as part of app initialisation:

_initMainScreen: function() {
    document.getElementById("main-controls").addEventListener("click",
       app.mainControlsHandler, false);
    },
_initMapScreen: function(){
    var dom = app.dom;
    util.hide(dom.mapScreen);
    dom.backToMain.addEventListener("click", app.goToMain, false);
}

In _initMainScreen() above, the main-controls node is found by id and the event handler is set.

In _initMapScreen() above, the back-to-main node has already been cached in the app.dom object:

app.dom = {
    ...// More assignments here
    mapScreen: d.getElementById("screen-map"),
    backToMain: d.getElementById("back-to-main")
};

and is referenced directly to set the event handler.

The final false argument of addEventListener() sets the handler to capture bubbled events; if true is used instead, the event can only be captured on the originating element.

When a button is clicked, since there are no handlers set directly on the buttons, the event propagates and is captured by the handler set on the containing <p> element.

Events captured on the main-controls <p> element are handled by mainControlsHandler(). The originating button element is identified from the event's target property, the button name is verified against its name="action" attribute/value, and its value attribute is switched on, either value="get-location" or value="show-map" depending on which button was pressed:

mainControlsHandler: function(event){
    var t = event.target;
    if(t.name != "action"){ // Expect name="action", otherwise
                            // it's not a button
    return;
    }
    //
    switch(t.value){
    case "get-location":
        // The "Get Location" button has been pressed
	... // Handle the event
        break;
    case "show-map":
        // The "Show On Map" button has been pressed
        ... // Handle the event
        break;
    }
}

Events captured on the back-to-main <p> element are handled by goToMain(), which switches from the map screen back to the main screen:

goToMain: function(){
    var dom = app.dom;
    util.hide(dom.mapScreen);
    util.show(dom.mainScreen);
}

Common

The Geolocation sample uses util.js for some generic JavaScript utilities, for example hiding and showing page elements, and uses the common style sheets common/css/base.css, common/css/forms.css, and common/css/layout.css.

The base.css and layout.css styles, including the use of media queries and the fixed header and footer layouts, are discussed in the Basic App sample code notes.

forms.css

The forms.css style sheet contains basic button and label styles.

Concluding remarks

As the Geolocation sample demonstrates, it is very easy to make widgets location aware using the JIL geolocation API. The basic use-case is a simple "one-shot" request with no cancellation.