All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geomajas.gwt2.client.map.MapPresenterImpl Maven / Gradle / Ivy

/*
 * 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.gwt2.client.map;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.geomajas.geometry.Matrix;
import org.geomajas.gwt.client.controller.MapEventParser;
import org.geomajas.gwt.client.event.HasAllPointerTouchHandlers;
import org.geomajas.gwt.client.event.PointerEvents;
import org.geomajas.gwt.client.map.RenderSpace;
import org.geomajas.gwt.client.util.Dom;
import org.geomajas.gwt2.client.controller.MapController;
import org.geomajas.gwt2.client.controller.MapEventParserImpl;
import org.geomajas.gwt2.client.controller.NavigationController;
import org.geomajas.gwt2.client.controller.TouchNavigationController;
import org.geomajas.gwt2.client.event.LayerAddedEvent;
import org.geomajas.gwt2.client.event.LayerRemovedEvent;
import org.geomajas.gwt2.client.event.MapCompositionHandler;
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.gfx.CanvasContainer;
import org.geomajas.gwt2.client.gfx.TransformableWidgetContainer;
import org.geomajas.gwt2.client.gfx.VectorContainer;
import org.geomajas.gwt2.client.map.layer.LayersModel;
import org.geomajas.gwt2.client.map.layer.LayersModelImpl;
import org.geomajas.gwt2.client.map.render.LayersModelRenderer;
import org.geomajas.gwt2.client.map.render.RenderingInfo;
import org.geomajas.gwt2.client.map.render.dom.DomLayersModelRenderer;
import org.geomajas.gwt2.client.map.render.dom.container.HtmlContainer;
import org.geomajas.gwt2.client.widget.DefaultMapWidget;
import org.geomajas.gwt2.client.widget.control.pan.PanControl;
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.zoom.ZoomStepControl;
import org.geomajas.gwt2.client.widget.control.zoomtorect.ZoomToRectangleControl;
import org.geomajas.gwt2.client.widget.map.MapWidgetImpl;
import org.vaadin.gwtgraphics.client.Transformable;

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.HasWidgets;
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.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 {

	private static Logger logger = Logger.getLogger("MapPresenterImpl");

	/**
	 * 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, HasAllPointerTouchHandlers {

		/**
		 * 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 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
		 */
		HasWidgets 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);

		CanvasContainer getNewWorldCanvas();
	}

	private final MapEventBus eventBus;

	private final MapEventParser mapEventParser;

	private final LayersModel layersModel;

	private final ViewPortImpl viewPort;

	private final MapWidget display;

	private final ContainerManager containerManager;

	private final Map> listeners;

	private MapConfiguration configuration;

	private List handlers;

	private MapController mapController;

	private MapController fallbackController;

	private LayersModelRenderer renderer;

	private boolean isTouchSupported;

	// ------------------------------------------------------------------------
	// Constructor:
	// ------------------------------------------------------------------------

	public MapPresenterImpl(final EventBus eventBus) {
		this.handlers = new ArrayList();
		this.listeners = new HashMap>();
		this.eventBus = new MapEventBusImpl(this, eventBus);
		this.display = new MapWidgetImpl();
		this.viewPort = new ViewPortImpl(this.eventBus);
		this.layersModel = new LayersModelImpl(this.viewPort, this.eventBus);
		this.mapEventParser = new MapEventParserImpl(this);
		this.renderer = new DomLayersModelRenderer(layersModel, viewPort, this.eventBus);
		this.containerManager = new ContainerManagerImpl(display, viewPort);
		this.isTouchSupported = Dom.isTouchSupported() || PointerEvents.isSupported();

		this.eventBus.addViewPortChangedHandler(new ViewPortChangedHandler() {

			@Override
			public void onViewPortChanged(ViewPortChangedEvent event) {
				renderer.render(new RenderingInfo(display.getMapHtmlContainer(), event.getTo(), event.getTrajectory()));
			}
		});
		this.eventBus.addMapCompositionHandler(new MapCompositionHandler() {

			@Override
			public void onLayerRemoved(LayerRemovedEvent event) {
			}

			@Override
			public void onLayerAdded(LayerAddedEvent event) {
				if (layersModel.getLayerCount() == 1) {
					renderer.setAnimated(event.getLayer(), true);
				}
			}
		});

		this.eventBus.addViewPortChangedHandler(new WorldTransformableRenderer());

		if (isTouchSupported) {
			if (PointerEvents.isSupported()) {
				logger.info("touch supported");
				try {
					logger.info("setting touch action");
					display.asWidget().getElement().getStyle().setProperty("touchAction", "none");
				} catch (Exception e) {
					logger.info("oooops " + e.getMessage());
				}
			}
			logger.fine("touch");
			fallbackController = new TouchNavigationController();
		} else {
			logger.fine("no touch");
			fallbackController = new NavigationController();
		}

		setMapController(fallbackController);
		setSize(100, 100);
	}

	/**
	 * Generic controller that is used on touch devices. Note that gestures and multi touch are not supported by some
	 * mobile browsers.
	 *
	 * @author Jan De Moerloose
	 * @since 2.0.0
	 */

	// ------------------------------------------------------------------------
	// MapPresenter implementation:
	// ------------------------------------------------------------------------
	public void initialize(MapConfiguration configuration, DefaultMapWidget... mapWidgets) {
		initialize(configuration, true, mapWidgets);
	}

	public void initialize(MapConfiguration configuration, boolean fireEvent, DefaultMapWidget... mapWidgets) {
		this.configuration = configuration;

		// Apply this configuration on the LayersModelRenderer:
		if (renderer instanceof DomLayersModelRenderer) {
			((DomLayersModelRenderer) renderer).setMapConfiguration(configuration);
		}

		// Configure the ViewPort. This will immediately zoom to the initial bounds:
		// viewPort.setMapSize(display.getWidth(), display.getHeight());
		viewPort.initialize(configuration);

		// Immediately zoom to the initial bounds as configured:
		viewPort.applyBounds(configuration.getHintValue(MapConfiguration.INITIAL_BOUNDS), ZoomOption.LEVEL_CLOSEST);
		renderer.render(new RenderingInfo(display.getMapHtmlContainer(), viewPort.getView(), null));

		// Adding the default map control widgets:
		if (getWidgetPane() != null) {
			getWidgetPane().add(new Watermark()); // We always add the watermark...
			for (DefaultMapWidget widget : mapWidgets) {
				switch (widget) {
					case SCALEBAR:
						getWidgetPane().add(new Scalebar(MapPresenterImpl.this));
						break;
					case PAN_CONTROL:
						getWidgetPane().add(new PanControl(MapPresenterImpl.this));
						break;
					case ZOOM_CONTROL:
						getWidgetPane().add(new ZoomControl(MapPresenterImpl.this));
						break;
					case ZOOM_STEP_CONTROL:
						getWidgetPane().add(new ZoomStepControl(MapPresenterImpl.this));
						break;
					case ZOOM_TO_RECTANGLE_CONTROL:
						getWidgetPane().add(new ZoomToRectangleControl(MapPresenterImpl.this));
						break;
				}
			}
		}
		// Fire initialization event
		if (fireEvent) {
			eventBus.fireEvent(new MapInitializationEvent(this));
		}
	}

	@Override
	public Widget asWidget() {
		return display.asWidget();
	}

	public void setRenderer(LayersModelRenderer renderer) {
		this.renderer = renderer;
	}

	public LayersModelRenderer getLayersModelRenderer() {
		return renderer;
	}

	@Override
	public MapConfiguration getConfiguration() {
		if (configuration == null) {
			configuration = new MapConfigurationImpl();
		}
		return configuration;
	}

	@Override
	public void setSize(int width, int height) {
		display.setPixelSize(width, height);
		viewPort.setMapSize(width, height);
		eventBus.fireEvent(new MapResizedEvent(width, height));
	}

	@Override
	public LayersModel getLayersModel() {
		return layersModel;
	}

	@Override
	public ViewPort getViewPort() {
		return viewPort;
	}

	@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 (isTouchSupported) {
				if (PointerEvents.isSupported()) {
					handlers.add(display.addPointerTouchStartHandler(mapController));
					handlers.add(display.addPointerTouchMoveHandler(mapController));
					handlers.add(display.addPointerTouchEndHandler(mapController));
					handlers.add(display.addPointerTouchCancelHandler(mapController));
					handlers.add(display.addMouseWheelHandler(mapController));
				} else {
					handlers.add(display.addTouchStartHandler(mapController));
					handlers.add(display.addTouchMoveHandler(mapController));
					handlers.add(display.addTouchEndHandler(mapController));
					handlers.add(display.addTouchCancelHandler(mapController));
					handlers.add(display.addGestureStartHandler(mapController));
					handlers.add(display.addGestureChangeHandler(mapController));
					handlers.add(display.addGestureEndHandler(mapController));
					handlers.add(display.addMouseWheelHandler(mapController));
				}
			} 
			// always listen to mouse events, controllers are responsible to avoid double-handling !!!
			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 (isTouchSupported) {
				if (PointerEvents.isSupported()) {
					registrations.add(display.addPointerTouchStartHandler(mapController));
					registrations.add(display.addPointerTouchMoveHandler(mapController));
					registrations.add(display.addPointerTouchEndHandler(mapController));
					registrations.add(display.addPointerTouchCancelHandler(mapController));
					registrations.add(display.addMouseWheelHandler(mapController));
				} else {
					registrations.add(display.addTouchStartHandler(mapController));
					registrations.add(display.addTouchMoveHandler(mapController));
					registrations.add(display.addTouchEndHandler(mapController));
					registrations.add(display.addTouchCancelHandler(mapController));
					registrations.add(display.addGestureStartHandler(mapController));
					registrations.add(display.addGestureChangeHandler(mapController));
					registrations.add(display.addGestureEndHandler(mapController));
					registrations.add(display.addMouseWheelHandler(mapController));
				}
			}
			// always listen to mouse events, listeners are responsible to avoid double-handling !!!
			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 HasWidgets getWidgetPane() {
		return display.getWidgetContainer();
	}

	@Override
	public ContainerManager getContainerManager() {
		return containerManager;
	}

	// ------------------------------------------------------------------------
	// Private classes:
	// ------------------------------------------------------------------------

	/**
	 * Handler that redraws all world space objects whenever the view on the map changes.
	 *
	 * @author Pieter De Graef
	 */
	private class WorldTransformableRenderer implements ViewPortChangedHandler {

		@Override
		public void onViewPortChanged(ViewPortChangedEvent event) {
			Matrix matrix = viewPort.getTransformationService().getTransformationMatrix(RenderSpace.WORLD,
					RenderSpace.SCREEN);

			for (Transformable worldTransformable : display.getWorldTransformables()) {
				worldTransformable.setTranslation(matrix.getDx(), matrix.getDy());
				worldTransformable.setScale(matrix.getXx(), matrix.getYy());
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy