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

org.geomajas.gwt2.client.controller.NavigationController Maven / Gradle / Ivy

There is a newer version: 2.4.3
Show newest version
/*
 * 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.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import org.geomajas.annotation.Api;
import org.geomajas.geometry.Coordinate;
import org.geomajas.geometry.service.MathService;
import org.geomajas.gwt.client.map.RenderSpace;
import org.geomajas.gwt2.client.animation.KineticTrajectory;
import org.geomajas.gwt2.client.animation.NavigationAnimationFactory;
import org.geomajas.gwt2.client.animation.NavigationAnimationImpl;
import org.geomajas.gwt2.client.map.MapPresenter;
import org.geomajas.gwt2.client.map.View;
import org.geomajas.gwt2.client.map.ViewPort;

import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.HumanInputEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;

/**
 * Generic navigation map controller. This controller allows for panning and zooming on the map in many different ways.
 * Options are the following:
 * 
    *
  • Panning: Drag the map to pan.
  • *
  • Double click: Double clicking on some location will see the map zoom in to that location.
  • *
  • Zoom to rectangle: By holding shift or ctrl and dragging at the same time, a rectangle will appear on the * map. On mouse up, the map will than zoom to that rectangle.
  • *
  • *
* For zooming using the mouse wheel there are 2 options, defined by the {@link ScrollZoomType} enum. These options are * the following: *
    *
  • ScrollZoomType.ZOOM_CENTER: Zoom in/out so that the current center of the map remains.
  • *
  • ScrollZoomType.ZOOM_POSITION: Zoom in/out so that the mouse world position remains the same. Can be great * for many subsequent double clicks, to make sure you keep zooming to the same location (wherever the mouse points to). *
  • *
* * @author Pieter De Graef * @since 2.0.0 */ @Api(allMethods = true) public class NavigationController extends AbstractMapController { private static Logger logger = Logger.getLogger("MapPresenterImpl"); /** Zooming types on mouse wheel scroll. */ public static enum ScrollZoomType { /** When scroll zooming, retain the center of the map position. */ ZOOM_CENTER, /** When scroll zooming, retain the mouse position. */ ZOOM_POSITION } private final ZoomToRectangleController zoomToRectangleController; protected Coordinate dragOrigin; protected List dragPositions = new ArrayList(); protected List dragTimes = new ArrayList(); protected Coordinate lastClickPosition; protected boolean zooming; protected boolean dragging; private ScrollZoomType scrollZoomType = ScrollZoomType.ZOOM_POSITION; private long lastMillis; private static final long MAX_KINETIC_DELAY = 30; // ------------------------------------------------------------------------ // Constructors: // ------------------------------------------------------------------------ /** Create a NavigationController instance. */ public NavigationController() { super(false); zoomToRectangleController = new ZoomToRectangleController(); } // ------------------------------------------------------------------------ // MapController implementation: // ------------------------------------------------------------------------ @Override public void onActivate(MapPresenter mapPresenter) { super.onActivate(mapPresenter); zoomToRectangleController.onActivate(mapPresenter); } @Override public void onMouseDown(MouseDownEvent event) { super.onMouseDown(event); if (event.isControlKeyDown() || event.isShiftKeyDown()) { // Trigger the dragging on the zoomToRectangleController: zoomToRectangleController.onMouseDown(event); } } @Override public void onDown(HumanInputEvent event) { if (event.isControlKeyDown() || event.isShiftKeyDown()) { zooming = true; } else if (!isRightMouseButton(event)) { dragging = true; dragOrigin = getLocation(event, RenderSpace.SCREEN); dragPositions.clear(); dragTimes.clear(); mapPresenter.setCursor("move"); } lastClickPosition = getLocation(event, RenderSpace.WORLD); } @Override public void onUp(HumanInputEvent event) { if (zooming) { logger.info("zooming"); Coordinate coordinate = getLocation(event, RenderSpace.WORLD); if (!coordinate.equals(lastClickPosition)) { zoomToRectangleController.onUp(event); } zooming = false; } else if (dragging) { stopPanning(event); } } @Override public void onMouseMove(MouseMoveEvent event) { if (zooming) { zoomToRectangleController.onMouseMove(event); } else if (dragging) { super.onMouseMove(event); } } @Override public void onDrag(HumanInputEvent event) { dragPositions.add(getLocation(event, RenderSpace.SCREEN)); Date time = new Date(); dragTimes.add(time); if (time.getTime() - this.dragTimes.get(0).getTime() > 200) { this.dragTimes.remove(0); this.dragPositions.remove(0); } updateView(event); } @Override public void onMouseOut(MouseOutEvent event) { if (zooming) { zoomToRectangleController.onMouseOut(event); } else { stopPanning(null); } } @Override public void onDoubleClick(DoubleClickEvent event) { mapPresenter.getViewPort().registerAnimation( NavigationAnimationFactory.createZoomIn(mapPresenter, calculatePosition(true, lastClickPosition))); } @Override public void onMouseWheel(MouseWheelEvent event) { final boolean isNorth; if (event.getDeltaY() == 0) { isNorth = (getWheelDelta(event.getNativeEvent()) < 0); } else { isNorth = event.isNorth(); } Coordinate location = getLocation(event, RenderSpace.WORLD); scrollZoomTo(isNorth, location); } // Getters and setters: // ------------------------------------------------------------------------ /** * Get the scroll zoom type of this controller. * * @return the scroll zoom type. */ public ScrollZoomType getScrollZoomType() { return scrollZoomType; } /** * Set the scroll zoom type of this controller. * * @param scrollZoomType the scroll zoom type. */ public void setScrollZoomType(ScrollZoomType scrollZoomType) { this.scrollZoomType = scrollZoomType; } // ------------------------------------------------------------------------ // Private methods: // ------------------------------------------------------------------------ protected void stopPanning(HumanInputEvent event) { dragging = false; mapPresenter.setCursor("default"); if (null != event && dragPositions.size() >= 2) { logger.info("kinetics started"); // start kinetic animation Coordinate dragStart = toWorld(dragPositions.get(0)); Coordinate dragStop = toWorld(dragPositions.get(dragPositions.size() - 1)); long timeStart = dragTimes.get(0).getTime(); long timeStop = dragTimes.get(dragTimes.size() - 1).getTime(); int delta = (int) (timeStop - timeStart); long delay = new Date().getTime() - timeStop; if (delta > 0 && delay < MAX_KINETIC_DELAY) { // map moves in the inverse direction ! Coordinate direction = subtract(dragStart, dragStop); double distance = abs(direction); Coordinate current = mapPresenter.getViewPort().getPosition(); double resolution = mapPresenter.getViewPort().getResolution(); KineticTrajectory trajectory = new KineticTrajectory(new View(current, resolution), direction, distance / delta); mapPresenter.getViewPort().registerAnimation( new NavigationAnimationImpl(mapPresenter.getViewPort(), mapPresenter.getEventBus(), trajectory, (int) trajectory.getDuration())); } else { mapPresenter.getViewPort().stopInteraction(); } } } private double abs(Coordinate c) { return MathService.distance(c, new Coordinate()); } private Coordinate toWorld(Coordinate coordinate) { return mapPresenter.getViewPort().getTransformationService() .transform(coordinate, RenderSpace.SCREEN, RenderSpace.WORLD); } private Coordinate subtract(Coordinate c1, Coordinate c2) { return new Coordinate(c1.getX() - c2.getX(), c1.getY() - c2.getY()); } private Coordinate add(Coordinate c1, Coordinate c2) { return new Coordinate(c2.getX() + c1.getX(), c2.getY() + c1.getY()); } protected void updateView(HumanInputEvent event) { if (dragging) { Coordinate end = getLocation(event, RenderSpace.SCREEN); Coordinate beginWorld = mapPresenter.getViewPort().getTransformationService() .transform(dragOrigin, RenderSpace.SCREEN, RenderSpace.WORLD); Coordinate endWorld = mapPresenter.getViewPort().getTransformationService() .transform(end, RenderSpace.SCREEN, RenderSpace.WORLD); double x = mapPresenter.getViewPort().getPosition().getX() + beginWorld.getX() - endWorld.getX(); double y = mapPresenter.getViewPort().getPosition().getY() + beginWorld.getY() - endWorld.getY(); View view = new View(new Coordinate(x, y), mapPresenter.getViewPort().getResolution()); view.setDragging(true); mapPresenter.getViewPort().applyView(view); dragOrigin = end; } } protected native int getWheelDelta(NativeEvent evt) /*-{ return Math.round(-evt.wheelDelta) || 0; }-*/; protected void scrollZoomTo(boolean isNorth, Coordinate location) { ViewPort viewPort = mapPresenter.getViewPort(); int index = viewPort.getResolutionIndex(viewPort.getResolution()); if (isNorth) { if (index < viewPort.getResolutionCount() - 1) { if (scrollZoomType == ScrollZoomType.ZOOM_POSITION) { Coordinate position = calculatePosition(true, location); viewPort.registerAnimation(NavigationAnimationFactory.createZoomIn(mapPresenter, position)); } else { viewPort.registerAnimation(NavigationAnimationFactory.createZoomIn(mapPresenter)); } } } else { if (index > 0) { if (scrollZoomType == ScrollZoomType.ZOOM_POSITION) { Coordinate position = calculatePosition(false, location); viewPort.registerAnimation(NavigationAnimationFactory.createZoomOut(mapPresenter, position)); } else { viewPort.registerAnimation(NavigationAnimationFactory.createZoomOut(mapPresenter)); } } } } /** * Calculate the target position should there be a rescale point. The idea is that after zooming in or out, the * mouse cursor would still lie at the same position in world space. */ protected Coordinate calculatePosition(boolean zoomIn, Coordinate rescalePoint) { ViewPort viewPort = mapPresenter.getViewPort(); Coordinate position = viewPort.getPosition(); int index = viewPort.getResolutionIndex(viewPort.getResolution()); double resolution = viewPort.getResolution(); if (zoomIn && index < viewPort.getResolutionCount() - 1) { resolution = viewPort.getResolution(index + 1); } else if (!zoomIn && index > 0) { resolution = viewPort.getResolution(index - 1); } double factor = viewPort.getResolution() / resolution; double dX = (rescalePoint.getX() - position.getX()) * (1 - 1 / factor); double dY = (rescalePoint.getY() - position.getY()) * (1 - 1 / factor); return new Coordinate(position.getX() + dX, position.getY() + dY); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy