Skip to main content

Java and GIS, Part 2: Mobile LBS

April 1, 2004

{cs.r.title}






Contents
Using LBS
The "Where Am I?" MIDLet
Adding in Dynamic Location
Meanwhile, Back on the Server
Wrapping Up Part Two

In part one of this Java and GIS series, we acquired the vocabulary that surrounds the GIS space. Understanding the terminology is critical when talking about GIS applications. In part two of this three-part series, we are going to take a look at the mobile Java client. We're going to build a simple Location Based Services (LBS) MIDLet to show what needs to happen on a mobile device and how to get that information to a server. Part three of this series will take the location information and show how a map can be created using the location coordinates. We're assuming that you've got some J2ME background (since we aren't going to be going into detail on the MIDLet life-cycle), as well as background on how to create and deploy a servlet. For our sample, we are working with MIDP 2.0, the Motorola iDEN i730 SDK, and the Motorola i730 device.

Using LBS

First, let's make sure that we understand what an LBS application is. Typically, an LBS application is trying to answer the question "Where am I?" and then do something with that information. There are a number of ways to answer this question:

  • I am on Earth.
  • I am in North America.
  • I'm in the US.
  • I'm in the Rocky Mountains.
  • I'm in Denver, CO.
  • I'm at 39.7°N,105°W.

As you can see, the answer to the question depends on how specific you require your data to be. Being able to determine your current, accurate location can take a few forms. When dealing with a mobile phone, there are also a number of options to determine your location. Typically, some sort of information is available through network operators, using methods that are often made invisible. An example of this is when you travel, and your phone resets its time to the time zone you have entered. All of this is done "under the covers" for you.

There are different ways that a phone can determine its location. Using Angle of Arrival (AOA) methods, where the angle of the phone to the more than one transmitter is determined, or Time Difference of Arrival (TDOA), where the signal is timed from the handset to the cell transmitters, are just two examples. What we are going to be talking about is Assisted GPS (AGPS) and GPS. In these cases, assistance information is produced by the cell network using a simple GPS receiver that is built into the phone handset. This is combined with information from the GPS satellites. What this means is that not only do you need a phone that supports the GPS receiver, but also an AGPS-enabled network. For our development, we've been using the i730 device and the Nextel network, since they provide the service in which we are interested. Using specific location APIs, we are able to access the location information that we need from the device and use it in our GIS application.

It is helpful to understand the flow of the code we are going to look at, which is available at the end of the article. First, we'll look at the client portion. In this case, we are dealing with a cell phone, so we are writing a J2ME MIDLet. The MIDLet allows us to see how the device interacts with the location APIs using canned values. From here, we'll add a little more to the mix and actually do a dynamic location API call. This will show how taking a staged approach is important, so that different parts of the code can be unit-tested in isolation. Lastly, we'll look at a simple servlet that will tie the server portion into the picture. Let's start with the MIDLet.

The "Where Am I?" MIDLet

In general, all MIDLets implement required methods to support certain J2ME life-cycle events. If you'd like to explore all of the methods, you can look at the MIDLet class in the javax.microedition.midlet package. Requests coming into the MIDLet are handled by an inner class, CommandListener, that is set up in the constructor. This MIDLet will tell us what our current lat./long. coordinates are, and then send them to a server using a standard HTTP network connection. Let's take a walk through the MIDLet code. However, keep in mind this isn't a MIDP tutorial. While we'll show the complete code, we'll only talk about the parts of interest as they relate to this application. First, let's get our basic MIDLet set up. Nothing too exciting, as we're just setting up some of our local variables.

package com.mobilogics.javanet;

import java.util.Vector;
import java.util.Calendar;
import java.util.Date;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

public class WhereAmIMidlet extends MIDlet
        implements CommandListener {
    public static final String NOT_SET = "Not Set";
    public static final String ID = "ID  :";
    public static final String LATITUDE = "Lat :";
    public static final String LONGITUDE = "Long:";
    public static final String STATUS = "Stat:";
    public static String m_sUrl = NOT_SET;
    protected String m_sPhoneId = "71";
    protected String m_sLat = NOT_SET;
    protected String m_sLong = NOT_SET;
    protected long m_lTimestamp = -1;
    protected String m_sStatus = NOT_SET;
    protected String m_sTicker = NOT_SET;
    protected String m_sAboutMessage = NOT_SET;
    protected String m_sAboutTitle = NOT_SET;
    protected Display m_Display = null;
    protected Form m_LocationForm = null;
    protected Command m_cmdGetLocationAndSubmit;
    protected Command m_CommandExit;
    protected Command m_CommandAbout;

Next we'll look at our constructor:

public WhereAmIMidlet() {
    m_sAboutTitle =
        this.getAppProperty("MIDlet-Description") +
        " " +
        this.getAppProperty("MIDlet-Version");
    m_sUrl = this.getAppProperty("url");

Here, we are setting our URL to be on the localhost (we happen to be using Tomcat), where the location information will be submitted to our WhereAmIServlet.

    if (null == m_sUrl || m_sUrl.length() == 0) {
        m_sUrl = "http://127.0.0.1:8080/javanet/lbs.do";
    }

Next, we define various screen commands. We've intentionally keep this sample simple by just having Submit, Exit, and About commands. Typically, you would probably have some type of GUI interaction with the user.

    m_sAboutMessage = m_sUrl;
    m_cmdGetLocationAndSubmit = new Command("Submit",
                           Command.SCREEN, 0);
    m_CommandExit = new Command("Exit", Command.STOP, 3);
    m_CommandAbout = new Command("About", Command.OK, 4);
    // Location Form //////
    // 1) Define Screen Content
    Item[] locationItems = new Item[3];
    locationItems[0] = new StringItem(ID, m_sPhoneId);
    locationItems[1] = new StringItem(LATITUDE, m_sLat);
    locationItems[2] = new StringItem(LONGITUDE, m_sLong);
    // 2) Construct the Displayable Object
    m_LocationForm = new Form("Summary", locationItems);
    // 3) Add Commands
    m_LocationForm.addCommand(m_cmdGetLocationAndSubmit);
    m_LocationForm.addCommand(m_CommandExit);
    m_LocationForm.addCommand(m_CommandAbout);

The interesting portion of this section is really the CommandListener, which handles the various screen command actions. The command of interest to us is the GetLocationAndSubmit, which is where we are actually getting the location information and then submitting it to the servlet. You'll notice that we are running the location APIs in their own thread. This is required because it could potentially take some time to get a GPS fix, and you don't want the MIDLet to be hanging in the GUI for the user.

    // 4) Define and Set CommandListener
    CommandListener cl = new CommandListener() {
        public void commandAction(Command c,
                                  Displayable d) {
            if (c == m_CommandExit) {
                destroyApp(true);
                notifyDestroyed();
            }
            else if (c == m_CommandAbout) {
                handleAbout(c, d);
                return;
            }
            else if (c == m_cmdGetLocationAndSubmit) {
                // This is a potentially long running
                // process and must be run in its own
                // thread. Therefore, wrap it in a
                // thread, start the thread and make a
                // quick return so this process execution
                // does not tie up the Midlet's main
                // thread.
                Thread t = new Thread() {
                    public void run() {
                        getLocationAndSubmit ();
                    }
                };
                t.start();
            }
        }
    };
    m_LocationForm.setCommandListener(cl);
    m_Display = Display.getDisplay(this);
}

The getLocationAndSubmit() method is actually broken down into a couple of sub- methods, for ease of reuse. We give some indication to the user that something is going on by displaying a ticker across the screen, and then both get the location and do the submit.

public void getLocationAndSubmit() {
    m_sTicker = "Getting Location";
    System.out.println(m_sTicker);
    updateUI();
    Thread tGetLocation = new Thread() {
        public void run() {
            getLocation();
        }
    };
    tGetLocation.start();
    try {
        tGetLocation.join();
        AlertType.INFO.playSound(m_Display);
        Thread tSubmitLocation = new Thread() {
            public void run() {
                submitLocation();
            }
        };
        m_sTicker = "Submitting Location";
        System.out.println(m_sTicker);
        updateUI();
        tSubmitLocation.start();
        tSubmitLocation.join();
        AlertType.INFO.playSound(m_Display);
        m_Display.setCurrent(m_LocationForm);
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

In this first MIDLet, we have implemented the getLocation() method with "dummy" information. This was done intentionally, because we first want to test the MIDLet as a plain vanilla MIDP application, without introducing the various complexities of the location API. After we're done testing our MIDLet and everything is working, we then override this method in the WhereAmIMotorola MIDLet, which will use the Motorola i730 location APIs. We'll look at that code in just a minute.

void getLocation() {
    // Canned latitude value for a given location
    m_sLat = "255515694";
    // Canned longitude value for a given location
    m_sLong = "-428707806";
    m_lTimestamp = System.currentTimeMillis();
    m_sStatus = "Location OK";
    m_sTicker = "Get Location Status:" + m_sStatus;
    updateUI();
}

After we've got our location information, we can submit it to the servlet by constructing the appropriate query string and sending it over an HTTP connection.

protected void submitLocation() {
    m_sTicker = "Submitting to Server";
    updateUI();
    try {
        HttpConnection http = null;
        StringBuffer sb = new StringBuffer(m_sUrl);
        sb.append("?");
        sb.append("id=");
        sb.append(m_sPhoneId);
        sb.append("&lat=");
        sb.append(m_sLat);
        sb.append("&long=");
        sb.append(m_sLong);
        sb.append("&time=");
        sb.append(m_lTimestamp);
        System.out.println(sb.toString());
        http = (HttpConnection)Connector.open(sb.toString());
        http.setRequestMethod(HttpConnection.GET);
        http.setRequestProperty("Connection", "close");
        m_sStatus = http.getResponseMessage();
        AlertType.INFO.playSound(m_Display);
    }
    catch (Exception e) {
        m_sStatus = "Error Submitting:" + e.getMessage();
        AlertType.ERROR.playSound(m_Display);
    }
    m_sTicker = "Submit Status:" + m_sStatus;
    System.out.println(m_sTicker);
    updateUI();
}

The various other methods that are contained in the MIDLet implement the MIDP life-cycle events (startApp(), pauseApp(), and destroyApp()), but are not directly related to the LBS functionality. You can download the sample source files we've provided if you want to take a look at them.

You can see in Figure 1 what happens when the MIDLet is first run.

Figure 2 shows what the location values look like after we've done a submit.

Figure 1 Figure 2
Figure 1. WhereAmIMidlet in emulator on startup Figure 2. Location values after a submit

Adding in Dynamic Location

Now that we've debugged and tested our WhereAmIMidlet, we are ready to add in the location APIs. Basically, we are just extending the WhereAmIMidlet that we just created and overriding the getLocation() method. The interesting portion is that we are now making calls to the location APIs that are included with the Motorola i730 SDK. Since these APIs are specific to Motorola devices, if you are working with another device you'll need to determine if they supply location APIs in their SDK.

We create a PositionConnection using the Connector (just as we do with the HTTP connection), and then make the position() method call. This will interact with the GPS hardware built into the i730 device. Once we have a fix, we can then access the various pieces of information that we are interested in. In this case, we're simply getting the lat./long. and timestamp information. The updateUI() method is a utility method that you can take a look at in the source download, if you are interested.

package com.mobilogics.javanet;

import java.util.Vector;
import java.util.Calendar;
import java.util.Date;

import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import com.motorola.iden.position.*;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

public class WhereAmIMotorola
    extends WhereAmIMidlet {

    protected void getLocation() {
        try {
            PositionConnection pc = null;
            AggregatePosition ap = null;
            m_sLat = NOT_SET;
            m_sLong = NOT_SET;
            try {
                pc = (PositionConnection)
                     Connector.open("mposition:delay=high");
                ap = pc.getPosition();
            }
            catch (Exception e) {
                m_sStatus = "Location Error: " +
                             e.getMessage();
                updateUI();
                return;
            }
            if (pc.getStatus() !=
                  PositionConnection.POSITION_RESPONSE_OK &&
                ap.getResponseCode() !=
                  PositionDevice.POSITION_OK) {
                    m_sStatus = "Location Error Code: " +
                                 ap.getResponseCode();
            } else {
                // get units in 1/100,000 minute
                int iLat = ap.getLatitude();
                int iLong = ap.getLongitude();
                m_sLat = "" + iLat;
                m_sLong = "" + iLong;
                m_lTimestamp = ap.getTimeStamp();
                m_sStatus = "Location OK";
            }
        }
        catch (Exception e) {
            m_sStatus = "Location Error:" +
                e.getMessage();
        }
        m_sTicker = "Get Location Status:" +
            m_sStatus;
        updateUI();
    }
}

We have our MIDLet running on our i730 device, telling us where we are. Now what? Let's take a peek at what comes into the servlet when we do our submitLocation() call.

Meanwhile, Back on the Server

As we can see, the MIDLet does the work of getting our current location for us and delivering it to the server. On the server side, we have a simple servlet running that accepts the request and then does something with the information. In a typical application, the servlet would actually be doing a bit more here, such as making a request to a GIS back end. For simplicity's sake, we have the servlet just taking the information and writing it to a text file called latlong.txt. This could easily be stored in a database or integrated into some other business logic.

The key part of WhereAmIServlet is the doGet() method, which does the work of parsing the incoming query string, writing the request parameters into our OutputStream, and then echoing the information to the response.

public void doGet (HttpServletRequest request,
                    HttpServletResponse response)
        throws ServletException, IOException {
    // CGI query string will look like this:
    // ?id=71&lat=123456&long=-123456&time=8983719387
    System.out.println("Inside WhereAmIServlet.doGet()");
    String sId, sLat, sLong, sTimestamp;
    sId = request.getParameter("id");
    sLat = request.getParameter("lat");
    sLong = request.getParameter("long");
    sTimestamp = request.getParameter("time");
    StringBuffer sb = new StringBuffer();
    sb.append(sId);
    sb.append(",");
    sb.append(sLat);
    sb.append(",");
    sb.append(sLong);
    sb.append(",");
    sb.append(sTimestamp);
    sb.append("\r\n");
    m_LatLongOutputStream.write(sb.toString().getBytes());
    System.out.println(sb.toString());
    StringBuffer sb2 =
        new StringBuffer("<html><body>");
    sb2.append(sb.toString());
    sb2.append("</body></html>");
    response.getOutputStream().write(
        sb2.toString().getBytes());
}

Note: In part three of this series, we'll see how to take this information from the servlet and actually produce a map using GIS integration.

The URL from our MIDLet corresponds to our servlet from setting the values in the web.xml file. The content of the web.xml is shown in the code below.

<web-app>
    <servlet>
        <servlet-name>lbs</servlet-name>
        <servlet-class>com.mobilogics.javanet.WhereAmIServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>2</param-value>
        </init-param>
        <init-param>
            <param-name>detail</param-name>
            <param-value>2</param-value>
        </init-param>
        <init-param>
            <param-name>validate</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Action Servlet Mapping -->
    <servlet-mapping>
        <servlet-name>lbs</servlet-name>
        <url-pattern>/lbs.do</url-pattern>
    </servlet-mapping>
    <!-- The Welcome File List -->
    <welcome-file-list>
        <welcome-file>latlong.txt</welcome-file>
    </welcome-file-list>
</web-app>

Wrapping Up Part Two

In this part two, we brought in the LBS portion of an application by writing a simple MIDLet to get location information. We hooked it up to a server portion to demonstrate how the information gets to the server, and then what types of activities can take place. In part three, we'll generate a map based on the location information that we received from the device and tie everything together.

Download the sample code for this article: gis.zip

Sue Spielman is president and senior consulting engineer for Switchback Software LLC , author of a number of books, and a speaker on various Java topics around the country.
Tom Whitehill is the co-founder of Mobilogics and has been working with Java since 1995.
Related Topics >> Mobility   |   Research   |