In the first part of this article, we introduced the concept of CellID, and OpenCellID – an open source database of CellIDs that can be used to build location based services, and we created a small program to determine location from CellID information. This time, we will go a step further and display a map on a mobile device, using the open source “OpenStreetMap”, and we position the map using the device’s location. This will deliver an experience similar to that offered by the Google Maps for Mobile “MyLocation” feature, and will run on top of JavaME.
So to achieve this, we will use the J2meMap component. J2meMap is a library that we have created to develop our flagship product, a mobile tourist guide. The main purpose of this library is to make it easier to create mobile applications which make use of maps. This library is free for non-commercial use. J2meMap can display a map based on any of the main tile providers, such as Google, MSN or others, including the open source provider OpenStreetMap. We will look at how to make use of OpenStreetMap below. In addition, with J2meMap, you can add extra layers as bitmap, KML, GeoRSS, GPX and much more. A complete description of the J2meMap is available at http://j2memap.8motions.com.
The HelloMap program
So how how do we use J2meMap? It’s very simple to get started; we use the MapDisplay
class. The most simple J2meMap program is listed below:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import com.eightmotions.map.MapDisplay; import com.eightmotions.util.UtilMidp; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class HelloMap extends MIDlet { public void startApp() { UtilMidp.checkMIDP(this); //Initialise the utility library... Display.getDisplay(this).setCurrent((new MapDisplay()).getCanvas()); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} } |
We create a MapDisplay
object, and display the related Canvas
. Already this gives us a good result: a map that we can play with and move about:
Using OpenStreeMap as a map provider
We now need to do two things: integrate OpenStreetMap, and make use of the OpenCellID database information. The OpenStreeMap integration is quite easy: you can implement or replace layers, by implementing the MapOverlay
interface. Even better still, there is the GenericOverlay
class, which abstracts away most of the complexity. For the GenericOverlay
class we just need a URL, and a few settings:
1 2 3 |
//Use GenericOverlay class MapOverlay openStreetMap = GenericOverlay("OpenStreetMap", "http://tile.openstreetmap.org/!z!/!x!/!y!.png"); |
The URL contains some special tokens, the ones between “!” characters: “!x!“,”!y!“, and “!z!“. These are dynamically replaced by appropriate values at run time. They are related to longitude, latitude, and zoom level. We will not go into detail of these values now, as they are beyond the scope of this article, but you should be aware that there are other related parameters such as !minLon!, !minLat!. In other words, this could be very easily used to connect to a Web Map Service (WMS) server.
Next we tell the engine to display it:
1 |
myMap.setMapProvider(0,openStreeMap); |
Retrieving results from OpenCellID
At this point, we now have a map application where we can zoom in and out, and which is connected to OpenStreeMap. We can now make use of the code from part I. We add the following code after we retrieve and process the OpenCellID result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
.... int pos=res.indexOf(','); String latStr=res.substring(0,pos); int pos2=res.indexOf(',',pos+1); String lonStr=res.substring(pos+1,pos2); myMap.setInfoOnScreen(latStr+" "+lonStr); float lat=Float.parseFloat(latStr); float lon=Float.parseFloat(lonStr); // If we are in a lower zoom mode, go into an higher mod if(myMap.getZoom()<10)myMap.setZoom(14); // If the 'current location' is not yet created, do it, and add it in the current track if(lastLoc==null){ lastLoc=new MyLoc(); t.addLoc(lastLoc); } lastLoc.setLonLat(lon,lat); myMap.goTo(lastLoc); |
Adding a custom marker
So what is this MyLoc
reference? Using the library, we can add elements on top of the map, such as pins or markers for example. We can also use images to make our own marker, but in this example, we will draw our own custom marker: an empty circle. To do this, we simply extend the default Marker
, and add the size
field.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class MyLoc extends Marker{ public int range=1000; public MyLoc(){ super(); } public void paint(Graphics g,int inpx,int inpy,int offx,int offy,int w,int h,int zoom){ int x=offx+w/2+(px-inpx)/(1<<zoom); int y=offy+h/2+(py-inpy)/(1<<zoom); g.setColor(0x8080A0); g.fillArc(x-6,y-6,12,12,0,360); g.setColor(0x0000C0); g.drawArc(x-50,y-50,100,100,0,360); } } |
Conclusion
The result of all this is as follows: when we press the “LocateMe” button on our device, a request will be sent to the OpenCellID server to see if there is a correspondence between this CellID and a geographical location stored on the server. If the CellID is identified, then we move the map to this location. That’s it!
Of course, the accuracy of the positioning depends on the quality of data in the OpenCellID database. Let us know how you get on, and if you see a gap in the database, you can help fill it by sending back information to the OpenCellID database using the client found here.
A few shortcuts are available:
- 1 to zoom in
- 3 to zoom out
- 5 to change of map provider (choose between satellite and map views from Google, Microsoft, Yahoo, and Ask)
Important: as mentioned in the first part of this article, right now the OpenCellID client only supports SonyEricsson devices. On other phones, if you attempt to use this feature, the program will warn you with a “this phone does not support CellID” message.
The complete source code is available below, and the compiled program is available as both jad and jar archives.
Please check the 8motions site for updates.
10 Comments
[quote] OpenCellIDSample.jar placed me at latitude 0, longitude 0 [/quote]
When I installed it on a SonyEricsson v640i on the Vodafone Ireland network, it placed me a couple of kilometers from my true location. After sending some location information back with the help of my trusty Holux M-1200 bluetooth GPS, it was pinpointing me perfectly, as it should.
I never got 0,0 for lat,lon though. You could try to send some data back to the database if you have a GPS handy. Thomas might be able to tell us if this is normal behaviour too.