/* * Copyright (c) 2016 Fraunhofer IGD * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Fraunhofer IGD <http://www.igd.fraunhofer.de/> */ package de.fhg.igd.mapviewer.server.wms; import java.net.URI; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdesktop.swingx.mapviewer.AbstractTileProvider; import org.jdesktop.swingx.mapviewer.GeotoolsConverter; import org.jdesktop.swingx.mapviewer.PixelConverter; import org.jdesktop.swingx.mapviewer.TileProvider; import de.fhg.igd.mapviewer.server.LinearBoundsConverter; import de.fhg.igd.mapviewer.server.wms.capabilities.Layer; import de.fhg.igd.mapviewer.server.wms.capabilities.WMSBounds; import de.fhg.igd.mapviewer.server.wms.capabilities.WMSCapabilities; import de.fhg.igd.mapviewer.server.wms.capabilities.WMSCapabilitiesException; import de.fhg.igd.mapviewer.server.wms.capabilities.WMSUtil; /** * WMSTileProvider * * @author <a href="mailto:simon.templer@igd.fhg.de">Simon Templer</a> * * @version $Id$ */ public class WMSTileProvider extends AbstractTileProvider { private static final int EPSG_WGS84 = 4326; private static final Log log = LogFactory.getLog(WMSTileProvider.class); private static final Set<String> SUPPORTED_FORMATS = new LinkedHashSet<String>(); static { SUPPORTED_FORMATS.add("image/png"); //$NON-NLS-1$ SUPPORTED_FORMATS.add("image/jpeg"); //$NON-NLS-1$ SUPPORTED_FORMATS.add("image/gif"); //$NON-NLS-1$ } private final String serverUrl; private final int maxZoom; private final int totalMapZoom; private final String version; private String format = null; private int epsg; private double minX; private double minY; private double xRange; private double yRange; private final int xTileSize; private final int yTileSize; private String layerString; private final WMSCapabilities capabilities; /** * Tile provider using WMS services * * @param baseUrl the base URL for the GetCapabilities request * @param preferredEpsg the EPSG code of preferred SRS * @param zoomLevels the number of zoom levels * @param minTileSize the minimum tile size * @param minMapSize the minimum map size * @param layers the layers to display * * @throws WMSCapabilitiesException if reading the capabilities fails */ public WMSTileProvider(final String baseUrl, final int preferredEpsg, final int zoomLevels, final int minTileSize, final int minMapSize, final String layers) throws WMSCapabilitiesException { this.capabilities = WMSUtil.getCapabilities(baseUrl); // get server URL String mapUrl = capabilities.getMapURL(); if (mapUrl.endsWith("?") || mapUrl.endsWith("&")) //$NON-NLS-1$ //$NON-NLS-2$ serverUrl = mapUrl; else if (mapUrl.indexOf('?') >= 0) serverUrl = mapUrl + '&'; else serverUrl = mapUrl + '?'; log.info("GetMap URL: " + serverUrl); //$NON-NLS-1$ // get bounding box WMSBounds bb = WMSUtil.getBoundingBox(capabilities, preferredEpsg); if (bb != null) { // determine parameters from bounding box try { epsg = Integer.parseInt(bb.getSRS().substring(5)); minX = bb.getMinX(); xRange = bb.getMaxX() - minX; minY = bb.getMinY(); yRange = bb.getMaxY() - minY; } catch (Exception e) { // invalid bb log.error("Invalid bounding box", e); //$NON-NLS-1$ bb = null; } } if (bb == null) { // no bounding boxes -> world wms log.warn("No valid bounding box found, creating world wms"); //$NON-NLS-1$ // default tiles epsg = EPSG_WGS84; minX = -180; xRange = 360; minY = -90; yRange = 180; } log.info("Map bounds - epsg: " + epsg + ", minX: " + minX + ", xRange: " + xRange //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ", minY: " + minY + ", yRange: " + yRange); //$NON-NLS-1$ //$NON-NLS-2$ // determine tile sizes double tileRatio = xRange / yRange; if (tileRatio >= 1.0) { // xRange is bigger yTileSize = minTileSize; xTileSize = (int) (tileRatio * yTileSize); } else { // yRange is bigger xTileSize = minTileSize; yTileSize = (int) (xTileSize / tileRatio); } log.info("Tile size: " + xTileSize + "x" + yTileSize); //$NON-NLS-1$ //$NON-NLS-2$ // determine format Iterable<String> formats = capabilities.getFormats(); Iterator<String> itFormat = formats.iterator(); while (format == null && itFormat.hasNext()) { String f = itFormat.next(); if (SUPPORTED_FORMATS.contains(f)) format = f; } if (format == null) { throw new WMSCapabilitiesException("No supported format found: " + formats); //$NON-NLS-1$ } // zoom levels maxZoom = zoomLevels - 1; totalMapZoom = (int) (maxZoom + (Math.log((double) minMapSize / (double) Math.max(xTileSize, yTileSize)) / Math.log(2))); // version version = capabilities.getVersion(); // layers List<Layer> layerList = WMSUtil.getLayers(layers, capabilities); this.layerString = WMSUtil.getLayerString(layerList, true); } /** * @see AbstractTileProvider#createConverter() */ @Override protected PixelConverter createConverter() { // TODO values for swapAxes, reverseX, reverseY? return new LinearBoundsConverter(GeotoolsConverter.getInstance(), this, minX, minY, xRange, yRange, epsg, false, false, true); } /** * @see TileProvider#getDefaultZoom() */ @Override public int getDefaultZoom() { return maxZoom; } /** * @see TileProvider#getMapHeightInTiles(int) */ @Override public int getMapHeightInTiles(int zoom) { return 1 << (totalMapZoom - zoom); } /** * @see TileProvider#getMapWidthInTiles(int) */ @Override public int getMapWidthInTiles(int zoom) { return 1 << (totalMapZoom - zoom); } /** * @see TileProvider#getMaximumZoom() */ @Override public int getMaximumZoom() { return maxZoom; } /** * @see TileProvider#getMinimumZoom() */ @Override public int getMinimumZoom() { return 0; } /** * @see TileProvider#getTileHeight(int) */ @Override public int getTileHeight(int zoom) { return yTileSize; } /** * @see TileProvider#getTileUris(int, int, int) */ @Override public URI[] getTileUris(int x, int y, int zoom) { String styles = ""; //$NON-NLS-1$ double geoTileWidth = xRange / getMapWidthInTiles(zoom); double geoTileHeight = yRange / getMapHeightInTiles(zoom); y = (getMapHeightInTiles(zoom) - 1) - y; // reverse y // Bounding Box needs lower left and upper right corner double lx = minX + geoTileWidth * x; double by = minY + geoTileHeight * y; double rx = minX + geoTileWidth * (x + 1); double ty = minY + geoTileHeight * (y + 1); String bbox = lx + "," + by + "," + rx + "," + ty; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // create query string String url = serverUrl + "VERSION=" + version + "&REQUEST=" + //$NON-NLS-1$ //$NON-NLS-2$ "GetMap&SERVICE=WMS&Layers=" + layerString + //$NON-NLS-1$ "&FORMAT=" + format + //$NON-NLS-1$ "&BBOX=" + bbox + //$NON-NLS-1$ "&WIDTH=" + xTileSize + "&HEIGHT=" + yTileSize + //$NON-NLS-1$ //$NON-NLS-2$ "&SRS=EPSG:" + epsg + //$NON-NLS-1$ "&STYLES=" + styles + //$NON-NLS-1$ "&TRANSPARENT=FALSE" + //$NON-NLS-1$ // "&BGCOLOR=0xffffff" + "&EXCEPTIONS=application/vnd.ogc.se_inimage" + //$NON-NLS-1$ ""; //$NON-NLS-1$ return new URI[] { URI.create(url) }; } /** * @see TileProvider#getTileWidth(int) */ @Override public int getTileWidth(int zoom) { return xTileSize; } /** * @see TileProvider#getTotalMapZoom() */ @Override public int getTotalMapZoom() { return totalMapZoom; } }