Mapping Locations Android

The Android SDK provides two different methods to show a location with Google Maps. The first method is to use a location Uri to launch the built-in Google Maps application with the specified location. The second method is to use a MapView embedded within your application to display the map location.

Mapping Intents

In the previous section, we demonstrated how to determine the latitude and longitude for a place name. Now we map the location using the built-in maps application. The following block of code demonstrates how to perform this:

String geoURI = String.format("geo:%f,%f", lat, lon);
Uri geo = Uri.parse(geoURI);
Intent geoMap = new Intent(Intent.ACTION_VIEW, geo);
startActivity(geoMap);

The first task is to create a String that conforms to the URI handled by the mapping application. In this case, it’s geo: followed by the latitude and longitude. This URI is then used to create a new Uri object for creating a new ACTION_VIEW Intent. Finally, we call the startActivity() method. If the latitude and longitude are valid, such as the location for the Acropolis, the screen would look like Figure.

Using this method of mapping launches the user into a built-in mapping application— in this case, Google Maps. If the application does not want to bother with the details of a full mapping application or does not need to provide any further control over the map, this is a fast-and-easy method to use. Users are typically accustomed to the controls of the mapping application on their handset, too.

Mapping Views

Sometimes, though, we want to have the map integrated into our application for a more seamless user experience. Let’s add a small map to our geocoding example to show the location immediately to the users when they enter a place name.

The resulting map for geocoding the term Acropolis and launching a geo URI.

The resulting map for geocoding the term Acropolis and launching a geo URI.

The following block of XML shows the change needed within the layout file to include a widget called the MapView:

<com.google.android.maps.MapView
android:id="@+id/map"
android:apiKey="yourMapKey"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

As you might have already noticed, the MapView XML is a little different. First, the tag name is the fully qualified name. And second, an apiKey attribute is needed. We get to the key in a moment.

The AndroidManifest.xml file also needs to be modified to allow for using the MapView with Google Maps. Here are the two changes needed:

<application
...
<uses-library
android:name="com.google.android.maps" />
</application>
<uses-permission
android:name="android.permission.INTERNET" />

Both of these permission lines are required.The MapView object specifically requires the INTERNET permission and its library must be referenced explicitly. Otherwise, an error occurs.

Finally, you can use a MapView only within a MapActivity. Accessing a MapView from outside a MapActivity results in an error. The MapActivity is similar to a normal Activity, but it requires implementing the isRouteDisplayed() method. This method must return true if a route will be displayed. Otherwise, false must be returned. Here is the default implementation for when no route is displayed:

@Override
protected boolean isRouteDisplayed() {
// we do not display routes
return false;
}

Now the application can use the MapView to display locations to the user. The following block of code demonstrates retrieval of a MapController object, which is used to control the location that the MapView displays:

MapView map = (MapView) findViewById(R.id.map);
map.setSatellite(true);
final MapController mapControl = map.getController();
mapControl.setZoom(17);

These lines of code set the display to show the satellite view, which is visually interesting. The MapController object then sets the zoom level of the map. Larger values are zoomed in farther, with 1 zoomed all the way out. The given value, 17, usually shows a few city blocks, but there are some areas where even this is too close for the data available. In a moment, we talk about how to easily give control of this to the user.

Building on the previous example, the following lines of code are added to the button handler for geocoding a place name:

GeoPoint newPoint = new
GeoPoint((int)(lat * 1E6), (int)(lon * 1E6));
mapControl.animateTo(newPoint);

In this case,we create a new GeoPoint to use with the animateTo() method. A GeoPoint object uses microdegrees, so we must multiply the result of the geocoding by 1E6 (1,000,000 or one million).The animateTo() method smoothly animates the MapView to the new location. How much of the interim mapping data displays depends on the speed of the Internet connection and what mode the MapView is in. The setCenter() method can set the center of the map.

Finally, this is almost enough to test the results. However, there is one last thing you need to take care of. You need to get a Google Maps API Key from Google to use its API

Getting Your Debug API Key

To use a MapView within your applications, you must obtain a Google Maps API Key from Google. The key is generated from an MD5 fingerprint of a certificate that you use to sign your applications.

For production distribution, you need to follow these steps, substituting your release distribution signing certificate. You can read more about this in Chapter “Selling Your Android Application.” For testing purposes, you can use the debug certificate that is created by the Android SDK.

You need to do the following to generate the appropriate API key:

  1. Generate an MD5 fingerprint for your debug certificate.
  2. Sign in to with a Google account.
  3. Accept the Terms of Service.
  4. Paste in the fingerprint from Step 1.
  5. Save the Android Maps API key presented on the next screen.

The first step is performed on your development machine. Locate the debug certificate used by the Android SDK. On all platforms, the filename is debug.keystore by default. If you use Eclipse, the location of the file is listed under the Android Build preferences. Using this file, you then need to execute the following command (make sure the Java tools are in your path):

keytool -list -keystore /path/to/debug.keystore -storepass android

The result is the fingerprint that you must paste into the form on step 4. Read the terms of service carefully before proceeding.Although the terms allow many types of applications, you need to make sure your application is allowed and that your anticipated usage is acceptable to Google.

When you have successfully completed the steps to get your key, you can then reference your map key in the Layout file definition for the MapView you use. Now, when you execute the code, you should be presented with a screen that looks like Figure.

MapView results for geocoding the term Sydney Opera House.

MapView results for geocoding the term Sydney Opera House.

Panning the Map View

Sometimes the locations returned either do not show the exact location that the user wants or the user might want to determine where in the world they are by exploring the map a bit. One way to do this is through panning the map. Luckily, this is as easy as enabling clicking from within the layout file:

<com.google.android.maps.MapView
android:id="@+id/map"
android:clickable="true"
android:apiKey="mapApiKey"
android:layout_width="fill_parent" android:layout_height="wrap_content" />

Now, if the user searches for Great Pyramids, he could then pan east a tad to see the Great Pyramid, as shown in Figure.

Results for Great Pyramids on the left, panned east to the Great Pyramid on the right.

Results for Great Pyramids on the left, panned east to the Great Pyramid on the right.

Zooming the Map View

Other times, panning won’t help users. They might want to zoom in or out from the same location. Our application does not have to re-implement the zoom controls, though. Instead, simply enable the built-in zoom controls as follows:

map.setBuiltInZoomControls(true);

When the user clicks on the map, the zoom controls fade in to view and are functional, as shown in Figure.

Marking the Spot

Now that panning and zooming works, users may lose track of their position. Sure, they could just search again, but wouldn’t it be more helpful if we marked the point of interest directly on the map? The Android SDK provides a few different ways to do this. One way is to use the MapView as a container for an arbitrary View object that can be assigned using a GeoPoint instead of typical screen or View coordinates. Another way is to use ItemizedOverlay, which is especially useful if you have more than one place to mark.

Finally, you can manually draw items over the map using the Overlay and implement the onDraw() method.

On the left, you see a bird’s eye view of the town of Wilmington, but zoom in to the south and you see The Long Man of Wilmington as shown on the right.

On the left, you see a bird’s eye view of the town of Wilmington

For the place name finder example, we use the first method. Assuming you have a suitable map marker as a drawable resource, the following code demonstrates how to do this:

GeoPoint newPoint = new GeoPoint((int)(lat * 1E6), (int)(lon*1E6));

// add a view at this point
MapView.LayoutParams mapMarkerParams = new
MapView.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
newPoint, MapView.LayoutParams.TOP_LEFT );
ImageView mapMarker = new ImageView(getApplicationContext());
mapMarker.setImageResource(R.drawable.paw);
map.addView(mapMarker, mapMarkerParams);

The MapView layout parameters enable you to set a GeoPoint. Doing this enables the added View to stay put at a geographic location and pan with the map, as shown in Figure.

The Kremlin at the top left of the

The Kremlin at the top left of the

marker (paw print in a circle). Keep in mind that the added View sticks around as long as the MapView does. If the application needs to present multiple locations to the user, though, there is a simpler way. Just use the ItemizedOverlay object.

In this example, a static ItemizedOverlay is created to represent the chain of backpacker huts in the White Mountains along the Appalachian Trail:

private class HutsItemizedOverlay
extends ItemizedOverlay<OverlayItem> {
public HutsItemizedOverlay(Drawable defaultMarker) {}
protected OverlayItem createItem(int i) {}
public int size() {}
}

To do this,we provide implementations for each of the required methods of ItemizedOverlay<OverlayItem>. First,we define the constructor:

public HutsItemizedOverlay(Drawable defaultMarker) {
super(defaultMarker);
boundCenterBottom(defaultMarker);
populate();
}

The Drawable passed in is one that we define later in the onCreate() method of MapActivity. The system does not provide a default marker. The call to the bound Center Bottom() method is made so that the map coordinates are at the centerbottom and the shadow is cast from the bottom of the marker, which is a more natural look. The default shadow is from the top. If, however, we’d rather turn off the shadow completely, you can override the draw() method, as follows:

@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, false);
}

Finally, within the constructor we call the populate() method. This should be done as soon as the location data is available. Because we have it statically compiled into the application, we call it before returning. The populate() method calls our implementation of the createItem() method for as many items as we defined in our implementation of the size() method. Here is the implementation of our createItem() method, along with a small array of hut locations, in no particular order:

public GeoPoint hutPoints[] = new GeoPoint[] {
// Lakes of the Clouds
new GeoPoint(44258793, -71318940),
// Zealand Falls
new GeoPoint(44195798, -71494402),
// Greanleaf
new GeoPoint(44160372, -71660385),
// Galehead
new GeoPoint(44187866, -71568734),
// Carter Notch
new GeoPoint(44259224, -71195633),
// Mizpah Spring
new GeoPoint(44219362, -71369473),
// Lonesome Lake
new GeoPoint(44138452, -71703064),
// Madison Spring
new GeoPoint(44327751, -71283283)
};
@Override
protected OverlayItem createItem(int i) {
OverlayItem item = new OverlayItem(hutPoints[i], null, null);
return item;
}

In the array, we’ve multiplied all the location values by one million so that they are in microdegrees, as required by the GeoPoint object. Within the createItem() method, the location array is indexed with the passed-in value. Neither of the two text fields, Title and Snippet, are used at this time, so they are set to null. The maximum index value is determined by the size() method, which, in this case, merely has to return the length of the array:

@Override
public int size() {
return hutPoints.length;
}

The necessary Itemized Overlay <OverlayItem> class is now implemented. Next, the application needs to tell the MapView about it. The following code demonstrates how to do this in the onCreate() method of our MapActivity:

@Override
protected void onCreate(Bundle data) {
super.onCreate(data);
setContentView(R.layout.huts);
Drawable marker = getResources().getDrawable(R.drawable.paw);
HutsItemizedOverlay huts = new HutsItemizedOverlay(marker);
MapView map = (MapView)findViewById(R.id.map);
map.setSatellite(true);
List<Overlay> overlays = map.getOverlays();
overlays.add(huts);
FrameLayout zoomFrame = (FrameLayout)
findViewById(R.id.map_zoom_holder);
zoomFrame.addView(map.getZoomControls());
}

First, the Drawable is retrieved from the resources. Next, we instantiate the HutsItemized Overlay object. The OverlayItems in it need to be added to the ones that might already exist within the MapView. The getOverlays() method of MapView returns a list of the current Overlay objects. Calling the add() method on this list inserts our new ones for each hut. Finally, the zoom controls are added to the MapView so that the user can zoom in and out. After launching this application and zooming in on New Hampshire, the user should see a screen like Figure.

Forcing the user to pan and zoom to the location of the huts is not user-friendly. Two utility methods that the ItemizedOverlay<OverlayItem> class provides return values for the span of the location of the items. Combining this functionality with an override to the default behavior of the getCenter() method, which normally returns the location of the first item, enables the map to start to draw at a convenient zoom level covering all the huts. You can add this block of code to the onCreate() method to do just that:

MapController mapControl = map.getController(); mapControl.setCenter(huts.getCenter());
mapControl.zoomToSpan(
huts.getLatSpanE6(), huts.getLonSpanE6());

A map with markers at each of the Appalachian Mountain Huts of New Hampshire.

A map with markers at each of the Appalachian Mountain Huts of New Hampshire.

The getCenter() method computes the average latitude and the average longitude across all the given hut locations. You could provide a central point, or you could place the first item near the center of all the points requiring no override of the getCenter() method.



Face Book Twitter Google Plus Instagram Youtube Linkedin Myspace Pinterest Soundcloud Wikipedia

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Android Topics