/*
* 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.widget.featureinfo.client.action.toolbar;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.geomajas.command.dto.SearchByLocationRequest;
import org.geomajas.command.dto.SearchByLocationResponse;
import org.geomajas.geometry.Coordinate;
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.controller.listener.AbstractListener;
import org.geomajas.gwt.client.controller.listener.ListenerEvent;
import org.geomajas.gwt.client.map.feature.Feature;
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.Mathlib;
import org.geomajas.gwt.client.spatial.WorldViewTransformer;
import org.geomajas.gwt.client.spatial.geometry.Point;
import org.geomajas.gwt.client.util.GeometryConverter;
import org.geomajas.gwt.client.util.Notify;
import org.geomajas.gwt.client.widget.MapWidget;
import org.geomajas.layer.wms.command.dto.SearchByPointRequest;
import org.geomajas.layer.wms.command.dto.SearchByPointResponse;
import org.geomajas.widget.featureinfo.client.FeatureInfoMessages;
import org.geomajas.widget.featureinfo.client.util.FitSetting;
import org.geomajas.widget.featureinfo.client.widget.MultiLayerFeatureInfoWindow;
import org.geomajas.widget.featureinfo.client.widget.factory.FeatureDetailWidgetFactory;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.NativeEvent;
import com.smartgwt.client.widgets.Window;
/**
* Shows information of the features (per visible vector layer) near the position where the user clicked on the map.
* First a list of all the appropriate features is shown in a floating window. Clicking on a feature of the list shows
* its attributes in a feature attribute window under the list window. As a starting point for this class the
* org.geomajas.gwt.client.controller.FeatureInfoController was used.
*
* @author An Buyle
* @author Oliver May
* @author Kristof Heirwegh
* @author Wout Swartenbroekx
*/
public class MultiLayerFeatureInfoListener extends AbstractListener {
private static final FeatureInfoMessages MESSAGES = GWT.create(FeatureInfoMessages.class);
private boolean dragging;
private boolean clickstart;
private final MapWidget mapWidget;
private boolean includeRasterLayers = FitSetting.featureinfoIncludeRasterLayer;
/**
* Number of pixels that describes the tolerance allowed when trying to select features.
*/
private int pixelTolerance = FitSetting.featureInfoPixelTolerance;
private final List<String> layersToExclude = new ArrayList<String>();
private Map<String, String> featuresListLabels;
/**
* Constructor.
*
* @param mapWidget map widget
*/
public MultiLayerFeatureInfoListener(MapWidget mapWidget) {
super();
this.mapWidget = mapWidget;
}
/**
* Constructor.
*
* @param mapWidget map widget
* @param pixelTolerance pixel tolerance
*/
public MultiLayerFeatureInfoListener(MapWidget mapWidget, int pixelTolerance) {
this(mapWidget);
this.pixelTolerance = pixelTolerance;
}
public int getPixelTolerance() {
return pixelTolerance;
}
public void setPixelTolerance(int pixelTolerance) {
this.pixelTolerance = pixelTolerance;
}
public void setLayersToExclude(String[] layerIds) {
this.layersToExclude.clear();
Collections.addAll(this.layersToExclude, layerIds);
}
@Override
public void onMouseDown(ListenerEvent event) {
if (event.getNativeButton() != NativeEvent.BUTTON_RIGHT) {
clickstart = true;
}
}
@Override
public void onMouseMove(ListenerEvent event) {
if (clickstart) {
dragging = true;
}
}
/**
* On mouse up, execute the search by location, and display a
* {@link org.geomajas.widget.featureinfo.client.widget.MultiLayerFeaturesList} if features are found.
*/
public void onMouseUp(ListenerEvent event) {
if (clickstart && !dragging) {
Coordinate worldPosition = event.getWorldPosition();
Point point = mapWidget.getMapModel().getGeometryFactory().createPoint(worldPosition);
SearchByLocationRequest request = new SearchByLocationRequest();
request.setLocation(GeometryConverter.toDto(point));
request.setCrs(mapWidget.getMapModel().getCrs());
request.setQueryType(SearchByLocationRequest.QUERY_INTERSECTS);
request.setSearchType(SearchByLocationRequest.SEARCH_ALL_LAYERS);
request.setBuffer(calculateBufferFromPixelTolerance());
request.setFeatureIncludes(GwtCommandDispatcher.getInstance().getLazyFeatureIncludesSelect());
for (Layer<?> layer : mapWidget.getMapModel().getLayers()) {
if (layer.isShowing() && layer instanceof VectorLayer && !layersToExclude.contains(layer.getId())) {
request.addLayerWithFilter(layer.getId(), layer.getServerLayerId(),
((VectorLayer) layer).getFilter());
}
}
final SearchByPointRequest rasterLayerRequest = new SearchByPointRequest();
rasterLayerRequest.setLocation(point.getCoordinate());
rasterLayerRequest.setCrs(mapWidget.getMapModel().getCrs());
rasterLayerRequest.setSearchType(SearchByPointRequest.SEARCH_ALL_LAYERS);
rasterLayerRequest.setPixelTolerance(pixelTolerance);
final Map<String, String> rasterLayerIds = new HashMap<String, String>();
for (Layer<?> layer : mapWidget.getMapModel().getLayers()) {
if (layer.isShowing() && layer instanceof RasterLayer && !layersToExclude.contains(layer.getId())) {
rasterLayerIds.put(layer.getId(), layer.getServerLayerId());
}
}
rasterLayerRequest.setLayerMapping(rasterLayerIds);
rasterLayerRequest.setBbox(toBbox(mapWidget.getMapModel().getMapView().getBounds()));
rasterLayerRequest.setScale(mapWidget.getMapModel().getMapView().getCurrentScale());
GwtCommand commandRequest = new GwtCommand(SearchByLocationRequest.COMMAND);
commandRequest.setCommandRequest(request);
// TODO: commands are now chained. Perhaps we should combine this
// into a single command?
GwtCommandDispatcher.getInstance().execute(commandRequest,
new AbstractCommandCallback<SearchByLocationResponse>() {
public void execute(final SearchByLocationResponse vectorResponse) {
if (includeRasterLayers) {
GwtCommand commandRequest = new GwtCommand(SearchByPointRequest.COMMAND);
commandRequest.setCommandRequest(rasterLayerRequest);
GwtCommandDispatcher.getInstance().execute(commandRequest,
new AbstractCommandCallback<SearchByPointResponse>() {
public void execute(final SearchByPointResponse rasterResponse) {
//Featuremap maps client layer on feature
Map<String, List<org.geomajas.layer.feature.Feature>> featureMap =
vectorResponse.getFeatureMap();
featureMap.putAll(rasterResponse.getFeatureMap());
showFeatureInfo(featureMap);
}
});
} else {
showFeatureInfo(vectorResponse.getFeatureMap());
}
}
});
} else {
dragging = false;
}
clickstart = false;
}
// -------------------------------------------------------------------------
// Private methods:
// -------------------------------------------------------------------------
private void showFeatureInfo(Map<String, List<org.geomajas.layer.feature.Feature>> featureMap) {
if (featureMap.size() > 0) {
if (featureMap.size() == 1 && featureMap.values().iterator().next().size() == 1) {
Layer<?> layer = (mapWidget.getMapModel().getLayer(featureMap.keySet().iterator().next()));
if (null != layer) {
org.geomajas.layer.feature.Feature featDTO = featureMap.values().iterator().next().get(0);
Feature feature;
if (layer instanceof VectorLayer) {
feature = new Feature(featDTO, (VectorLayer) layer);
} else {
feature = new Feature(featDTO, null);
}
Window window = FeatureDetailWidgetFactory.createFeatureDetailWindow(feature, layer, false);
window.setPageTop(mapWidget.getAbsoluteTop() + 25);
window.setPageLeft(mapWidget.getAbsoluteLeft() + 25);
window.setKeepInParentRect(true);
window.draw();
} else {
Notify.error(MESSAGES.multiLayerFeatureInfoLayerNotFound());
}
} else {
Window window = new MultiLayerFeatureInfoWindow(mapWidget, featureMap, featuresListLabels);
window.setPageTop(mapWidget.getAbsoluteTop() + 10);
window.setPageLeft(mapWidget.getAbsoluteLeft() + 50);
window.draw();
}
} else {
Notify.info(MESSAGES.multiLayerFeatureInfoNoResult());
}
}
/**
* Convert to Bbox DTO.
*
* @param bounds bounds
* @return DTO
*/
private org.geomajas.geometry.Bbox toBbox(Bbox bounds) {
return new org.geomajas.geometry.Bbox(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
}
private double calculateBufferFromPixelTolerance() {
WorldViewTransformer transformer = mapWidget.getMapModel().getMapView().getWorldViewTransformer();
Coordinate c1 = transformer.viewToWorld(new Coordinate(0, 0));
Coordinate c2 = transformer.viewToWorld(new Coordinate(pixelTolerance, 0));
return Mathlib.distance(c1, c2);
}
/**
* Set whether to include raster layers.
*
* @param includeRasterLayers
* whether to include raster layers in the result
*/
public void setIncludeRasterLayers(boolean includeRasterLayers) {
this.includeRasterLayers = includeRasterLayers;
}
/**
* Are raster layers included?
*
* @return the whether to include raster layer features in the result
*/
public boolean isIncludeRasterLayers() {
return includeRasterLayers;
}
/**
* Set list labels.
*
* @param featuresListLabels the featuresListLabels to set
*/
public void setFeaturesListLabels(Map<String, String> featuresListLabels) {
this.featuresListLabels = featuresListLabels;
}
}