Java tutorial
/** * This program is free software: you can redistribute it and/or modify * it 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Arne Kepp, OpenGeo, Copyright 2009 * */ package org.geowebcache.service.wmts; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.geowebcache.config.meta.ServiceContact; import org.geowebcache.config.meta.ServiceInformation; import org.geowebcache.config.meta.ServiceProvider; import org.geowebcache.conveyor.Conveyor.CacheResult; import org.geowebcache.filter.parameters.ParameterFilter; import org.geowebcache.grid.Grid; import org.geowebcache.grid.GridSet; import org.geowebcache.grid.GridSetBroker; import org.geowebcache.grid.GridSubset; import org.geowebcache.grid.SRS; import org.geowebcache.io.XMLBuilder; import org.geowebcache.layer.TileLayer; import org.geowebcache.layer.TileLayerDispatcher; import org.geowebcache.layer.meta.LayerMetaInformation; import org.geowebcache.mime.MimeType; import org.geowebcache.stats.RuntimeStats; import org.geowebcache.util.ServletUtils; import org.geowebcache.util.URLMangler; public class WMTSGetCapabilities { private static Log log = LogFactory.getLog(WMTSGetCapabilities.class); private TileLayerDispatcher tld; private GridSetBroker gsb; private String baseUrl; protected WMTSGetCapabilities(TileLayerDispatcher tld, GridSetBroker gsb, HttpServletRequest servReq, String baseUrl, String contextPath, URLMangler urlMangler) { this.tld = tld; this.gsb = gsb; String forcedBaseUrl = ServletUtils.stringFromMap(servReq.getParameterMap(), servReq.getCharacterEncoding(), "base_url"); if (forcedBaseUrl != null) { this.baseUrl = forcedBaseUrl; } else { this.baseUrl = urlMangler.buildURL(baseUrl, contextPath, WMTSService.SERVICE_PATH); } } protected void writeResponse(HttpServletResponse response, RuntimeStats stats) { final Charset encoding = StandardCharsets.UTF_8; byte[] data = generateGetCapabilities(encoding).getBytes(encoding); response.setStatus(HttpServletResponse.SC_OK); response.setContentType("application/vnd.ogc.wms_xml"); response.setCharacterEncoding(encoding.name()); response.setContentLength(data.length); response.setHeader("content-disposition", "inline;filename=wmts-getcapabilities.xml"); stats.log(data.length, CacheResult.OTHER); try { OutputStream os = response.getOutputStream(); os.write(data); os.flush(); } catch (IOException ioe) { log.debug("Caught IOException" + ioe.getMessage()); } } private String generateGetCapabilities(Charset encoding) { StringBuilder str = new StringBuilder(); XMLBuilder xml = new XMLBuilder(str); try { xml.header("1.0", encoding); xml.indentElement("Capabilities"); xml.attribute("xmlns", "http://www.opengis.net/wmts/1.0"); xml.attribute("xmlns:ows", "http://www.opengis.net/ows/1.1"); xml.attribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); xml.attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); xml.attribute("xmlns:gml", "http://www.opengis.net/gml"); xml.attribute("xsi:schemaLocation", "http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd"); xml.attribute("version", "1.0.0"); // There were some contradictions in the draft schema, haven't checked whether they've fixed those //str.append("xsi:schemaLocation=\"http://www.opengis.net/wmts/1.0 http://geowebcache.org/schema/opengis/wmts/1.0.0/wmtsGetCapabilities_response.xsd\"\n"); serviceIdentification(xml); serviceProvider(xml); operationsMetadata(xml); contents(xml); xml.indentElement("ServiceMetadataURL") .attribute("xlink:href", baseUrl + "?REQUEST=getcapabilities&VERSION=1.0.0").endElement(); xml.endElement("Capabilities"); return str.toString(); } catch (IOException e) { // Should not happen as StringBuilder doesn't throw throw new IllegalStateException(e); } } private void serviceIdentification(XMLBuilder xml) throws IOException { ServiceInformation servInfo = tld.getServiceInformation(); xml.indentElement("ows:ServiceIdentification"); if (servInfo != null) { appendTag(xml, "ows:Title", servInfo.getTitle(), "Web Map Tile Service - GeoWebCache"); appendTag(xml, "ows:Abstract", servInfo.getDescription(), null); if (servInfo != null && servInfo.getKeywords() != null) { xml.indentElement("ows:Keywords"); Iterator<String> keywordIter = servInfo.getKeywords().iterator(); while (keywordIter.hasNext()) { appendTag(xml, "ows:Keyword", keywordIter.next(), null); } xml.endElement(); } } else { xml.simpleElement("ows:Title", "Web Map Tile Service - GeoWebCache", true); } xml.simpleElement("ows:ServiceType", "OGC WMTS", true); xml.simpleElement("ows:ServiceTypeVersion", "1.0.0", true); if (servInfo != null) { appendTag(xml, "ows:Fees", servInfo.getFees(), null); appendTag(xml, "ows:AccessConstraints", servInfo.getAccessConstraints(), null); } xml.endElement("ows:ServiceIdentification"); } private void serviceProvider(XMLBuilder xml) throws IOException { ServiceInformation servInfo = tld.getServiceInformation(); ServiceProvider servProv = null; if (servInfo != null) { servProv = servInfo.getServiceProvider(); } xml.indentElement("ows:ServiceProvider"); if (servProv != null) { appendTag(xml, "ows:ProviderName", servProv.getProviderName(), null); if (servProv.getProviderSite() != null) { xml.indentElement("ows:ProviderSite").attribute("xlink:href", servProv.getProviderSite()) .endElement(); } ServiceContact servCont = servProv.getServiceContact(); if (servCont != null) { xml.indentElement("ows:ServiceContact"); appendTag(xml, "ows:IndividualName", servCont.getIndividualName(), null); appendTag(xml, "ows:PositionName", servCont.getPositionName(), null); xml.indentElement("ows:ContactInfo"); if (servCont.getPhoneNumber() != null || servCont.getFaxNumber() != null) { xml.indentElement("ows:Phone"); appendTag(xml, "ows:Voice", servCont.getPhoneNumber(), null); appendTag(xml, "ows:Facsimile", servCont.getFaxNumber(), null); xml.endElement(); } xml.indentElement("ows:Address"); appendTag(xml, "ows:DeliveryPoint", servCont.getAddressStreet(), null); appendTag(xml, "ows:City", servCont.getAddressCity(), null); appendTag(xml, "ows:AdministrativeArea", servCont.getAddressAdministrativeArea(), null); appendTag(xml, "ows:PostalCode", servCont.getAddressPostalCode(), null); appendTag(xml, "ows:Country", servCont.getAddressCountry(), null); appendTag(xml, "ows:ElectronicMailAddress", servCont.getAddressEmail(), null); xml.endElement("ows:Address"); xml.endElement(); xml.endElement(); } } else { appendTag(xml, "ows:ProviderName", baseUrl, null); xml.indentElement("ows:ProviderSite").attribute("xlink:href", baseUrl).endElement(); xml.indentElement("ows:ServiceContact"); appendTag(xml, "ows:IndividualName", "GeoWebCache User", null); xml.endElement(); } xml.endElement("ows:ServiceProvider"); } private void operationsMetadata(XMLBuilder xml) throws IOException { xml.indentElement("ows:OperationsMetadata"); operation(xml, "GetCapabilities", baseUrl); operation(xml, "GetTile", baseUrl); operation(xml, "GetFeatureInfo", baseUrl); xml.endElement("ows:OperationsMetadata"); } private void operation(XMLBuilder xml, String operationName, String baseUrl) throws IOException { xml.indentElement("ows:Operation").attribute("name", operationName); xml.indentElement("ows:DCP"); xml.indentElement("ows:HTTP"); xml.indentElement("ows:Get").attribute("xlink:href", baseUrl + "?"); xml.indentElement("ows:Constraint").attribute("name", "GetEncoding"); xml.indentElement("ows:AllowedValues"); xml.simpleElement("ows:Value", "KVP", true); xml.endElement(); xml.endElement(); xml.endElement(); xml.endElement(); xml.endElement(); xml.endElement("ows:Operation"); } private void contents(XMLBuilder xml) throws IOException { xml.indentElement("Contents"); Iterable<TileLayer> iter = tld.getLayerList(); for (TileLayer layer : iter) { if (!layer.isEnabled() || !layer.isAdvertised()) { continue; } layer(xml, layer, baseUrl); } for (GridSet gset : gsb.getGridSets()) { tileMatrixSet(xml, gset); } xml.endElement("Contents"); } private void layer(XMLBuilder xml, TileLayer layer, String baseurl) throws IOException { xml.indentElement("Layer"); LayerMetaInformation layerMeta = layer.getMetaInformation(); if (layerMeta == null) { appendTag(xml, "ows:Title", layer.getName(), null); } else { appendTag(xml, "ows:Title", layerMeta.getTitle(), null); appendTag(xml, "ows:Abstract", layerMeta.getDescription(), null); } layerWGS84BoundingBox(xml, layer); appendTag(xml, "ows:Identifier", layer.getName(), null); // We need the filters for styles and dimensions List<ParameterFilter> filters = layer.getParameterFilters(); layerStyles(xml, layer, filters); layerFormats(xml, layer); layerInfoFormats(xml, layer); if (filters != null) { layerDimensions(xml, layer, filters); } layerGridSubSets(xml, layer); // TODO REST // str.append(" <ResourceURL format=\"image/png\" resourceType=\"tile\" template=\"http://www.maps.cat/wmts/BlueMarbleNextGeneration/default/BigWorldPixel/{TileMatrix}/{TileRow}/{TileCol}.png\"/>\n"); xml.endElement("Layer"); } private void layerWGS84BoundingBox(XMLBuilder xml, TileLayer layer) throws IOException { GridSubset subset = layer.getGridSubsetForSRS(SRS.getEPSG4326()); if (subset != null) { double[] coords = subset.getOriginalExtent().getCoords(); xml.indentElement("ows:WGS84BoundingBox"); xml.simpleElement("ows:LowerCorner", coords[0] + " " + coords[1], true); xml.simpleElement("ows:UpperCorner", coords[2] + " " + coords[3], true); xml.endElement("ows:WGS84BoundingBox"); return; } subset = layer.getGridSubsetForSRS(SRS.getEPSG900913()); if (subset != null) { double[] coords = subset.getOriginalExtent().getCoords(); double originShift = 2 * Math.PI * 6378137 / 2.0; double mx = coords[0]; double my = coords[1]; double lon = (mx / originShift) * 180.0; double lat = (my / originShift) * 180.0; lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0); xml.indentElement("ows:WGS84BoundingBox"); xml.simpleElement("ows:LowerCorner", lon + " " + lat, true); mx = coords[2]; my = coords[3]; lon = (mx / originShift) * 180.0; lat = (my / originShift) * 180.0; lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0); xml.simpleElement("ows:UpperCorner", lon + " " + lat, true); xml.endElement("ows:WGS84BoundingBox"); return; } } private void layerStyles(XMLBuilder xml, TileLayer layer, List<ParameterFilter> filters) throws IOException { String defStyle = layer.getStyles(); if (filters == null) { xml.indentElement("Style"); xml.attribute("isDefault", "true"); if (defStyle == null) { xml.simpleElement("ows:Identifier", "", true); } else { xml.simpleElement("ows:Identifier", "TileLayer.encodeDimensionValue(defStyle)", true); } xml.endElement("Style"); } else { ParameterFilter stylesFilter = null; Iterator<ParameterFilter> iter = filters.iterator(); while (stylesFilter == null && iter.hasNext()) { ParameterFilter filter = iter.next(); if (filter.getKey().equalsIgnoreCase("STYLES")) { stylesFilter = filter; } } List<String> legalStyles = null; if (stylesFilter != null) legalStyles = stylesFilter.getLegalValues(); if (legalStyles != null && !legalStyles.isEmpty()) { // There's a style filter listing at least one value String defVal = stylesFilter.getDefaultValue(); if (defVal == null) { if (defStyle != null) { defVal = defStyle; } else { defVal = ""; } } for (String value : legalStyles) { xml.indentElement("Style"); if (value.equals(defVal)) { xml.attribute("isDefault", "true"); } xml.simpleElement("ows:Identifier", TileLayer.encodeDimensionValue(value), true); xml.endElement(); } } else { // Couldn't get a list of styles so just say there's a default. xml.indentElement("Style"); xml.attribute("isDefault", "true"); xml.simpleElement("ows:Identifier", "", true); xml.endElement(); } } } private void layerFormats(XMLBuilder xml, TileLayer layer) throws IOException { Iterator<MimeType> mimeIter = layer.getMimeTypes().iterator(); while (mimeIter.hasNext()) { xml.simpleElement("Format", mimeIter.next().getFormat(), true); } } private void layerInfoFormats(XMLBuilder xml, TileLayer layer) throws IOException { if (layer.isQueryable()) { Iterator<MimeType> mimeIter = layer.getInfoMimeTypes().iterator(); while (mimeIter.hasNext()) { xml.simpleElement("InfoFormat", mimeIter.next().getFormat(), true); } } } private void layerDimensions(XMLBuilder xml, TileLayer layer, List<ParameterFilter> filters) throws IOException { Iterator<ParameterFilter> iter = filters.iterator(); while (iter.hasNext()) { ParameterFilter filter = iter.next(); if (!filter.getKey().equalsIgnoreCase("STYLES")) { List<String> values = filter.getLegalValues(); if (values != null) { dimensionDescription(xml, filter, values); } } } } private void dimensionDescription(XMLBuilder xml, ParameterFilter filter, List<String> values) throws IOException { xml.startElement("Dimension"); xml.simpleElement("Identifier", filter.getKey(), false); String defaultStr = TileLayer.encodeDimensionValue(filter.getDefaultValue()); xml.simpleElement("Default", defaultStr, false); Iterator<String> iter = values.iterator(); while (iter.hasNext()) { String value = TileLayer.encodeDimensionValue(iter.next()); xml.simpleElement("Value", value, false); } xml.endElement("Dimension"); } private void layerGridSubSets(XMLBuilder xml, TileLayer layer) throws IOException { for (String gridSetId : layer.getGridSubsets()) { GridSubset gridSubset = layer.getGridSubset(gridSetId); xml.indentElement("TileMatrixSetLink"); xml.simpleElement("TileMatrixSet", gridSubset.getName(), true); if (!gridSubset.fullGridSetCoverage()) { String[] levelNames = gridSubset.getGridNames(); long[][] wmtsLimits = gridSubset.getWMTSCoverages(); xml.indentElement("TileMatrixSetLimits"); for (int i = 0; i < levelNames.length; i++) { xml.indentElement("TileMatrixLimits"); xml.simpleElement("TileMatrix", levelNames[i], true); xml.simpleElement("MinTileRow", Long.toString(wmtsLimits[i][1]), true); xml.simpleElement("MaxTileRow", Long.toString(wmtsLimits[i][3]), true); xml.simpleElement("MinTileCol", Long.toString(wmtsLimits[i][0]), true); xml.simpleElement("MaxTileCol", Long.toString(wmtsLimits[i][2]), true); xml.endElement(); } xml.endElement(); } xml.endElement("TileMatrixSetLink"); } } private void tileMatrixSet(XMLBuilder xml, GridSet gridSet) throws IOException { xml.indentElement("TileMatrixSet"); xml.simpleElement("ows:Identifier", gridSet.getName(), true); // If the following is not good enough, please get in touch and we will try to fix it :) xml.simpleElement("ows:SupportedCRS", "urn:ogc:def:crs:EPSG::" + gridSet.getSrs().getNumber(), true); // TODO detect these str.append(" <WellKnownScaleSet>urn:ogc:def:wkss:GlobalCRS84Pixel</WellKnownScaleSet>\n"); Grid[] grids = gridSet.getGridLevels(); for (int i = 0; i < grids.length; i++) { double[] tlCoordinates = gridSet.getOrderedTopLeftCorner(i); tileMatrix(xml, grids[i], tlCoordinates, gridSet.getTileWidth(), gridSet.getTileHeight(), gridSet.isScaleWarning()); } xml.endElement("TileMatrixSet"); } private void tileMatrix(XMLBuilder xml, Grid grid, double[] tlCoordinates, int tileWidth, int tileHeight, boolean scaleWarning) throws IOException { xml.indentElement("TileMatrix"); if (scaleWarning) { xml.simpleElement("ows:Abstract", "The grid was not well-defined, the scale therefore assumes 1m per map unit.", true); } xml.simpleElement("ows:Identifier", grid.getName(), true); xml.simpleElement("ScaleDenominator", Double.toString(grid.getScaleDenominator()), true); xml.indentElement("TopLeftCorner").text(Double.toString(tlCoordinates[0])).text(" ") .text(Double.toString(tlCoordinates[1])).endElement(); xml.simpleElement("TileWidth", Integer.toString(tileWidth), true); xml.simpleElement("TileHeight", Integer.toString(tileHeight), true); xml.simpleElement("MatrixWidth", Long.toString(grid.getNumTilesWide()), true); xml.simpleElement("MatrixHeight", Long.toString(grid.getNumTilesHigh()), true); xml.endElement("TileMatrix"); } private void appendTag(XMLBuilder xml, String tagName, String value, String defaultValue) throws IOException { if (value == null) { if (defaultValue == null) return; else value = defaultValue; } xml.simpleElement(tagName, value, true); } }