The Problem
Ever used the built-in Geocoder class in Android to display a location on a map? If so, you may have found that the geocoder does not provide you with the viewport data needed to zoom the map appropriately. Given the reality of different size locations (countries, states, cities, buildings) how can we zoom the map appropriately to show the complete location on the screen, at a zoom level that’s not too high and not too low?
The Solution
GeocoderPlus is a geocoding library that uses the Google Maps Web Services APIs V3 to retrieve both location coordinates and viewport info. Thanks to the viewport info, the GeocoderPlus library can display locations on a map at the right zoom level.
In Part 1 of this post we looked at the inner workings of the library. In this post we will build an sample app that shows a map and allows the user to enter a location. When the user presses Go, the app calls the GeocoderPlus library to retrieve matching addresses. If there are multiple addresses that match, the app asks the user to select the desired address. Finally, the app displays the selected address at the correct zoom on the map.
Installation
GeocoderPlus is provided as a JAR library. Download the library from here and add it to your Android project like any other JAR library.
Interface
The GeocoderPlus interface is similar to that of the Android geocoder. The exported Geocoder class contains these methods:
public List <Address> getFromLocationName(String locationName) throws IllegalArgumentException, IOException public List <Address> getFromLocationName (String locationName, int maxResults) throws IllegalArgumentException, IOException
Usage
The following method shows how to perform geocoding and handle any exceptions:
public List<Address> geocodeLocation(String locationName) { // Geocode the location Geocoder geocoder = new Geocoder(); try { List<Address> addresses = geocoder.getFromLocationName(locationName); return addresses; } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
The geocoding call is a blocking call, so proper Android programming dictates that it should not take place on the UI thread. The easiest way to transfer the call to a different thread is to use the AsyncTask class. The code shown below extends the AsyncTask class and creates a new class called GeocodeTask. Within the new class, onPreExecute uses showLocationPendingDialog to display an indeterminate progress dialog with a status message. The doInBackground function is executed on a separate thread and calls our geocodeLocation() function (shown earlier) to perform the geocoding work. Once geocoding is complete, onPostExecute uses cancelLocationPendingDialog to hide the indeterminate progress dialog, and then takes appropriate action based on the number of results. The user will see a warning if there are zero results, a location picker if there are multiple results and the actual location if there is one result.
public class GeocodeTask extends AsyncTask<String, Void, List<Address>> { // Removed helper function implementations for brevity. You can see // the full code in the Github repository @Override protected void onPreExecute() { super.onPreExecute(); showLocationPendingDialog(); } @Override protected List<Address> doInBackground(String... args) { // Declare List<Address> addresses; // Extract parameters String locationName = args[0]; // Geocode addresses = geocodeLocation(locationName); // Return return addresses; } @Override protected void onPostExecute(List<Address> addresses) { super.onPostExecute(addresses); cancelLocationPendingDialog(); // Check the number of results if (addresses != null) { if (addresses.size() > 1) { // Determine which address to display showLocationPicker(addresses); } else { // Display address displayLocation(addresses.get(0)); } } else { showAlert("No results found!", "Error"); } } };
To display the address on the map, we use the data in the Address class (latitude, longitude, viewport latitude span, viewport longitude span) to center the map and set the zoom.
private void displayLocation(Address address) { // Get the map center GeoPoint mapCenter = new GeoPoint((int) (address.getLatitudeE6()), (int) (address.getLongitudeE6())); // Get the map controller MapController mapController = mMapView.getController(); // Fix position mapController.animateTo(mapCenter); // Fix zoom mapController.zoomToSpan(address.getViewPort().getLatitudeSpanE6(), address.getViewPort().getLongitudeSpanE6()); }
Finally, we can invoke the GeocodeTask class and perform geocoding in the background as follows:
String locationName = "Rome"; GeocodeTask geocodeTask = new GeocodeTask(); geocodeTask.execute(locationName);
Conclusion
This post showed how to use the GeocoderPlus library to display an address at the appropriate zoom level on a map.
The source code for this post is on Github at:
http://www.github.com/bricolsoftconsulting/GeocoderPlusExample/