/* * 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.widget.control.zoom; import org.geomajas.annotation.Api; import org.geomajas.gwt2.client.GeomajasImpl; import org.geomajas.gwt2.client.animation.LinearTrajectory; import org.geomajas.gwt2.client.animation.NavigationAnimation; import org.geomajas.gwt2.client.animation.NavigationAnimationFactory; import org.geomajas.gwt2.client.animation.Trajectory; import org.geomajas.gwt2.client.event.ViewPortChangedEvent; import org.geomajas.gwt2.client.event.ViewPortChangedHandler; import org.geomajas.gwt2.client.map.MapPresenter; import org.geomajas.gwt2.client.map.View; import org.geomajas.gwt2.client.map.ViewPort; import org.geomajas.gwt2.client.widget.AbstractMapWidget; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Position; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; /** * Map widget that displays a button for each zoom step on the map. Also displays a zoom in and zoom out button.This * widget is meant to be added to the map's widget pane (see {@link MapPresenter#getWidgetPane()}). * * @author Pieter De Graef * @since 2.0.0 */ @Api(allMethods = true) public class ZoomStepControl extends AbstractMapWidget { /** * UI binder definition for the {@link ZoomStepControl} widget. * * @author Pieter De Graef */ interface ZoomStepGadgetUiBinder extends UiBinder<Widget, ZoomStepControl> { } private static final ZoomStepGadgetUiBinder UI_BINDER = GWT.create(ZoomStepGadgetUiBinder.class); private static final int ZOOMSTEP_HEIGHT = 10; private static final int ZOOMBUTTON_SIZE = 22; private final ZoomStepControlResource resource; private int top; private int left; private ViewPort viewPort; private boolean stretched; @UiField protected SimplePanel zoomInElement; @UiField protected SimplePanel zoomOutElement; @UiField protected AbsolutePanel zoomStepsPanel; @UiField protected SimplePanel zoomHandle; private int handleDragStartY; // ------------------------------------------------------------------------ // Constructors: // ------------------------------------------------------------------------ /** * Create a new instance for the given map. * * @param mapPresenter * The map presenter. */ public ZoomStepControl(MapPresenter mapPresenter) { this(mapPresenter, GeomajasImpl.getClientBundleFactory().createZoomStepControlResource()); } /** * Create a new instance for the given map. * * @param mapPresenter * The map presenter. * @param resource * Custom resource bundle in case you want to provide your own style for this widget. */ public ZoomStepControl(MapPresenter mapPresenter, ZoomStepControlResource resource) { super(mapPresenter); this.resource = resource; this.resource.css().ensureInjected(); viewPort = mapPresenter.getViewPort(); initWidget(UI_BINDER.createAndBindUi(this)); getElement().getStyle().setPosition(Position.ABSOLUTE); getElement().getStyle().setTop(60, Unit.PX); getElement().getStyle().setLeft(17, Unit.PX); mapPresenter.getEventBus().addViewPortChangedHandler(new ViewPortChangedHandler() { public void onViewPortChanged(ViewPortChangedEvent event) { if (!stretched) { positionZoomHandle(); } } }); } @Override protected void onAttach() { super.onAttach(); buildGui(); } // ------------------------------------------------------------------------ // Private methods: // ------------------------------------------------------------------------ private void buildGui() { left = toInteger(getElement().getStyle().getLeft()); top = toInteger(getElement().getStyle().getTop()); zoomInElement.getElement().setInnerText("+"); zoomOutElement.getElement().setInnerText("-"); StopPropagationHandler preventWeirdBehaviourHandler = new StopPropagationHandler(); // Calculate height: int y = 0; for (int i = 0; i < viewPort.getResolutionCount(); i++) { final int count = i; SimplePanel zoomStep = new SimplePanel(); zoomStep.setSize(ZOOMBUTTON_SIZE + "px", (ZOOMSTEP_HEIGHT + 1) + "px"); zoomStep.setStyleName(resource.css().zoomStepControlStep()); zoomStep.addDomHandler(new ClickHandler() { public void onClick(ClickEvent event) { double scale = viewPort.getResolution(viewPort.getResolutionCount() - count - 1); viewPort.applyResolution(scale); event.stopPropagation(); } }, ClickEvent.getType()); zoomStep.addDomHandler(preventWeirdBehaviourHandler, MouseDownEvent.getType()); zoomStep.addDomHandler(preventWeirdBehaviourHandler, MouseUpEvent.getType()); zoomStep.addDomHandler(preventWeirdBehaviourHandler, ClickEvent.getType()); zoomStep.addDomHandler(preventWeirdBehaviourHandler, DoubleClickEvent.getType()); zoomStepsPanel.add(zoomStep, 0, y); y += ZOOMSTEP_HEIGHT; } zoomStepsPanel.setSize(ZOOMBUTTON_SIZE + "px", (y + 1) + "px"); setSize(ZOOMBUTTON_SIZE + "px", (y + (ZOOMBUTTON_SIZE * 2) + 1) + "px"); // Zoom in button: zoomInElement.addDomHandler(new ClickHandler() { public void onClick(ClickEvent event) { int index = viewPort.getResolutionIndex(viewPort.getResolution()); if (index < viewPort.getResolutionCount() - 1) { viewPort.registerAnimation(NavigationAnimationFactory.createZoomIn(mapPresenter)); } event.stopPropagation(); } }, ClickEvent.getType()); zoomInElement.addDomHandler(preventWeirdBehaviourHandler, MouseDownEvent.getType()); zoomInElement.addDomHandler(preventWeirdBehaviourHandler, MouseUpEvent.getType()); zoomInElement.addDomHandler(preventWeirdBehaviourHandler, ClickEvent.getType()); zoomInElement.addDomHandler(preventWeirdBehaviourHandler, DoubleClickEvent.getType()); // Zoom out button: zoomOutElement.addDomHandler(new ClickHandler() { public void onClick(ClickEvent event) { int index = viewPort.getResolutionIndex(viewPort.getResolution()); if (index > 0) { viewPort.registerAnimation(NavigationAnimationFactory.createZoomOut(mapPresenter)); } event.stopPropagation(); } }, ClickEvent.getType()); zoomOutElement.addDomHandler(preventWeirdBehaviourHandler, MouseDownEvent.getType()); zoomOutElement.addDomHandler(preventWeirdBehaviourHandler, MouseUpEvent.getType()); zoomOutElement.addDomHandler(preventWeirdBehaviourHandler, ClickEvent.getType()); zoomOutElement.addDomHandler(preventWeirdBehaviourHandler, DoubleClickEvent.getType()); // Add the zoom handle: ZoomStephandler zoomStepHandler = new ZoomStephandler(); zoomStepHandler.setMinY(top + ZOOMBUTTON_SIZE); zoomStepHandler.setMaxY(top + ZOOMBUTTON_SIZE + (viewPort.getResolutionCount() - 1) * ZOOMSTEP_HEIGHT); zoomHandle.addDomHandler(zoomStepHandler, MouseDownEvent.getType()); addDomHandler(zoomStepHandler, MouseUpEvent.getType()); addDomHandler(zoomStepHandler, MouseMoveEvent.getType()); addDomHandler(zoomStepHandler, MouseOutEvent.getType()); // Apply correct positions for all widgets: applyPositions(); } private void stretchLayout() { getElement().getStyle().setTop(0, Unit.PX); getElement().getStyle().setLeft(0, Unit.PX); setSize(viewPort.getMapWidth() + "px", viewPort.getMapHeight() + "px"); stretched = true; applyPositions(); } private void shrinkLayout() { stretched = false; getElement().getStyle().setTop(top, Unit.PX); getElement().getStyle().setLeft(left, Unit.PX); int y = viewPort.getResolutionCount() * ZOOMSTEP_HEIGHT; setSize(ZOOMBUTTON_SIZE + "px", (y + 1 + (ZOOMBUTTON_SIZE * 2)) + "px"); applyPositions(); } private void positionZoomHandle() { int index = viewPort.getResolutionCount() - viewPort.getResolutionIndex(viewPort.getResolution()) - 1; int handleY = getBaseTop() + ZOOMBUTTON_SIZE + 1 + (index * ZOOMSTEP_HEIGHT); int handleX = getBaseLeft(); zoomHandle.getElement().getStyle().setLeft(handleX, Unit.PX); zoomHandle.getElement().getStyle().setTop(handleY, Unit.PX); handleDragStartY = handleY; } private void applyPositions() { int top = getBaseTop(); int left = getBaseLeft(); zoomInElement.getElement().getStyle().setTop(top, Unit.PX); zoomInElement.getElement().getStyle().setLeft(left, Unit.PX); zoomStepsPanel.getElement().getStyle().setTop(top + ZOOMBUTTON_SIZE, Unit.PX); zoomStepsPanel.getElement().getStyle().setLeft(left, Unit.PX); int y = viewPort.getResolutionCount() * ZOOMSTEP_HEIGHT; zoomOutElement.getElement().getStyle().setTop(top + ZOOMBUTTON_SIZE + y + 1, Unit.PX); zoomOutElement.getElement().getStyle().setLeft(left, Unit.PX); positionZoomHandle(); } private int getBaseTop() { return stretched ? top : 0; } private int getBaseLeft() { return stretched ? left : 0; } private int toInteger(String str) { int index = str.indexOf("px"); try { return Integer.parseInt(str.substring(0, index)); } catch (Exception e) { return 0; } } private void zoomToY(int y) { int stepsY = y - top - ZOOMBUTTON_SIZE; int zoomStepMin = (int) Math.floor(ZOOMSTEP_HEIGHT / 2); int zoomStepMax = viewPort.getResolutionCount() * ZOOMSTEP_HEIGHT - zoomStepMin; if (stepsY < zoomStepMin) { viewPort.applyResolution(viewPort.getMinimumResolution()); return; } else if (stepsY > zoomStepMax) { viewPort.applyResolution(viewPort.getMaximumResolution()); return; } int step = (int) Math.round((stepsY - zoomStepMin) / ZOOMSTEP_HEIGHT); int zoomStepY = (stepsY - zoomStepMin) % ZOOMSTEP_HEIGHT; int tileLevelBelow = Math.max(0, viewPort.getResolutionCount() - step - 2); if (tileLevelBelow == 0) { return; } else if (tileLevelBelow > viewPort.getResolutionCount()) { return; } else { double scaleBelow = viewPort.getResolution(tileLevelBelow); double scaleAbove = viewPort.getResolution(tileLevelBelow - 1); double scale = scaleBelow + (scaleAbove - scaleBelow) * ((double) zoomStepY / (double) ZOOMSTEP_HEIGHT); Trajectory trajectory = new LinearTrajectory(viewPort.getView(), new View(viewPort.getPosition(), scale)); NavigationAnimation animation = NavigationAnimationFactory.create(mapPresenter, trajectory, 0); viewPort.registerAnimation(animation); } } // ------------------------------------------------------------------------ // Private classes: // ------------------------------------------------------------------------ /** * Handler for dragging the zoom step handle. The mouse down goes onto the handle, the rest onto a large rectangle. * * @author Pieter De Graef */ private class ZoomStephandler implements MouseDownHandler, MouseUpHandler, MouseMoveHandler, MouseOutHandler { private int minY, maxY; private int currentY; private int handleY; private boolean dragging; public void onMouseUp(MouseUpEvent event) { if (dragging) { dragging = false; shrinkLayout(); int index = viewPort.getResolutionIndex(viewPort.getResolution()); double scale = viewPort.getResolution(index); Trajectory trajectory = new LinearTrajectory(viewPort.getView(), new View(viewPort.getPosition(), scale)); NavigationAnimation animation = NavigationAnimationFactory.create(mapPresenter, trajectory, 400); viewPort.registerAnimation(animation); } } public void onMouseDown(MouseDownEvent event) { dragging = true; stretchLayout(); event.stopPropagation(); event.preventDefault(); handleY = handleDragStartY; currentY = handleY + event.getY(); } public void onMouseMove(MouseMoveEvent event) { if (dragging) { int y = event.getY(); if (y < minY) { y = minY; } if (y > maxY) { y = maxY; } zoomHandle.getElement().getStyle().setTop(y, Unit.PX); handleY += (y - currentY); zoomToY(handleY); currentY = y; event.stopPropagation(); } } public void onMouseOut(MouseOutEvent event) { if (dragging) { dragging = false; shrinkLayout(); int index = viewPort.getResolutionCount() - viewPort.getResolutionIndex(viewPort.getResolution()) - 1; int handleY = getBaseTop() + ZOOMBUTTON_SIZE + 1 + (index * ZOOMSTEP_HEIGHT); zoomHandle.getElement().getStyle().setTop(handleY, Unit.PX); } } public void setMinY(int minY) { this.minY = minY; } public void setMaxY(int maxY) { this.maxY = maxY; } } }