Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

The Java Location API


January, 2006: The Java Location API

David is a senior lecturer in the Institute of Information and Mathematical Sciences at Massey University. He can be contacted at [email protected].


The Java Location API for J2ME is intended to run on small client devices such as mobile phones. It integrates generic positioning and orientation data with persistent storage of point of interest (POI) objects known as "Landmarks." Although there are phones and software tools, including models and emulators from Ericsson and Nokia, that implement this API, developers wishing to work with it have been constrained by a limited set of options for simulating location data. Both the current Nokia and Ericsson tools, for instance, limit input to GPS data (from a log file, for example), while the CityGuide demo application shipped with the J2ME Wireless Toolkit (Version 2.3) uses an XML file of 'waypoints' as input. For a more interactive approach, I present in this article an open-source map-based simulation environment designed to work with the Sun J2ME Wireless Toolkit (http://java.sun.com/products/ j2mewtoolkit/index.html).

lif/lifindex.html). This protocol works on the assumption that the system is a device-aware cellular network, and that location information is pulled in XML format from a server-hosted API. In contrast, the Location API for J2ME (http://jcp.org/aboutJava/ communityprocess/final/jsr179/index.html) is a client-side generic location interface intended to work with many different positioning methods. Generic interfaces let you implement systems that span multiple sources of location information at the same time. This becomes increasingly important as devices and channels for tracking locations increase, letting us aggregate and prioritize different information sets that relate to the same target, as well as being able to choose between multiple methods of determining the location of a single device. This provides a number of advantages, including fail-over, indoor/outdoor transparency, and a choice between the speed and accuracy trade-offs that could be made between GPS, cellular, or other positioning mechanisms. In this article, I first examine the object model used in the Location API, then code examples that you can explore using the map-based simulator, which can be freely downloaded.

The Location API Object Model

The Location API object model consists of 11 classes and two listener interfaces (LocationListener and ProximityListener), all in the javax.microedition.location package. Their design approach uses several standard patterns—Facade, Factory Method, Singleton, and Value Object, and standard JavaBeans-style accessors. Of the 11 classes, two are Exception classes (LocationException and LandmarkException) and another four (AddressInfo, Criteria, Orientation, and QualifiedCoordinates) are primarily value objects. Many of the properties of these objects may in practice be unavailable, depending on the location-finding technology implementing the API. Still, these properties anticipate likely future developments in mobile networks and devices and the level of location and context detail that they provide.

Location objects are immutable aggregates of AddressInfo and QualifiedCoordinates objects. Location instances are acquired from a LocationProvider, a facade to the mobile device's underlying location information that consists of a factory method (parameterized by a Criteria object), to retrieve a LocationProvider instance, methods to return current or last-known Location objects, and methods to register listeners for location and proximity events. The Coordinates class (the superclass of QualifiedCoordinates) encapsulates geometric methods such as calculating the azimuth (angle) and distance between locations. The Orientation class is completely separate from the rest of the object model, having no association or dependency relationships with any other classes. This is presumably because not all devices can support orientation information. At minimum, the device must be able to provide a compass azimuth value to support Orientation objects, with optional support for pitch and roll values. Orientation objects can be derived either from a factory method or a parameterized constructor.

The classes discussed so far encapsulate the subset of the API that is directly related to the acquisition of location information from whatever underlying technology is available to the device. Figure 1 summarizes these classes.

The SimpleLocation MIDlet I present here (available electronically; see "Resource Center," page 4) displays the current location of the device on the screen. The interaction with the Location API in this example is straightforward. First, you access the LocationProvider using the static factory method getInstance. The parameter is a Criteria object that can be used to set quality of service parameters; but in Listing One(a), I pass a null reference. The LocationProvider can now be used to get the current location. The parameter to the getLocation method is the timeout period; see Listing One(b).

Once you have a Location object, you can use it to find out the current latitude, longitude, altitude, direction, and speed by accessing the QualifiedCoordinates object that is aggregated inside it:

coordinates =
location.getQualifiedCoordinates();

A location may also contain an AddressInfo object, with details such as the location's postal address, phone number, country, URL, and the like. Depending on the implementation and context, however, there may not actually be an AddressInfo associated with a given location. For example, if you are standing in the middle of a field, there will be no AddressInfo data. In the simulator implementation described here, the AddressInfo is always null.

The current course, speed, and altitude are returned from the QualifiedCoordinates object as float values, while latitude and longitude are returned as doubles. To assist the display of location data, the Coordinates class includes a method to convert a coordinate object into a String representation in either of two formats:

  • Format 1. Degrees, Minutes, and decimal fractions of a minute. For example, for the double value of the coordinate 61.51d, the formatted string is 61:30:36.
  • Format 2. Degrees, Minutes, Seconds, and decimal fractions of a second. For example, for the double value of the coordinate 61.51d, the formatted string is 61:30.6.

In the example MIDlet (available electronically), I use Format 2, selected using the constant field Coordinates.DD_MM_SS. Alternatively, Format 1 can be selected using Coordinates.DD_MM. Listing Two is the MIDlet code where the location information is displayed on the screen, using a series of StringItems. Figure 2 is from the J2ME Wireless Toolkit with the current location details displayed.

Using the Location and Proximity Listeners

The LocationProvider can register two types of listener—a single LocationListener and/or multiple ProximityListeners (Figure 3). These listeners help us to create more dynamic location-based services that can be triggered by location-related events.

In the previous example, I used the LocationProvider to find out the current location whenever the Refresh button was pressed on the phone. This is not a flexible approach and it would be better if you could automatically trigger an event whenever the current location changed, without having to make an explicit call to getLocation. You can achieve this behavior by using the LocationListener interface, which you can use to listen for updates to the current location. Only one LocationListener can be registered with the LocationProvider at any one time, so this listener can therefore act as a kind of Singleton for all components that need location information. The LocationListener is updated with current location information at specified intervals and can expose and process that information in an application-specific context to be accessed by other components on demand.

In the next example, I make the MIDlet itself the LocationListener (available electronically). To listen to location events, the MIDlet must implement the LocationListener interface; see Listing Three(a). Once the MIDlet implements the LocationListener interface, it can be registered with the LocationProvider as the current LocationListener via the setLocationListener method. The parameters to this method are the listener, interval between updates, timeout period, and maximum acceptable age for location information; see Listing Three(b).

The listener interface contains two methods—locationUpdated and providerStateChanged. The LocationProvider triggers the locationUpdated method whenever the location changes. The providerStateChanged method is informed when the state of the LocationProvider changes between its three possible states: LocationProvider.AVAILABLE, LocationProvider .TEMPORARILY_UNAVAILABLE, and LocationProvider.OUT_OF_SERVICE. In the MIDlet, only the locationUpdated method is implemented, calling the method that refreshes the screen form; see Listing Three(c). With these few changes to the MIDlet, you can automatically update the location display whenever the location has changed, without having to press the Refresh button and do a manual update.

In the previous example, I illustrated how a mobile application can use the LocationListener interface to listen for updates to the current location. In addition, you can implement the ProximityListener interface, which lets you trigger events when you approach specified locations. Unlike the LocationListener, which can have only one registered instance, the LocationProvider lets you register multiple ProximityListeners. The ProximityListener MIDlet (available electronically) adds proximity monitoring to the existing location monitoring; see Listing Four(a). This time when the MIDlet starts up, it registers itself three times so that you can listen for proximity events for three different locations (in this case, three buildings on the map used in the simulator). Each registration requires the listener, coordinates, and required proximity radius from these coordinates as parameters; see Listing Four(b).

The ProximityListener interface has two methods—monitoringStateChanged and proximityEvent. The monitoringStateChanged method informs listeners whether proximity notification is currently active. The proximityEvent method provides two parameters—the coordinates originally registered with the LocationProvider and the current location. In this implementation, I compare the given coordinates with the ones I am listening for and select the matching location; see Listing Four(c). The various alerts display an image of the appropriate building. Figure 4 shows the proximity alert for the Study Center displayed on the mobile device.

Landmarks and the LandmarkStore

What particularly distinguishes this API from other location-based class libraries is the use of local storage to provide a persistent database of landmarks. This shifts the emphasis onto the mobile client in terms of location-aware applications, enabling local mappings from physical positions to points of interest. This approach means that at least part of a location-aware application can be installed on the mobile device rather than on the server. One key advantage of this is that applications are more likely to have useful functionality, even where network connectivity is unreliable.

The API includes two classes to support the persistent storage of location-related data—Landmark and LandmarkStore (Figure 5). Landmark objects, like Location objects, are partial aggregates of AddressInfo and QualifiedCoordinates objects, but Landmarks and Locations have different roles in the architecture. Location objects are immutable and transitory, reflecting the dynamic movement of a mobile device. In contrast, Landmark objects are intended to be persisted in the mobile data store and are mutable, so they might be updated over time.

The LandmarkStore acts as a facade to the underlying data store on the device, and is a collection of landmarks. There can be many LandmarkStores on a device, shared by multiple applications. Landmarks may optionally be stored under a category name, and may be added to multiple stores and multiple categories. The only restriction is that a Landmark cannot be added to the same category in the same LandmarkStore more than once. Be aware, however, that the local data store has restricted capacity, with storage limited to no more than a few hundred landmarks.

An important feature of the API is that a Landmark object can be populated from the coordinate and address data of a Location generated by the LocationProvider (provided, of course, that the LocationProvider is able to include an AddressInfo along with the QualifiedCoordinates). This means that Landmarks can be dynamically added to the store.

In addition, Landmark objects already in the LandmarkStore can be linked with Location objects in terms of listener behavior (Figure 6). A mobile application can register multiple ProximityListeners that can be triggered when the current location is within a specific range of a given Landmark. When a proximity event occurs, the appropriate Landmark can be retrieved from the store.

There are two basic scenarios for an application that utilizes the LandmarkStore. First, the store can be a small, static collection of application-specific Landmarks that rarely needs updating. ProximityListeners could be permanently registered to trigger proximity responses to the fixed landmarks. Systems that must deal with a larger set of Landmarks would require more dynamic provision of landmark information. In this context, the LandmarkStore would not contain preloaded objects, but would add/remove them dynamically, using suitable push/pull mechanisms. A hybrid approach might also be used, with batch replacements of data based on movement between larger areas. ProximityListener registration/deregistration would also need to be dynamic, or alternatively, instead of using Proximity Listeners, the API would support searching the LandmarkStore for Landmarks that fall within a specified area.

To keep things simple, the final example (ProximityListener MIDlet; available electronically) assumes a prepopulated LandmarkStore that you use to display information about Landmarks when triggered by a proximity listener. In the previous example, I used a ProximityListener to display alerts when I came close to certain coordinates. In this MIDlet, I display information from the LandmarkStore instead.

The first step is to get an instance of the LandmarkStore (the null parameter indicates the default store):

landmarkStore =
LandmarkStore.getInstance(null);

In this example, I create the Landmark objects at startup and write them to the LandmarkStore. The Landmark constructor requires a landmark name, description, set of coordinates, and AddressInfo:

Landmark landmark1 = new Landmark
("Study Centre",
"Building on campus",
studyCentreCoordinates, info);

Each landmark is added to the LandmarkStore using a category name, but categories must already exist before landmarks can be added to them. In this example, I first add the "campus" category to the LandmarkStore:

landmarkStore.addCategory("campus");

It is then possible to add a Landmark object to the store under the "campus" category:

landmarkStore.addLandmark(
landmark1, "campus");

Later in the MIDlet, I retrieve the needed landmark using the getLandmarks method, passing the names of the category and the Landmark as parameters:

Enumeration e =
landmarkStore.getLandmarks(
"campus", "Study Centre");
Landmark landmark =
(Landmark)e.nextElement();
AddressInfo info =
landmark.getAddressInfo();

Once the AddressInfo object is retrieved, it can be displayed onscreen (Figure 7).

Using the Location API For J2ME Simulator

To test the code, you need to install and run the map-based simulator (available electronically). The simulator consists of three components:

  • A Swing application that allows a user to control the direction and speed of a virtual mobile device moving across a map in a desktop environment. The virtual mobile device makes itself available to clients by way of the RMI registry.
  • A simple web application that reads position data from the Swing application and publishes it to HTTP clients via a JavaBean embedded in a JavaServer Page.
  • An implementation of the Location API for J2ME that can be deployed into the Sun J2ME Wireless Toolkit as a JAR file. This implementation acquires position data by connecting over HTTP to the web application.

Although this three-layer design may seem a little complex, it enables loose coupling between the Wireless Toolkit and simulator. Since the LocationProvider implementation could only work with libraries available on the CLDC/MIDP platform (or we would not be able to also use the implementation on a mobile device), it is not possible to use RMI directly from the Wireless Toolkit. One advantage of this architecture is that both simulated and actual wireless devices can easily access the same data from the web server. Figure 8 summarizes the various layers and components in the simulation system. Detailed instructions on how to configure and run the simulator can be found in the readme file in the downloadable archive.

What's Not In the API?

One feature that is present in some APIs, but not this one, is topological data, which is usually derived from the shapes of cell site coverage. Regardless of the underlying technology, area-of-interest data can be just as useful as point-of-interest data. The best you can do in this API is to define an area by its radius from a point, but the facility to acquire topological data when available might be useful in some applications. There is no support for location-based queries either, though these are fundamental to the use of server-hosted spatial databases (such as Oracle's) that support LBS. Perhaps a client-side query language could be a useful feature, enabling clients to build queries to be executed on the server. Other aspects of spatial databases such as yellow pages, routing, and mapping are also key components of location-based service, but could not reasonably be expected to be hosted on a client device. However, it might be useful if the API provided some facilities to interact with such systems. Perhaps in future versions of the spec, we will see extensions to the API to enable more functionality on the mobile client.

References

RI Binary for JSR-179 Location API for J2METM, 2004, Nokia. http://forum.nokia .com/info/sw.nokia.com/id/e7c4ed92-a6d7-4cbc-8a20-a31b17616ad9/jsr-179- ri-1_0abin(was1_1).zip.html; last accessed June 3rd, 2005, Redknee, Synaxis-2200: ELS Release 2.0.

Client Interface Specification Document, Redknee Inc., 2002, http://www.sourceo2 .com/O2_Developers/Tools/Location_API .htm; last accessed June 3rd, 2005.

Orange, Orange UK Location API, 2004, http://www.orangepartner.com/ site/enuk/tools/orange_network_apis/ orangelocationapi/p_orange_uk_location _api.jsp; last accessed June 3rd, 2005.

Ericsson, Mobile Positioning Protocol Specification Version 5.0, 2003, http://www .ericsson.com/mobilityworld/developerszonedown/downloads/docs/mobile_ positioning/mpp50_spec.pdf; last accessed June 3rd, 2005.

Oracle, Introduction to Location APIs, Oracle Corp., 2004, http://www.oracle.com/ technology/sample_codeproducts/iaswe/iASWE-LocationSample/doc/LocationAPI.html; last accessed June 3rd, 2005.

http://www.ericsson.com/mobilityworld/sub/open/technologies/mobile_ positioning; last accessed June 3rd, 2005.

DDJ



Listing One
(a)

try
{
  locationProvider = LocationProvider.getInstance(null);
}
  catch(LocationException e)
{
  // handle exception
}

(b)
try
{
  location = locationProvider.getLocation(20);
}
  catch(LocationException e)
{
  // handle exception
}
  catch(InterruptedException e)
{
  // handle exception
}
Back to article


Listing Two
latitude = new StringItem("Latitude: " +
  Coordinates.convert(coordinates.getLatitude(), Coordinates.DD_MM_SS), "");
longitude = new StringItem("Longitude: " +
  Coordinates.convert(coordinates.getLongitude(), Coordinates.DD_MM_SS), "");
altitude = new StringItem("Altitude: " + coordinates.getAltitude(), "");
direction = new StringItem("Course: " + location.getCourse(), "");
speed = new StringItem("Speed: " + location.getSpeed(), "");
locationForm.append(latitude);
locationForm.append(longitude);
locationForm.append(altitude);
locationForm.append(direction);
locationForm.append(speed);
display.setCurrent(locationForm);
Back to article


Listing Three
(a)
public class LocationListenerMIDlet extends MIDlet
  implements CommandListener, LocationListener

(b)
try
{
  locationProvider = LocationProvider.getInstance(null);
  locationProvider.setLocationListener(this, 20, 10, 10);
}
  catch(LocationException e)
{
  // handle exception
}

(c)
public void locationUpdated(LocationProvider provider, Location location)
{
  this.locationInfo();
}
Back to article


Listing Four
(a)
public class ProximityListenerMIDlet extends MIDlet 
  implements CommandListener, LocationListener, ProximityListener 

(b)
try
{
  locationProvider = LocationProvider.getInstance(null);
  locationProvider.setLocationListener(this, 20, 10, 10);
// radius used to trigger proximity alerts
  float radius = 100.0F;
// coordinates of the Study Centre
  locationProvider.addProximityListener(this, studyCentreCoordinates, radius);
// coordinates of the Quadrangle building
  locationProvider.addProximityListener(this, quadrangleCoordinates, radius);
// coordinates of the Atrium
  locationProvider.addProximityListener(this, atriumCoordinates, radius);
}

(c)
public void proximityEvent(Coordinates coordinates, Location location)
{
  if(coordinates.equals(studyCentreCoordinates))
  {
    showStudyCentreProximityAlert();
  }
  if(coordinates.equals(quadrangleCoordinates))
  {
     showQuadrangleProximityAlert();
  }
  if(coordinates.equals(atriumCoordinates))
  {
    showAtriumProximityAlert();
  }
}
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.