Java and GIS, Part 2: Mobile LBS (From www.java.net)

转载 2004年08月08日 21:25:00

by Sue Spielman and Tom Whitehill
04/01/2004

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.

用REDIS实现LBS系统

用REDIS实现LBS系统前言LBS系统其实并不是什么难的问题,现在已经有很多成熟的框架,比如MongoDB就提供一套性能不错的LBS系统。但是前些日子从公司接手这份差事的时候,(由于当时的整个应用架...
  • kimjuny
  • kimjuny
  • 2015年09月23日 17:19
  • 1822

使用Part上传附件

/* * To change this template, choose Tools | Templates * and open the template in the editor. */ ...
  • kcj991932907
  • kcj991932907
  • 2014年11月27日 21:01
  • 628

百度——LBS.云 v2.0——云存储的POI创建和删除

百度云存储接口功能:支持单个geotable亿级数据及数百G大小的存储,每天支持百万量级读写,每秒支持万量级读写。   有了LBS云,个人开发LBS应用就非常方便了。今天研究了一下。在android上...
  • wdxzkp
  • wdxzkp
  • 2013年09月04日 20:33
  • 6313

百度地图API 云存储·LBS.云 工具类封装(Java)

百度地图API 云存储·LBS.云 工具类封装(Java)一、设计思路根据百度地图 云存储API 以经典的DAO设计模式编写,目录如下: 二、源码部分...
  • dfs4df5
  • dfs4df5
  • 2015年12月21日 13:16
  • 1820

百度地图API 云存储·LBS.云 VO类封装(Java)

百度地图API 云存储·LBS.云 VO类封装(Java)一、设计思路首先将LBS(版本服务器域名)和USER(ak,geotable_id等)的基本信息分别定义在 LBSINFO.java 和 US...
  • dfs4df5
  • dfs4df5
  • 2015年12月21日 13:01
  • 1275

使用Redis来实现LBS的应用

微信、陌陌 架构方案分析 近两年、手机应用,莫过于微信、陌陌之类最受欢迎;但实现原理,分享文章甚少。 故,提出两种方案,供分享;不对之处,敬请留言学习。 目标 查找附近的某某某,由近到远返回结果,且结...
  • Real_Myth
  • Real_Myth
  • 2016年04月13日 09:28
  • 1484

Netflix公布个性化和推荐系统架构

http://www.infoq.com/cn/news/2013/04/netflix-ml-architecture Netflix的推荐和个性化功能向来精准,前不久,他们公布了自己在这...
  • haizhiguang
  • haizhiguang
  • 2015年05月25日 15:04
  • 842

微信开发 LBS位置定位存在问题的改进 基于java语言和mysql数据库

微信开发 解决LBS位置开发获取不到最新位置记录的问题 LBS位置定位的存在问题的改进 基于java语言和mysql数据库...
  • wyx100
  • wyx100
  • 2015年05月25日 09:47
  • 1450

常用开源GIS项目

常用开源GIS项目 常用开源桌面GIS软件 QGIS 始于2002年5月,算得上是开源GIS平台中的后起之秀。界面友好,分析功能可与GRASS GIS相媲美。 ...
  • ZXGIS
  • ZXGIS
  • 2014年01月02日 09:52
  • 14081

每个程序员都应该了解的 CPU 高速缓存 英文原文:Memory part 2: CPU caches

现在的CPU比25年前要精密得多了。在那个年代,CPU的频率与内存总线的频率基本在同一层面上。内存的访问速度仅比寄存器慢那么一点点。但是,这一局面在上世纪90年代被打破了。CPU的频率大大提升,但内存...
  • yangxi_001
  • yangxi_001
  • 2014年06月11日 17:14
  • 1075
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章: Java and GIS, Part 2: Mobile LBS (From www.java.net)
举报原因:
原因补充:

(最多只允许输入30个字)