/* * This file is part of Geomajas, a component framework for building * rich Internet applications (RIA) with sophisticated capabilities for the * display, analysis and management of geographic information. * It is a building block that allows developers to add maps * and other geographic data capabilities to their web applications. * * Copyright 2008-2015 Geosparc, http://www.geosparc.com, Belgium * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.geomajas.gwt.client.map; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.geomajas.configuration.client.BoundsLimitOption; import org.geomajas.geometry.Coordinate; import org.geomajas.gwt.client.map.event.MapViewChangedEvent; import org.geomajas.gwt.client.map.event.MapViewChangedHandler; import org.geomajas.gwt.client.spatial.Bbox; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.google.gwt.event.shared.HandlerRegistration; /** * Tests map view behaviour (zoom options, resolutions) * * @author Jan De Moerloose */ public class MapViewTest { MapView mapView; @Before public void setUp() { mapView = new MapView(); mapView.setSize(200, 100); mapView.setMaxBounds(new Bbox(0, 0, 1000, 400)); mapView.setViewBoundsLimitOption(BoundsLimitOption.COMPLETELY_WITHIN_MAX_BOUNDS); mapView.setMaximumScale(2); mapView.setCurrentScale(1.0, MapView.ZoomOption.LEVEL_CLOSEST); mapView.setCenterPosition(new Coordinate(500, 200)); } @Test public void testPanning() { CaptureHandler handler = new CaptureHandler(); mapView.addMapViewChangedHandler(handler); // pan to other allowed position mapView.setCenterPosition(new Coordinate(900, 300)); handler.expect(new Bbox(800, 250, 200, 100), 1.0, true); // pan outside max bounds mapView.setCenterPosition(new Coordinate(1000, 400)); // should pan as far as possible handler.expect(new Bbox(800, 300, 200, 100), 1.0, true); // translate outside max bounds mapView.translate(100, 100); // no movement handler.expect(new Bbox(800, 300, 200, 100), 1.0, true); handler.validate(); } @Test public void testZoomingNoResolutions() { CaptureHandler handler = new CaptureHandler(); mapView.addMapViewChangedHandler(handler); // pan to initial position mapView.applyBounds(new Bbox(400, 150, 200, 100), MapView.ZoomOption.LEVEL_CLOSEST); handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); // zoom out mapView.setCurrentScale(0.5, MapView.ZoomOption.LEVEL_CLOSEST); handler.expect(new Bbox(300, 100, 400, 200), 0.5, false); // zoom out beyond maximum bounds mapView.setCurrentScale(0.2, MapView.ZoomOption.LEVEL_CLOSEST); // should zoom out as far as possible handler.expect(new Bbox(100, 0, 800, 400), 0.25, false); // zoom in beyond maximum scale mapView.setCurrentScale(3, MapView.ZoomOption.LEVEL_CLOSEST); // should zoom in as far as possible handler.expect(new Bbox(450, 175, 100, 50), 2, false); handler.validate(); } @Test public void testZoomingWithResolutions() { List<Double> resolutions = new ArrayList<Double>(); resolutions.add(1 / 0.01); resolutions.add(1 / 0.1); resolutions.add(1 / 0.4); resolutions.add(1 / 1.0); resolutions.add(1 / 2.0); mapView.setResolutions(resolutions); CaptureHandler handler = new CaptureHandler(); mapView.addMapViewChangedHandler(handler); // apply initial bounds mapView.applyBounds(new Bbox(300, 100, 400, 200), MapView.ZoomOption.LEVEL_CLOSEST); // should snap to closest (scale 0.5 -> 0.4) handler.expect(new Bbox(250, 75, 500, 250), 0.4, false); // force next level mapView.scale(1.001, MapView.ZoomOption.LEVEL_CHANGE); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, false); handler.validate(); } @Test public void testSetMaxViewBoundsCompletelyWithinMaxBounds() { // test the bounds are limited correctly if setViewBoundsLimitOption of mapView is COMPLETELY_WITHIN_MAX_BOUNDS mapView.setViewBoundsLimitOption(BoundsLimitOption.COMPLETELY_WITHIN_MAX_BOUNDS); Assert.assertEquals(BoundsLimitOption.COMPLETELY_WITHIN_MAX_BOUNDS, mapView.getViewBoundsLimitOption()); mapView.setSize(200, 100); mapView.setMaxBounds(new Bbox(0, 0, 1000, 400)); mapView.setCurrentScale(1.0, MapView.ZoomOption.LEVEL_CLOSEST); mapView.setCenterPosition(new Coordinate(500, 200)); CaptureHandler handler = new CaptureHandler(); mapView.addMapViewChangedHandler(handler); // pan to non-allowed center position for BoundsLimitOption.COMPLETELY_WITHIN_MAX_BOUNDS mapView.setCenterPosition(new Coordinate(1000, 400)); // should pan as far as possible handler.expect(new Bbox(800, 300, 200, 100), 1.0, true); // translate viewBounds outside max bounds mapView.translate(100, 100); // no movement handler.expect(new Bbox(800, 300, 200, 100), 1.0, true); handler.validate(); } /** * Test that the bounds are limited correctly if setViewBoundsLimitOption of mapView is BoundsLimitOption.CENTER_WITHIN_MAX_BOUNDS */ @Test public void testSetMaxViewBoundsCenterWithinMaxBounds() { // final int halfWidthOfView = 100; final int halfHeightOfView = 50; // Avoid having to round to nearest pixel position final int widthOfView = halfWidthOfView * 2; final int heightOfView = halfHeightOfView * 2; // avoid having to round to neariest pixel position final double xMinOfMaxBounds = 0.0; final double yMinOfMaxBounds = 0.0; final double halfWidthOfMaxBounds = 500.0; final double halfHeightOfMaxBounds = 200.0; // Avoid having to round to nearest pixel position final double widthOfMaxBounds = halfWidthOfMaxBounds * 2.0; final double heightOfMaxBounds = halfHeightOfMaxBounds * 2.0; final double xMaxOfMaxBounds = xMinOfMaxBounds + widthOfMaxBounds; final double yMaxOfMaxBounds = yMinOfMaxBounds + heightOfMaxBounds; Coordinate center = null; Bbox expectedBBox = null; CaptureHandler captureHandler = new CaptureHandler(); mapView.setViewBoundsLimitOption(BoundsLimitOption.CENTER_WITHIN_MAX_BOUNDS); Assert.assertEquals(BoundsLimitOption.CENTER_WITHIN_MAX_BOUNDS, mapView.getViewBoundsLimitOption()); mapView.setSize(widthOfView, heightOfView); mapView.setMaxBounds(new Bbox(xMinOfMaxBounds, yMinOfMaxBounds, widthOfMaxBounds, heightOfMaxBounds)); // Scale is set to 1.0 for ease of testing mapView.setCurrentScale(1.0, MapView.ZoomOption.LEVEL_CLOSEST); mapView.addMapViewChangedHandler(captureHandler); center = new Coordinate(xMinOfMaxBounds + widthOfMaxBounds/2.0, yMinOfMaxBounds + yMaxOfMaxBounds/2.0); expectedBBox = new Bbox(xMinOfMaxBounds + widthOfMaxBounds/2.0 - halfWidthOfView, yMinOfMaxBounds + heightOfMaxBounds/2.0 - halfHeightOfView, widthOfView, heightOfView); testChangeCenterPoint(captureHandler, center, expectedBBox, true, true); // Pan to allowed center position (x = max_x - 1 and y = max_y -1) // for BoundsLimitOption.CENTER_WITHIN_MAX_BOUNDS center = new Coordinate(xMaxOfMaxBounds-1.0, yMaxOfMaxBounds-1.0); expectedBBox = new Bbox(xMaxOfMaxBounds- 1.0- halfWidthOfView , yMaxOfMaxBounds -1.0 - halfHeightOfView, widthOfView, heightOfView); testChangeCenterPoint(captureHandler, center, expectedBBox, true, true); // Pan to center position with xMax and yMax for BoundsLimitOption.CENTER_WITHIN_MAX_BOUNDS center = new Coordinate(xMaxOfMaxBounds, yMaxOfMaxBounds); expectedBBox = new Bbox(xMaxOfMaxBounds - halfWidthOfView, yMaxOfMaxBounds - halfHeightOfView, widthOfView, heightOfView); testChangeCenterPoint(captureHandler, center, expectedBBox, true, true); // Pan to center position outside of maxBounds (previous center + 1.0) center = new Coordinate(xMaxOfMaxBounds + 1.0, yMaxOfMaxBounds + 1.0); // no movement because of maxBounds limitations, so expectedBBox remains the same testChangeCenterPoint(captureHandler, center, expectedBBox, false, true); // Pan to center position that much outside of maxBounds that // new Bbox without max bounds limitations would be located completely out of maxBounds center = new Coordinate(xMaxOfMaxBounds+halfWidthOfView + 1.0, yMaxOfMaxBounds + halfHeightOfView + 1.0); // No movement (so no change of bBox) because of maxBounds limitations testChangeCenterPoint(captureHandler, center, expectedBBox, false, false); captureHandler.validate(); } /** * @param handler * @param center new center position * @param expectedBBox * @param withinMaxBounds * @param withinBBox if true, the new center is located within the maxBounds of the mapView */ private void testChangeCenterPoint(CaptureHandler captureHandler, Coordinate center, Bbox expectedBBox, boolean withinMaxBounds, boolean withinBBox) { Assert.assertTrue(mapView.getMaxBounds().contains(center) == withinMaxBounds); Assert.assertTrue(expectedBBox.contains(center) == withinBBox); mapView.setCenterPosition(center); // should pan to requested center (since possible) captureHandler.expect(expectedBBox, 1.0, true); } /** * Tests the lower and upper boundaries of the resolution list (GWT-36). */ @Test public void testResolutionSnapping() { List<Double> resolutions = new ArrayList<Double>(); resolutions.add(1 / 0.01); resolutions.add(1 / 0.1); resolutions.add(1 / 0.4); resolutions.add(1 / 1.0); resolutions.add(1 / 2.0); mapView.setResolutions(resolutions); // no scale limitations mapView.setMaximumScale(Double.MAX_VALUE); mapView.setMaxBounds(new Bbox(-1E20, -1E20, 2E20, 2E20)); CaptureHandler handler = new CaptureHandler(); HandlerRegistration registration = mapView.addMapViewChangedHandler(handler); // force 1.0 mapView.setCurrentScale(1.0, MapView.ZoomOption.LEVEL_CLOSEST); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); // force 2.0 mapView.setCurrentScale(2.0, MapView.ZoomOption.LEVEL_CLOSEST); // zooms in to 2.0 handler.expect(new Bbox(450, 175, 100, 50), 2.0, false); // force 0.01 mapView.setCurrentScale(0.01, MapView.ZoomOption.LEVEL_CLOSEST); // zooms in to 0.01 handler.expect(new Bbox(-9500, -4800, 20000, 10000), 0.01, false); // fitting // force 1.0 mapView.setCurrentScale(1.0, MapView.ZoomOption.LEVEL_FIT); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, false); // force 2.0 mapView.setCurrentScale(2.0, MapView.ZoomOption.LEVEL_FIT); // zooms in to 2.0 handler.expect(new Bbox(450, 175, 100, 50), 2.0, false); // force 0.01 mapView.setCurrentScale(0.01, MapView.ZoomOption.LEVEL_FIT); // zooms in to 0.01 handler.expect(new Bbox(-9500, -4800, 20000, 10000), 0.01, false); handler.validate(); registration.removeHandler(); // test for 1 resolution resolutions = new ArrayList<Double>(); resolutions.add(1 / 1.0); mapView.setResolutions(resolutions); handler = new CaptureHandler(); mapView.addMapViewChangedHandler(handler); // force 2.0 mapView.setCurrentScale(2.0, MapView.ZoomOption.LEVEL_FIT); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, false); // force 1.0 mapView.setCurrentScale(2.0, MapView.ZoomOption.LEVEL_FIT); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); // force 0.5 mapView.setCurrentScale(0.5, MapView.ZoomOption.LEVEL_FIT); // zooms in to 1.0 (which is not fitting, but there is no other option) handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); // force 2.0 mapView.setCurrentScale(2.0, MapView.ZoomOption.LEVEL_CLOSEST); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); // force 1.0 mapView.setCurrentScale(2.0, MapView.ZoomOption.LEVEL_CLOSEST); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); // force 0.5 mapView.setCurrentScale(0.5, MapView.ZoomOption.LEVEL_CLOSEST); // zooms in to 1.0 handler.expect(new Bbox(400, 150, 200, 100), 1.0, true); handler.validate(); } /** * Capture all MapViewChangedEvent events in a list, and offers functionality * to compare the captured events to the expected events. * */ private class CaptureHandler implements MapViewChangedHandler { private List<MapViewChangedEvent> actualEvents = new LinkedList<MapViewChangedEvent>(); private List<MapViewChangedEvent> expectedEvents = new LinkedList<MapViewChangedEvent>(); private double delta = 0.0000001; public void onMapViewChanged(MapViewChangedEvent event) { actualEvents.add(event); } public void expect(Bbox bounds, double scale, boolean panning) { MapViewChangedEvent event = new MapViewChangedEvent(bounds, scale, panning, false, false, null); expectedEvents.add(event); } public void validate() { for (MapViewChangedEvent expected : expectedEvents) { Assert.assertFalse("Expected event " + expected + " but got nothing", actualEvents.isEmpty()); MapViewChangedEvent actual = actualEvents.remove(0); Assert.assertEquals(expected.getScale(), actual.getScale(), delta); Assert.assertTrue("Expected " + expected.getBounds() + " but was " + actual.getBounds() + " for precision " + (delta), expected.getBounds().equals(actual.getBounds(), delta)); Assert.assertEquals(expected.isSameScaleLevel(), actual.isSameScaleLevel()); } Assert.assertTrue(actualEvents.size() + " unexpected events", actualEvents.isEmpty()); } } }