/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.gwc.wms;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.geoserver.catalog.PublishedInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.gwc.GWC;
import org.geoserver.ows.LocalWorkspace;
import org.geoserver.wms.ExtendedCapabilitiesProvider;
import org.geoserver.wms.GetCapabilitiesRequest;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSInfo;
import org.geotools.util.NumberRange;
import org.geotools.util.Version;
import org.geowebcache.grid.BoundingBox;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.mime.MimeType;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.NamespaceSupport;
/**
* Implementation of the {@link ExtendedCapabilitiesProvider} extension point to contribute WMS-C
* DTD elements and TileSet definitions to the capabilities document of the regular GeoServer WMS.
*
* <p>
* A {@code TileSet} is added at {@link #encode} for each GWC {@link TileLayer}, but respecting the
* {@link GetCapabilitiesRequest#getNamespace() namespace} filter if set.
* </p>
*
* @author Gabriel Roldan
*
*/
public class CachingExtendedCapabilitiesProvider implements ExtendedCapabilitiesProvider {
private final GWC gwc;
public CachingExtendedCapabilitiesProvider(final GWC gwc) {
this.gwc = gwc;
}
/**
* @see org.geoserver.wms.ExtendedCapabilitiesProvider#getSchemaLocations()
*/
public String[] getSchemaLocations(String schemaBaseURL) {
return new String[0];
}
/**
* @return {@code TileSet*}
* @see org.geoserver.wms.ExtendedCapabilitiesProvider#getVendorSpecificCapabilitiesRoots
*/
public List<String> getVendorSpecificCapabilitiesRoots(final GetCapabilitiesRequest request) {
if (gwc.getConfig().isDirectWMSIntegrationEnabled() && isTiled(request)) {
return Collections.singletonList("TileSet*");
}
return Collections.emptyList();
}
private boolean isTiled(GetCapabilitiesRequest request) {
return Boolean.valueOf(request.getRawKvp().get("TILED")).booleanValue();
}
/**
* @see org.geoserver.wms.ExtendedCapabilitiesProvider#getVendorSpecificCapabilitiesChildDecls()
*/
public List<String> getVendorSpecificCapabilitiesChildDecls(final GetCapabilitiesRequest request) {
if (gwc.getConfig().isDirectWMSIntegrationEnabled() && isTiled(request)) {
List<String> wmscElements = new ArrayList<String>();
wmscElements
.add("<!ELEMENT TileSet (SRS, BoundingBox?, Resolutions, Width, Height, Format, Layers*, Styles*) >");
wmscElements.add("<!ELEMENT Resolutions (#PCDATA) >");
wmscElements.add("<!ELEMENT Width (#PCDATA) >");
wmscElements.add("<!ELEMENT Height (#PCDATA) >");
wmscElements.add("<!ELEMENT Layers (#PCDATA) >");
wmscElements.add("<!ELEMENT Styles (#PCDATA) >");
return wmscElements;
}
return Collections.emptyList();
}
/**
* Empty implementation, no namespaces to add until we support the WMS-C 1.3 profile
*
* @see org.geoserver.wms.ExtendedCapabilitiesProvider#registerNamespaces(org.xml.sax.helpers.NamespaceSupport)
*/
public void registerNamespaces(NamespaceSupport namespaces) {
// nothing to do
}
/**
* @see org.geoserver.wms.ExtendedCapabilitiesProvider#encode(org.geoserver.wms.ExtendedCapabilitiesProvider.Translator,
* org.geoserver.wms.WMSInfo, org.geotools.util.Version)
*/
public void encode(final Translator tx, final WMSInfo wms, final GetCapabilitiesRequest request)
throws IOException {
if (!gwc.getConfig().isDirectWMSIntegrationEnabled()) {
return;
}
Version version = WMS.version(request.getVersion(), true);
if (!WMS.VERSION_1_1_1.equals(version) || !isTiled(request)) {
return;
}
final String namespacePrefixFilter = request.getNamespace();
Iterable<? extends TileLayer> tileLayers;
tileLayers = gwc.getTileLayersByNamespacePrefix(namespacePrefixFilter);
final String nsPrefix;
{
final WorkspaceInfo localWorkspace = LocalWorkspace.get();
if (localWorkspace == null) {
nsPrefix = null;
} else {
nsPrefix = localWorkspace.getName() + ":";
}
}
for (TileLayer layer : tileLayers) {
String advertisedName = layer.getName();
if (nsPrefix != null) {
if (!advertisedName.startsWith(nsPrefix)) {
continue;
}
advertisedName = advertisedName.substring(nsPrefix.length());
}
for (String gridSetId : layer.getGridSubsets()) {
GridSubset grid = layer.getGridSubset(gridSetId);
for (MimeType mime : layer.getMimeTypes()) {
vendorSpecificTileset(tx, layer, advertisedName, grid, mime.getFormat());
}
}
}
}
private void vendorSpecificTileset(final Translator tx, final TileLayer layer,
final String advertisedLayerName, final GridSubset grid, final String format) {
String srsStr = grid.getSRS().toString();
StringBuilder resolutionsStr = new StringBuilder();
double[] res = grid.getResolutions();
for (int i = 0; i < res.length; i++) {
resolutionsStr.append(Double.toString(res[i]) + " ");
}
String[] bs = boundsPrep(grid.getCoverageBestFitBounds());
tx.start("TileSet");
tx.start("SRS");
tx.chars(srsStr);
tx.end("SRS");
AttributesImpl atts;
atts = new AttributesImpl();
atts.addAttribute("", "SRS", "SRS", "", srsStr);
atts.addAttribute("", "minx", "minx", "", bs[0]);
atts.addAttribute("", "miny", "miny", "", bs[1]);
atts.addAttribute("", "maxx", "maxx", "", bs[2]);
atts.addAttribute("", "maxy", "maxy", "", bs[3]);
tx.start("BoundingBox", atts);
tx.end("BoundingBox");
tx.start("Resolutions");
tx.chars(resolutionsStr.toString());
tx.end("Resolutions");
tx.start("Width");
tx.chars(String.valueOf(grid.getTileWidth()));
tx.end("Width");
tx.start("Height");
tx.chars(String.valueOf(grid.getTileHeight()));
tx.end("Height");
tx.start("Format");
tx.chars(format);
tx.end("Format");
tx.start("Layers");
tx.chars(advertisedLayerName);
tx.end("Layers");
// TODO ignoring styles for now
tx.start("Styles");
tx.end("Styles");
tx.end("TileSet");
}
String[] boundsPrep(BoundingBox bbox) {
String[] bs = { Double.toString(bbox.getMinX()), Double.toString(bbox.getMinY()),
Double.toString(bbox.getMaxX()), Double.toString(bbox.getMaxY()) };
return bs;
}
@Override
public void customizeRootCrsList(Set<String> srs) {
// nothing to do
}
@Override
public NumberRange<Double> overrideScaleDenominators(PublishedInfo layer,
NumberRange<Double> scaleDenominators) {
return scaleDenominators;
}
}