org.geowebcache.service.wmts.WMTSGetCapabilities.java Source code

Java tutorial

Introduction

Here is the source code for org.geowebcache.service.wmts.WMTSGetCapabilities.java

Source

/**
 * 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);
    }
}