Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.geomajas.gwt2.client.map.ViewPortImpl 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.Collections;
import java.util.Comparator;
import java.util.List;
import org.geomajas.geometry.Bbox;
import org.geomajas.geometry.Coordinate;
import org.geomajas.geometry.service.BboxService;
import org.geomajas.gwt.client.util.Dom;
import org.geomajas.gwt2.client.animation.NavigationAnimation;
import org.geomajas.gwt2.client.event.NavigationStopEvent;
import org.geomajas.gwt2.client.event.NavigationStopHandler;
import org.geomajas.gwt2.client.event.ViewPortChangedEvent;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
/**
* Implementation of the ViewPort interface.
*
* @author Pieter De Graef
*/
public final class ViewPortImpl implements ViewPort {
private static final double METER_PER_INCH = 0.0254;
private final MapEventBus eventBus;
private final ViewPortTransformationService transformationService;
private MapConfiguration configuration;
/**
* The map's width in pixels.
*/
private int mapWidth;
/**
* The map's height in pixels.
*/
private int mapHeight;
/**
* The maximum bounding box available to this MapView. Never go outside it!
*/
private Bbox maxBounds;
private final List resolutions = new ArrayList();
private String crs;
private View view = new View(new Coordinate(), 1.0);
private NavigationAnimation currentAnimation;
/**
* Used as fallback map size value, both width and heigth.
*/
protected static final int MAP_SIZE_FALLBACK_VALUE = 100;
// -------------------------------------------------------------------------
// Constructors:
// -------------------------------------------------------------------------
public ViewPortImpl(MapEventBus eventBus) {
this.eventBus = eventBus;
this.transformationService = new ViewPortTransformationServiceImpl(this);
eventBus.addNavigationStopHandler(new NavigationStopHandler() {
@Override
public void onNavigationStopped(NavigationStopEvent event) {
currentAnimation = null;
}
});
}
// -------------------------------------------------------------------------
// Configuration stuff:
// -------------------------------------------------------------------------
/**
* Copy {@link MapConfiguration} information. Specifically, copy or calculate the list of available resolutions.
* This resolution list will not change after initialization.
*
* @param configuration map configuration object
*/
protected void initialize(MapConfiguration configuration) {
this.configuration = configuration;
this.crs = configuration.getCrs();
// Calculate maximum bounds:
this.maxBounds = new Bbox(configuration.getMaxBounds().getX(), configuration.getMaxBounds().getY(),
configuration.getMaxBounds().getWidth(), configuration.getMaxBounds().getHeight());
if (configuration.getResolutions() != null && configuration.getResolutions().size() > 0) {
for (Double resolution : configuration.getResolutions()) {
resolutions.add(resolution);
}
} else if (configuration.getMinimumResolution() != 0) {
// If there are no resolutions, we'll calculate them:
double tempResolution = getMaxBoundsResolution();
if (tempResolution == 0.0) {
throw new IllegalStateException("Could not initialize the map. Could it be it has no size?");
}
// always add one resolution under the minimum resolution:
// this ensures the possibility to display the map at minimum resolution
resolutions.add(tempResolution);
while (tempResolution > configuration.getMinimumResolution()) {
tempResolution /= 2;
resolutions.add(tempResolution);
}
} else {
throw new IllegalStateException(
"The map configuration must either contain a fixed list resolutions or a minimum resolution");
}
Collections.sort(resolutions, new Comparator() {
@Override
public int compare(Double o1, Double o2) {
return o2.compareTo(o1);
}
});
}
@Override
public Bbox getMaximumBounds() {
return maxBounds;
}
@Override
public double getMaximumResolution() {
if (resolutions.size() == 0) {
return Double.MAX_VALUE;
}
return resolutions.get(0);
}
@Override
public double getMinimumResolution() {
if (resolutions.size() == 0) {
return 0;
}
return resolutions.get(resolutions.size() - 1);
}
@Override
public int getResolutionCount() {
return resolutions.size();
}
@Override
public double getResolution(int index) {
if (index < 0) {
throw new IllegalArgumentException("Resolution cannot be found.");
}
if (index >= resolutions.size()) {
throw new IllegalArgumentException("Resolution cannot be found.");
}
return resolutions.get(index);
}
@Override
public int getResolutionIndex(double resolution) {
double maximumResolution = getMaximumResolution();
if (resolution >= maximumResolution) {
return 0;
}
double minimumResolution = getMinimumResolution();
if (resolution <= minimumResolution) {
return resolutions.size() - 1;
}
for (int i = 0; i < resolutions.size(); i++) {
double upper = resolutions.get(i);
double lower = resolutions.get(i + 1);
if (resolution < upper && resolution >= lower) {
if (Math.abs(upper - resolution) >= Math.abs(lower - resolution)) {
return i + 1;
} else {
return i;
}
}
}
return 0;
}
protected void setMapSize(int width, int height) {
if (this.mapWidth != width || this.mapHeight != height) {
View oldView = getView();
this.mapWidth = width;
this.mapHeight = height;
if (eventBus != null) {
eventBus.fireEvent(new ViewPortChangedEvent(oldView, getView(), currentAnimation));
}
}
}
@Override
public String getCrs() {
return crs;
}
@Override
public int getMapWidth() {
return mapWidth;
}
@Override
public int getMapHeight() {
return mapHeight;
}
// -------------------------------------------------------------------------
// Methods that retrieve what is visible on the map:
// -------------------------------------------------------------------------
@Override
public Coordinate getPosition() {
return (Coordinate) view.getPosition().clone();
}
@Override
public double getResolution() {
return view.getResolution();
}
@Override
public View getView() {
return copy(view);
}
/**
* Given the information in this ViewPort object, what is the currently visible area? This value is expressed in
* world coordinates.
*
* @return Returns the bounding box that covers the currently visible area on the map.
*/
public Bbox getBounds() {
double w = mapWidth * getResolution();
double h = mapHeight * getResolution();
double x = getPosition().getX() - w / 2;
double y = getPosition().getY() - h / 2;
return new Bbox(x, y, w, h);
}
// -------------------------------------------------------------------------
// Methods that manipulate what is visible on the map:
// -------------------------------------------------------------------------
@Override
public void registerAnimation(NavigationAnimation animation) {
// boolean cancelSupport = configuration.getHintValue(MapConfiguration.ANIMATION_CANCEL_SUPPORT);
// if (!cancelSupport && currentAnimation != null) {
// return;
// }
if (currentAnimation != null) {
currentAnimation.cancel();
}
this.currentAnimation = animation;
// Schedule the animation from the moment the browser event loop returns:
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
if (currentAnimation != null) {
if (Dom.isTransformationSupported()) {
currentAnimation.run();
} else {
applyView(currentAnimation.getEndView());
currentAnimation = null;
}
}
}
});
}
@Override
public void applyPosition(Coordinate coordinate) {
Coordinate tempPosition = checkPosition(coordinate, getResolution());
if (!tempPosition.equals(getPosition())) {
view = new View(tempPosition, getResolution());
applyViewNoChecks(view);
}
}
@Override
public void stopInteraction() {
View oldView = getView();
view = copy(oldView);
view.setInteractive(false);
view.setDragging(false);
eventBus.fireEvent(new ViewPortChangedEvent(oldView, view, null));
}
@Override
public void applyResolution(double resolution) {
applyResolution(resolution, getPosition(), ZoomOption.FREE);
}
@Override
public void applyResolution(double resolution, ZoomOption zoomOption) {
applyResolution(resolution, getPosition(), zoomOption);
}
@Override
public void applyView(View view) {
applyView(view, ZoomOption.FREE);
}
@Override
public void applyView(View view, ZoomOption zoomOption) {
double tempResolution = checkResolution(view.getResolution(), ZoomOption.FREE);
Coordinate tempPosition = checkPosition(view.getPosition(), tempResolution);
View copy = copy(view, tempPosition, tempResolution);
applyViewNoChecks(copy);
}
@Override
public void applyBounds(Bbox bounds) {
applyBounds(bounds, ZoomOption.FREE);
}
@Override
public void applyBounds(Bbox bounds, ZoomOption zoomOption) {
double tempResolution = getResolutionForBounds(bounds, zoomOption);
Coordinate tempPosition = checkPosition(BboxService.getCenterPoint(bounds), tempResolution);
View copy = copy(view, tempPosition, tempResolution);
applyViewNoChecks(copy);
}
@Override
public double toResolution(double scaleDenominator) {
double pixelsPerUnit = getPixelLength() / configuration.getUnitLength();
return pixelsPerUnit * scaleDenominator;
}
@Override
public Bbox asBounds(View view) {
double w = mapWidth * view.getResolution();
double h = mapHeight * view.getResolution();
double x = view.getPosition().getX() - w / 2;
double y = view.getPosition().getY() - h / 2;
return new Bbox(x, y, w, h);
}
@Override
public View asView(Bbox bounds, ZoomOption zoomOption) {
double tempResolution = getResolutionForBounds(bounds, zoomOption);
Coordinate tempPosition = checkPosition(BboxService.getCenterPoint(bounds), tempResolution);
return new View(tempPosition, tempResolution);
}
@Override
public ViewPortTransformationService getTransformationService() {
return transformationService;
}
// -------------------------------------------------------------------------
// Private functions:
// -------------------------------------------------------------------------
protected double getPixelLength() {
return METER_PER_INCH / configuration.getHintValue(MapConfiguration.DPI);
}
private void applyResolution(double newResolution, Coordinate rescalePoint, ZoomOption zoomOption) {
double validResolution = checkResolution(newResolution, zoomOption);
if (validResolution != getResolution()) {
// Calculate theoretical new bounds. First create a BBOX of correct size:
Bbox newBbox = new Bbox(0, 0, getMapWidth() * validResolution, getMapHeight() * validResolution);
// Calculate translate vector to assure rescalePoint is on the same position as before.
double factor = getResolution() / validResolution;
double dX = (rescalePoint.getX() - getPosition().getX()) * (1 - 1 / factor);
double dY = (rescalePoint.getY() - getPosition().getY()) * (1 - 1 / factor);
// Apply translation to set the BBOX on the correct location:
newBbox = BboxService.setCenterPoint(newBbox, getPosition());
newBbox = BboxService.translate(newBbox, dX, dY);
// Now apply on this view port:
Coordinate tempPosition = checkPosition(BboxService.getCenterPoint(newBbox), validResolution);
applyViewNoChecks(new View(tempPosition, validResolution));
}
}
private double getResolutionForBounds(Bbox bounds, ZoomOption zoomOption) {
double wRatio;
double boundsWidth = bounds.getWidth();
if (boundsWidth <= 0) {
wRatio = getMinimumResolution();
} else {
wRatio = boundsWidth / mapWidth;
}
double hRatio;
double boundsHeight = bounds.getHeight();
if (boundsHeight <= 0) {
hRatio = getMinimumResolution();
} else {
hRatio = boundsHeight / mapHeight;
}
// Return the checked resolution for the minimum to fit inside:
return checkResolution(wRatio > hRatio ? wRatio : hRatio, zoomOption);
}
private double getMaxBoundsResolution() {
if (maxBounds == null) {
return 0;
}
double wRatio;
double boundsWidth = maxBounds.getWidth();
if (boundsWidth > 0) {
wRatio = boundsWidth / (mapWidth > 0 ? mapWidth : MAP_SIZE_FALLBACK_VALUE);
} else {
wRatio = getMaximumResolution();
}
double hRatio;
double boundsHeight = maxBounds.getHeight();
if (boundsHeight > 0) {
hRatio = boundsHeight / (mapHeight > 0 ? mapHeight : MAP_SIZE_FALLBACK_VALUE);
} else {
hRatio = getMaximumResolution();
}
// Return the checked resolution for the minimum to fit inside:
return wRatio < hRatio ? wRatio : hRatio;
}
// Returns a position that's within the maximum bounds:
private Coordinate checkPosition(final Coordinate newPosition, final double newResolution) {
double xCenter = newPosition.getX();
double yCenter = newPosition.getY();
if (maxBounds != null) {
double w = mapWidth * newResolution / 2;
double h = mapHeight * newResolution / 2;
Coordinate minCoordinate = BboxService.getOrigin(maxBounds);
Coordinate maxCoordinate = BboxService.getEndPoint(maxBounds);
if ((w * 2) > maxBounds.getWidth()) {
xCenter = BboxService.getCenterPoint(maxBounds).getX();
} else {
if ((xCenter - w) < minCoordinate.getX()) {
xCenter = minCoordinate.getX() + w;
}
if ((xCenter + w) > maxCoordinate.getX()) {
xCenter = maxCoordinate.getX() - w;
}
}
if ((h * 2) > maxBounds.getHeight()) {
yCenter = BboxService.getCenterPoint(maxBounds).getY();
} else {
if ((yCenter - h) < minCoordinate.getY()) {
yCenter = minCoordinate.getY() + h;
}
if ((yCenter + h) > maxCoordinate.getY()) {
yCenter = maxCoordinate.getY() - h;
}
}
}
return new Coordinate(xCenter, yCenter);
}
// Returns a resolution as requested by the zoom option:
private double checkResolution(double resolution, ZoomOption zoomOption) {
double allowedResolution = resolution;
double maximumResolution = getMaximumResolution();
double minimumResolution = getMinimumResolution();
if (allowedResolution > maximumResolution) {
allowedResolution = maximumResolution;
} else if (allowedResolution < minimumResolution) {
allowedResolution = minimumResolution;
}
if (zoomOption == ZoomOption.FREE) {
return allowedResolution;
}
for (int i = 0; i < resolutions.size() - 1; i++) {
double upper = resolutions.get(i);
double lower = resolutions.get(i + 1);
if (allowedResolution == upper) {
return upper;
} else if (allowedResolution == lower) {
return lower;
} else if (allowedResolution < upper && allowedResolution > lower) {
switch (zoomOption) {
case LEVEL_FIT:
return upper;
case LEVEL_CLOSEST:
if (Math.abs(upper - allowedResolution) < Math.abs(allowedResolution - lower)) {
return upper;
} else {
return lower;
}
default:
return allowedResolution;
}
}
}
return allowedResolution;
}
private void applyViewNoChecks(View nextView) {
if (!view.equals(nextView)) {
View oldView = view;
view = nextView;
if (!nextView.isAnimation() && currentAnimation != null) {
currentAnimation.cancel();
}
eventBus.fireEvent(new ViewPortChangedEvent(oldView, nextView, currentAnimation));
}
}
private View copy(View view) {
View clone = new View((Coordinate) view.getPosition().clone(), view.getResolution());
for (Hint> hint : view.getHints()) {
clone.setHint((Hint) hint, view.getHint(hint));
}
return clone;
}
private View copy(View view, Coordinate position, double resolution) {
View clone = new View((Coordinate) position.clone(), resolution);
for (Hint> hint : view.getHints()) {
clone.setHint((Hint) hint, view.getHint(hint));
}
return clone;
}
public void applyViewNoEvent(View view) {
double tempResolution = checkResolution(view.getResolution(), ZoomOption.FREE);
Coordinate tempPosition = checkPosition(view.getPosition(), tempResolution);
this.view = copy(view, tempPosition, tempResolution);
}
}