10 September 2010

Controlling the Z-Index of Map Overlays using the Google Maps Library for GWT

Google Maps is a great way to show location-based data on a map. In Choosel, we implemented a generic map widget which leverages Google Maps. This can for example be used to visualize the locations of recent earthquakes. Choosel also supports multiple coordinated view with selections and highlighting of items across different views (see Video):



However, occlusion in the map becomes a problem when trying to highlight resources across views. For example, when highlighting a particular earthquake in the timeline, it might be hidden by other earthquake overlays which are displayed on top of it in the map. The z-index of the earthquake overlay in the map would need to be adjusted such that the earthquake is displayed on top while being highlighted, but this is not directly possible using the Google Maps API 1.0 for GWT.

I implemented a custom Overlay class that uses a GWT label to display a data item. Using a GWT widget in the custom overlay has several advantages: (1) we can change the CSS styling, including the z-index, (2) we can use standard GWT event handlers, and (3) we don’t need to load images.

Before, we used the MapIconMaker from the gmaps utility library. The CSS based approach has some cross-browser limitation, e.g. rounded corner on IE, but it is faster because no images are loaded any more. However, the z-index changes outlined here are possible with any GWT widget, so switching to an Image widget should be easy.

This are the main elements of the LabelOverlay implementation used in Choosel:
public class LabelOverlay extends Overlay {

private Label label;

private LatLng latLng;

private MapWidget map;

private Point offset;

private MapPane pane;

private Point locationPoint;

public LabelOverlay(LatLng latLng, Point offset,
String text, String styleName) {

this.latLng = latLng;
this.offset = offset;
this.label = new Label(text);
this.label.setStyleName(styleName);
}

public HandlerRegistration addClickHandler(
ClickHandler handler) {

return label.addClickHandler(handler);
}

// ... other mouse handlers (down, move etc.)

@Override
protected final Overlay copy() {
return new LabelOverlay(latLng, offset,
label.getText(), label.getStyleName());
}

@Override
protected final void initialize(MapWidget map) {
this.map = map;

pane = map.getPane(MapPaneType.MARKER_PANE);
pane.add(label);

updatePosition(
map.convertLatLngToDivPixel(latLng));
}

@Override
protected final void redraw(boolean force) {
/*
* We check if the location has changed, because
* Google Maps allows infinite panning along the
* east-west-axis and requires an updated widget
* location in this case, although it will not
* force redrawing.
*/
Point newLocationPoint =
map.convertLatLngToDivPixel(latLng);

if (!force
&& sameLocation(newLocationPoint)) {
return;
}

updatePosition(newLocationPoint);
}

@Override
protected final void remove() {
label.removeFromParent();
}

private boolean sameLocation(Point newLocationPoint) {
assert newLocationPoint != null;
return locationPoint != null
&& locationPoint.getX()
== newLocationPoint.getX()
&& locationPoint.getY()
== newLocationPoint.getY();
}

public void setZIndex(int zIndex) {
// CSS is a class in Choosel
CSS.setZIndex(label, zIndex);
}

// ... other CSS attribute setters

private void updatePosition(Point newLocationPoint) {
assert newLocationPoint != null;
locationPoint = newLocationPoint;
pane.setWidgetPosition(label,
locationPoint.getX() + offset.getX(),
locationPoint.getY() + offset.getY());
}

}
The code uses some convenience methods from the Choosel CSS library class. The overlay are styled by default using this CSS class:
.resourceItemIcon {
width: 16px;
height: 16px;
border: 1px solid darkgray;
text-align:center;
vertical-align:middle;
font-weight:bold;
font-size: 12px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
padding: 0px;
}
The rest is pretty straightforward, the overlays are created and added:
overlay = new LabelOverlay(point, Point.newInstance(-10, -10),
label, CSS_RESOURCE_ITEM_ICON);
map.addOverlay(overlay);
In Choosel, this functionality is distributed in the MapItem and MapViewContentDisplay classes.

Using the approach outline in this blog post, you can use GWT widgets in map overlays and control CSS attributes such as the z-Index.

No comments: