Aug 27

Creating Custom Google Maps Overlays with GWT Widgets

So this is one of those that I went around and around on before finding out the solution, which of course turned out to be pretty darn simple.

What I was trying to do was add a box with some text under a marker on a Google Map.

I’m using Google Web Toolkit 1.5.1 and the official gwt-google-apis maps-api.jar to bind the maps stuff into GWT.

It turns out that the secret is coming to an understanding of how Panes work in Google Maps.

The pane is the whole map.  Even the stuff that you can’t see in the little container box on your page.

That pane has Pixel coordinates.

Those coordinates do not change, even when you scroll the map.

That’s right.

The pixel coordinates of the map pane do not change when you scroll the map around inside the box on your page.

So if you add a widget to the pane at a certain pixel location, that widget doesn’t have to change it’s location when you scroll the map.

It also means that the widget can slide under the edge of your viewing port just like Markers do.

So all you need to do is decide where you want your GWT Widget to be in terms of Lat/Long and call the handy myMapWidget.convertLatLngToDivPixel(LatLong) method, and you’ll get back a Point on the map that is an absolute location on the map pane to place your Widget on.

As soon as you realize that the map pane coordinates don’t change when you scroll, and that you need to add your widgets to the pane rather than some other panel, life is super-easy.

Here is the class I wrote to implement the label.  I use it to place under Markers to label them on the map.

/* Custom Map Overlay Code – Copyright 2008 Cyface Design, Released Under the Apache 2.2 License */

import com.google.gwt.maps.client.MapPane; import
com.google.gwt.maps.client.MapPaneType; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.geom.Point; import com.google.gwt.maps.client.overlay.Overlay; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.SimplePanel;
 
public class MapMarkerTextOverlay extends Overlay {
 
private final LatLng latLng;
private final SimplePanel textPanel;
private MapWidget parentMap;
private MapPane pane;
private String text;
private Point offset;
 
/** * Main constructor *
@param latLng
*/
 
public MapMarkerTextOverlay(LatLng latLng, String text, Point offset) {
 
/* Save our inputs to the object */
this.latLng = latLng;
this.text = text;
this.offset = offset;
 
/* Create a widget for the text */
HTML textWidget = new HTML(text);
 
/* Create the panel to hold the text */
textPanel = new SimplePanel(); textPanel.setStyleName(“textOverlayPanel”);
textPanel.add(textWidget);
 
/* Panel gets added to the map and placed in the initialize method */
 
}
 
@Override protected final void initialize(MapWidget map) {
 
/* Save a handle to the parent map widget */
parentMap = map;
//If we need to do redraws we’ll need this
/* Add our textPanel to the main map pane */
 
pane = map.getPane(MapPaneType.MARKER_PANE); pane.add(textPanel);
 
/* Place the textPanel on the pane in the correct spot */
Point locationPoint = parentMap.convertLatLngToDivPixel(getLatLng()); Point offsetPoint = new Point(locationPoint.getX()-getOffset().getX(), locationPoint.getY()-getOffset().getY()); pane.setWidgetPosition(textPanel, offsetPoint.getX(), offsetPoint.getY());
}
 
@Override protected final Overlay copy() {
return new MapMarkerTextOverlay(getLatLng(), getText(), getOffset());
} @Override protected final void redraw(boolean force) {
 
/* Shouldn’t need to do anything here since we’re on the Marker pane. */
} @Override protected final void remove() { textPanel.removeFromParent();
} public LatLng getLatLng() {
return latLng;
 
} public String getText() { return text; }
 
public void setText(String text) { this.text = text; }
public Point getOffset() { return offset; }
public void setOffset(Point offset) { this.offset = offset; }
}