/*
* GeoUtil.java
*
* Created on June 26, 2006, 10:44 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.jdesktop.swingx.mapviewer.util;
import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.mapviewer.GeoBounds;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.jdesktop.swingx.mapviewer.TileFactory;
import org.jdesktop.swingx.mapviewer.TileFactoryInfo;
/**
* These are math utilities for converting between pixels, tiles, and geographic coordinates. Implements a Google Maps
* style mercator projection.
* @author joshy
*/
public final class GeoUtil
{
/**
* @param zoom the zoom level
* @param info the tile factory info
* @return the size of the map at the given zoom, in tiles (num tiles tall by num tiles wide)
*/
public static Dimension getMapSize(int zoom, TileFactoryInfo info)
{
return new Dimension(info.getMapWidthInTilesAtZoom(zoom), info.getMapWidthInTilesAtZoom(zoom));
}
/**
* @param x the x value
* @param y the y value
* @param zoomLevel the zoom level
* @param info the tile factory info
* @return true if this point in <em>tiles</em> is valid at this zoom level. For example, if the zoom level is 0
* (zoomed all the way out, where there is only one tile), then x,y must be 0,0
*/
public static boolean isValidTile(int x, int y, int zoomLevel, TileFactoryInfo info)
{
// int x = (int)coord.getX();
// int y = (int)coord.getY();
// if off the map to the top or left
if (x < 0 || y < 0)
{
return false;
}
// if of the map to the right
if (info.getMapCenterInPixelsAtZoom(zoomLevel).getX() * 2 <= x * info.getTileSize(zoomLevel))
{
return false;
}
// if off the map to the bottom
if (info.getMapCenterInPixelsAtZoom(zoomLevel).getY() * 2 <= y * info.getTileSize(zoomLevel))
{
return false;
}
// if out of zoom bounds
if (zoomLevel < info.getMinimumZoomLevel() || zoomLevel > info.getMaximumZoomLevel())
{
return false;
}
return true;
}
/**
* Given a position (latitude/longitude pair) and a zoom level, return the appropriate point in <em>pixels</em>. The
* zoom level is necessary because pixel coordinates are in terms of the zoom level
* @param c A lat/lon pair
* @param zoomLevel the zoom level to extract the pixel coordinate for
* @param info the tile factory info
* @return the coordinate
*/
public static Point2D getBitmapCoordinate(GeoPosition c, int zoomLevel, TileFactoryInfo info)
{
return getBitmapCoordinate(c.getLatitude(), c.getLongitude(), zoomLevel, info);
}
/**
* Given a position (latitude/longitude pair) and a zoom level, return the appropriate point in <em>pixels</em>. The
* zoom level is necessary because pixel coordinates are in terms of the zoom level
* @param latitude the latitude
* @param longitude the longitude
* @param zoomLevel the zoom level to extract the pixel coordinate for
* @param info the tile factory info
* @return the coordinate
*/
public static Point2D getBitmapCoordinate(double latitude, double longitude, int zoomLevel, TileFactoryInfo info)
{
double x = info.getMapCenterInPixelsAtZoom(zoomLevel).getX() + longitude
* info.getLongitudeDegreeWidthInPixels(zoomLevel);
double e = Math.sin(latitude * (Math.PI / 180.0));
if (e > 0.9999)
{
e = 0.9999;
}
if (e < -0.9999)
{
e = -0.9999;
}
double y = info.getMapCenterInPixelsAtZoom(zoomLevel).getY() + 0.5 * Math.log((1 + e) / (1 - e)) * -1
* (info.getLongitudeRadianWidthInPixels(zoomLevel));
return new Point2D.Double(x, y);
}
/**
* Convert an on screen pixel coordinate and a zoom level to a geo position
* @param pixelCoordinate the coordinate in pixels
* @param zoom the zoom level
* @param info the tile factory info
* @return a geo position
*/
public static GeoPosition getPosition(Point2D pixelCoordinate, int zoom, TileFactoryInfo info)
{
// p(" --bitmap to latlon : " + coord + " " + zoom);
double wx = pixelCoordinate.getX();
double wy = pixelCoordinate.getY();
// this reverses getBitmapCoordinates
double flon = (wx - info.getMapCenterInPixelsAtZoom(zoom).getX()) / info.getLongitudeDegreeWidthInPixels(zoom);
double e1 = (wy - info.getMapCenterInPixelsAtZoom(zoom).getY())
/ (-1 * info.getLongitudeRadianWidthInPixels(zoom));
double e2 = (2 * Math.atan(Math.exp(e1)) - Math.PI / 2) / (Math.PI / 180.0);
double flat = e2;
GeoPosition wc = new GeoPosition(flat, flon);
return wc;
}
/**
* Gets the map bounds.
* @param mapViewer The map viewer.
* @return Returns the bounds.
*/
public static GeoBounds getMapBounds(JXMapViewer mapViewer)
{
return new GeoBounds(getMapGeoBounds(mapViewer));
}
/**
* Gets the bounds as a set of two <code>GeoPosition</code> objects.
* @param mapViewer The map viewer.
* @return Returns the set of two <code>GeoPosition</code> objects that represent the north west and south east
* corners of the map.
*/
private static Set<GeoPosition> getMapGeoBounds(JXMapViewer mapViewer)
{
Set<GeoPosition> set = new HashSet<GeoPosition>();
TileFactory tileFactory = mapViewer.getTileFactory();
int zoom = mapViewer.getZoom();
Rectangle2D bounds = mapViewer.getViewportBounds();
Point2D pt = new Point2D.Double(bounds.getX(), bounds.getY());
set.add(tileFactory.pixelToGeo(pt, zoom));
pt = new Point2D.Double(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight());
set.add(tileFactory.pixelToGeo(pt, zoom));
return set;
}
}