January 25, 2015

Maps, ItemizedOverlay, and You!

This post synthesizes a discussion of getting ItemizedOverlay to work held on the [android-developers] Google Group. Many thanks to the folk who chimed in on that thread (particularly MarcelP), and for everyone on the Android Google Groups who have been helping other developers in need.


If you have ever used the full-size edition of Google Maps, you are probably used to seeing things overlaid atop the map itself, such as “push-pins” indicating businesses near the location being searched. In map parlance — and, for that matter, in many serious graphic editors — the push-pins are on a separate layer than the map itself, and what you are seeing is the composition of the push-pin layer atop the map layer.

Android’s mapping allows you to create layers as well, so you can mark up the maps as you need to based on user input and your application’s purpose.

Any overlay you want to add to your map needs to be implemented as a subclass of Overlay. There is an ItemizedOverlay subclass available if you are looking to add push-pins or the like; ItemizedOverlay simplifies this process.

To attach an overlay class to your map, just call getOverlays() on your MapView and add() your Overlay instance to it:

[sourcecode language=’java’]
Drawable marker=getResources().getDrawable(R.drawable.marker);

marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());
map.getOverlays().add(new SitesOverlay(marker));
[/sourcecode]

We will explain that marker in just a bit.

As the name suggests, ItemizedOverlay allows you to supply a list of points of interest to be displayed on the map — specifically, instances of OverlayItem. The overlay, then, handles much of the drawing logic for you. Here are the minimum steps to make this work:

  1. First, override ItemizedOverlay<OverlayItem> as your own subclass (in this example, SitesOverlay)
  2. In the constructor, build your roster of OverlayItem instances, and call populate() when they are ready for use by the overlay
  3. Implement size() to return the number of items to be handled by the overlay
  4. Override createItem() to return OverlayItem instances given an index
  5. When you instantiate your ItemizedOverlay subclass, provide it with a Drawable that represents the default icon (e.g., push-pin) to display for each item

The marker from the above code snippet is the Drawable used for the last bullet above.

You may also wish to override draw() to do a better job of handling the shadow for your markers. While the map will handle casting a shadow for you, it appears you need to provide a bit of assistance for it to know where the “bottom” of your icon is, so it can draw the shadow appropriately.

For example, here is an ItemizedOverlay subclass (SitesOverlay) that puts four pins on a map of Manhattan:

[sourcecode language=’java’]
private class SitesOverlay extends ItemizedOverlay {
private List items=new ArrayList();
private Drawable marker=null;

public SitesOverlay(Drawable marker) {
super(marker);
this.marker=marker;

items.add(new OverlayItem(getPoint(40.748963847316034, -73.96807193756104),
“UN”, “United Nations”));
items.add(new OverlayItem(getPoint(40.76866299974387, -73.98268461227417),
“Lincoln Center”, “Home of Jazz at Lincoln Center”));
items.add(new OverlayItem(getPoint(40.765136435316755, -73.97989511489868),
“Carnegie Hall”, “Where you go with practice, practice, practice”));
items.add(new OverlayItem(getPoint(40.70686417491799, -74.01572942733765),
“The Downtown Club”, “Original home of the Heisman Trophy”));

populate();
}

@Override
protected OverlayItem createItem(int i) {
return(items.get(i));
}

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

boundCenterBottom(marker);
}

@Override
protected boolean onTap(int i) {
Toast.makeText(NooYawk.this, items.get(i).getSnippet(), Toast.LENGTH_SHORT).show();

return(true);
}

@Override
public int size() {
return(items.size());
}
}
[/sourcecode]

If we open up a MapView and use that overlay, you would see something like this:

An ItemizedOverlay showing two Manhattan locations

An Overlay subclass can also implement onTap(), to be notified when the user taps on the map, so the overlay can adjust what it draws. For example, in full-size Google Maps, clicking on a push-pin pops up a bubble with information about the business at that pin’s location. With onTap(), you can do much the same in Android.

The onTap() method for ItemizedOverlay receives the index of the OverlayItem that was clicked. It is up to you to do something worthwhile with this event.

In the case of SitesOverlay, as shown above, onTap() just tosses up a short Toast with the “snippet” from the OverlayItem, returning true to indicate we handled the tap.



18 Comments

  1. Ash

    An excellent tutorial, keep it up. Will this work with SDK 1.0 release, haven’t tried it yet.

  2. Mark Murphy

    The code shown here should work. Note that in Android 1.0, they got rid of yet another means of providing mock location data, so you’re pretty much down to using DDMS.

  3. Adam

    Nice tutorial! You shouldn’t have to call boundCenterBottom() in each draw(), though. One call in the constructor should be enough, e.g. “super(boundCenterBottom(marker))”. Then you won’t have to override draw() at all.k Alternately you can do the correct setBounds() in line 3 of the first code snippet.

  4. Sudhakar

    Wow, unbelievable I have been trying to overlay items since a week with no luck. This tutorial works.Thank You very much for sharing your solution :)

  5. Matt

    What is the statement NooYawk.this on line 35 referring to? I am not familiar with this syntax. It seems as though you are referencing the static member this of the NooYawk class, which is somehow a Context then.

    Further explanation would be appreciated.

  6. Mark Murphy

    NooYawk.this has been standard Java syntax since at least Java 1.5.

    The this “variable” refers to the object instance itself. However, with inner class instances, sometimes you need the “this” that represents the instance of the outer class, instead of the “this” that represents the inner class. NooYawk.this is the syntax to say, from an inner class instance, “I want the ‘this’ that is the outer class instance, please”.

  7. Mapquest directions

    yes i believe it has been the same standard since java syntax 1.5 but i going to check though and ill tell you if it is, anyway i believe the syntax is here to stay

  8. Chris

    Hi could you explain what NooYawk is? Is it an Activity class or what? It doesn't describe it anywhere in your code

  9. Rich

    I’m working on a mapping application and this example is probably going to be REALLY helpful. However, I am a serious Android noob, and my understanding of it screen drawing methodology is extremely limited.

    I want to override draw so that I can display labels on my items all the time (their titles), but I don’t know anything about creating the canvas to pass in (or even if I have to).

    Can anyone point me at a drawing tutorial??

  10. Hemant

    Thanks for the post, but how this will work in Maps API v2? Overlays is not supported in new version of API.