DISPLAYING MAPS

Google Maps is one of the many applications bundled with the Android platform. In addition to simply using the Maps application, you can also embed it into your own applications and make it do some very cool things. This section describes how to use Google Maps in your Android applications and programmatically perform the following:

  • Change the views of Google Maps
  • Obtain the latitude and longitude of locations in Google Maps
  • Perform geocoding and reverse geocoding (translating an address to latitude and longitude and vice versa)
  • Add markers to Google Maps

Creating the Project

To get started, you need to first create an Android project so you can display the Google Maps in your Android application.

TRY IT OUT: Creating the Project

codefile LBS.zip available for download at Wrox.com

1. Using Eclipse, create an Android project as shown in Figure 4-1. Be sure to check the Google APIs checkbox in the Build Target section.

image

NOTE In order to use Google Maps in your Android application, you need to ensure that you check the Google APIs as your build target. Google Maps is not part of the standard Android SDK, so you need to find it in the Google APIs add-on, as was discussed in Chapter 1.

2. Once the project is created, observe the additional JAR file (maps.jar) located under the Google APIs folder (see Figure 4-2).

How It Works

This simple Try It Out created an Android project that uses the Google APIs add-on. The Google APIs add-on includes the standard Android library, with the addition of the Maps library, packaged within the maps.jar file.

Obtaining the Maps API Key

Beginning with the Android SDK release v1.0, you need to apply for a free Google Maps API key before you can integrate Google Maps into your Android application. When you apply for the key, you must also agree to Google’s terms of use, so be sure to read them carefully.

To apply for a key, follow the series of steps outlined next.

image

NOTE Google provides detailed documentation on applying for a Maps API key at http://code.google.com/android/add-ons/google-apis/mapkey.html.

First, if you are testing the application on the Android emulator or an Android device directly connected to your development machine, locate the SDK debug certificate located in the default folder (C:Users<username>.android for Windows 7 users). You can verify the existence of the debug certificate by going to Eclipse and selecting Window ⇒ Preferences. Expand the Android item and select Build (see Figure 4-3). On the right side of the window, you will be able to see the debug certificate’s location.

image

NOTE For Windows XP users, the default Android folder is C: Documents and Settings<username>Local SettingsApplication DataAndroid.

The filename of the debug keystore is debug.keystore. This is the certificate that Eclipse uses to sign your application so that it may be run on the Android emulator or other devices.

Using the debug keystore, you need to extract its MD5 fingerprint using the Keytool.exe application included with your JDK installation. This fingerprint is needed to apply for the free Google Maps key. You can usually find the Keytool.exe in the C:Program FilesJava<JDK_version_number>in folder.

Issue the following command (see Figure 4-4) to extract the MD5 fingerprint:

keytool.exe -list -alias androiddebugkey -keystore
"C:Users<username>.androiddebug.keystore" -storepass android
-keypass android

In this example, my MD5 fingerprint is EF:7A:61:EA:AF:E0:B4:2D:FD:43:5E:1D:26:04:34:BA.

Copy the MD5 certificate fingerprint and navigate your web browser to: http://code.google.com/android/maps-api-signup.html. Follow the instructions on the page to complete the application and obtain the Google Maps key. When you are done, you should see something similar to what is shown in Figure 4-5.

image

NOTE Although you can use the MD5 fingerprint of the debug keystore to obtain the Maps API key for debugging your application on the Android emulator or other devices, the key is not valid if you try to deploy your Android application as an APK file. When you are ready to deploy your application to the Android Market (or use another method of distribution), you need to reapply for a Maps API key using the certificate that will be used to sign your application. Chapter 6 discusses this topic in more detail.

Displaying the Map

You are now ready to display Google Maps in your Android application. This involves two main tasks:

  • Modify your AndroidManifest.xml file by adding both the <uses-library> element and the INTERNET permission.
  • Add the MapView element to your UI.

The following Try It Out shows you how.

TRY IT OUT: Displaying Google Maps

1. Using the project created in the previous section, add two XML files to the res/layout folder and name them locations.xml and showmap.xml. Also, add two class files under the package name and name them Locations.java and ShowMap.java. Figure 4-6 shows the four files added to the project.

2. Populate locations.xml as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="List of Locations"
    android:textSize="30sp"
    android:textColor="#adff2f" />

<ListView
    android:id="@id/android:list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:drawSelectorOnTop="false"/>

</LinearLayout>

3. Populate showmap.xml as follows (be sure to replace the value of the apiKey attribute with the API key you obtained earlier):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

<com.google.android.maps.MapView
    android:id="@+id/mapView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:enabled="true"
    android:clickable="true"
    android:apiKey="<your_maps_API_key_here>" />

</LinearLayout>

4. Populate the Locations.java file as follows:

package net.learn2develop.LBS;

import android.app.ListFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class Locations extends ListFragment {
    String[] locations = {
        "Grand Canyon, Arizona (Valley)",
        "Bill Gates’ house",
        "Yosemite National Park, California (Park)",
    };

    String[] latlng = {
        "36.1125,-113.995833",
        "47.627787,-122.242135",
        "36.849722,-119.5675",
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //---displays the list of locations in the ListView---
        setListAdapter(new ArrayAdapter<String>(getActivity(),
            android.R.layout.simple_list_item_1, locations));
    }

    public void onListItemClick(ListView parent, View v,
    int position, long id)
    {
        Toast.makeText(getActivity(),
            "You have selected " + locations[position],
            Toast.LENGTH_SHORT).show();
    }

    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.locations, container, false);
    }
}

5. Populate the ShowMap.java file as follows:

package net.learn2develop.LBS;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

public class ShowMap extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(
            R.layout.showmap, container, false);
    }
}

6. Populate the main.xml file as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <fragment
        android:name="net.learn2develop.LBS.Locations"
        android:id="@+id/locationFragment"
        android:layout_weight="0.25"
        android:layout_width="20dp"
        android:layout_height="match_parent" />
    <fragment
        android:name="net.learn2develop.LBS.ShowMap"
        android:id="@+id/mapFragment"
        android:layout_weight="1"
        android:layout_width="0px"
        android:layout_height="match_parent" />

</LinearLayout>

7. Add the following statements in bold to the MainActivity.java file. Note that MainActivity is now extending the MapActivity class.

package net.learn2develop.LBS;

import android.os.Bundle;
import com.google.android.maps.MapActivity;

public class MainActivity extends MapActivity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }
}

8. Add the following lines in bold to the AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.learn2develop.LBS"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="11" />

    <uses-permission android:name="android.permission.INTERNET">
    </uses-permission>

    <application android:icon="@drawable/icon" android:label="Where Am I">
        <uses-library android:name="com.google.android.maps" />
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

9. Press F11 to debug the application on the Android emulator. Figure 4-7 shows the Android emulator displaying two fragments — one showing a list of locations and another showing Google Maps.

How It Works

In this Try It Out, you have two fragments, Locations and ShowMap. The Locations fragment is a list fragment showing a list of locations. The ShowMap fragment displays Google Maps. Note that any activity that is going to host a fragment that displays Google Maps must extend the MapActivity base class, which itself is an extension of the Activity class. Hence, you needed to make the following changes to MainActivity.java:

public class MainActivity extends MapActivity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }
}

For the MapActivity base class, you need to implement one method: isRouteDisplayed(). This method is used for Google’s accounting purposes, and you should return true for this method if you are displaying routing information on the map. For most simple cases, you can simply return false.

In order to display Google Maps in your application, you need to have the INTERNET permission in your manifest file. You then add the <com.google.android.maps.MapView> element to the XML file to embed the map within the fragment.

The Locations fragment contains a TextView showing the text “List of Locations” displayed in green. It also contains a ListView to display the list of locations:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="List of Locations"
    android:textSize="30sp"
    android:textColor="#adff2f" />

<ListView
    android:id="@id/android:list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:drawSelectorOnTop="false"/>

</LinearLayout>

The Locations class extends the ListFragment base class. It also contains two arrays — locations contains the names of various locations and latlng contains the corresponding latitude and longitude pair for each location stored in the locations array:

public class Locations extends ListFragment {
    String[] locations = {
        "Grand Canyon, Arizona (Valley)",
        "Bill Gates’ house",
        "Yosemite National Park, California (Park)",
    };

    String[] latlng = {
        "36.1125,-113.995833",
        "47.627787,-122.242135",
        "36.849722,-119.5675",
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //---displays the list of locations in the ListView---
        setListAdapter(new ArrayAdapter<String>(getActivity(),
            android.R.layout.simple_list_item_1, locations));
    }

    public void onListItemClick(ListView parent, View v,
    int position, long id)
    {
        Toast.makeText(getActivity(),
            "You have selected " + locations[position],
            Toast.LENGTH_SHORT).show();
    }

    @Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.locations, container, false);
    }
}

When a location is clicked (or tapped on a real device), you display the name of the location using the Toast class.

The ShowMap fragment simply loads the showmap.xml file to display Google Maps:

public class ShowMap extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(
            R.layout.showmap, container, false);
    }
}

CAN’T SEE THE MAP?

If instead of seeing Google Maps displayed you see an empty screen with grids, then most likely you are using the wrong API key in the main.xml file. It is also possible that you omitted the INTERNET permission in your AndroidManifest.xml file. Finally, ensure that you have Internet access on your emulator/devices.

If your program does not run (i.e., it crashes), then you probably forgot to add the following statement to the AndroidManifest.xml file:

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

Note its placement in the AndroidManifest.xml file; it should be within the <Application> element.

Displaying the Zoom Control

The previous section showed how you can display Google Maps in your Android application. You can pan the map to any desired location and it will be updated on the fly. However, on the emulator there is no way to zoom in or out from a particular location (on a real Android device you can pinch the map to zoom it). Thus, in this section, you learn how you can enable users to zoom in or out of the map using the built-in zoom controls.

TRY IT OUT: Displaying the Built-In Zoom Controls

1. Using the project created in the previous activity, add the following statements in bold to the ShowMap.java file:

public class ShowMap extends Fragment {
    private MapView mapView;

    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(
            R.layout.showmap, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        mapView = (MapView) getActivity().findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
    }
}

2. Press F11 to debug the application on the Android emulator. Observe the built-in zoom controls that appear at the bottom of the map when you click it (see Figure 4-8). Click the minus (–) icon to zoom out of the map, and the plus (+) icon to zoom into the map.

How It Works

To display the built-in zoom controls, you first get a reference to the map and then call the setBuiltInZoomControls() method:

       mapView = (MapView) getActivity().findViewById(R.id.mapView);
       mapView.setBuiltInZoomControls(true);

Besides displaying the zoom controls, you can also programmatically zoom in or out of the map by obtaining an instance of the MapController class from the MapView object and then calling the zoomIn() or zoomOut() method of the MapController class:

public class ShowMap extends Fragment {
    private MapView mapView;
    private MapController mc;

    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(
            R.layout.showmap, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        mapView = (MapView) getActivity().findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);

        mc = mapView.getController();
        mc.zoomIn();  //---zoom into the map---
        mc.zoomOut(); //---zoom out of the map---
    }
}

Changing Views

By default, Google Maps is displayed in a map view, which is basically drawings of streets and places of interest. You can also set Google Maps to display in satellite view using the setSatellite() method of the MapView class:

    @Override
    public void onStart() {
        super.onStart();
        mapView = (MapView) getActivity().findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);

        mc = mapView.getController();
        mapView.setSatellite(true);
    }

Figure 4-9 shows Google Maps displayed in satellite view.

If you want to display traffic conditions on the map, use the setTraffic() method:

        mapView.setTraffic(true);

Figure 4-10 shows the map displaying the current traffic conditions. The different colors reflect the varying traffic conditions. In general, green means smooth traffic of about 50 miles per hour, yellow means moderate traffic of about 25–50 miles per hour, and red means slow traffic of about less than 25 miles per hour.

Currently, traffic information is available only in major cities in the United States, France, Britain, Australia, and Canada, although new cities and countries are frequently added.

Navigating to a Specific Location

By default, Google Maps displays the map of the United States when it is first loaded. However, you can set Google Maps to display a particular location. To do so, use the animateTo() method of the MapController class.

The following Try It Out shows how you can programmatically animate Google Maps to a particular location.

TRY IT OUT: Setting the Map to Display a Specific Location

1. Using the project created in the previous activity, add the following statements in bold to the ShowMap.java file:

public class ShowMap extends Fragment {
    private MapView mapView;
    private MapController mc;
    private GeoPoint p;

    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(
            R.layout.showmap, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        mapView = (MapView) getActivity().findViewById(R.id.mapView);
        mc = mapView.getController();

        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
    }

    //---go to a particular location---
    public void gotoLocation(String latlng)
    {
        //---the location is represented as "lat,lng"---
        String[] coordinates = latlng.split(",");
        double lat = Double.parseDouble(coordinates[0]);
        double lng = Double.parseDouble(coordinates[1]);
        p = new GeoPoint((int) (lat * 1E6),
                         (int) (lng * 1E6));
        mc.animateTo(p);
        mc.setZoom(16);
        mapView.invalidate();
    }
}

2. Add the following statements in bold to the Locations.java file:

    public void onListItemClick(ListView parent, View v,
    int position, long id)
    {
        Toast.makeText(getActivity(),
            "You have selected " + locations[position],
            Toast.LENGTH_SHORT).show();

        //---obtain a reference to the ShowMap fragment---
        ShowMap mapFragment =
            (ShowMap)getFragmentManager().findFragmentById(
             R.id.mapFragment);
        //---invoke the method from the fragment---
        mapFragment.gotoLocation(latlng[position]);
    }

3. Press F11 to debug the application on the Android emulator. Click a particular location listed in the left fragment. Observe that the map animates to the selected location (see Figure 4-11).

How It Works

In the ShowMap fragment, you defined the gotoLocation() method, which takes a single input string argument containing the location in the following format: Latitude,longitude. From this string, you can extract the latitude and longitude of the location and then use a GeoPoint object to represent a geographical location. Note that for this class, the latitude and longitude of a location are represented in micro degrees. This means that they are stored as integer values. For a latitude value of 40.747778, for example, you need to multiply it by 1e6 (which is one million) to obtain 40747778.

To navigate the map to a particular location, you used the animateTo() method of the MapController class. The setZoom() method enables you to specify the zoom level at which the map is displayed (the bigger the number, the more details you see on the map). The invalidate() method forces the MapView to be redrawn.

In the Locations fragment, when a location is clicked in the ListView, you call the gotoLocation() method that you have defined in the ShowMap fragment:

        //---obtain a reference to the ShowMap fragment---
        ShowMap mapFragment =
            (ShowMap)getFragmentManager().findFragmentById(
             R.id.mapFragment);
        //---invoke the method from the fragment---
        mapFragment.gotoLocation(latlng[position]);

You made use of the getFragmentManager() method to obtain an instance of the FragmentManager object, and then called its findFragmentById() method to obtain an instance of the ShowMap fragment. You then called the gotoLocation() method available in the fragment.

Adding Markers

Adding markers to a map to indicate places of interest enables your users to easily locate the places they are looking for. The following Try It Out shows you how to add a marker to Google Maps.

TRY IT OUT: Adding Markers to the Map

1. Create a GIF image containing a pushpin (see Figure 4-12) and copy it into the res/drawable-mdpi folder of the project. For the best effect, make the background of the image transparent so that it does not block parts of the map when the image is added to the map.

2. Using the project created in the previous activity, add the following statements in bold to the ShowMap.java file:

package net.learn2develop.LBS;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

//...

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import com.google.android.maps.Overlay;
import java.util.List;

public class ShowMap extends Fragment {
    private MapView mapView;
    private MapController mc;
    private GeoPoint p;

    class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            super.draw(canvas, mapView, shadow);

            //---translate the GeoPoint to screen pixels---
            Point screenPts = new Point();
            mapView.getProjection().toPixels(p, screenPts);

            //---add the marker---
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.pushpin);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);
            return true;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(
            R.layout.showmap, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        mapView = (MapView) getActivity().findViewById(R.id.mapView);
        mc = mapView.getController();

        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
    }

    //---go to a particular location---
    public void gotoLocation(String latlng)
    {
        //---the location is represented as "lat,lng"---
        String[] coordinates = latlng.split(",");
        double lat = Double.parseDouble(coordinates[0]);
        double lng = Double.parseDouble(coordinates[1]);
        p = new GeoPoint((int) (lat * 1E6),
                         (int) (lng * 1E6));
        mc.animateTo(p);
        mc.setZoom(16);

        //---Add a location marker---
        MapOverlay mapOverlay = new MapOverlay();
        List<Overlay> listOfOverlays = mapView.getOverlays();
        listOfOverlays.clear();
        listOfOverlays.add(mapOverlay);

        mapView.invalidate();
    }
}

3. Press F11 to debug the application on the Android emulator. Click a location on the left fragment to see the marker added to the map, as shown in Figure 4-13.

How It Works

To add a marker to the map, you first need to define a class that extends the Overlay class:

    class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            super.draw(canvas, mapView, shadow);

            //...
        }
    }

An overlay represents an individual item that you can draw on the map. You can add as many overlays as you want. In the MapOverlay class, you override the draw() method so that you can draw the pushpin image on the map. In particular, note that you need to translate the geographical location (represented by a GeoPoint object, p) into screen coordinates:

            //---translate the GeoPoint to screen pixels---
            Point screenPts = new Point();
            mapView.getProjection().toPixels(p, screenPts);

Because you want the pointed tip of the pushpin to indicate the position of the location, you need to deduct the height of the image (which is 50 pixels) from the y coordinate of the point (see Figure 4-14) and draw the image at that location:

            //---add the marker---
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.pushpin);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);

To add the marker, you create an instance of the MapOverlay class and add it to the list of overlays available on the MapView object:

        //---Add a location marker---
        MapOverlay mapOverlay = new MapOverlay();
        List<Overlay> listOfOverlays = mapView.getOverlays();
        listOfOverlays.clear();
        listOfOverlays.add(mapOverlay);

Getting the Location That Was Touched

After using Google Maps for a while, you may want to know the latitude and longitude of a location corresponding to the position on the screen that was just touched. Knowing this information is very useful, as you can determine a location’s address, a process known as reverse geocoding (you will learn how this is done in the next section).

If you have added an overlay to the map, you can override the onTouchEvent() method within the MapOverlay class. This method is fired every time the user touches the map. This method has two parameters: MotionEvent and MapView. Using the MotionEvent parameter, you can determine whether the user has lifted his or her finger from the screen using the getAction() method. In the following code snippet, if the user has touched and then lifted the finger, you display the latitude and longitude of the location touched:

import android.view.MotionEvent;

//...

    class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            super.draw(canvas, mapView, shadow);

            //---translate the GeoPoint to screen pixels---
            Point screenPts = new Point();
            mapView.getProjection().toPixels(p, screenPts);

            //---add the marker---
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.pushpin);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);
            return true;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event, MapView mapView)
        {
            //---when user lifts his finger---
            if (event.getAction() == 1) {
                GeoPoint p = mapView.getProjection().fromPixels(
                    (int) event.getX(),
                    (int) event.getY());

                Toast.makeText(getActivity(),
                    "Location: "+
                    p.getLatitudeE6() / 1E6 + "," +
                    p.getLongitudeE6() /1E6 ,
                    Toast.LENGTH_SHORT).show();
            }
            return false;
        }
    }

The getProjection() method returns a projection for converting between screen-pixel coordinates and latitude/longitude coordinates. The fromPixels() method then converts the screen coordinates into a GeoPoint object.

Figure 4-15 shows the map displaying a set of coordinates when the user clicks a location on the map.

Geocoding and Reverse Geocoding

As mentioned in the preceding section, if you know the latitude and longitude of a location, you can find out its address using a process known as reverse geocoding. Google Maps in Android supports this via the Geocoder class. The following code snippet shows how you can retrieve the address of a location just touched using the getFromLocation() method:

import android.location.Address;
import android.location.Geocoder;
import java.util.Locale;
import java.io.IOException;
//...

    class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            super.draw(canvas, mapView, shadow);

            //---translate the GeoPoint to screen pixels---
            Point screenPts = new Point();
            mapView.getProjection().toPixels(p, screenPts);

            //---add the marker---
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.pushpin);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);
            return true;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event, MapView mapView)
        {
            //---when user lifts his finger---
            if (event.getAction() == 1) {
                GeoPoint p = mapView.getProjection().fromPixels(
                    (int) event.getX(),
                    (int) event.getY());
               
                /*
                Toast.makeText(getActivity(),
                    "Location: "+
                    p.getLatitudeE6() / 1E6 + "," +
                    p.getLongitudeE6() /1E6 ,
                    Toast.LENGTH_SHORT).show();
                */
               
                Geocoder geoCoder = new Geocoder(
                    getActivity(), Locale.getDefault());
                try {
                    List<Address> addresses = geoCoder.getFromLocation(
                        p.getLatitudeE6()  / 1E6,
                        p.getLongitudeE6() / 1E6, 1);
        
                    String add = "";
                    if (addresses.size() > 0) {
                        for (int i=0;
                             i<addresses.get(0).getMaxAddressLineIndex();
                             i++)
                           add += addresses.get(0).getAddressLine(i) +
                           "
";
                    }
                    Toast.makeText(getActivity(), add,
                        Toast.LENGTH_SHORT).show();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            }
            return false;
        }
    }

The Geocoder object converts the latitude and longitude into an address using the getFromLocation() method. Once the address is obtained, you display it using the Toast class. Figure 4-16 shows the application displaying the address of a location that was touched on the map.

If you know the address of a location but want to know its latitude and longitude, you can do so via geocoding. Again, you can use the Geocoder class for this purpose. The following code shows how you can find the exact location of the Empire State Building by using the getFromLocationName() method:

        //---geo-coding---
        Geocoder geoCoder = new Geocoder(this, Locale.getDefault());
        try {
            List<Address> addresses = geoCoder.getFromLocationName(
                "empire state building", 5);

            String add = "";
            if (addresses.size() > 0) {
                p = new GeoPoint(
                        (int) (addresses.get(0).getLatitude() * 1E6),
                        (int) (addresses.get(0).getLongitude() * 1E6));
                mc.animateTo(p);
                mapView.invalidate();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset