Skip to content

Commit

Permalink
[GEOS-9154] Env variables not always expanded when classifying layer
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Mar 18, 2019
1 parent cabddd9 commit d83239f
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 18 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ public Object classify(
throw new RestException(e.getMessage(), HttpStatus.BAD_REQUEST, e); throw new RestException(e.getMessage(), HttpStatus.BAD_REQUEST, e);
} }


if (rules == null || rules.isEmpty()) {
throw new RestException(
"Could not generate any rule, there is likely no data matching the request (layer is empty, of filtered down to no matching features/pixels)",
HttpStatus.NOT_FOUND);
}

if (fullSLD) { if (fullSLD) {
StyledLayerDescriptor sld = SF.createStyledLayerDescriptor(); StyledLayerDescriptor sld = SF.createStyledLayerDescriptor();
NamedLayer namedLayer = SF.createNamedLayer(); NamedLayer namedLayer = SF.createNamedLayer();
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@
import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GranuleSource;
import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.coverage.processing.CoverageProcessor; import org.geotools.coverage.processing.CoverageProcessor;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.geometry.Envelope2D; import org.geotools.geometry.Envelope2D;
import org.geotools.geometry.GeneralEnvelope; import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.JTS;
Expand All @@ -30,8 +36,11 @@
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygon;
import org.opengis.coverage.grid.GridEnvelope; import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.filter.Filter;
import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup; import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException; import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.CoordinateReferenceSystem;
Expand Down Expand Up @@ -89,12 +98,11 @@ public ImageReader invoke() throws IOException, TransformException, FactoryExcep
AbstractGridFormat.USE_JAI_IMAGEREAD.getName().getCode()); AbstractGridFormat.USE_JAI_IMAGEREAD.getName().getCode());


// grab the original grid geometry // grab the original grid geometry
GridEnvelope originalGridrange = reader.getOriginalGridRange(); Filter readFilter = getReadFilter(readParameters);
GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); GridGeometry originalGeometry = getOriginalGridGeometry(reader, readFilter);
MathTransform g2w = reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER); Envelope2D originalEnvelope = ((GridGeometry2D) originalGeometry).getEnvelope2D();
CoordinateReferenceSystem crs = originalEnvelope.getCoordinateReferenceSystem(); CoordinateReferenceSystem crs = originalEnvelope.getCoordinateReferenceSystem();
GridGeometry2D originalGeometry = MathTransform g2w = reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER);
new GridGeometry2D(originalGridrange, PixelInCell.CELL_CORNER, g2w, crs, null);
GridGeometry2D readGeometry = new GridGeometry2D(originalGeometry); GridGeometry2D readGeometry = new GridGeometry2D(originalGeometry);


// do we need to restrict to a specific bounding box? // do we need to restrict to a specific bounding box?
Expand Down Expand Up @@ -134,6 +142,17 @@ public ImageReader invoke() throws IOException, TransformException, FactoryExcep
readGeometry = new GridGeometry2D(reducedRange, readGeometry.getEnvelope()); readGeometry = new GridGeometry2D(reducedRange, readGeometry.getEnvelope());
} }


// if there is a filter, set it back as it might have been simplified (e.g., env var
// expansion)
if (readFilter != null) {
readParameters =
CoverageUtils.mergeParameter(
parameterDescriptors,
readParameters,
readFilter,
ImageMosaicFormat.FILTER.getName().getCode());
}

if (!readGeometry.equals(originalGeometry)) { if (!readGeometry.equals(originalGeometry)) {
readParameters = readParameters =
CoverageUtils.mergeParameter( CoverageUtils.mergeParameter(
Expand Down Expand Up @@ -169,6 +188,11 @@ public ImageReader invoke() throws IOException, TransformException, FactoryExcep


// finally perform the read // finally perform the read
coverage = reader.read(readParameters); coverage = reader.read(readParameters);
if (coverage == null) {
throw new RestException(
"Could not generate any rule, there is likely no data matching the request (layer is empty, of filtered down to no matching features/pixels)",
HttpStatus.NOT_FOUND);
}


// the reader can do a best-effort on grid geometry, might not have cut it // the reader can do a best-effort on grid geometry, might not have cut it
if (readEnvelope != null && isUncut(coverage, readGeometry)) { if (readEnvelope != null && isUncut(coverage, readGeometry)) {
Expand Down Expand Up @@ -202,6 +226,46 @@ public ImageReader invoke() throws IOException, TransformException, FactoryExcep
return this; return this;
} }


private GridGeometry2D getOriginalGridGeometry(GridCoverage2DReader reader, Filter readFilter)
throws IOException {
MathTransform g2w = reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER);
CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem();

if (readFilter != null && reader instanceof StructuredGridCoverage2DReader) {
StructuredGridCoverage2DReader sr = (StructuredGridCoverage2DReader) reader;
String coverageName = reader.getGridCoverageNames()[0];
GranuleSource granules = sr.getGranules(coverageName, true);
SimpleFeatureCollection filteredGranules =
granules.getGranules(new Query(null, readFilter));
ReferencedEnvelope bounds = filteredGranules.getBounds();
if (bounds == null || bounds.isEmpty()) {
throw new RestException(
"Could not generate any rule, there is likely no data matching the request (layer is empty, of filtered down to no matching features/pixels)",
HttpStatus.NOT_FOUND);
}

return new GridGeometry2D(PixelInCell.CELL_CORNER, g2w, bounds, null);
} else {
GridEnvelope originalGridRange = reader.getOriginalGridRange();
return new GridGeometry2D(originalGridRange, PixelInCell.CELL_CORNER, g2w, crs, null);
}
}

private Filter getReadFilter(GeneralParameterValue[] readParameters) {
for (GeneralParameterValue readParameter : readParameters) {
if (readParameter instanceof ParameterValue
&& ImageMosaicFormat.FILTER
.getName()
.equals(readParameter.getDescriptor().getName())) {
ParameterValue pv = (ParameterValue) readParameter;
Filter filter = (Filter) pv.getValue();
return (Filter) filter.accept(new SimplifyingFilterVisitor(), null);
}
}

return null;
}

private boolean isUncut(GridCoverage2D coverage, GridGeometry2D targetGridGeometry) { private boolean isUncut(GridCoverage2D coverage, GridGeometry2D targetGridGeometry) {
Envelope2D actual = coverage.getEnvelope2D(); Envelope2D actual = coverage.getEnvelope2D();
Envelope2D expected = targetGridGeometry.getEnvelope2D(); Envelope2D expected = targetGridGeometry.getEnvelope2D();
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.media.jai.PlanarImage; import javax.media.jai.PlanarImage;
Expand All @@ -50,10 +51,12 @@
import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.factory.CommonFactoryFinder; import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.function.EnvFunction;
import org.geotools.filter.function.FilterFunction_parseDouble; import org.geotools.filter.function.FilterFunction_parseDouble;
import org.geotools.filter.text.cql2.CQL; import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException; import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL; import org.geotools.filter.text.ecql.ECQL;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.image.util.ImageUtilities; import org.geotools.image.util.ImageUtilities;
import org.geotools.referencing.CRS; import org.geotools.referencing.CRS;
Expand Down Expand Up @@ -757,6 +760,19 @@ public void testEnvVectorGroup0() throws Exception {
assertNull(CQL.toExpression("env('group')").evaluate(null)); assertNull(CQL.toExpression("env('group')").evaluate(null));
} }


@Test
public void testEnvVectorNotThere() throws Exception {
final String restPath =
RestBaseController.ROOT_PATH
+ "/sldservice/cite:FilteredPoints/"
+ getServiceUrl()
+ ".xml?"
+ "attribute=name&method=uniqueInterval&fullSLD=true&env=group:NotAGroup";
MockHttpServletResponse response = getAsServletResponse(restPath);
assertEquals(404, response.getStatus());
assertNull(CQL.toExpression("env('group')").evaluate(null));
}

@Test @Test
public void testBlueRamp() throws Exception { public void testBlueRamp() throws Exception {
final String restPath = final String restPath =
Expand Down Expand Up @@ -1587,7 +1603,7 @@ public void testRasterEnv() throws Exception {
checkRasterEnv("SE", 1214, 1735); checkRasterEnv("SE", 1214, 1735);
} }


public void checkRasterEnv(String direction, double low, double high) throws Exception { private void checkRasterEnv(String direction, double low, double high) throws Exception {
final String restPath = final String restPath =
RestBaseController.ROOT_PATH RestBaseController.ROOT_PATH
+ "/sldservice/cite:sfdem_mosaic/" + "/sldservice/cite:sfdem_mosaic/"
Expand All @@ -1604,6 +1620,18 @@ public void checkRasterEnv(String direction, double low, double high) throws Exc
assertEquals(high, entries[1].getQuantity().evaluate(null, Double.class), 0.1); assertEquals(high, entries[1].getQuantity().evaluate(null, Double.class), 0.1);
} }


@Test
public void testRasterEnvNotFound() throws Exception {
final String restPath =
RestBaseController.ROOT_PATH
+ "/sldservice/cite:sfdem_mosaic/"
+ getServiceUrl()
+ ".xml?"
+ "method=equalInterval&intervals=1&ramp=jet&fullSLD=true&env=direction:IDontExist";
MockHttpServletResponse servletResponse = getAsServletResponse(restPath);
assertEquals(404, servletResponse.getStatus());
}

private Map<GeneralParameterDescriptor, Object> getParametersMap( private Map<GeneralParameterDescriptor, Object> getParametersMap(
List<GeneralParameterValue> readParameters) { List<GeneralParameterValue> readParameters) {
return readParameters return readParameters
Expand Down Expand Up @@ -1631,4 +1659,32 @@ private void assertBoundsEquals2D(Envelope env1, Envelope env2, double eps) {
} }
} }
} }

@Test
public void testImageReaderEnv() throws Exception {
// the backing reader supports native selection
CoverageInfo coverage = getCatalog().getCoverageByName(getLayerId(SFDEM_MOSAIC));
try {
EnvFunction.setLocalValue("direction", "NE");
ImageReader reader = new ImageReader(coverage, 1, DEFAULT_MAX_PIXELS, null).invoke();

List<GeneralParameterValue> readParameters = reader.getReadParameters();
Map<GeneralParameterDescriptor, Object> parameterValues =
getParametersMap(readParameters);

// check no duplicates
Set<String> parameterCodes =
readParameters
.stream()
.map(rp -> rp.getDescriptor().getName().getCode())
.collect(Collectors.toSet());
assertEquals(readParameters.size(), parameterCodes.size());

// the filter has been set with env vars expanded
Filter filter = (Filter) parameterValues.get(ImageMosaicFormat.FILTER);
assertEquals(ECQL.toFilter("direction = 'NE'"), filter);
} finally {
EnvFunction.clearLocalValues();
}
}
} }
32 changes: 25 additions & 7 deletions src/main/src/main/java/org/geoserver/data/util/CoverageUtils.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/ */
package org.geoserver.data.util; package org.geoserver.data.util;


import java.awt.Color; import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
Expand Down Expand Up @@ -418,12 +418,30 @@ public static GeneralParameterValue[] mergeParameter(
final ParameterValue pv = (ParameterValue) pd.createValue(); final ParameterValue pv = (ParameterValue) pd.createValue();
pv.setValue(value); pv.setValue(value);


// add to the list // if it's in the list already, override
GeneralParameterValue[] readParametersClone = int existingPvIndex = -1;
new GeneralParameterValue[readParameters.length + 1]; for (int i = 0; i < readParameters.length && existingPvIndex < 0; i++) {
System.arraycopy(readParameters, 0, readParametersClone, 0, readParameters.length); GeneralParameterValue oldPv = readParameters[i];
readParametersClone[readParameters.length] = pv; if (aliases.contains(oldPv.getDescriptor().getName().getCode())) {
readParameters = readParametersClone; existingPvIndex = i;
}
}

if (existingPvIndex >= 0) {
GeneralParameterValue[] clone =
new GeneralParameterValue[readParameters.length];
System.arraycopy(readParameters, 0, clone, 0, readParameters.length);
clone[existingPvIndex] = pv;
readParameters = clone;

} else {
// add to the list
GeneralParameterValue[] clone =
new GeneralParameterValue[readParameters.length + 1];
System.arraycopy(readParameters, 0, clone, 0, readParameters.length);
clone[readParameters.length] = pv;
readParameters = clone;
}


// leave // leave
break; break;
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.geotools.feature.collection.SortedSimpleFeatureCollection; import org.geotools.feature.collection.SortedSimpleFeatureCollection;
import org.geotools.filter.spatial.DefaultCRSFilterVisitor; import org.geotools.filter.spatial.DefaultCRSFilterVisitor;
import org.geotools.filter.spatial.ReprojectingFilterVisitor; import org.geotools.filter.spatial.ReprojectingFilterVisitor;
import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS; import org.geotools.referencing.CRS;
import org.geotools.util.factory.Hints; import org.geotools.util.factory.Hints;
Expand Down Expand Up @@ -228,7 +229,7 @@ public static GeoServerFeatureSource create(
* @throws DataSourceException If query could not meet the restrictions of definitionQuery * @throws DataSourceException If query could not meet the restrictions of definitionQuery
*/ */
protected Query makeDefinitionQuery(Query query, SimpleFeatureType schema) throws IOException { protected Query makeDefinitionQuery(Query query, SimpleFeatureType schema) throws IOException {
if ((query == Query.ALL) || query.equals(Query.ALL)) { if (definitionQuery == null && linearizationTolerance == null) {
return query; return query;
} }


Expand Down Expand Up @@ -327,8 +328,16 @@ protected Filter makeDefinitionFilter(Filter filter) throws DataSourceException
Filter newFilter = filter; Filter newFilter = filter;


try { try {
if (definitionQuery != Filter.INCLUDE) { if (definitionQuery == Filter.INCLUDE) {
newFilter = ff.and(definitionQuery, filter); return filter;
}
SimplifyingFilterVisitor visitor = new SimplifyingFilterVisitor();
Filter simplifiedDefinitionQuery = (Filter) definitionQuery.accept(visitor, null);
if (filter == Filter.INCLUDE) {
newFilter = simplifiedDefinitionQuery;
} else if (simplifiedDefinitionQuery != Filter.INCLUDE) {
// expand eventual env vars before hitting the store machinery
newFilter = ff.and(simplifiedDefinitionQuery, filter);
} }
} catch (Exception ex) { } catch (Exception ex) {
throw new DataSourceException("Can't create the definition filter", ex); throw new DataSourceException("Can't create the definition filter", ex);
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@
*/ */
package org.geoserver.data.util; package org.geoserver.data.util;


import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;


import java.awt.Color; import java.awt.*;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.gce.imagemosaic.ImageMosaicFormat; import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.junit.Test; import org.junit.Test;
import org.opengis.filter.Filter;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue; import org.opengis.parameter.ParameterValue;


Expand All @@ -38,4 +45,21 @@ public void testMaxTiles() {
assertTrue(value instanceof Integer); assertTrue(value instanceof Integer);
assertEquals(Integer.valueOf(1), value); assertEquals(Integer.valueOf(1), value);
} }

@Test
public void testMergeExisting() throws CQLException {
List<GeneralParameterDescriptor> descriptors =
new ImageMosaicFormat().getReadParameters().getDescriptor().descriptors();
GeneralParameterValue[] oldParams = new GeneralParameterValue[1];
oldParams[0] = ImageMosaicFormat.FILTER.createValue();
Filter filter = CQL.toFilter("a = 6");
GeneralParameterValue[] newParams =
CoverageUtils.mergeParameter(
descriptors,
oldParams,
filter,
ImageMosaicFormat.FILTER.getName().getCode());
assertEquals(1, newParams.length);
assertEquals(filter, ((ParameterValue) newParams[0]).getValue());
}
} }

0 comments on commit d83239f

Please sign in to comment.