Skip to content

Commit

Permalink
[GEOS-8611] Allow DescribeDomain to return a selection of dimensions (#…
Browse files Browse the repository at this point in the history
…2779)

[GEOS-8611] Allow DescribeDomain to return a selection of dimensions
  • Loading branch information
aaime committed Mar 1, 2018
1 parent fdf0925 commit c44ed73
Show file tree
Hide file tree
Showing 16 changed files with 359 additions and 225 deletions.
24 changes: 22 additions & 2 deletions doc/en/user/source/community/wmts-multidimensional/index.rst
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -154,14 +154,17 @@ This operation is useful to understand which domains are available in our layer
* - TileMatrixSet * - TileMatrixSet
- Yes - Yes
- Tile matrix set identifier - Tile matrix set identifier
* - BBOX=minx,miny,maxx,maxy * - bbox=minx,miny,maxx,maxy
- No - No
- Bounding box corners (lower left, upper right) in CRS units - Bounding box corners (lower left, upper right) in CRS units
* - DimensionIdentifier * - DimensionIdentifier
- No - No
- At most one per dimension, a range described as min/max, restricting the domain of this dimension - At most one per dimension, a range described as min/max, restricting the domain of this dimension
* - Domains
- No
- A comma separated list of domain names to be returned, in case only a subset is required. The space domain is identified by "bbox".


The ``BBOX`` parameter allows the client to restrict the ``DescribeDomains`` operation to a certain spatial area, by default the layer extent will be used. The ``bbox`` parameter allows the client to restrict the ``DescribeDomains`` operation to a certain spatial area, by default the layer extent will be used.


The ``DimensionIdentifier`` parameter can be used to restrict the domain values of a certain dimension, this is useful to answer questions like which elevations values are available in a specific day. The ``DimensionIdentifier`` parameter can be used to restrict the domain values of a certain dimension, this is useful to answer questions like which elevations values are available in a specific day.


Expand Down Expand Up @@ -233,6 +236,23 @@ the result will be similar to this:
So for time 2016-02-23T03:00:00.000Z there is only values measured at 200.0 meters. So for time 2016-02-23T03:00:00.000Z there is only values measured at 200.0 meters.


In case only the space domain is of interest, the following request will do:

.. code-block:: guess
http://localhost:8080/geoserver/gwc/service/wmts?REQUEST=DescribeDomains&Version=1.0.0&Layer=some_layer&TileMatrixSet=EPSG:4326&elevation=0/500&time=2016-02-23T03:00:00.000Z&domains=bbox
and the result will be similar to this:

.. code-block:: xml
<Domains xmlns="http://demo.geo-solutions.it/share/wmts-multidim/wmts_multi_dimensional.xsd" xmlns:ows="http://www.opengis.net/ows/1.1">
<SpaceDomain>
<BoundingBox CRS="EPSG:4326"
maxx="179.875" maxy="89.9375" minx="-180.125" miny="-90.125"/>
</SpaceDomain>
</Domains>
GetHistogram GetHistogram
^^^^^^^^^^^^ ^^^^^^^^^^^^


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ public void encode(Object object) throws IllegalArgumentException {
}); });
start("Domains", nameSpaces); start("Domains", nameSpaces);
Map<String, Tuple<Integer, List<String>>> domainsValues = new HashMap<>(); Map<String, Tuple<Integer, List<String>>> domainsValues = new HashMap<>();
ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope();
domains.getDimensions().forEach(dimension -> { domains.getDimensions().forEach(dimension -> {
Tuple<ReferencedEnvelope, Tuple<Integer, List<String>>> dimensionValues = dimension.getDomainValuesAsStrings(domains.getFilter()); Tuple<Integer, List<String>> dimensionValues = dimension.getDomainValuesAsStrings(domains.getFilter());
referencedEnvelope.expandToInclude(dimensionValues.first); domainsValues.put(dimension.getDimensionName(), dimensionValues);
domainsValues.put(dimension.getDimensionName(), dimensionValues.second);
}); });
handleBoundingBox(referencedEnvelope); if (domains.getSpatialDomain() != null && !domains.getSpatialDomain().isEmpty()) {
handleBoundingBox(domains.getSpatialDomain());
}
domainsValues.entrySet().forEach(dimensionValues -> handleDimension(dimensionValues.getKey(), dimensionValues.getValue())); domainsValues.entrySet().forEach(dimensionValues -> handleDimension(dimensionValues.getKey(), dimensionValues.getValue()));
end("Domains"); end("Domains");
} }
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
public class Domains { public class Domains {


private final List<Dimension> dimensions; private final List<Dimension> dimensions;
private final ReferencedEnvelope boundingBox; private final ReferencedEnvelope spatialDomain;
private final Filter filter; private final Filter filter;


private final LayerInfo layerInfo; private final LayerInfo layerInfo;
Expand All @@ -38,16 +38,16 @@ public class Domains {
public Domains(List<Dimension> dimensions, LayerInfo layerInfo, ReferencedEnvelope boundingBox, Filter filter) { public Domains(List<Dimension> dimensions, LayerInfo layerInfo, ReferencedEnvelope boundingBox, Filter filter) {
this.dimensions = dimensions; this.dimensions = dimensions;
this.layerInfo = layerInfo; this.layerInfo = layerInfo;
this.boundingBox = boundingBox; this.spatialDomain = boundingBox;
this.filter = filter; this.filter = filter;
} }


public List<Dimension> getDimensions() { public List<Dimension> getDimensions() {
return dimensions; return dimensions;
} }


ReferencedEnvelope getBoundingBox() { ReferencedEnvelope getSpatialDomain() {
return boundingBox; return spatialDomain;
} }


public Filter getFilter() { public Filter getFilter() {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
package org.geoserver.gwc.wmts; package org.geoserver.gwc.wmts;


import org.geoserver.catalog.Catalog; import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.DimensionInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.PublishedInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.gwc.layer.CatalogConfiguration; import org.geoserver.gwc.layer.CatalogConfiguration;
import org.geoserver.gwc.layer.GeoServerTileLayer; import org.geoserver.gwc.layer.GeoServerTileLayer;
Expand All @@ -17,16 +21,16 @@
import org.geoserver.ows.util.KvpUtils; import org.geoserver.ows.util.KvpUtils;
import org.geoserver.platform.ServiceException; import org.geoserver.platform.ServiceException;
import org.geoserver.wms.WMS; import org.geoserver.wms.WMS;
import org.geoserver.wms.dimension.DimensionFilterBuilder;
import org.geotools.factory.CommonFactoryFinder; import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.visitor.SimplifyingFilterVisitor; import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.gml2.bindings.GML2EncodingUtils;
import org.geotools.referencing.CRS; import org.geotools.referencing.CRS;
import org.geotools.resources.CRSUtilities;
import org.geotools.util.logging.Logging; import org.geotools.util.logging.Logging;
import org.geowebcache.GeoWebCacheException; import org.geowebcache.GeoWebCacheException;
import org.geowebcache.conveyor.Conveyor; import org.geowebcache.conveyor.Conveyor;
import org.geowebcache.grid.GridSubset; import org.geowebcache.grid.GridSubset;
import org.geowebcache.grid.SRS;
import org.geowebcache.io.XMLBuilder; import org.geowebcache.io.XMLBuilder;
import org.geowebcache.layer.TileLayer; import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher; import org.geowebcache.layer.TileLayerDispatcher;
Expand All @@ -35,12 +39,18 @@
import org.geowebcache.storage.StorageBroker; import org.geowebcache.storage.StorageBroker;
import org.opengis.filter.Filter; import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory; import org.opengis.filter.FilterFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;


import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;


Expand All @@ -51,6 +61,8 @@
public final class MultiDimensionalExtension extends WMTSExtensionImpl { public final class MultiDimensionalExtension extends WMTSExtensionImpl {


private final static Logger LOGGER = Logging.getLogger(MultiDimensionalExtension.class); private final static Logger LOGGER = Logging.getLogger(MultiDimensionalExtension.class);
public static final String SPACE_DIMENSION = "bbox";
public static final Set<String> ALL_DOMAINS = Collections.emptySet();


private final FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(); private final FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory();


Expand Down Expand Up @@ -101,26 +113,23 @@ public boolean handleRequest(Conveyor candidateConveyor) throws OWSException {
executeDescribeDomainsOperation(conveyor); executeDescribeDomainsOperation(conveyor);
} catch (Exception exception) { } catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Error executing describe domains operation.", exception); LOGGER.log(Level.SEVERE, "Error executing describe domains operation.", exception);
throw new OWSException(500, "NoApplicableCode", "", return rethrowException(exception, "Error executing describe domains operation:");
"Error executing describe domains operation:" + exception.getMessage());
} }
break; break;
case GET_HISTOGRAM: case GET_HISTOGRAM:
try { try {
executeGetHistogramOperation(conveyor); executeGetHistogramOperation(conveyor);
} catch (Exception exception) { } catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Error executing get histogram operation.", exception); LOGGER.log(Level.SEVERE, "Error executing get histogram operation.", exception);
throw new OWSException(500, "NoApplicableCode", "", rethrowException(exception, "Error executing get histogram operation:");
"Error executing get histogram operation:" + exception.getMessage());
} }
break; break;
case GET_FEATURE: case GET_FEATURE:
try { try {
executeGetFeatureOperation(conveyor); executeGetFeatureOperation(conveyor);
} catch (Exception exception) { } catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Error executing get feature operation.", exception); LOGGER.log(Level.SEVERE, "Error executing get feature operation.", exception);
throw new OWSException(500, "NoApplicableCode", "", rethrowException(exception, "Error executing get feature operation:");
"Error executing get feature operation:" + exception.getMessage());
} }
break; break;
default: default:
Expand All @@ -129,24 +138,37 @@ public boolean handleRequest(Conveyor candidateConveyor) throws OWSException {
return true; return true;
} }


public boolean rethrowException(Exception exception, String s) throws OWSException {
if (exception instanceof OWSException) {
throw (OWSException) exception;
}
throw new OWSException(500, "NoApplicableCode", "", s + exception.getMessage());
}

@Override @Override
public void encodeLayer(XMLBuilder xmlBuilder, TileLayer tileLayer) throws IOException { public void encodeLayer(XMLBuilder xmlBuilder, TileLayer tileLayer) throws IOException {
LayerInfo layerInfo = getLayerInfo(tileLayer, tileLayer.getName()); LayerInfo layerInfo = getLayerInfo(tileLayer, tileLayer.getName());
if (layerInfo == null) { if (layerInfo == null) {
// dimension are not supported for this layer (maybe is a layer group) // dimension are not supported for this layer (maybe is a layer group)
return; return;
} }
List<Dimension> dimensions = DimensionsUtils.extractDimensions(wms, layerInfo); try {
encodeLayerDimensions(xmlBuilder, dimensions); List<Dimension> dimensions = DimensionsUtils.extractDimensions(wms, layerInfo, ALL_DOMAINS);
encodeLayerDimensions(xmlBuilder, dimensions);
} catch (OWSException e) {
// should not happen
throw new RuntimeException(e);
}
} }


private Domains getDomains(SimpleConveyor conveyor) throws Exception { private Domains getDomains(SimpleConveyor conveyor) throws Exception {
// getting and parsing the mandatory parameters // getting and parsing the mandatory parameters
String layerName = (String) conveyor.getParameter("layer", true); String layerName = (String) conveyor.getParameter("layer", true);
TileLayer tileLayer = tileLayerDispatcher.getTileLayer(layerName); TileLayer tileLayer = tileLayerDispatcher.getTileLayer(layerName);
LayerInfo layerInfo = getLayerInfo(tileLayer, layerName); LayerInfo layerInfo = getLayerInfo(tileLayer, layerName);
Set<String> requestedDomains = getRequestedDomains(conveyor.getParameter("domains", false));
// getting this layer dimensions along with its values // getting this layer dimensions along with its values
List<Dimension> dimensions = DimensionsUtils.extractDimensions(wms, layerInfo); List<Dimension> dimensions = DimensionsUtils.extractDimensions(wms, layerInfo, requestedDomains);
// let's see if we have a spatial limitation // let's see if we have a spatial limitation
ReferencedEnvelope boundingBox = (ReferencedEnvelope) conveyor.getParameter("bbox", false); ReferencedEnvelope boundingBox = (ReferencedEnvelope) conveyor.getParameter("bbox", false);
// if we have a bounding box we need to set the crs based on the tile matrix set // if we have a bounding box we need to set the crs based on the tile matrix set
Expand All @@ -162,15 +184,42 @@ private Domains getDomains(SimpleConveyor conveyor) throws Exception {
boundingBox = new ReferencedEnvelope(boundingBox, CRS.decode(gridSubset.getSRS().toString())); boundingBox = new ReferencedEnvelope(boundingBox, CRS.decode(gridSubset.getSRS().toString()));
} }
// add any domain provided restriction and set the bounding box // add any domain provided restriction and set the bounding box
Filter filter = Filter.INCLUDE; ResourceInfo resource = layerInfo.getResource();
Filter filter = DimensionsUtils.getBoundingBoxFilter(resource, boundingBox, filterFactory);
for (Dimension dimension : dimensions) { for (Dimension dimension : dimensions) {
Object restriction = conveyor.getParameter(dimension.getDimensionName(), false); Object restriction = conveyor.getParameter(dimension.getDimensionName(), false);
dimension.setBoundingBox(boundingBox); if (restriction != null) {
dimension.addDomainRestriction(restriction); Tuple<String, String> attributes = DimensionsUtils.getAttributes(resource, dimension);
filter = filterFactory.and(filter, dimension.getFilter()); filter = appendDomainRestrictionsFilter(filter, attributes.first, attributes.second, restriction);
}
} }
// compute the bounding box
ReferencedEnvelope spatialDomain = null;
if (requestedDomains == ALL_DOMAINS || requestedDomains.contains(SPACE_DIMENSION)) {
spatialDomain = DimensionsUtils.getBounds(resource, filter);
}
// encode the domains // encode the domains
return new Domains(dimensions, layerInfo, boundingBox, SimplifyingFilterVisitor.simplify(filter)); return new Domains(dimensions, layerInfo, spatialDomain, SimplifyingFilterVisitor.simplify(filter));
}

/**
* Helper method that will build a dimension domain values filter based on this dimension start and end
* attributes. The created filter will be merged with the provided filter.
*/
protected Filter appendDomainRestrictionsFilter(Filter filter, String startAttribute, String endAttribute, Object domainRestrictions) {
DimensionFilterBuilder dimensionFilterBuilder = new DimensionFilterBuilder(filterFactory);
List<Object> restrictionList = domainRestrictions instanceof Collection ? new ArrayList<>((Collection) domainRestrictions) : Arrays.asList(domainRestrictions);
dimensionFilterBuilder.appendFilters(startAttribute, endAttribute, restrictionList);
return filterFactory.and(filter, dimensionFilterBuilder.getFilter());
}

private Set<String> getRequestedDomains(Object domains) {
if (domains == null) {
return ALL_DOMAINS;
}

String[] domainNames = domains.toString().trim().split("\\s*,\\s*");
return new LinkedHashSet<>(Arrays.asList(domainNames));
} }


private void executeDescribeDomainsOperation(SimpleConveyor conveyor) throws Exception { private void executeDescribeDomainsOperation(SimpleConveyor conveyor) throws Exception {
Expand Down Expand Up @@ -242,7 +291,7 @@ private void encodeLayerDimension(XMLBuilder xml, Dimension dimension) throws IO
xml.simpleElement("ows:Identifier", dimension.getDimensionName(), true); xml.simpleElement("ows:Identifier", dimension.getDimensionName(), true);
// default value is mandatory // default value is mandatory
xml.simpleElement("Default", dimension.getDefaultValueAsString(), true); xml.simpleElement("Default", dimension.getDefaultValueAsString(), true);
for (String value : dimension.getDomainValuesAsStrings(Filter.INCLUDE).second.second) { for (String value : dimension.getDomainValuesAsStrings(Filter.INCLUDE).second) {
xml.simpleElement("Value", value, true); xml.simpleElement("Value", value, true);
} }
xml.endElement("Dimension"); xml.endElement("Dimension");
Expand Down

0 comments on commit c44ed73

Please sign in to comment.