package com.revolsys.swing.map.layer.webmercatortilecache; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import javax.imageio.ImageIO; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.util.Property; public class WebMercatorTileCacheClient { private static final double[] METRES_PER_PIXEL = { 78271.517, 39135.7585, 19567.8792, 9783.9396, 4891.9698, 2445.9849, 1222.9925, 611.4962, 305.7481, 152.8741, 76.437, 38.2185, 19.1093, 9.5546, 4.7773, 2.3887, 1.1943, 0.5972, 0.2986, 0.1493, 0.0746 }; private final String serverUrl; public WebMercatorTileCacheClient(final String serverUrl) { if (Property.hasValue(serverUrl)) { this.serverUrl = serverUrl; } else { throw new IllegalArgumentException("Open Street Map tile server URL must be specified"); } } public BoundingBox getBoundingBox(final int zoomLevel, final int tileX, final int tileY) { final double lon1 = getLongitude(zoomLevel, tileX); final double lat1 = getLatitude(zoomLevel, tileY); final double lon2 = getLongitude(zoomLevel, tileX + 1); final double lat2 = getLatitude(zoomLevel, tileY + 1); return GeometryFactory.wgs84() .newBoundingBox(lon1, lat1, lon2, lat2) .convert(GeometryFactory.worldMercator()); } protected BufferedImage getImage(final String url) { try { final URLConnection connection = new URL(url).openConnection(); final InputStream in = connection.getInputStream(); return ImageIO.read(in); } catch (final IOException e) { throw new RuntimeException("Unable to download image from: " + url, e); } } public double getLatitude(final int zoomLevel, final int tileY) { final double n = Math.PI - 2.0 * Math.PI * tileY / Math.pow(2.0, zoomLevel); return Math.toDegrees(Math.atan(Math.sinh(n))); } public double getLongitude(final int zoomLevel, final int tileX) { return tileX / Math.pow(2.0, zoomLevel) * 360.0 - 180; } public BufferedImage getMapImage(final int zoomLevel, final double longitude, final double latitude) { final String url = getMapUrl(zoomLevel, longitude, latitude); return getImage(url); } public BufferedImage getMapImage(final int zoomLevel, final int tileX, final int tileY) { final String url = getMapUrl(zoomLevel, tileX, tileY); return getImage(url); } public String getMapUrl(final int zoomLevel, final double longitude, final double latitude) { final int tileX = getTileX(zoomLevel, longitude); final int tileY = getTileY(zoomLevel, latitude); return getMapUrl(zoomLevel, tileX, tileY); } public String getMapUrl(final int zoomLevel, final int tileX, final int tileY) { return this.serverUrl + zoomLevel + "/" + tileX + "/" + tileY + ".png"; } public double getResolution(final int zoomLevel) { return METRES_PER_PIXEL[zoomLevel]; } public String getServerUrl() { return this.serverUrl; } public int getTileX(final int zoomLevel, final double longitude) { final double ratio = (longitude + 180) / 360; int tileX = (int)Math.floor(ratio * (1 << zoomLevel)); if (ratio >= 1) { tileX--; } return tileX; } public int getTileY(final int zoomLevel, final double latitude) { final double radians = Math.toRadians(latitude); final int tileY = (int)Math.floor( (1 - Math.log(Math.tan(radians) + 1 / Math.cos(radians)) / Math.PI) / 2 * (1 << zoomLevel)); return tileY; } public int getZoomLevel(final double metresPerPixel) { double previousLevelMetresPerPixel = METRES_PER_PIXEL[0]; for (int i = 0; i < METRES_PER_PIXEL.length; i++) { final double levelMetresPerPixel = METRES_PER_PIXEL[i]; if (metresPerPixel > levelMetresPerPixel) { if (i == 0) { return 0; } else { final double range = levelMetresPerPixel - previousLevelMetresPerPixel; final double ratio = (metresPerPixel - previousLevelMetresPerPixel) / range; if (ratio < 0.8) { return i - 1; } else { return i; } } } previousLevelMetresPerPixel = levelMetresPerPixel; } return METRES_PER_PIXEL.length - 1; } }