All of the following code snippets are included with the basic demo midlet app included with the API source.
Map Canvas
The core of the API is the Map class which extends javax.microedition.lcdui.Canvas. In order to customize the Map you will probably want to extend this class or at least override the commandListeners. Because the centerOnPosition method makes a network connection you will embedd this code into a new Thread.
Map map=new ExtendedMap();
Position pos = new Position("37.78192 -122.40381");
map.setZoomLevel(17);
map.centerOnPosition(pos);
midlet.display.setCurrent(map);
Zooming Map
Because the zoom operations method makes a network connection you will embed this code into a new Thread. You can zoom to new levels (allowed values are 1-21 where 1 is the whole world on one tile and 21 is the lowest street level). Or you can use the convienence methods:
map.zoomIn(); // zooms in one level
map.zoomOut(); // zooms out one level
Thread t = new Thread((new Runnable() {
public void run() {
try {
map.zoomTo(newLevel); // call in new thread
} catch (Exception e) {
//handle
}
}}));
t.start();
Geocoding
For many reasons, mainly performance, the underlying geospatial engine requires information in latitude and longitude coordinates. But, human beings don't think in terms of latitude and longitude. You've most likely never heard someone say, "Hey Bob, meet me at 37.1232, -121.43566 for a beer at 5 o'clock." Instead, human beings reference locations by address. Geocoding is the act of taking an address (example: "4 N Second St., San Jose, CA 95113") and translating it into a pair of coordinates on a map.
Many times a user will put in an incomplete address, misspell a street name, or might only have a partial address available to input. Example: The distance between 2000 N. Main St., Anytowne USA and 2000 S. Main St., Anytowne USA might be miles; providing an address of 2000 Main St., Anytowne USA, without the directional before the street name, has a chance of inconveniently misplacing a user. As you can see, to assume your users will impeccably enter addresses is not wise.
To provide effective address translation, use the Geocoder object. The Geocoder object has a method geocode that takes a FreeFormAddress and returns a Vector of GeocodeResponse. By using the Geocoder object, you'll be able to let the user choose the best match for vague inputs.
Thread t = new Thread((new Runnable() {
public void run() {
try {
Geocoder geo = new Geocoder();
resp = geo.geocode(new FreeFormAddress("4 N 2nd Street 95113"));
if (resp.isEmpty()) { // unable to geocodeCmd
// report to user
} else if (resp.size() > 0) { // present choices
for (int i = 0; i < resp.size(); i++) {
// present geocode response candidates to user
}
}
} catch (Exception e) {
// handle
}
}
}));
t.start();
You can also make a geocoding request using StructuredAddress instead of FreeFormAddress, we will internally transform the correct request to the server. And you can indicate the returning Address Type to FreeFormAddress for geocoding, the default returning type is StructuredAddress.
Thread t = new Thread(new Runnable() {
public void run() {
try {
Geocoder geo = new Geocoder();
geo.setReturnFreeFormAddress(true); //set the returning type for geocode
StructuredAddress structuredAddress = new StructuredAddress();
structuredAddress.setBuildingNumber("1");
structuredAddress.setStreet("Post Street");
structuredAddress.setMunicipality("san jose");
structuredAddress.setCountrySubdivision("ca");
structuredAddress.setPostalCode("95113");
resp = geo.geocode(structuredAddress); //geocode with StructuredAddress
if (resp.isEmpty()) { // unable to geocodeCmd
// report to user
} else if (resp.size() > 0) { // present choices
for (int i = 0; i < resp.size(); i++) {
// present geocode response candidates to user
//the returning type now is FreeFormAddress
FreeFormAddress address = (FreeFormAddress)((GeocodeResponse) resp.elementAt(i)).getAddress();
}
}
} catch (Exception e) {
}
}
});
t.start();
Reverse Geocoding
There may be times when you need to translate a coordinate (example: 37.786505, -122.3862) into a human understandable street address. Most often this is needed in tracking applications where you receive a GPS feed from the device or asset and wish to know what address the point is located.
The Geocoder object has a reverseGeocode method. reverseGeocode takes a Position type (instead of an address) and returns a StructuredAddress type. This example alerts the returned address. The address found will be an approximate, interpolated address. It will throw an Exception if the position cannot be pinned to an address.
Thread t = new Thread((new Runnable() {
public void run() {
try {
Geocoder geo = new Geocoder();
StructuredAddress resp = geo.reverseGeocode(getCenterPosition());
Alert alert = new Alert("reverse geocoding");
alert.setTimeout(50000);
alert.setString("You are located at:\n"+getCenterPosition().toString()+
"\nwhich is:\n"+resp.toString());
alert.addCommand(new Command("CANCEL", Command.CANCEL, 2));
app.display.setCurrent(alert);
}catch(Exception e){
// handle
}
}}));
t.start();
Local Search / POI Requests
The POIQuery object can be used to search for POIs (Points of Interest) that are in the base map data. All base map data contains a relatively diverse set of POIs. The types of POIs that can be retrieved from the data are outside of the scope of this document. For a detailed list of the types of POIs that can be retrieved, please contact your data provider.
The POIQuery takes a POISearchCriteria object and returns a Vector of found POI objects.
This example queries the base data for "Starbucks". POI searches also match substrings. A search for "Star" would find "Starbucks", "Star Donuts", "Nightstar Restaurant", and any other POI containing "Star" in its name.
NOTE: our system also supports spatial searching side relational databases. This is useful for friend finder apps as well as custom business directories. We will be providing demos for this in the future.
Thread t = new Thread((new Runnable() {
public void run() {
try {
POISearchCriteria crit = new POISearchCriteria(map.getCenterPosition(), // center
map.getRadiusY(), // radius for search
"Starbucks"); // search term
// execute search
Vector pois = POIQuery.query(crit);
for (int i = 0; i < pois.size(); i++) {
// create new Pin
Pin pin = new Pin(((POI) pois.elementAt(i)).getPosition());
// give Pin a message
pin.setMessage(((POI) pois.elementAt(i)).getName());
// add Pin to Map
map.addPin(pin);
}
} catch (Exception e) {
// handle
}
}}));
t.start();
Routing
Routing is crucial to all LBS applications. There are two main aspects to a route. The visualization of the route and the turn by turn route instructions.
For visualizing the route you need set the route preference to return route geometry. The route response will include a Vector of Positions which can be used in conjunction with the Shape API's for rendering the route on screen. In order to keep things fast we have added route generalization features for minimizing the number of Positions returned at each zoom level. The steps are the following:
- get Positions from user, usually using the Geocoder to locate the Origin and Destination
- create route preference object with proper generalization level
- add positions to the route preference object
- specify whether you would also like to retrieve the turn by turn route instructions from the server. (this example leaves out route instructions)
For including the route instructions use:
- routePreferences.setReturnInstructions(true);
Thread t = new Thread((new Runnable() {
public void run() {
try {
Position origin=getCenterPosition();
Position dest = userSuppliedPosition; // assumed
RouteQuery rq = new RouteQuery();
// make bbox of the origin and dest then get the zoom level for the bbox for generalizing
int zoomToFitRoute = Util.getZoomLevelToFitBoundingBox(getWidth(),
getHeight(),
Util.getBoundingBox(origin, dest));
Vector poss = new Vector();
poss.addElement(origin); // route origin (center of map)
poss.addElement(routeDestPos); // destintation
RoutePreference pref = new RoutePreference();
pref.setReturnGeometry(true);
pref.setReturnRouteId(false); //return geometry points instead of routeid
pref.setReturnInstructions(false);
pref.setGeneralizationLevel(zoomToFitRoute);// important for generalizing
pref.setStyle("Fastest");
Route route = rq.query(poss, pref);
// recalculate the exact zoom from the BBox the server returned
int zoomToFitRoute2 = Util.getZoomLevelToFitBoundingBox(getWidth(),
getHeight(),
route.getBoundingBox());
map.setZoomLevel(zoomToFitRoute2);
Line line = new Line();
line.setWidth(5);
line.setRGB(200, 0, 200);
line.setPositionGeometry(route.getRouteGeometry());
map.removeOverlays();
// get center of the route to correctly frame screen
map.centerOnPosition(route.getBoundingBox().getCenterPosition());
app.display.setCurrent(map);
Pin originPin = new Pin(origin);
Pin destPin = new Pin(dest);
originPin.setIcon(new Icon(Image.createImage("/greenDot.png"), new XY(6,6)));
destPin.setIcon(new Icon(Image.createImage("/redDot.png"), new XY(6,6)));
map.addPin(originPin);
map.addPin(destPin);
map.addOverlay(line);
addCommand(new Command(REMOVE_ROUTE, Command.SCREEN, 2));
} catch (Exception e) {
// handle
}
}
}));
t.start();
You can also indicate the server render route for you. You need to indicate the server return routeid first and then you need to add routeid onto the map. as below:
- pref.setReturnGeometry(true); //return the geometry information
pref.setReturnRouteId(true); //using routeid represent the geometry
- map.addRouteId(route.getRouteId()); //add the routeid onto the map
Thread t = new Thread((new Runnable() {
public void run() {
try {
Position origin=getCenterPosition();
Position dest = routeDestPos.clone();
RouteQuery rq = new RouteQuery();
// make bbox of the origin and dest then get the zoom level for the bbox for generalizing
int zoomToFitRoute = Util.getZoomLevelToFitBoundingBox(getWidth(), getHeight(), Util.getBoundingBox(origin, dest));
Vector poss = new Vector();
poss.addElement(origin); // route origin (center of map)
poss.addElement(routeDestPos); // destintation
RoutePreference pref = new RoutePreference();
pref.setReturnGeometry(true);
pref.setReturnRouteId(true);
pref.setReturnInstructions(false);
pref.setGeneralizationLevel(zoomToFitRoute);// important for generalizing
pref.setStyle("Fastest");
Route route = rq.query(poss, pref);
// recalculate the exact zoom from the BBox the server returned
int zoomToFitRoute2 = Util.getZoomLevelToFitBoundingBox(getWidth(), getHeight(), route.getBoundingBox());
map.setZoomLevel(zoomToFitRoute2);
map.removeOverlays();
map.removePins();
map.addRouteId(route.getRouteId()); // add the routeid onto the map
map.centerOnPosition(route.getBoundingBox().getCenterPosition());
app.display.setCurrent(map);
Pin originPin = new Pin(origin);
Pin destPin = new Pin(dest);
originPin.setIcon(new Icon(Image.createImage("/greenDot.png"), new XY(6,6)));
destPin.setIcon(new Icon(Image.createImage("/redDot.png"), new XY(6,6)));
map.addPin(originPin);
map.addPin(destPin);
addCommand(new Command(REMOVE_ROUTE, Command.SCREEN, 2));
} catch (Exception e) {
// handle
}
}
}));
t.start();
GPS Location
We have added a few methods for using the device GPS. Location positioning is perhaps the most important and most device specific aspect of mobile LBS development. We will be rolling out several new approaches to positioning, including wifi and cell tower positioning.
Thread t = new Thread((new Runnable() {
public void run() {
Alert alert = new Alert("locating GPS");
try {
alert.setTimeout(120000);
alert.addCommand(new Command("CANCEL", Command.CANCEL, 2));
alert.setImage(Image.createImage("/satellite.gif"));
app.display.setCurrent(alert);
alert.setString("looking for satellites...");
Position gps = Locate.getCurrentLocation();
map.centerOnPosition(gps);
} catch (Exception e) {
alert.setString(e.getMessage());
} finally {
app.display.setCurrent(map);
}
}}));
t.start();