package com.revolsys.swing.map.old; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.measure.Measurable; import javax.measure.quantity.Length; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.swing.map.ComponentViewport2D; import com.revolsys.swing.map.Viewport2D; public class FixedScaleZoomMode implements ZoomMode { public static final FixedScaleZoomMode METRIC = new FixedScaleZoomMode(100, 250, 500, 1000, 2500, 5000, 10000, 20000, 50000, 100000, 250000, 500000, 1000000, 2500000, 5000000, 10000000, 25000000, 50000000, 100000000, 250000000); /** The ordered list of scales. */ private final List<Double> scales = new ArrayList<>(); public FixedScaleZoomMode() { this(1693.0, 3385.0, 6771.0, 14000.0, 27000.0, 54000.0, 108000.0, 217000.0, 433000.0, 867000.0, 2000000.0, 3000000.0, 7000000.0, 14000000.0, 28000000.0, 55000000.0, 111000000.0); } public FixedScaleZoomMode(final double... scales) { for (final double scale : scales) { this.scales.add(scale); } Collections.sort(this.scales); Collections.reverse(this.scales); } private double getBestScale(final ComponentViewport2D viewport, final double scale) { double maxScale = viewport.getMaxScale(); maxScale = getScale(maxScale, true); final double newScale = Math.min(scale, maxScale); return newScale; } /** * Get the best bounding box matching the zoom mode policy * <ul> * <li>If the coordinate system for the bounding box wasn't specified it will * be set to the coordinate system of the viewport.</li> * <li>The bounding box will be converted to the coordinate system of the * viewport.</li> * </ul> * * @param viewport The viewport. * @param boundingBox The bounding box. * @return The bounding box. */ @Override public BoundingBox getBoundingBox(final ComponentViewport2D viewport, final BoundingBox boundingBox) { final double viewAspectRatio = viewport.getViewAspectRatio(); if (!Double.isNaN(viewAspectRatio) && !boundingBox.isEmpty()) { final GeometryFactory geometryFactory = viewport.getGeometryFactory(); final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem(); final BoundingBox areaBoundingBox = coordinateSystem.getAreaBoundingBox(); final GeometryFactory bboxGeometryFactory = boundingBox.getGeometryFactory(); BoundingBox newBoundingBox = boundingBox; if (bboxGeometryFactory == null) { newBoundingBox = boundingBox.convert(geometryFactory); } else { if (bboxGeometryFactory.equals(geometryFactory)) { newBoundingBox = boundingBox; } else { newBoundingBox = boundingBox.convert(geometryFactory); } } double maxScale = viewport.getMaxScale(); maxScale = getScale(maxScale, false); double scale = getScale(viewport, newBoundingBox); if (!Double.isNaN(scale)) { scale = getScale(scale, false); if (scale > maxScale) { scale = maxScale; } final double x = newBoundingBox.getCentreX(); final double y = newBoundingBox.getCentreY(); final BoundingBox boundingBox2 = viewport.getBoundingBox(x, y, scale); // double ax1 = areaBoundingBox.getMinX(); // double ax2 = areaBoundingBox.getMaxX(); // double ay1 = areaBoundingBox.getMinY(); // double ay2 = areaBoundingBox.getMaxY(); // double x1 = boundingBox2.getMinX(); // double x2 = boundingBox2.getMaxX(); // double y1 = boundingBox2.getMinY(); // double y2 = boundingBox2.getMaxY(); // double width = boundingBox2.getWidth(); // double height = boundingBox2.getHeight(); // if (x1 < ax1) { // x1 = ax1; // x2 = ax1 + width; // } else if (x2 > ax2) { // x1 = ax2 - width; // x2 = ax2; // } // if (y1 < ay1) { // y1 = ay1; // y2 = ay1 + height; // } else if (y2 > ay2) { // y1 = ay2 - height; // y2 = ay2; // } // boundingBox2 = new BoundingBox(coordinateSystem, x1, y1, x2, // y2); return boundingBox2; } return newBoundingBox; } return boundingBox; } private double getNextScale(final ComponentViewport2D viewport, final boolean larger) { final double scale = viewport.getScale(); return getNextScale(scale, larger); } private double getNextScale(final double scale, final boolean larger) { double previousScale = this.scales.get(0); if (scale - 1 > previousScale) { if (!larger || this.scales.size() == 1) { return previousScale; } else { return this.scales.get(1); } } else { for (int i = 1; i < this.scales.size(); i++) { final double nextScale = this.scales.get(i); if (Math.abs(scale - nextScale) < 1) { if (larger) { if (i == this.scales.size() - 1) { return nextScale; } else { return this.scales.get(i + 1); } } else { return previousScale; } } else if (scale > nextScale) { if (larger) { return nextScale; } else { return previousScale; } } previousScale = nextScale; } return previousScale; } } protected double getScale(final ComponentViewport2D viewport, final BoundingBox newBoundingBox) { final Measurable<Length> viewWidth = viewport.getViewWidthLength(); final Measurable<Length> viewHeight = viewport.getViewHeightLength(); final Measurable<Length> modelWidth = newBoundingBox.getWidthLength(); final Measurable<Length> modelHeight = newBoundingBox.getHeightLength(); final double horizontalScale = Viewport2D.getScale(viewWidth, modelWidth); final double verticalScale = Viewport2D.getScale(viewHeight, modelHeight); final double scale = Math.max(horizontalScale, verticalScale); return scale; } private double getScale(final double scale, final boolean larger) { double previousScale = this.scales.get(0); if (Double.isNaN(scale) || scale - 1 > previousScale) { return previousScale; } else { for (int i = 1; i < this.scales.size(); i++) { final double nextScale = this.scales.get(i); if (Math.abs(scale - nextScale) < 1) { return nextScale; } else if (scale > nextScale) { if (larger) { return nextScale; } else { return previousScale; } } previousScale = nextScale; } return previousScale; } } public double getScale(final int zoomLevel) { return this.scales.get(zoomLevel); } public int getZoomLevel(final ComponentViewport2D viewport) { final double scale = viewport.getScale(); double previousScale = this.scales.get(0); if ((int)scale >= (int)previousScale) { return 0; } else { for (int i = 1; i < this.scales.size(); i++) { final double nextScale = this.scales.get(i); if ((int)scale >= (int)nextScale) { return i; } previousScale = nextScale; } return this.scales.size() - 1; } } /** * Zoom the map so that the specified bounding box is visible. * * @param viewport The viewport. * @param boundingBox The bounding box. */ @Override public void zoom(final ComponentViewport2D viewport, final BoundingBox boundingBox) { final BoundingBox newBoundingBox = getBoundingBox(viewport, boundingBox); viewport.setBoundingBox(newBoundingBox); } /** * Zoom the map to include the bounding box specified by the model coordinate * pair. * * @param viewport The viewport. * @param x1 The first x coordinate. * @param y1 The first y coordinate. * @param x2 The second x coordinate. * @param y2 The second y coordinate. */ @Override public void zoom(final ComponentViewport2D viewport, final double x1, final double y1, final double x2, final double y2) { final double viewWidth = viewport.getViewWidthPixels(); final double viewHeight = viewport.getViewHeightPixels(); final double xc1 = Math.max(Math.min(x1, viewWidth - 1), 0); final double yc1 = Math.max(Math.min(y1, viewHeight - 1), 0); final double xc2 = Math.max(Math.min(x2, viewWidth - 1), 0); final double yc2 = Math.max(Math.min(y2, viewHeight - 1), 0); final BoundingBox boundingBox = viewport.getBoundingBox(xc1, yc1, xc2, yc2); final double x = boundingBox.getCentreX(); final double y = boundingBox.getCentreY(); final Measurable<Length> modelWidth = boundingBox.getWidthLength(); double scale = Viewport2D.getScale(viewport.getViewWidthLength(), modelWidth); scale = getNextScale(scale, false); final double newScale = getBestScale(viewport, scale); final BoundingBox boundingBox1 = viewport.getBoundingBox(x, y, newScale); viewport.setBoundingBox(boundingBox1); } /** * Zoom the map to the map scale at the view coordinate, re-centring the map * at the model coordinate represented by the view coordinate. * * @param viewport The viewport to zoom. * @param x The x coordinate. * @param y The y coordinate. * @param scale The new map scale. */ private void zoomAndRecentre(final ComponentViewport2D viewport, final double x, final double y, final double scale) { final double newScale = getBestScale(viewport, scale); final BoundingBox boundingBox = viewport.getBoundingBox(x, y, newScale); viewport.setBoundingBox(boundingBox); } /** * Zoom the map in one level at the view coordinate, with the model coordinate * being maintained at the same view coordinate. * * @param viewport The viewport to zoom. * @param x The x coordinate. * @param y The y coordinate. */ @Override public void zoomIn(final ComponentViewport2D viewport, final double x, final double y) { final double scale = getNextScale(viewport, true); zoomProportional(viewport, x, y, scale); } /** * Zoom the map in one level at the view coordinate, re-centring the map at * the model coordinate represented by the view coordinate. * * @param viewport The viewport to zoom. * @param x The x coordinate. * @param y The y coordinate. */ @Override public void zoomInAndRecentre(final ComponentViewport2D viewport, final double x, final double y) { final double scale = getNextScale(viewport, true); final double[] coord = viewport.toModelCoordinates(x, y); zoomAndRecentre(viewport, coord[0], coord[1], scale); } /** * Zoom the map out one level at the view coordinate, with the model * coordinate being maintained at the same view coordinate. * * @param viewport The viewport to zoom. * @param x The x coordinate. * @param y The y coordinate. */ @Override public void zoomOut(final ComponentViewport2D viewport, final double x, final double y) { final double scale = getNextScale(viewport, false); zoomProportional(viewport, x, y, scale); } /** * Zoom the map in one level at the view coordinate, re-centring the map at * the model coordinate represented by the view coordinate. * * @param viewport The viewport to zoom. * @param x The x coordinate. * @param y The y coordinate. */ @Override public void zoomOutAndRecentre(final ComponentViewport2D viewport, final double x, final double y) { final double scale = getNextScale(viewport, false); final double[] coord = viewport.toModelCoordinates(x, y); zoomAndRecentre(viewport, coord[0], coord[1], scale); } /** * Zoom the map in by the multiplication factor at the view coordinate, with * the model coordinate being maintained at the same view coordinate. * * @param viewport The viewport to zoom. * @param x The x coordinate. * @param y The y coordinate. * @param scale The new map scale. */ private void zoomProportional(final ComponentViewport2D viewport, final double x, final double y, final double scale) { final GeometryFactory geometryFactory = viewport.getGeometryFactory(); final double newScale = getBestScale(viewport, scale); final double[] ordinates = viewport.toModelCoordinates(x, y); final double mapX = ordinates[0]; final double mapY = ordinates[1]; final double viewWidth = viewport.getViewWidthPixels(); final double xProportion = x / viewWidth; final double viewHeight = viewport.getViewHeightPixels(); final double yProportion = (viewHeight - y) / viewHeight; final double width = viewport.getModelWidth(newScale); final double height = viewport.getModelHeight(newScale); final double x1 = mapX - width * xProportion; final double y1 = mapY - height * yProportion; final double x2 = x1 + width; final double y2 = y1 + height; final BoundingBox boundingBox = geometryFactory.newBoundingBox(x1, y1, x2, y2); viewport.setBoundingBox(boundingBox); } }