/*******************************************************************************
* Copyright (c) 2008, 2012 Stepan Rutz.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stepan Rutz - initial implementation
* Hallvard Trætteberg - further cleanup and development
*******************************************************************************/
package org.eclipse.nebula.widgets.geomap;
import org.eclipse.nebula.widgets.geomap.internal.GeoMapPositioned;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
/**
* @since 3.3
*
*/
public class GeoMapUtil {
/* must be the same as GeoMapHelper's TILE_SIZE */
private static final int TILE_SIZE = 256;
/**
* Returns the pair of longitude, latitude for a position at a certain zoom level
* @param position the position
* @param zoom the zoom level
* @return the pair of longitude, latitude as a PointD
*/
public static PointD getLongitudeLatitude(Point position, int zoom) {
return new PointD(
GeoMapUtil.position2lon(position.x, zoom),
GeoMapUtil.position2lat(position.y, zoom));
}
/**
* Returns the position at a certain zoom level for a pair of longitude, latitude
* @param coords the pair of longitude, latitude
* @param zoom the zoom level
* @return the position as a Point
*/
public static Point computePosition(PointD coords, int zoom) {
int x = GeoMapUtil.lon2position(coords.x, zoom);
int y = GeoMapUtil.lat2position(coords.y, zoom);
return new Point(x, y);
}
/**
* Converts position to longitude.
* @param x position x coord (pixels in this swt control)
* @param z the current zoom level.
* @return the longitude
*/
public static double position2lon(int x, int z) {
double xmax = TILE_SIZE * (1 << z);
return x / xmax * 360.0 - 180;
}
/**
* Converts position to latitude.
* @param y position y coord (pixels in this swt control)
* @param z the current zoom level.
* @return the latitude
*/
public static double position2lat(int y, int z) {
double ymax = TILE_SIZE * (1 << z);
return Math.toDegrees(Math.atan(Math.sinh(Math.PI - (2.0 * Math.PI * y) / ymax)));
}
/**
* Converts longitude to x position.
* @param lon the longitude
* @param z the zoom level.
* @return the x position
*/
public static int lon2position(double lon, int z) {
double xmax = TILE_SIZE * (1 << z);
return (int) Math.floor((lon + 180) / 360 * xmax);
}
/**
* Converts latitude to y position.
* @param lat the latitude
* @param z the zoom level.
* @return the y position
*/
public static int lat2position(double lat, int z) {
double ymax = TILE_SIZE * (1 << z);
return (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * ymax);
}
// helper methods operating on position and/or zoom
/**
* Translates the position of the upper left corner of this GeoMap, without any panning effect.
* @param geoMap the geoMap
* @param tx the relative distance in x-direction
* @param ty the relative distance in y-direction
*/
public static void translateMapPosition(GeoMapPositioned geoMap, int tx, int ty) {
Point mapPosition = geoMap.getMapPosition();
geoMap.setMapPosition(mapPosition.x + tx, mapPosition.y + ty);
}
/**
* Zooms in, while ensuring that the pivot point remains at the same screen location.
* @param geoMap the geoMap
* @param pivot the point that will remain at the same screen location.
*/
public static void zoomIn(GeoMapPositioned geoMap, Point pivot) {
if (geoMap.getZoom() >= geoMap.getMaxZoom()) {
return;
}
Point mapPosition = geoMap.getMapPosition();
int dx = pivot.x;
int dy = pivot.y;
geoMap.setZoom(geoMap.getZoom() + 1);
geoMap.setMapPosition(mapPosition.x * 2 + dx, mapPosition.y * 2 + dy);
}
/**
* Zooms out, while ensuring that the pivot point remains at the same screen location.
* @param geoMap the geoMap
* @param pivot the point that will remain at the same screen location.
*/
public static void zoomOut(GeoMapPositioned geoMap, Point pivot) {
if (geoMap.getZoom() <= 1) {
return;
}
Point mapPosition = geoMap.getMapPosition();
int dx = pivot.x;
int dy = pivot.y;
geoMap.setZoom(geoMap.getZoom() - 1);
geoMap.setMapPosition((mapPosition.x - dx) / 2, (mapPosition.y - dy) / 2);
}
static private int zoomMargin = 10;
/**
* Zooms into and centers on the specified rectangle.
* @param geoMap the geoMap
* @param mapSize the size of the map, containes the zoom rectangle
* @param rect the rectangle
* @param maxZoom the maximum level to zoom to, or -1 to default to the geoMap's max zoom
*/
public static void zoomTo(GeoMapPositioned geoMap, Point mapSize, Rectangle rect, int maxZoom) {
Rectangle zoomRectangle = new Rectangle(rect.x, rect.y, rect.width, rect.height);
// don't try to zoom if the map is too small, e.g. if the map hasn't been layed out
if (mapSize.x < zoomMargin || mapSize.y < zoomMargin) {
return;
}
if (maxZoom < 0) {
maxZoom = geoMap.getMaxZoom();
}
int zoom = geoMap.getZoom();
// compute zoom by zooming out until the rectangle fits within the viewport
while (zoom > 0 && (mapSize.x < zoomRectangle.width + zoomMargin || mapSize.y < zoomRectangle.height + zoomMargin || zoom > maxZoom)) {
// zoom out and scale zoom rectangle down
zoom--;
zoomRectangle.x /= 2;
zoomRectangle.y /= 2;
zoomRectangle.width /= 2;
zoomRectangle.height /= 2;
}
// compute zoom by zooming in as long as the rectangle will fit within the viewport
while (mapSize.x > zoomRectangle.width * 2 + zoomMargin && mapSize.y > zoomRectangle.height * 2 + zoomMargin && zoom < maxZoom) {
// zoom in and scale zoom rectangle up
zoom++;
zoomRectangle.x *= 2;
zoomRectangle.y *= 2;
zoomRectangle.width *= 2;
zoomRectangle.height *= 2;
}
geoMap.setZoom(zoom);
geoMap.setMapPosition(zoomRectangle.x + (zoomRectangle.width - mapSize.x) / 2, zoomRectangle.y + (zoomRectangle.height - mapSize.y) / 2);
}
}