RSS

Search Engine

Tuesday, August 24, 2010

Mapping with Google APIs in Android

Mapping functionality has become a must-have feature for new mobile devices. With all the new technology advances, mobile devices, especially cell phones, are more than capable of handling complicated mathematical calculations on their own or keeping up with the high-traffic communication with the servers. GPS devices used to be the dominant player with the mapping capabilities, but more and more mobile devices are offering almost full-featured GPS functionalities. Google's Android provides direct access to its popular mapping tools. You will explore key programming APIs that power its mapping features.

What Google APIs Are Available for Mapping?

Before you start, all the necessary development tools, plug-ins, and sample code you need are from Google's own Android site at http://code.google.com/android/. It also provides simple-to-follow instructions to get you started. I recommend you do that first if you have not done so already.

The majority of the mapping APIs are within the package com.google.android.maps. At a minimum, two of them are required to embed the mapping tools inside your software: MapActivity and MapView. MapActivity manages the activity life cycle and services behind a MapView. MapView is an Android View that displays a map. Other than these APIs, you also have MapController to perform panning and zooming a map. MyLocationOverlay and Overlay are used to draw the user's info or objects on top of the map.

Discussing mapping without mentioning GPS is nearly impossible now because GPS has become one of the indispensable features most people would want from their mobile devices. The package android.location is included for GPS support. LocationManager is the most important API; it provides access to the system location services. The mapping and GPS APIs are the essential elements for building location-based services (LBS). You will pretty much cover all these APIs in a working example later.

Constructing a MapView by a MapActivity

You can construct a MapView only by a MapActivity because it depends on background threads that access the network and filesystem managed by MapActivity. That is to say, you should always start by extending your class from MapActivity as follows:

public class TutorialOnMaps extends MapActivity {
private static MapView mMapView;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);

requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);

// Get the map view from resource file
mMapView = (MapView)findViewById(R.id.mv);
}
}

While in the default resource file main.xml, you add in some on-screen buttons with transparent panels. To properly "inflate" the MapView, you use a well-known solution among Android developers to declare a MapView. Figure 1 shows the initial screen on the emulator with your on-screen buttons.









Figure 1: Initial map centered on a specified position

Panning, Zooming, and Toggling Map Modes

As was mentioned before, MapController is used to pan and zoom a map. You need to associate each button with a click listener. One example is as follows:

// Set up the button for "Pan East"
mPanE = (Button)findViewById(R.id.pane);
mPanE.setOnClickListener(new OnClickListener(){
// @Override
public void onClick(View arg0) {
panEast();
}
});

The panEast() function is implemented to pan a quarter of the current screen size. Other panning functionalities are done in a similar fashion. Actually, the map itself is already draggable if the device is equipped with a touch screen LCD. For non-touch-screen devices, you use these buttons or the arrow keys of the rocker pad to pan the map.

public void panEast() {
Point pt = new Point(
mMapView.getMapCenter().getLatitudeE6(),
mMapView.getMapCenter().getLongitudeE6()
+ mMapView.getLongitudeSpan() / 4);
mMapView.getController().centerMapTo(pt, true);
}

Zooming is done simply by increasing or decreasing the current zoom level through the controller.

public void zoomIn() {
mMapView.getController().zoomTo(mMapView.getZoomLevel() + 1);
}

To toggle the map display for viewing satellite imagery or traffic, you can enable it directly from MapView. The results are shown in Figures 2 and 3.

public void toggleSatellite() {
mMapView.toggleSatellite();
}

public void toggleTraffic() {
mMapView.toggleTraffic();
}

Figure 2: Satellite imagery

Figure 3: Traffic on satellite imagery

Retrieving Info in Current MapView

When a MapView is successfully created, lots of info pertaining to the current view can be queried. Here is a list of the main functions to help you better understand the current map status.

  • getMapCenter: Returns the longitude and latitude of the map center point

  • getLatitudeSpan and getLongitudeSpan: Return the span sizes of the current view's bounding rectangle

  • getZoomLevel and getMaxZoomLevel: Return the zoom level. Each zoom level is scaled by a factor of 2.

  • isSatellite: Checks whether the map view is currently in satellite imagery mode

  • isTraffic: Checks whether the map view is displaying traffic info

  • isShowMyLocation: Checks whether the current location is displayed on the map

  • isStreetView: Checks whether the map view is currently in street-view mode

Displaying Your Info on MapView

The base class Overlay represents an overlay that can be used to draw and display custom graphic objects on top of a map. Your code basically overrides the draw method by drawing a circle for the default position as well as enclosing its caption within a transparent rounded rectangle. Watch how I convert the position into screen coordinates with PixelCalculator, how I calculate the font metrics with Paint's measureText, and how I draw anti-aliased text with Paint's setAntiAlias.

// This is used draw an overlay on the map
protected class MyOverlay extends Overlay {
@Override
public void draw(Canvas canvas, PixelCalculator pc,
boolean shadow) {
super.draw(canvas, pc, shadow);

if (mDefCaption.length() == 0) {
return;
}

Paint p = new Paint();
int[] scoords = new int[2];
int sz = 5;

// Convert to screen coords
pc.getPointXY(mDefPoint, scoords);

// Draw point caption and its bounding rectangle
p.setTextSize(14);
p.setAntiAlias(true);
int sw = (int)(p.measureText(mDefCaption) + 0.5f);
int sh = 25;
int sx = scoords[0] - sw / 2 - 5;
int sy = scoords[1] - sh - sz - 2;
RectF rec = new RectF(sx, sy, sx + sw + 10, sy + sh);

p.setStyle(Style.FILL);
p.setARGB(128, 255, 0, 0);
canvas.drawRoundRect(rec, 5, 5, p);
p.setStyle(Style.STROKE);
p.setARGB(255, 255, 255, 255);
canvas.drawRoundRect(rec, 5, 5, p);

canvas.drawText(mDefCaption, sx + 5, sy + sh - 8, p);

// Draw point body and outer ring
p.setStyle(Style.FILL);
p.setARGB(88, 255, 0, 0);
p.setStrokeWidth(1);
RectF spot = new RectF(scoords[0] - sz, scoords[1] + sz,
scoords[0] + sz, scoords[1] - sz);
canvas.drawOval(spot, p);

p.setARGB(255, 255, 0, 0);
p.setStyle(Style.STROKE);
canvas.drawCircle(scoords[0], scoords[1], sz, p);
}
}

The overlay just created needs to be added to MapView's overlay controller before it goes into effect. Here is the code segment showing how you set up the overlay controller.

// Set up the overlay controller
mOverlayController = mMapView.createOverlayController();
MyOverlay mo = new MyOverlay();
mOverlayController.add(mo, true);

Figure 4: Label overlay on the map

Integrating with the Current GPS Location

android.location is the package that contains APIs allowing you to query the list of location providers as well as registering for periodic updates of current positions. Each location provider maintains the files under the /data/misc/location/ directory. Therefore, the default mock GPS provider "gps" can be found on the emulator in /data/misc/location/gps/. Different providers can generate the GPS files in different formats. More info can be found at Android's site in the references. Please note that, when you try to activate LocationManager the first time, it takes longer to initialize. Subsequent calls are immediately responsive. The following code segment gets the GPS position from the provider and then feeds it to MapView's controller.

private void centerOnGPSPosition() {
String provider = "gps";
LocationManager lm =
(LocationManager)getSystemService(Context.LOCATION_SERVICE);

// NOTE: When LocationManager is called the first time,
// lat / lon is initialized to 0.
// Subsequent calls are fine and fast.
Location loc = lm.getCurrentLocation(provider);

mDefPoint = new Point((int)(loc.getLatitude() * 1000000),
(int)(loc.getLongitude() * 1000000));
mDefCaption = "GPS location";

mMapView.getController().animateTo(mDefPoint);
mMapView.getController().centerMapTo(mDefPoint, true);
}

Figure 5: GPS info overlay on the map

Conclusion

After this introduction, you are probably very excited to experiment with the mapping software and perhaps add in your own features. The entire software package is available for download from the references and you can simply import the project into Eclipse.

Android's mapping APIs are simple to use and yet powerful, with direct access to Google's already popular tools. As Android continues to grow with more advanced APIs, I believe more map features will be ported to this platform as well. Here are what I can think of if you would like to give it a try:

  • If you are an avid user of Google Maps, I am sure you will like its style of a draggable tool bar to control the map zooming and panning. You can go ahead and model after that user interface and functionalities. It will be an excellent addition.

  • What about adding an address search functionality, just like Google Maps can do through the web? Parsing the search results is really the key to this feature.

  • Usually, GPS needs a little while to initialize when it is taken to a new location, so you might get an ANR dialog (Application Not Responding) if the wait is longer than approximately five seconds. To make your software execute smoothly without any system interruption, you should create a child thread for this purpose and allow it to communicate with the main thread through a message handling scheme.

1 comments:

Anonymous said...

Thanks for the article.

Could you post the code snippets for the xml files too ? I have a hard time putting buttons on a mapview.

Thanks,
Stéphane

Post a Comment