Author: aaime Date: 2012-03-07 02:29:08 -0800 (Wed, 07 Mar 2012) New Revision: 38613 Added: trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/CoverageCenterFunction.java trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/RenderingTransformationTest.java trunk/modules/library/render/src/test/resources/META-INF/ trunk/modules/library/render/src/test/resources/META-INF/services/ trunk/modules/library/render/src/test/resources/META-INF/services/org.opengis.filter.expression.Function Modified: trunk/modules/library/render/pom.xml trunk/modules/library/render/src/main/java/org/geotools/renderer/lite/StreamingRenderer.java Log: [GEOT-4059] Coverage based rendering transformation will likely fail if map is reprojected Modified: trunk/modules/library/render/pom.xml =================================================================== --- trunk/modules/library/render/pom.xml 2012-03-06 06:10:23 UTC (rev 38612) +++ trunk/modules/library/render/pom.xml 2012-03-07 10:29:08 UTC (rev 38613) @@ -150,6 +150,12 @@ <version>${project.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-geotiff</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> </dependencies> <build> Modified: trunk/modules/library/render/src/main/java/org/geotools/renderer/lite/StreamingRenderer.java =================================================================== --- trunk/modules/library/render/src/main/java/org/geotools/renderer/lite/StreamingRenderer.java 2012-03-06 06:10:23 UTC (rev 38612) +++ trunk/modules/library/render/src/main/java/org/geotools/renderer/lite/StreamingRenderer.java 2012-03-07 10:29:08 UTC (rev 38613) @@ -2014,6 +2014,10 @@ * @param attributeNames */ void checkAttributeExistence(FeatureType schema, Query query) { + if(query.getProperties() == null) { + return; + } + for (PropertyName attribute : query.getProperties()) { if(attribute.evaluate(schema) == null) { if (schema instanceof SimpleFeatureType) { @@ -2034,7 +2038,7 @@ } FeatureCollection applyRenderingTransformation(Expression transformation, - FeatureSource featureSource, Query query, GridGeometry2D gridGeometry) throws IOException, SchemaException, TransformException { + FeatureSource featureSource, Query query, GridGeometry2D gridGeometry) throws IOException, SchemaException, TransformException, FactoryException { Object result = null; // check if it's a wrapper coverage or a wrapped reader @@ -2043,12 +2047,10 @@ if(schema instanceof SimpleFeatureType) { SimpleFeatureType simpleSchema = (SimpleFeatureType) schema; GridCoverage2D coverage = null; - if(FeatureUtilities.isWrappedCoverage(simpleSchema)) { + if(FeatureUtilities.isWrappedCoverage(simpleSchema) || FeatureUtilities.isWrappedCoverageReader(simpleSchema)) { isRasterData = true; - throw new UnsupportedOperationException("Don't have support for plain coverages " + - "in rendering transformations now"); - } else if(FeatureUtilities.isWrappedCoverageReader(simpleSchema)) { - isRasterData = true; + + // get the desired grid geometry GridGeometry2D readGG = gridGeometry; if(transformation instanceof RenderingTransformation) { RenderingTransformation tx = (RenderingTransformation) transformation; @@ -2056,45 +2058,52 @@ // TODO: override the read params and force this grid geometry, or something // similar to this (like passing it as a param to readCoverage } + Feature gridWrapper = featureSource.getFeatures().features().next(); - final Object params = paramsPropertyName.evaluate(gridWrapper); - final AbstractGridCoverage2DReader reader = (AbstractGridCoverage2DReader) gridPropertyName.evaluate(gridWrapper); - // don't read more than the native resolution (in case we are oversampling) - if(CRS.equalsIgnoreMetadata(reader.getCrs(), gridGeometry.getCoordinateReferenceSystem())) { - MathTransform g2w = reader.getOriginalGridToWorld(PixelInCell.CELL_CENTER); - if(g2w instanceof AffineTransform2D && readGG.getGridToCRS2D() instanceof AffineTransform2D) { - AffineTransform2D atOriginal = (AffineTransform2D) g2w; - AffineTransform2D atMap = (AffineTransform2D) readGG.getGridToCRS2D(); - if(XAffineTransform.getScale(atMap) < XAffineTransform.getScale(atOriginal)) { - // we need to go trough some convoluted code to make sure the new grid geometry - // has at least one pixel - - org.opengis.geometry.Envelope worldEnvelope = gridGeometry.getEnvelope(); - GeneralEnvelope transformed = org.geotools.referencing.CRS.transform(atOriginal.inverse(), worldEnvelope); - int minx = (int) Math.floor(transformed.getMinimum(0)); - int miny = (int) Math.floor(transformed.getMinimum(1)); - int maxx = (int) Math.ceil(transformed.getMaximum(0)); - int maxy = (int) Math.ceil(transformed.getMaximum(1)); - Rectangle rect = new Rectangle(minx, miny, (maxx - minx), (maxy - miny)); - GridEnvelope2D gridEnvelope = new GridEnvelope2D(rect); - readGG = new GridGeometry2D(gridEnvelope, atOriginal, worldEnvelope.getCoordinateReferenceSystem()); - - // readGG = new GridGeometry2D(PixelInCell.CELL_CORNER, ); + if(FeatureUtilities.isWrappedCoverageReader(simpleSchema)) { + final Object params = paramsPropertyName.evaluate(gridWrapper); + final AbstractGridCoverage2DReader reader = (AbstractGridCoverage2DReader) gridPropertyName.evaluate(gridWrapper); + // don't read more than the native resolution (in case we are oversampling) + if(CRS.equalsIgnoreMetadata(reader.getCrs(), gridGeometry.getCoordinateReferenceSystem())) { + MathTransform g2w = reader.getOriginalGridToWorld(PixelInCell.CELL_CENTER); + if(g2w instanceof AffineTransform2D && readGG.getGridToCRS2D() instanceof AffineTransform2D) { + AffineTransform2D atOriginal = (AffineTransform2D) g2w; + AffineTransform2D atMap = (AffineTransform2D) readGG.getGridToCRS2D(); + if(XAffineTransform.getScale(atMap) < XAffineTransform.getScale(atOriginal)) { + // we need to go trough some convoluted code to make sure the new grid geometry + // has at least one pixel + + org.opengis.geometry.Envelope worldEnvelope = gridGeometry.getEnvelope(); + GeneralEnvelope transformed = org.geotools.referencing.CRS.transform(atOriginal.inverse(), worldEnvelope); + int minx = (int) Math.floor(transformed.getMinimum(0)); + int miny = (int) Math.floor(transformed.getMinimum(1)); + int maxx = (int) Math.ceil(transformed.getMaximum(0)); + int maxy = (int) Math.ceil(transformed.getMaximum(1)); + Rectangle rect = new Rectangle(minx, miny, (maxx - minx), (maxy - miny)); + GridEnvelope2D gridEnvelope = new GridEnvelope2D(rect); + readGG = new GridGeometry2D(gridEnvelope, atOriginal, worldEnvelope.getCoordinateReferenceSystem()); + } } - } + } + coverage = readCoverage(reader, params, readGG); + } else { + coverage = (GridCoverage2D) gridPropertyName.evaluate(gridWrapper); } - coverage = readCoverage(reader, params, readGG); // readers will return null if there is no coverage in the area if(coverage != null) { if(readGG != null) { // Crop will fail if we try to crop outside of the coverage area - GeneralEnvelope cropEnvelope = new GeneralEnvelope(readGG.getEnvelope()); - if(coverage.getEnvelope2D().intersects(cropEnvelope.toRectangle2D())) { + ReferencedEnvelope renderingEnvelope = new ReferencedEnvelope(readGG.getEnvelope()); + CoordinateReferenceSystem coverageCRS = coverage.getCoordinateReferenceSystem2D(); + if(!CRS.equalsIgnoreMetadata(renderingEnvelope.getCoordinateReferenceSystem(), coverageCRS)) { + renderingEnvelope = renderingEnvelope.transform(coverageCRS, true); + } + if(coverage.getEnvelope2D().intersects(renderingEnvelope)) { // the resulting coverage might be larger than the readGG envelope, shall we crop it? final ParameterValueGroup param = CROP.getParameters(); param.parameter("Source").setValue(coverage); - param.parameter("Envelope").setValue(cropEnvelope); + param.parameter("Envelope").setValue(renderingEnvelope); coverage = (GridCoverage2D) PROCESSOR.doOperation(param); } else { coverage = null; @@ -2158,7 +2167,7 @@ // transform them result = transformation.evaluate(originalFeatures); - } + } // null safety, a transformation might be free to return null if(result == null) { @@ -2227,6 +2236,13 @@ Filter original = query.getFilter(); Filter renamedFilter = (Filter) original.accept(visitor, null); result.setFilter(renamedFilter); + } else if(originalSchema instanceof SimpleFeatureType + && (FeatureUtilities.isWrappedCoverage((SimpleFeatureType) originalSchema) + || FeatureUtilities.isWrappedCoverageReader((SimpleFeatureType) originalSchema))) { + // use all the properties generated by the transformation, the query normally in this + // case contains grid/params/geom or grid/geom which have nothing to do with + // the actual attributes we are going to use, especially in raster to vector transformations + result.setFilter(query.getFilter()); } else { result.setPropertyNames(query.getPropertyNames()); result.setFilter(query.getFilter()); Added: trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/CoverageCenterFunction.java =================================================================== --- trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/CoverageCenterFunction.java (rev 0) +++ trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/CoverageCenterFunction.java 2012-03-07 10:29:08 UTC (rev 38613) @@ -0,0 +1,52 @@ +package org.geotools.renderer.lite; + +import static org.geotools.filter.capability.FunctionNameImpl.*; + +import org.geotools.coverage.grid.GridCoverage2D; +import org.geotools.data.DataUtilities; +import org.geotools.feature.simple.SimpleFeatureBuilder; +import org.geotools.feature.simple.SimpleFeatureTypeBuilder; +import org.geotools.filter.FunctionExpressionImpl; +import org.geotools.filter.capability.FunctionNameImpl; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.filter.capability.FunctionName; +import org.opengis.geometry.Envelope; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.Point; + +/** + * A test rendering transformation that returns the center of the provided coverage + * + * @author Andrea Aime - GeoSolutions + */ +public class CoverageCenterFunction extends FunctionExpressionImpl { + + public static FunctionName NAME = new FunctionNameImpl("CoverageCenter", parameter("coverage", + GridCoverage2D.class)); + + public CoverageCenterFunction() { + super(NAME); + } + + public Object evaluate(Object gc) { + GridCoverage2D coverage = (GridCoverage2D) gc; + + Envelope env = coverage.getEnvelope(); + GeometryFactory gf = new GeometryFactory(); + Point center = gf.createPoint(new Coordinate(env.getMedian(0), env.getMedian(1))); + + SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); + tb.setName("center"); + tb.add("geom", Point.class, coverage.getCoordinateReferenceSystem2D()); + SimpleFeatureType ft = tb.buildFeatureType(); + + SimpleFeatureBuilder fb = new SimpleFeatureBuilder(ft); + fb.add(center); + SimpleFeature f = fb.buildFeature(null); + + return DataUtilities.collection(f); + } +} Added: trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/RenderingTransformationTest.java =================================================================== --- trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/RenderingTransformationTest.java (rev 0) +++ trunk/modules/library/render/src/test/java/org/geotools/renderer/lite/RenderingTransformationTest.java 2012-03-07 10:29:08 UTC (rev 38613) @@ -0,0 +1,100 @@ +package org.geotools.renderer.lite; + +import static org.junit.Assert.*; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +import org.geotools.gce.geotiff.GeoTiffReader; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.map.GridCoverageLayer; +import org.geotools.map.GridReaderLayer; +import org.geotools.map.MapContent; +import org.geotools.referencing.CRS; +import org.geotools.styling.Style; +import org.geotools.TestData; +import org.junit.Before; +import org.junit.Test; + +public class RenderingTransformationTest { + + private static final long TIME = 4000; + + @Before + public void setup() { + // System.setProperty("org.geotools.test.interactive", "true"); + } + + @Test + public void testTransformReprojectedGridReader() throws Exception { + Style style = RendererBaseTest.loadStyle(this, "coverageCenter.sld"); + + GeoTiffReader reader = new GeoTiffReader(TestData.copy(this, "geotiff/world.tiff")); + + MapContent mc = new MapContent(); + mc.addLayer(new GridReaderLayer(reader, style)); + + StreamingRenderer renderer = new StreamingRenderer(); + renderer.setMapContent(mc); + + ReferencedEnvelope reWgs84 = new ReferencedEnvelope(-70, 70, -160, 160, CRS.decode("EPSG:4326")); + ReferencedEnvelope re = reWgs84.transform(CRS.decode("EPSG:3857"), true); + + BufferedImage image = RendererBaseTest.showRender("Lines with circle stroke", renderer, + TIME, re); + // if everything worked we are going to have a red dot in the middle of the map + assertEquals(Color.RED, getPixelColor(image, image.getWidth() / 2, image.getHeight() / 2)); + assertEquals(Color.WHITE, getPixelColor(image, image.getWidth() / 4, image.getHeight() / 2)); + assertEquals(Color.WHITE, getPixelColor(image, image.getWidth() / 2, image.getHeight() / 4)); + assertEquals(Color.WHITE, getPixelColor(image, image.getWidth() / 4, image.getHeight() / 4)); + } + + @Test + public void testTransformReprojectedGridCoverage() throws Exception { + Style style = RendererBaseTest.loadStyle(this, "coverageCenter.sld"); + + GeoTiffReader reader = new GeoTiffReader(TestData.copy(this, "geotiff/world.tiff")); + + MapContent mc = new MapContent(); + mc.addLayer(new GridCoverageLayer(reader.read(null), style)); + + StreamingRenderer renderer = new StreamingRenderer(); + renderer.setMapContent(mc); + + ReferencedEnvelope reWgs84 = new ReferencedEnvelope(-70, 70, -160, 160, CRS.decode("EPSG:4326")); + ReferencedEnvelope re = reWgs84.transform(CRS.decode("EPSG:3857"), true); + + BufferedImage image = RendererBaseTest.showRender("Lines with circle stroke", renderer, + TIME, re); + // if everything worked we are going to have a red dot in the middle of the map + assertEquals(Color.RED, getPixelColor(image, image.getWidth() / 2, image.getHeight() / 2)); + assertEquals(Color.WHITE, getPixelColor(image, image.getWidth() / 4, image.getHeight() / 2)); + assertEquals(Color.WHITE, getPixelColor(image, image.getWidth() / 2, image.getHeight() / 4)); + assertEquals(Color.WHITE, getPixelColor(image, image.getWidth() / 4, image.getHeight() / 4)); + + } + + /** + * Gets a specific pixel color from the specified buffered image + * @param image + * @param i + * @param j + * @param color + * @return + */ + protected Color getPixelColor(BufferedImage image, int i, int j) { + ColorModel cm = image.getColorModel(); + Raster raster = image.getRaster(); + Object pixel = raster.getDataElements(i, j, null); + + Color actual; + if(cm.hasAlpha()) { + actual = new Color(cm.getRed(pixel), cm.getGreen(pixel), cm.getBlue(pixel), cm.getAlpha(pixel)); + } else { + actual = new Color(cm.getRed(pixel), cm.getGreen(pixel), cm.getBlue(pixel), 255); + } + return actual; + } +} Added: trunk/modules/library/render/src/test/resources/META-INF/services/org.opengis.filter.expression.Function =================================================================== --- trunk/modules/library/render/src/test/resources/META-INF/services/org.opengis.filter.expression.Function (rev 0) +++ trunk/modules/library/render/src/test/resources/META-INF/services/org.opengis.filter.expression.Function 2012-03-07 10:29:08 UTC (rev 38613) @@ -0,0 +1 @@ +org.geotools.renderer.lite.CoverageCenterFunction \ No newline at end of file |