
org.geomajas.gwt2.client.map.MapPresenterImpl Maven / Gradle / Ivy
/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2013 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.gwt2.client.map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.geomajas.command.dto.GetMapConfigurationRequest;
import org.geomajas.command.dto.GetMapConfigurationResponse;
import org.geomajas.configuration.FeatureStyleInfo;
import org.geomajas.configuration.client.ClientMapInfo;
import org.geomajas.geometry.Geometry;
import org.geomajas.geometry.Matrix;
import org.geomajas.gwt.client.command.AbstractCommandCallback;
import org.geomajas.gwt.client.command.GwtCommand;
import org.geomajas.gwt.client.controller.MapEventParser;
import org.geomajas.gwt.client.map.RenderSpace;
import org.geomajas.gwt.client.util.Browser;
import org.geomajas.gwt2.client.controller.MapController;
import org.geomajas.gwt2.client.controller.MapEventParserFactory;
import org.geomajas.gwt2.client.controller.NavigationController;
import org.geomajas.gwt2.client.controller.TouchNavigationController;
import org.geomajas.gwt2.client.event.FeatureDeselectedEvent;
import org.geomajas.gwt2.client.event.FeatureSelectedEvent;
import org.geomajas.gwt2.client.event.FeatureSelectionHandler;
import org.geomajas.gwt2.client.event.LayerHideEvent;
import org.geomajas.gwt2.client.event.LayerShowEvent;
import org.geomajas.gwt2.client.event.LayerVisibilityHandler;
import org.geomajas.gwt2.client.event.LayerVisibilityMarkedEvent;
import org.geomajas.gwt2.client.event.MapInitializationEvent;
import org.geomajas.gwt2.client.event.MapResizedEvent;
import org.geomajas.gwt2.client.event.ViewPortChangedEvent;
import org.geomajas.gwt2.client.event.ViewPortChangedHandler;
import org.geomajas.gwt2.client.event.ViewPortChangingEvent;
import org.geomajas.gwt2.client.event.ViewPortChangingHandler;
import org.geomajas.gwt2.client.event.ViewPortScaledEvent;
import org.geomajas.gwt2.client.event.ViewPortScalingEvent;
import org.geomajas.gwt2.client.event.ViewPortTranslatedEvent;
import org.geomajas.gwt2.client.event.ViewPortTranslatingEvent;
import org.geomajas.gwt2.client.gfx.CanvasContainer;
import org.geomajas.gwt2.client.gfx.GfxUtil;
import org.geomajas.gwt2.client.gfx.HtmlContainer;
import org.geomajas.gwt2.client.gfx.TransformableWidgetContainer;
import org.geomajas.gwt2.client.gfx.VectorContainer;
import org.geomajas.gwt2.client.map.feature.Feature;
import org.geomajas.gwt2.client.map.feature.FeatureService;
import org.geomajas.gwt2.client.map.feature.FeatureServiceFactory;
import org.geomajas.gwt2.client.map.layer.LayersModel;
import org.geomajas.gwt2.client.map.render.MapRenderer;
import org.geomajas.gwt2.client.map.render.MapRendererFactory;
import org.geomajas.gwt2.client.service.CommandService;
import org.geomajas.gwt2.client.widget.control.scalebar.Scalebar;
import org.geomajas.gwt2.client.widget.control.watermark.Watermark;
import org.geomajas.gwt2.client.widget.control.zoom.ZoomControl;
import org.geomajas.gwt2.client.widget.control.zoomtorect.ZoomToRectangleControl;
import org.vaadin.gwtgraphics.client.Transformable;
import org.vaadin.gwtgraphics.client.VectorObject;
import org.vaadin.gwtgraphics.client.shape.Circle;
import com.google.gwt.event.dom.client.HasAllGestureHandlers;
import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
import com.google.gwt.event.dom.client.HasMouseDownHandlers;
import com.google.gwt.event.dom.client.HasMouseMoveHandlers;
import com.google.gwt.event.dom.client.HasMouseOutHandlers;
import com.google.gwt.event.dom.client.HasMouseOverHandlers;
import com.google.gwt.event.dom.client.HasMouseUpHandlers;
import com.google.gwt.event.dom.client.HasMouseWheelHandlers;
import com.google.gwt.event.dom.client.HasTouchCancelHandlers;
import com.google.gwt.event.dom.client.HasTouchEndHandlers;
import com.google.gwt.event.dom.client.HasTouchMoveHandlers;
import com.google.gwt.event.dom.client.HasTouchStartHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;
/**
* Default implementation of the map presenter interface. In other words this is the default GWT map object.
*
* @author Pieter De Graef
*/
public final class MapPresenterImpl implements MapPresenter {
/**
* Map view definition.
*
* @author Pieter De Graef
*/
public interface MapWidget extends HasMouseDownHandlers, HasMouseUpHandlers, HasMouseOutHandlers,
HasMouseOverHandlers, HasMouseMoveHandlers, HasMouseWheelHandlers, HasDoubleClickHandlers, IsWidget,
RequiresResize, HasTouchStartHandlers, HasTouchEndHandlers, HasTouchCancelHandlers, HasTouchMoveHandlers,
HasAllGestureHandlers {
/**
* Returns the HTML container of the map. This is a normal HTML container that contains the images of rasterized
* tiles (both vector and raster layers).
*
* @return the container
*/
HtmlContainer getMapHtmlContainer();
/**
* Returns the vector container that contains the vectorized tiles (SVG/VML) of vector layers.
*
* @return the container
*/
VectorContainer getMapVectorContainer();
/**
* Returns the list of user-defined vector containers for world-space objects.
*
* @return the container
*/
List getWorldVectorContainers();
/**
* Returns the list of user-defined containers (vector + canvas) for world-space objects.
*
* @return the container
*/
List getWorldTransformables();
/**
* Returns a new user-defined container for screen space objects.
*
* @return the container
*/
VectorContainer getNewScreenContainer();
/**
* Returns a new user-defined container for world space objects.
*
* @return the container
*/
VectorContainer getNewWorldContainer();
/**
* Returns a new user-defined container for world space widgets.
*
* @return the container
*/
TransformableWidgetContainer getNewWorldWidgetContainer();
/**
* Removes a user-defined container.
*
* @param container container
* @return true if removed, false if unknown
*/
boolean removeVectorContainer(VectorContainer container);
/**
* Removes a user-defined container.
*
* @param container container
* @return true if removed, false if unknown
*/
boolean removeWorldWidgetContainer(TransformableWidgetContainer container);
/**
* Brings the user-defined container to the front (relative to its world-space or screen-space peers!).
*
* @param container
* container
* @return true if successful
*/
boolean bringToFront(VectorContainer container);
/**
* Returns a new user-defined container of map gadgets.
*
* @return the container
*/
AbsolutePanel getWidgetContainer();
/**
* Get the total width of the view.
*
* @return width in pixels
*/
int getWidth();
/**
* Get the total height of the view.
*
* @return height in pixels
*/
int getHeight();
/**
* Set the total size of the view.
*
* @param width
* width
* @param height
* height
*/
void setPixelSize(int width, int height);
/**
* Schedules an animated scaling operation.
*
* @param xx
* the new x-axis scale factor
* @param yy
* the new y-axis scale factor
* @param animationMillis
* the animation time in millis
*/
void scheduleScale(double xx, double yy, int animationMillis);
CanvasContainer getNewWorldCanvas();
void scheduleTransform(double xx, double yy, double dx, double dy, int animationMillis);
}
private final MapEventBus eventBus;
@Inject
private CommandService commandService;
private final MapEventParser mapEventParser;
private List handlers;
private final Map> listeners;
private MapController mapController;
private MapController fallbackController;
@Inject
private LayersModel layersModel;
@Inject
private ViewPort viewPort;
private MapRenderer mapRenderer;
private WorldContainerRenderer worldContainerRenderer;
@Inject
private MapWidget display;
@Inject
private GfxUtil gfxUtil;
private MapConfiguration configuration;
private final MapRendererFactory mapRendererFactory;
private FeatureService featureService;
private boolean isMobileBrowser;
@Inject
private MapPresenterImpl(final FeatureServiceFactory featureServiceFactory,
final MapEventParserFactory mapEventParserFactory, final MapRendererFactory mapRendererFactory,
final EventBus eventBus) {
this.mapRendererFactory = mapRendererFactory;
handlers = new ArrayList();
listeners = new HashMap>();
featureService = featureServiceFactory.create(this);
mapEventParser = mapEventParserFactory.create(this);
this.eventBus = new MapEventBusImpl(this, eventBus);
this.configuration = new MapConfigurationImpl();
}
// ------------------------------------------------------------------------
// MapPresenter implementation:
// ------------------------------------------------------------------------
@Override
public void initialize(String applicationId, String id) {
mapRenderer = mapRendererFactory.create(layersModel, viewPort, configuration, display.getMapHtmlContainer());
isMobileBrowser = Browser.isMobile();
eventBus.addViewPortChangedHandler(mapRenderer);
eventBus.addViewPortChangingHandler(mapRenderer);
eventBus.addMapResizedHandler(mapRenderer);
eventBus.addLayerOrderChangedHandler(mapRenderer);
eventBus.addMapCompositionHandler(mapRenderer);
eventBus.addLayerVisibilityHandler(mapRenderer);
eventBus.addLayerStyleChangedHandler(mapRenderer);
eventBus.addLayerRefreshedHandler(mapRenderer);
final FeatureSelectionRenderer selectionRenderer = new FeatureSelectionRenderer();
eventBus.addLayerVisibilityHandler(selectionRenderer);
eventBus.addFeatureSelectionHandler(selectionRenderer);
worldContainerRenderer = new WorldContainerRenderer();
eventBus.addViewPortChangedHandler(worldContainerRenderer);
eventBus.addViewPortChangingHandler(worldContainerRenderer);
if (isMobileBrowser) {
fallbackController = new TouchNavigationController();
} else {
fallbackController = new NavigationController();
}
setMapController(fallbackController);
GwtCommand commandRequest = new GwtCommand(GetMapConfigurationRequest.COMMAND);
commandRequest.setCommandRequest(new GetMapConfigurationRequest(id, applicationId));
commandService.execute(commandRequest, new AbstractCommandCallback() {
public void execute(GetMapConfigurationResponse response) {
// Initialize the MapModel and ViewPort:
ClientMapInfo mapInfo = response.getMapInfo();
((MapConfigurationImpl) configuration).setServerConfiguration(mapInfo);
// Configure the ViewPort. This will immediately zoom to the initial bounds:
viewPort.setMapSize(display.getWidth(), display.getHeight());
layersModel.initialize(mapInfo, viewPort, eventBus);
viewPort.initialize(mapInfo, eventBus);
// Immediately zoom to the initial bounds as configured:
viewPort.applyBounds(mapInfo.getInitialBounds());
// Initialize the FeatureSelectionRenderer:
selectionRenderer.initialize(mapInfo);
// Adding the default map control widgets:
if (getWidgetPane() != null) {
getWidgetPane().add(new Watermark());
getWidgetPane().add(new Scalebar(MapPresenterImpl.this));
getWidgetPane().add(new ZoomControl(MapPresenterImpl.this), 5, 5);
if (!isMobileBrowser) {
getWidgetPane().add(new ZoomToRectangleControl(MapPresenterImpl.this), 60, 5);
}
}
// Fire initialization event
eventBus.fireEvent(new MapInitializationEvent());
}
});
}
@Override
public Widget asWidget() {
return display.asWidget();
}
/** @todo javadoc unknown. */
public void setMapRenderer(MapRenderer mapRenderer) {
this.mapRenderer = mapRenderer;
}
/** @todo javadoc unknown. */
public MapRenderer getMapRenderer() {
return mapRenderer;
}
@Override
public MapConfiguration getConfiguration() {
return configuration;
}
@Override
public void setSize(int width, int height) {
display.setPixelSize(width, height);
if (viewPort != null) {
viewPort.setMapSize(width, height);
}
eventBus.fireEvent(new MapResizedEvent(width, height));
}
@Override
public VectorContainer addWorldContainer() {
VectorContainer container = display.getNewWorldContainer();
// set transform parameters once, after that all is handled by WorldContainerRenderer
Matrix matrix = viewPort.getTransformationMatrix(RenderSpace.WORLD, RenderSpace.SCREEN);
container.setScale(matrix.getXx(), matrix.getYy());
container.setTranslation(matrix.getDx(), matrix.getDy());
return container;
}
@Override
public TransformableWidgetContainer addWorldWidgetContainer() {
TransformableWidgetContainer container = display.getNewWorldWidgetContainer();
// set transform parameters once, after that all is handled by WorldContainerRenderer
Matrix matrix = viewPort.getTransformationMatrix(RenderSpace.WORLD, RenderSpace.SCREEN);
container.setScale(matrix.getXx(), matrix.getYy());
container.setTranslation(matrix.getDx(), matrix.getDy());
return container;
}
@Override
public CanvasContainer addWorldCanvas() {
CanvasContainer container = display.getNewWorldCanvas();
// set transform parameters once, after that all is handled by WorldContainerRenderer
Matrix matrix = viewPort.getTransformationMatrix(RenderSpace.WORLD, RenderSpace.SCREEN);
container.setScale(matrix.getXx(), matrix.getYy());
container.setTranslation(matrix.getDx(), matrix.getDy());
return container;
}
@Override
public VectorContainer addScreenContainer() {
return display.getNewScreenContainer();
}
@Override
public boolean removeWorldWidgetContainer(TransformableWidgetContainer container) {
return display.removeWorldWidgetContainer(container);
}
@Override
public boolean removeVectorContainer(VectorContainer container) {
return display.removeVectorContainer(container);
}
@Override
public boolean bringToFront(VectorContainer container) {
return display.bringToFront(container);
}
@Override
public LayersModel getLayersModel() {
return layersModel;
}
@Override
public ViewPort getViewPort() {
return viewPort;
}
@Override
public FeatureService getFeatureService() {
return featureService;
}
@Override
public MapEventBus getEventBus() {
return eventBus;
}
@Override
public void setMapController(MapController mapController) {
for (HandlerRegistration registration : handlers) {
registration.removeHandler();
}
if (this.mapController != null) {
this.mapController.onDeactivate(this);
this.mapController = null;
}
handlers = new ArrayList();
if (null == mapController) {
mapController = fallbackController;
}
if (mapController != null) {
if (isMobileBrowser) {
handlers.add(display.addTouchStartHandler(mapController));
handlers.add(display.addTouchMoveHandler(mapController));
handlers.add(display.addTouchCancelHandler(mapController));
handlers.add(display.addGestureStartHandler(mapController));
handlers.add(display.addGestureChangeHandler(mapController));
handlers.add(display.addGestureEndHandler(mapController));
} else {
handlers.add(display.addMouseDownHandler(mapController));
handlers.add(display.addMouseMoveHandler(mapController));
handlers.add(display.addMouseOutHandler(mapController));
handlers.add(display.addMouseOverHandler(mapController));
handlers.add(display.addMouseUpHandler(mapController));
handlers.add(display.addMouseWheelHandler(mapController));
handlers.add(display.addDoubleClickHandler(mapController));
}
this.mapController = mapController;
mapController.onActivate(this);
}
}
@Override
public MapController getMapController() {
return mapController;
}
@Override
public boolean addMapListener(MapController mapListener) {
if (mapListener != null && !listeners.containsKey(mapListener)) {
List registrations = new ArrayList();
if (isMobileBrowser) {
registrations.add(display.addTouchStartHandler(mapListener));
registrations.add(display.addTouchMoveHandler(mapListener));
registrations.add(display.addTouchCancelHandler(mapListener));
registrations.add(display.addGestureStartHandler(mapListener));
registrations.add(display.addGestureChangeHandler(mapListener));
registrations.add(display.addGestureEndHandler(mapListener));
} else {
registrations.add(display.addMouseDownHandler(mapListener));
registrations.add(display.addMouseMoveHandler(mapListener));
registrations.add(display.addMouseOutHandler(mapListener));
registrations.add(display.addMouseOverHandler(mapListener));
registrations.add(display.addMouseUpHandler(mapListener));
registrations.add(display.addMouseWheelHandler(mapListener));
}
mapListener.onActivate(this);
listeners.put(mapListener, registrations);
return true;
}
return false;
}
@Override
public boolean removeMapListener(MapController mapListener) {
if (mapListener != null && listeners.containsKey(mapListener)) {
List registrations = listeners.get(mapListener);
for (HandlerRegistration registration : registrations) {
registration.removeHandler();
}
listeners.remove(mapListener);
mapListener.onDeactivate(this);
return true;
}
return false;
}
@Override
public Collection getMapListeners() {
return listeners.keySet();
}
@Override
public void setCursor(String cursor) {
DOM.setStyleAttribute(display.asWidget().getElement(), "cursor", cursor);
}
@Override
public MapEventParser getMapEventParser() {
return mapEventParser;
}
@Override
public AbsolutePanel getWidgetPane() {
return display.getWidgetContainer();
}
private int getAnimationMillis() {
Long time = (Long) configuration.getMapHintValue(MapConfiguration.ANIMATION_TIME);
return time == null ? 0 : time.intValue();
}
// ------------------------------------------------------------------------
// Private classes:
// ------------------------------------------------------------------------
/**
* Class that updates all the world containers when the view on the map changes.
*
* @author Pieter De Graef
*/
private class WorldContainerRenderer implements ViewPortChangedHandler, ViewPortChangingHandler {
public void onViewPortChanged(ViewPortChangedEvent event) {
Matrix matrix = viewPort.getTransformationMatrix(RenderSpace.WORLD, RenderSpace.SCREEN);
display.scheduleTransform(matrix.getXx(), matrix.getYy(), matrix.getDx(), matrix.getDy(),
getAnimationMillis());
}
public void onViewPortScaled(ViewPortScaledEvent event) {
Matrix matrix = viewPort.getTransformationMatrix(RenderSpace.WORLD, RenderSpace.SCREEN);
// We must translate as well because zooming and keeping the same center point
// also involves a translation (scale origin != center point) !!!
display.scheduleTransform(matrix.getXx(), matrix.getYy(), matrix.getDx(), matrix.getDy(),
getAnimationMillis());
}
public void onViewPortTranslated(ViewPortTranslatedEvent event) {
translate();
}
public void onViewPortChanging(ViewPortChangingEvent event) {
}
public void onViewPortScaling(ViewPortScalingEvent event) {
}
public void onViewPortTranslating(ViewPortTranslatingEvent event) {
translate();
}
private void translate() {
Matrix matrix = viewPort.getTransformationMatrix(RenderSpace.WORLD, RenderSpace.SCREEN);
for (Transformable transformable : display.getWorldTransformables()) {
transformable.setTranslation(matrix.getDx(), matrix.getDy());
}
}
}
/**
* This feature selection handler will render the selection on the map.
*
* @author Pieter De Graef
*/
private class FeatureSelectionRenderer implements FeatureSelectionHandler, LayerVisibilityHandler {
private final VectorContainer container;
private final Map shapes;
private FeatureStyleInfo pointStyle;
private FeatureStyleInfo lineStyle;
private FeatureStyleInfo ringStyle;
public FeatureSelectionRenderer() {
container = addWorldContainer();
shapes = new HashMap();
}
public void initialize(ClientMapInfo mapInfo) {
pointStyle = mapInfo.getPointSelectStyle();
lineStyle = mapInfo.getLineSelectStyle();
ringStyle = mapInfo.getPolygonSelectStyle();
}
public void onFeatureSelected(FeatureSelectedEvent event) {
Feature feature = event.getFeature();
if (feature.getLayer().isShowing()) {
render(feature);
}
}
public void onFeatureDeselected(FeatureDeselectedEvent event) {
remove(event.getFeature());
}
public void onShow(LayerShowEvent event) {
}
public void onHide(LayerHideEvent event) {
}
public void onVisibilityMarked(LayerVisibilityMarkedEvent event) {
}
private void render(Feature f) {
VectorObject shape = gfxUtil.toShape(f.getGeometry());
String type = f.getGeometry().getGeometryType();
if (Geometry.POINT.equals(type) || Geometry.MULTI_POINT.equals(type)) {
// if no radius is defined in symbol circle it will be default 5px
if (shape instanceof Circle && null != pointStyle.getSymbol()
&& null != pointStyle.getSymbol().getCircle()) {
((Circle) shape).setUserRadius(pointStyle.getSymbol().getCircle().getR());
}
gfxUtil.applyStroke(shape, pointStyle.getStrokeColor(), pointStyle.getStrokeOpacity(),
pointStyle.getStrokeWidth(), pointStyle.getDashArray());
gfxUtil.applyFill(shape, pointStyle.getFillColor(), pointStyle.getFillOpacity());
} else if (Geometry.LINE_STRING.equals(type) || Geometry.MULTI_LINE_STRING.equals(type)) {
gfxUtil.applyStroke(shape, lineStyle.getStrokeColor(), lineStyle.getStrokeOpacity(),
lineStyle.getStrokeWidth(), lineStyle.getDashArray());
gfxUtil.applyFill(shape, lineStyle.getFillColor(), lineStyle.getFillOpacity());
} else {
gfxUtil.applyStroke(shape, ringStyle.getStrokeColor(), ringStyle.getStrokeOpacity(),
ringStyle.getStrokeWidth(), ringStyle.getDashArray());
gfxUtil.applyFill(shape, ringStyle.getFillColor(), ringStyle.getFillOpacity());
}
container.add(shape);
shapes.put(f.getId(), shape);
}
private void remove(Feature feature) {
VectorObject shape = shapes.get(feature.getId());
if (shape != null) {
container.remove(shape);
shapes.remove(feature.getId());
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy