Vous êtes sur la page 1sur 7

GPS Tracker

Introduction How It Works Phone Upload Webpage Database Data Retriever and XML Converter Google Map Display Webpage

Introduction
Thanks for trying out GPS Tracker. This project allows someone to track a GPS enabled cell phone using Google maps. For this project I used a Motorola i355 cell phone on the Sprint/Nextel network. You need to have a data plan so that you can make updates to your website from the cellphone. Please read the ReadMe.txt file in the download for installation instructions. I hope you enjoy the project. If you have any questions, feel free ask them in the forum. There are two projects available. The first project is built with .NET and Microsoft SQL Server. The second project is built with PHP and MySQL. I apologize in advance for my poor PHP coding skills. If you have any suggestions, please feel free to let me know. Both projects use java (J2ME) on the phone. Please note that while it's ok to track your cell phone with Google maps, it is against their Terms of Service to track vehicles with their free plan. In order to be able to track vehicles, you must purchase their Google Maps for Enterprise.

How It Works
None of the code for this project is very difficult, but it does span a number of tiers and languages which may be unfamiliar to some. Figure 1 shows the data flow from phone to Google map.

Fig. 1 Data Flow

Phone
Let's start with the code on the phone. This app is written in java using Java 2 Micro Edition (J2ME). Java is very similar to C#. As you look through the code, the only thing that might confuse a C# coder is the vector. A java vector is pretty much a C# ArrayList, a dynamic array. There are 2 classes in the app, LBSMidlet7 and Qworker. A midlet is an app that runs on cell phones. Take a look at the class definition. It extends the MIDlet class and implements a LocationListener interface. That means that we need to put all the method definitions of that interface into our class. We'll get to that in a bit, right now let's look at the constructor.

Fig. 2 Phone Classes

We do 2 things in the constructor. We create a QWorker object and pass it this and the website that we will be uploading to. The getAppProperty method gets attributes out of the JAD file. Open the JAD file in your favorite text editor and there you'll see the webpage that you'll be sending GPS data to. Notice how we're passing "this" to the GWorker object? That's the LBSMidlet7 object. Take a quick look at the QWorker class, it extends the Thread class. That's why we call worker.start() in the LBSMidlet7 constructor. We want to start our worker thread. When you start a thread, what you are doing is creating an object and then running that object's run() method. Take a look at the run method. It has an endless loop and in the loop the first thing it does is calls queue.wait(). Look at the definition of the queue. The queue is an abstract data type (ADT), it just like a queue at a bank, enter the queue at the back of the line and leave the queue when you get to the front of the line. Look at the definition of the queue, it's our vector (dynamic array). When you call wait() on an object within a class that extends the Thread class, it puts that object to sleep. Think about that a little. When we hit that line, our QWorker object is now waiting... What's it waiting for? We'll get to that in a minute. Before we do that, take a look at the synchronized keyword. Notice that it's wrapping the queue. What that does is it puts a lock on the queue and tells all other processes not to touch the queue until that little block of code is done with it. Ok, so now we've started a worker thread and put it to sleep. Let's now go back to the LBSMidlet7 class and take a look at the startApp() method. In the lifecycle of a midlet, the constructor is called once and then the startApp() method is called next. In fact it can

be called several times, like for instance when you close a flip phone and then open it again. What happens is that the app is suspended and when you flip the phone open again, startApp() is called again. In startApp(), we get our display and then we create a LocationProvider if one hasn't already been created and we create another thread... Why are we creating all these threads? Good question. When a midlet (app) is suspended, the backgroud threads that are created keep running. That allows us to get our GPS data and send it to our webserver while we do other important stuff, like make phone calls. The LocationProvider is what gets our GPS data. First we create a criteria, we're using the default, but you can set stuff like accuracy, response time etc. Next we create our Location Listener. It's pretty much just what it sounds like. Here you can set the interval for how often you want to get GPS data. It's currently set to 60 which is in seconds. When data comes in, the locationUpdated() method is called. This is another one of the required methods in the LocationListener interface. Here we create yet another thread and call getLocation(). The getLocation() method gets the GPS coordinates, creates a queryString which we will send to the web server a little later and then calls worker.addToQueue in the QWorker class. Let's go back over to the QWorker class and see what happens in that method. It add the queryString to the queue and then calls queue.notify(). Guess what queue.notify() does? It wakes up our sleeping QWorker thread and tells it to get to work! Notice that our calls to the queue are once again wrapped in a synchronized block. Please practice safe threading... When notify() is called on a thread, what it does is go back to the run() method and execute the next line of code right after where we told the queue to wait(). So now we are just about ready to send the GPS data to the web server. We have a couple of interesting lines of code there. First we call peekInQueue() which gets the queryString out of the queue but leaves it there for now. Then it sends the queryString to the getUrl method which attempts to send the queryString to our web server. If it's successful, we can remove the queryString from the queue. If not, we leave the queryString in the queue and try to send it to the webserver again later. Why in the world do we have this complicated queue here? I'm glad you asked. There may be times when you are receiving GPS data but are not actually in an area that has a cell phone connection. If we don't have a cell phone connection, we can't send our GPS data to our web server. So we stick our queryString in our queue and wait until we get back into an area with cell phone connectability. Can you hear me now? Well, we've spent a pretty fair bit of time explaining the phone code. It's a little complicated but it's important to know what's going on if you want to take the code and make modifications to it to suit your needs. Here's a good article on the Sun website to let you know about more capabilities of the Location Based Services API. Right about now, our queryString should be arriving at our website, let's catch up to it and see what happens.

Upload Webpage

Things get a lot easier now. In both the .Net and PHP versions, we get our queryString and send it to the database. Notice that there is a method called getDateFromJavaDate(). All this does it convert it from a java date to something that is a little more human readable. In the .NET version, we have a nice helper class that let us call a stored procedure with our sql parameters. In both the .NET version and the PHP version, we send a 1 back to the phone to let it know that we had a successful update of the DB. This is how the phone knows to remove the queryString from the queue. In the PHP version, the INSERT query is on the PHP page, this allows us to use versions of the MySQL database older than version 5 (version 5 supports stored procedures).

Database
Both databases have just one table called gpslocations. Most of the fields are pretty straight forward except for that last one. The locationMethod field. Cell phones have different ways of getting it's location. The first one is obviously from satellites. Sometimes a cell phone may not be able to get a fix on satellites but certain phones can get a location using the location of the nearest cell phone tower. As you move around with your cell phone, you'll see that a value of 327681 is a satellite fix and a value of 8 is a cell tower location. You can actually look on your Google map and see where the cell tower that you are using is. The MySQL database just has the table in it. The Microsoft SQL Server has 2 stored procedures. The first prcSaveGPSLocation just inserts a row into the gpslocations table and returns a value greater than 0 on success. The prcGetGPSLocations is a little more interesting. There we are using the FOR XML PATH statement to generate XML. That FOR XML PATH really is the greatest thing since sliced bread. Unfortunately, MySQL does not have any XML support for queries. As great as MySQL is, that really is it's shortcoming. The XML that we are getting looks like this: <gps> <locations latitude="47.508464" longitude="-122.023040" speed="40" direction="356" distance="2" locationMethod="327681" gpsTime="9:02pm Tue, Jun 19 07"/> <locations latitude="47.518773" longitude="-122.030123" speed="35" direction="335" distance="3" locationMethod="327681" gpsTime="9:03pm Tue, Jun 19 07"/> <locations latitude="47.526416" longitude="-122.036533" speed="24" direction="330" distance="3" locationMethod="8" gpsTime="9:04pm Tue, Jun 19 07"/> </gps>

Data Retriever and XML Converter


Here is where the PHP page catches up. We simply concatenate the XML together with the data to form an XML string. I could have used the DOM or any number of PHP classes to form the XML but I will leave that as an exercise for the reader (I was being lazy...). But the PHP code as it is does work. Please note that on the PHP page as well as the .aspx page, the header "Content-Type" is being set to "text/xml". This is important. When we make the ajax call later from the Google map webpage, it's wants to see that

data coming in as text/xml in order for it to parse properly. ResponseText will work and it looks like XML but we want to use ResponseXML which is expecting text/xml. There is another important thing to notice on the .NET page. I have removed all the html except for .NET page header from the GetGpsLocations.aspx page. This allows us to just send XML to the Google maps page. The .NET page uses a nice helper class to retrieve the XML from the DB.

Google Map Display Webpage


Ok, we have finally arrived at the webpage that will display the GPS data on Google maps. At this point the .NET version and the PHP version are the same. The PHP version has a .html extension and the .NET version has an .aspx extension but it doesn't need it, it could have been .html as well. The nice thing about packaging our data as XML is that we have used two different methods of delivery (PHP and .NET) and we are now able to use the same webpage and javascript to process the data. The Google Maps API is pretty nice. There are number of helper functions built in to help us display the data. You'll notice that the DisplayMap webpage is pretty simple. It has one line of javascript code and and a div to hold the map. You'll need to register with Google to get your own maps API key. Replace the key that is there with your new one. You have to put this code on a webserver with a registered domain name. This will not work on localhost. On the webpage, you'll also notice that there are 2 images with a style of "display:none". These are hidden but they need to be there for Internet Explorer. It preloads the images so that IE doesn't make a separate call back to the webserver for every single marker that you display on the map. On to the final code. The first helper function is called GDownloadUrl, it takes as arguments the webpage that has our GPS data and the callback function that will process our data when we get it back. This function is just a wrapper function for XMLHttpRequest, which as you know is just Ajax. The code that actually processes the XML is in a javascript file called maps.js in the javascript directory. Let's take a look at that now. If we have data it is parsed into an XML Document using another Google function called GXml.parse. Very handy. If you look at our XML above, you'll see that our data is in an element called locations so we get those. The little markers you see on the map are called markers. Just wanted to make sure that we were on the same page on that one. The next thing we do is create our map and add a couple of controls, you can comment out those lines to see what controls are added. Next we call map.setCenter, this sets where in the world the map will display. I have taken our first marker and used that GPS location as the center of our map. This is the first location you get when you turn on the cell phone. The markers variable is a list of all your GPS locations and we iterate over that list to create each marker. Within the for loop we process the markers one by one by passing them to a helper function. The helper function creates an icon, and then creates a GMarker using the icon and a point structure that holds your latitude and longitude. We create an event listener that waits for you to click on your marker. When you click the marker a little dialog

bubble pops up and displays lots of interesting information like speed, the total distance you have traveled from the time you started the app on your phone, the time you got the GPS data and a little compass image. The direction field is a compass direction or azimuth. There are 8 little compass images in the image directory that points in the direction you're going. You can see the getcompassImage function that figures out which image to use. Finally we return the marker to the calling function and add it to the map with map.addOverlay(marker). Well, that's about it from phone to Google Maps in just a few easy steps. I hope you enjoyed this explanation. If you found any incorrect information or have any questions about the code, please let me know about it in the support forum.

Vous aimerez peut-être aussi