/* * This is part of Geomajas, a GIS framework, http://www.geomajas.org/. * * Copyright 2008-2015 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.map.layer; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.geomajas.annotation.Api; import org.geomajas.command.dto.RegisterNamedStyleInfoRequest; import org.geomajas.command.dto.RegisterNamedStyleInfoResponse; import org.geomajas.configuration.client.ClientVectorLayerInfo; import org.geomajas.gwt.client.command.AbstractCommandCallback; import org.geomajas.gwt.client.command.GwtCommand; import org.geomajas.gwt.client.command.GwtCommandDispatcher; import org.geomajas.gwt.client.gfx.PaintableGroup; import org.geomajas.gwt.client.gfx.PainterVisitor; import org.geomajas.gwt.client.gfx.paintable.Composite; import org.geomajas.gwt.client.gfx.style.PictureStyle; import org.geomajas.gwt.client.map.MapModel; import org.geomajas.gwt.client.map.cache.TileCache; import org.geomajas.gwt.client.map.cache.tile.TileFunction; import org.geomajas.gwt.client.map.cache.tile.VectorTile; import org.geomajas.gwt.client.map.event.FeatureDeselectedEvent; import org.geomajas.gwt.client.map.event.FeatureSelectedEvent; import org.geomajas.gwt.client.map.event.FeatureSelectionHandler; import org.geomajas.gwt.client.map.event.HasFeatureSelectionHandlers; import org.geomajas.gwt.client.map.event.HasLayerFilteredHandlers; import org.geomajas.gwt.client.map.event.LayerFilteredEvent; import org.geomajas.gwt.client.map.event.LayerFilteredHandler; import org.geomajas.gwt.client.map.event.LayerStyleChangeEvent; import org.geomajas.gwt.client.map.feature.Feature; import org.geomajas.gwt.client.map.store.VectorLayerStore; import org.geomajas.gwt.client.spatial.Bbox; import org.geomajas.gwt.client.util.EqualsUtil; import com.google.gwt.event.shared.HandlerRegistration; /** * <p> * The client side representation of a vector layer. * </p> * * @author Pieter De Graef * @since 1.6.0 */ @Api public class VectorLayer extends AbstractLayer<ClientVectorLayerInfo> implements HasFeatureSelectionHandlers, HasLayerFilteredHandlers { /** Storage of features in this layer. */ private TileCache cache; private String filter; private double opacity = 1.0; private Composite featureGroup = new Composite("features"); private Composite selectionGroup = new Composite("selection"); private Composite labelGroup = new Composite("labels"); /** selected features id -> feature map */ private Map<String, Feature> selectedFeatures = new HashMap<String, Feature>(); // ------------------------------------------------------------------------- // Constructors: // ------------------------------------------------------------------------- /** * The only constructor! Set the MapModel and the metadata. * * @param mapModel * The model of layers and features behind a map. This layer will be a part of this model. * @param layerInfo * client layer descriptor */ public VectorLayer(MapModel mapModel, ClientVectorLayerInfo layerInfo) { super(mapModel, layerInfo); Bbox maxExtent = new Bbox(layerInfo.getMaxExtent()); cache = new TileCache(this, maxExtent); } public final HandlerRegistration addFeatureSelectionHandler(FeatureSelectionHandler handler) { return handlerManager.addHandler(FeatureSelectionHandler.TYPE, handler); } public final HandlerRegistration addLayerFilteredHandler(LayerFilteredHandler handler) { return handlerManager.addHandler(LayerFilteredHandler.TYPE, handler); } // ------------------------------------------------------------------------- // Paintable implementation: // ------------------------------------------------------------------------- public void accept(final PainterVisitor visitor, final Object group, final Bbox bounds, boolean recursive) { // Draw layer-specific stuff (see VectorLayerPainter) visitor.visit(this, group); // When visible, take care of fetching through an queryAndSync: if (recursive && isShowing()) { TileFunction<VectorTile> onDelete = new TileFunction<VectorTile>() { // When deleting a tile, delete selected features from the paint ! public void execute(final VectorTile tile) { visitor.remove(tile, group); } }; TileFunction<VectorTile> onUpdate = new TileFunction<VectorTile>() { // Updating a tile, re-rendering it: public void execute(VectorTile tile) { tile.accept(visitor, group, bounds, true); } }; cache.queryAndSync(bounds, filter, onDelete, onUpdate); // also re-render the selected features ! for (Feature feature : selectedFeatures.values()) { visitor.visit(feature, group); } } } // ------------------------------------------------------------------------- // Public methods for feature Selection // ------------------------------------------------------------------------- /** * Return whether the feature with given id is selected. * * @param featureId * feature id to test * @return true when the feature with given ide is selected */ public boolean isFeatureSelected(String featureId) { return selectedFeatures.containsKey(featureId); } /** * Select a feature: set the feature's selected state and add it to the layer's selection. * * @param feature * The feature that is to be selected. * @return true when the feature was deselected */ public boolean selectFeature(Feature feature) { if (!selectedFeatures.containsKey(feature.getId())) { // make sure we get the layer's instance (if we can find it) ! Feature layerFeature = getFeatureStore().getPartialFeature(feature.getId()); if (null != layerFeature) { selectedFeatures.put(layerFeature.getId(), layerFeature); handlerManager.fireEvent(new FeatureSelectedEvent(layerFeature)); } else { selectedFeatures.put(feature.getId(), feature); handlerManager.fireEvent(new FeatureSelectedEvent(feature)); } return true; } else { return false; } } /** * Deselect a feature: set the feature's selected state and remove it from the layer's selection. * * @param feature * The feature that is to be selected. * @return true when the feature was selected */ public boolean deselectFeature(Feature feature) { if (selectedFeatures.containsKey(feature.getId())) { Feature org = selectedFeatures.remove(feature.getId()); handlerManager.fireEvent(new FeatureDeselectedEvent(org)); return true; } else { return false; } } /** Clear the list of selected features. */ public void clearSelectedFeatures() { List<Feature> clone = new LinkedList<Feature>(selectedFeatures.values()); for (Feature feature : clone) { selectedFeatures.remove(feature.getId()); handlerManager.fireEvent(new FeatureDeselectedEvent(feature)); } } /** * Returns a set of selected features in this layer by their ID's. * * @return set of selected feature IDs */ public Set<String> getSelectedFeatures() { return selectedFeatures.keySet(); } /** * Returns the collection of selected features (the objects) in this layer. * * @return selected features */ public Collection<Feature> getSelectedFeatureValues() { return selectedFeatures.values(); } // ------------------------------------------------------------------------- // Public methods: // ------------------------------------------------------------------------- public String getFilter() { return filter; } public void setFilter(String filter) { String oldFilter = this.filter; this.filter = filter; if (!EqualsUtil.isEqual(this.filter, oldFilter)) { clearSelectedFeatures(); // these features may not comply with the current filter cache.clear(); // need to clear this cache as this contains data for another filter handlerManager.fireEvent(new LayerFilteredEvent(this)); } } @Override public void setOpacity(double opacity) { this.opacity = opacity; for (VectorTile tile : cache.getTiles()) { tile.setPictureStyle(new PictureStyle(opacity)); } handlerManager.fireEvent(new LayerStyleChangeEvent(this)); } @Override public double getOpacity() { return opacity; } public VectorLayerStore getFeatureStore() { return cache; } public PaintableGroup getFeatureGroup() { return featureGroup; } public PaintableGroup getSelectionGroup() { return selectionGroup; } public PaintableGroup getLabelGroup() { return labelGroup; } @Override public void updateStyle() { GwtCommand commandRequest = new GwtCommand(RegisterNamedStyleInfoRequest.COMMAND); RegisterNamedStyleInfoRequest request = new RegisterNamedStyleInfoRequest(); request.setLayerId(getServerLayerId()); request.setNamedStyleInfo(getLayerInfo().getNamedStyleInfo()); commandRequest.setCommandRequest(request); GwtCommandDispatcher.getInstance().execute(commandRequest, new AbstractCommandCallback<RegisterNamedStyleInfoResponse>() { @Override public void execute(RegisterNamedStyleInfoResponse response) { getLayerInfo().getNamedStyleInfo().setName(response.getStyleName()); handlerManager.fireEvent(new LayerStyleChangeEvent(VectorLayer.this)); } }); } }