/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.gwt.client.widget;
import java.util.ArrayList;
import java.util.List;
import org.geomajas.configuration.FeatureStyleInfo;
import org.geomajas.geometry.Coordinate;
import org.geomajas.gwt.client.Geomajas;
import org.geomajas.gwt.client.gfx.GraphicsContext;
import org.geomajas.gwt.client.gfx.paintable.Composite;
import org.geomajas.gwt.client.gfx.style.FontStyle;
import org.geomajas.gwt.client.gfx.style.PictureStyle;
import org.geomajas.gwt.client.gfx.style.ShapeStyle;
import org.geomajas.gwt.client.map.MapModel;
import org.geomajas.gwt.client.map.event.LayerChangedHandler;
import org.geomajas.gwt.client.map.event.LayerFilteredEvent;
import org.geomajas.gwt.client.map.event.LayerFilteredHandler;
import org.geomajas.gwt.client.map.event.LayerLabeledEvent;
import org.geomajas.gwt.client.map.event.LayerShownEvent;
import org.geomajas.gwt.client.map.event.LayerStyleChangeEvent;
import org.geomajas.gwt.client.map.event.LayerStyleChangedHandler;
import org.geomajas.gwt.client.map.event.MapModelEvent;
import org.geomajas.gwt.client.map.event.MapModelHandler;
import org.geomajas.gwt.client.map.layer.Layer;
import org.geomajas.gwt.client.map.layer.RasterLayer;
import org.geomajas.gwt.client.map.layer.VectorLayer;
import org.geomajas.gwt.client.spatial.Bbox;
import org.geomajas.gwt.client.spatial.geometry.LineString;
import org.geomajas.gwt.client.widget.event.GraphicsReadyEvent;
import org.geomajas.gwt.client.widget.event.GraphicsReadyHandler;
import org.geomajas.layer.LayerType;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.HandlerRegistration;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.events.ResizedEvent;
import com.smartgwt.client.widgets.events.ResizedHandler;
/**
* <p>
* Widget that shows the styles of the currently visible layers. For vector layers, there can be many styles. Note that
* this widget will react automatically to the visibility status of the layers.
* </p>
*
* @author Frank Wynants
* @author Pieter De Graef
*/
public class Legend extends Canvas {
private List<HandlerRegistration> registrations = new ArrayList<HandlerRegistration>();
private HandlerRegistration loadedRegistration;
private HandlerRegistration resizeRegistration;
private HandlerRegistration graphicsRegistration;
private MapModel mapModel;
private GraphicsWidget widget;
private GraphicsContext graphics;
private Composite parentGroup = new Composite("legend-group");
private FontStyle fontStyle = new FontStyle("#000000", 14, "Arial", "normal", "normal");
// -------------------------------------------------------------------------
// Constructor:
// -------------------------------------------------------------------------
/**
* A legend needs to be instantiated with the MapModel that contains (or will contain) the list of layers that this
* legend should listen to.
*/
public Legend(MapModel mapModel) {
super();
setWidth100();
setHeight100();
this.mapModel = mapModel;
widget = new GraphicsWidget(SC.generateID());
widget.setBackgroundColor("#FFFFFF");
// adding the graphics here causes problems when embedding in HTML !
// addChild(widget);
graphics = widget.getVectorContext();
loadedRegistration = mapModel.addMapModelHandler(new MapModelHandler() {
public void onMapModelChange(MapModelEvent event) {
initialize();
}
});
resizeRegistration = addResizedHandler(new ResizedHandler() {
public void onResized(ResizedEvent event) {
// Triggered by the render method (setHeight):
widget.setSize(getWidthAsString(), getHeightAsString());
widget.resize();
}
});
graphicsRegistration = widget.addGraphicsReadyHandler(new GraphicsReadyHandler() {
public void onReady(GraphicsReadyEvent event) {
// Triggered by the resized handler (i.e. we're ready to render the legend):
renderWithoutResize();
}
});
}
// -------------------------------------------------------------------------
// Public methods:
// -------------------------------------------------------------------------
/**
* Render the legend. This triggers a complete redraw.
*/
public void render() {
int y = 5;
for (Layer<?> layer : mapModel.getLayers()) {
if (layer.isShowing()) {
if (layer instanceof VectorLayer) {
VectorLayer vLayer = (VectorLayer) layer;
y += 21 * vLayer.getLayerInfo().getNamedStyleInfo().getFeatureStyles().size();
} else if (layer instanceof RasterLayer) {
y += 20;
}
}
}
setHeight(y);
}
// -------------------------------------------------------------------------
// Getters and setters:
// -------------------------------------------------------------------------
public MapModel getMapModel() {
return mapModel;
}
public FontStyle getFontStyle() {
return fontStyle;
}
public void setFontStyle(FontStyle fontStyle) {
this.fontStyle = fontStyle;
}
// -------------------------------------------------------------------------
// Private methods:
// -------------------------------------------------------------------------
/** Render the legend, without actually resizing the widget. */
private void renderWithoutResize() {
graphics.deleteGroup(parentGroup);
parentGroup = new Composite("legend-group");
graphics.drawGroup(null, parentGroup);
// Then go over all layers, to draw styles:
int lineCount = 0;
int y = 5;
for (Layer<?> layer : mapModel.getLayers()) {
if (layer.isShowing()) {
// Go over every truly visible layer:
if (layer instanceof VectorLayer) {
VectorLayer vLayer = (VectorLayer) layer;
// For vector layer; loop over the style definitions:
for (FeatureStyleInfo styleInfo : vLayer.getLayerInfo().getNamedStyleInfo().getFeatureStyles()) {
ShapeStyle style = new ShapeStyle(styleInfo);
graphics.drawSymbolDefinition(null, styleInfo.getStyleId(), styleInfo.getSymbol(),
new ShapeStyle(styleInfo), null);
lineCount++;
if (vLayer.getLayerInfo().getLayerType() == LayerType.LINESTRING
|| vLayer.getLayerInfo().getLayerType() == LayerType.MULTILINESTRING) {
// Lines, draw a LineString;
Coordinate[] coordinates = new Coordinate[4];
coordinates[0] = new Coordinate(10, y);
coordinates[1] = new Coordinate(10 + 10, y + 5);
coordinates[2] = new Coordinate(10 + 5, y + 10);
coordinates[3] = new Coordinate(10 + 15, y + 15);
LineString line = mapModel.getGeometryFactory().createLineString(coordinates);
graphics.drawLine(parentGroup, "style" + lineCount, line, style);
} else if (vLayer.getLayerInfo().getLayerType() == LayerType.POLYGON
|| vLayer.getLayerInfo().getLayerType() == LayerType.MULTIPOLYGON) {
// Polygons: draw a rectangle:
Bbox rect = new Bbox(10, y, 16, 16);
graphics.drawRectangle(parentGroup, "style" + lineCount, rect, style);
} else if (vLayer.getLayerInfo().getLayerType() == LayerType.POINT
|| vLayer.getLayerInfo().getLayerType() == LayerType.MULTIPOINT) {
// Points: draw a symbol:
graphics.drawSymbol(parentGroup, "style" + lineCount, new Coordinate(18, y + 8), style,
styleInfo.getStyleId());
}
// After the style, draw the style's name:
Coordinate textPosition = new Coordinate(30, y - 2);
graphics.drawText(parentGroup, "text" + lineCount, styleInfo.getName(), textPosition,
fontStyle);
y += 21;
}
} else if (layer instanceof RasterLayer) {
// For raster layers; show a nice symbol:
lineCount++;
graphics.drawImage(parentGroup, "style" + lineCount, Geomajas.getIsomorphicDir()
+ "geomajas/osgeo/layer-raster.png", new Bbox(10, y, 16, 16), new PictureStyle(1));
Coordinate textPosition = new Coordinate(30, y - 2);
graphics.drawText(parentGroup, "text" + lineCount, layer.getLabel(), textPosition, fontStyle);
y += 20;
}
}
}
}
/** Called when the MapModel configuration has been loaded. */
private void initialize() {
addChild(widget);
for (Layer<?> layer : mapModel.getLayers()) {
registrations.add(layer.addLayerChangedHandler(new LayerChangedHandler() {
public void onLabelChange(LayerLabeledEvent event) {
}
public void onVisibleChange(LayerShownEvent event) {
GWT.log("Legend: onVisibleChange()");
render();
}
}));
registrations.add(layer.addLayerStyleChangedHandler(new LayerStyleChangedHandler() {
public void onLayerStyleChange(LayerStyleChangeEvent event) {
GWT.log("Legend: onLayerStyleChange()");
render();
}
}));
}
for (final VectorLayer layer : mapModel.getVectorLayers()) {
layer.addLayerFilteredHandler(new LayerFilteredHandler() {
public void onFilterChange(LayerFilteredEvent event) {
render();
}
});
}
GWT.log("Legend.initialize");
render();
}
/** Remove all handlers on unload. */
protected void onUnload() {
if (registrations != null) {
for (HandlerRegistration registration : registrations) {
registration.removeHandler();
}
}
loadedRegistration.removeHandler();
resizeRegistration.removeHandler();
graphicsRegistration.removeHandler();
super.onUnload();
}
}