diff --git a/deegree-core/deegree-core-geometry/src/test/java/org/deegree/geometry/GeometryTransformerTest.java b/deegree-core/deegree-core-geometry/src/test/java/org/deegree/geometry/GeometryTransformerTest.java new file mode 100644 index 0000000000..da0cc08690 --- /dev/null +++ b/deegree-core/deegree-core-geometry/src/test/java/org/deegree/geometry/GeometryTransformerTest.java @@ -0,0 +1,69 @@ +package org.deegree.geometry; + +import org.deegree.cs.coordinatesystems.ICRS; +import org.deegree.cs.exceptions.TransformationException; +import org.deegree.cs.exceptions.UnknownCRSException; +import org.deegree.cs.persistence.CRSManager; +import org.deegree.geometry.standard.DefaultEnvelope; +import org.deegree.geometry.standard.primitive.DefaultPoint; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class GeometryTransformerTest { + + @Test + public void transformEnvelopeKeepsMinAndMaxIn25833To4326 () throws UnknownCRSException, TransformationException { + ICRS targetCrs = CRSManager.lookup("EPSG:4326"); + GeometryTransformer transformer = new GeometryTransformer(targetCrs); + Envelope srcEnvelope = createEnvelopeInEpsg25833(); + Envelope targetEnvelope = transformer.transform(srcEnvelope); + double minx = targetEnvelope.getMin().get0(); + double miny = targetEnvelope.getMin().get1(); + double maxx = targetEnvelope.getMax().get0(); + double maxy = targetEnvelope.getMax().get1(); + + assertTrue(targetEnvelope.getCoordinateSystem().equals(targetCrs)); + assertTrue(minx < maxx); + assertTrue(miny < maxy); + } + + @Test + public void transformEnvelopeKeepsMinAndMaxIn4326To25833 () throws UnknownCRSException, TransformationException { + ICRS targetCrs = CRSManager.lookup("EPSG:25833"); + GeometryTransformer transformer = new GeometryTransformer(targetCrs); + Envelope srcEnvelope = createEnvelopeInEpsg4326(); + Envelope targetEnvelope = transformer.transform(srcEnvelope); + double minx = targetEnvelope.getMin().get0(); + double miny = targetEnvelope.getMin().get1(); + double maxx = targetEnvelope.getMax().get0(); + double maxy = targetEnvelope.getMax().get1(); + + assertTrue(targetEnvelope.getCoordinateSystem().equals(targetCrs)); + assertTrue(minx < maxx); + assertTrue(miny < maxy); + } + + private Envelope createEnvelopeInEpsg25833( ) throws UnknownCRSException { + ICRS crs = CRSManager.lookup("EPSG:25833"); + double minx = 372988.94024799997; + double miny = 5723566.818151; + double maxx = 382478.052521; + double maxy = 5734058.460988999; + DefaultPoint minPoint = new DefaultPoint( "minPoint", crs, null, new double[] { miny, minx } ); + DefaultPoint maxPoint = new DefaultPoint( "maxPoint", crs, null, new double[] { maxy, maxx } ); + return new DefaultEnvelope( "newEnvelope", crs, null, minPoint, maxPoint ); + } + + private Envelope createEnvelopeInEpsg4326( ) throws UnknownCRSException { + ICRS crs = CRSManager.lookup("EPSG:4326"); + double minx = 51.64873620668787; + double miny = 13.164151617571893; + double maxx = 51.74509300270947; + double maxy = 13.297706688558224; + DefaultPoint minPoint = new DefaultPoint( "minPoint", crs, null, new double[] { miny, minx } ); + DefaultPoint maxPoint = new DefaultPoint( "maxPoint", crs, null, new double[] { maxy, maxx } ); + return new DefaultEnvelope( "newEnvelope", crs, null, minPoint, maxPoint ); + } + +} \ No newline at end of file diff --git a/deegree-core/deegree-core-rendering-2d/pom.xml b/deegree-core/deegree-core-rendering-2d/pom.xml index c45b193d3a..f84794b400 100644 --- a/deegree-core/deegree-core-rendering-2d/pom.xml +++ b/deegree-core/deegree-core-rendering-2d/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 deegree-core-rendering-2d deegree-core-rendering-2d @@ -55,6 +56,10 @@ junit junit + + org.mockito + mockito-core + org.apache.xmlgraphics batik-codec diff --git a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/ImageTransformer.java b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/ImageTransformer.java new file mode 100644 index 0000000000..eee3a5ad98 --- /dev/null +++ b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/ImageTransformer.java @@ -0,0 +1,28 @@ +package org.deegree.rendering.r2d; + +import org.deegree.geometry.Envelope; + +import java.awt.image.BufferedImage; + +/** + * Interface for image transformers. + * + * @author Dirk Stenger + * @author last edited by: $Author: stenger $ + * + * @version $Revision: $, $Date: $ + */ +public interface ImageTransformer { + + /** + * Transforms an image. + * + * @param image + * image to transform, never null + * @param sourceEnvelope + * source envelope of image, never null + * @return transformed image + */ + BufferedImage transform( BufferedImage image, Envelope sourceEnvelope ); + +} \ No newline at end of file diff --git a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/Java2DTileRenderer.java b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/Java2DTileRenderer.java index f0488510b7..cff02260cc 100644 --- a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/Java2DTileRenderer.java +++ b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/Java2DTileRenderer.java @@ -40,19 +40,27 @@ Occam Labs UG (haftungsbeschränkt) ----------------------------------------------------------------------------*/ package org.deegree.rendering.r2d; -import static java.awt.Color.RED; -import static org.slf4j.LoggerFactory.getLogger; - -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; - import org.deegree.commons.utils.math.MathUtils; +import org.deegree.cs.coordinatesystems.ICRS; +import org.deegree.cs.exceptions.TransformationException; +import org.deegree.cs.exceptions.UnknownCRSException; import org.deegree.geometry.Envelope; +import org.deegree.geometry.GeometryTransformer; import org.deegree.tile.Tile; import org.deegree.tile.TileIOException; import org.slf4j.Logger; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.util.Iterator; + +import static java.awt.Color.RED; +import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR; +import static org.slf4j.LoggerFactory.getLogger; + /** * Java2DTileRenderer * @@ -61,48 +69,193 @@ Occam Labs UG (haftungsbeschränkt) * * @version $Revision: 31882 $, $Date: 2011-09-15 02:05:04 +0200 (Thu, 15 Sep 2011) $ */ - public class Java2DTileRenderer implements TileRenderer { private static final Logger LOG = getLogger( Java2DTileRenderer.class ); - private Graphics2D graphics; + private final Graphics2D graphics; + + private final int width; + + private final int height; + + private final Envelope envelope; + + private final ImageTransformer imageTransformer; private AffineTransform worldToScreen = new AffineTransform(); /** + * When transform is called, all tiles are rendered into main graphics. + * * @param graphics + * main graphics, never null * @param width + * query width * @param height + * query height * @param envelope + * query envelope, never null + * @param imageTransformer + * image transformer to transform image, if source CRS of tiles does not match CRS of query envelope. If + * null, image transformation is not possible. */ - public Java2DTileRenderer( Graphics2D graphics, int width, int height, Envelope envelope ) { + public Java2DTileRenderer( Graphics2D graphics, int width, int height, Envelope envelope, + ImageTransformer imageTransformer ) { this.graphics = graphics; + this.width = width; + this.height = height; + this.envelope = envelope; + this.imageTransformer = imageTransformer; RenderHelper.getWorldToScreenTransform( worldToScreen, envelope, width, height ); } @Override - public void render( Tile tile ) { + public void render( Iterator tiles ) { + BufferedImage image = new BufferedImage( width, height, TYPE_4BYTE_ABGR ); + Graphics g = image.getGraphics(); + ICRS crsOfTile = renderAllTilesInTileCrs( tiles, g ); + renderToMainGraphics( image, crsOfTile ); + } + + private ICRS renderAllTilesInTileCrs( Iterator tiles, Graphics g ) { + ICRS crsOfTile = null; + Tile firstNonNullTile = retrieveFirstNonNullTile( tiles ); + if ( firstNonNullTile != null ) { + crsOfTile = firstNonNullTile.getEnvelope().getCoordinateSystem(); + AffineTransform worldToScreenTransformInTileCrs = createWorldToScreenTransform( crsOfTile ); + renderInTileCrs( firstNonNullTile, g, worldToScreenTransformInTileCrs ); + processRestOfTiles( tiles, g, worldToScreenTransformInTileCrs ); + } + return crsOfTile; + } + + private Tile retrieveFirstNonNullTile( Iterator tiles ) { + while ( tiles.hasNext() ) { + Tile tile = tiles.next(); + if ( tile != null ) + return tile; + } + return null; + } + + private void processRestOfTiles( Iterator tiles, Graphics g, AffineTransform worldToScreenTransformInTileCrs ) { + while ( tiles.hasNext() ) { + renderInTileCrs( tiles.next(), g, worldToScreenTransformInTileCrs ); + } + } + + private AffineTransform createWorldToScreenTransform( ICRS sourceCrs ) { + try { + if ( !sourceCrs.equals( envelope.getCoordinateSystem() ) ) { + AffineTransform worldToScreenInTileCrs = new AffineTransform(); + Envelope transformedEnvelope = transformQueryEnvelope( sourceCrs ); + RenderHelper.getWorldToScreenTransform( worldToScreenInTileCrs, transformedEnvelope, width, height ); + return worldToScreenInTileCrs; + } + return worldToScreen; + } catch ( UnknownCRSException e ) { + handleWorldToScreenTransformException( e ); + return worldToScreen; + } catch ( TransformationException e ) { + handleWorldToScreenTransformException( e ); + return worldToScreen; + } + } + + private void renderInTileCrs( Tile tile, Graphics g, AffineTransform worldToScreenInTileCrs ) { if ( tile == null ) { LOG.debug( "Not rendering null tile." ); return; } + BufferedImage image = tile.getAsImage(); + drawImage( image, g, worldToScreenInTileCrs, tile.getEnvelope() ); + } + + private void renderToMainGraphics( BufferedImage image, ICRS sourceCrs ) { + if ( sourceCrs != null && !envelope.getCoordinateSystem().equals( sourceCrs ) ) { + BufferedImage transformedImage = transformImage( image, sourceCrs ); + drawImage( transformedImage, graphics, worldToScreen, envelope ); + } else { + drawImage( image, graphics, worldToScreen, envelope ); + } + } + + private BufferedImage transformImage( BufferedImage image, ICRS sourceCrs ) { + try { + checkIfImageTransformationIsPossible(); + Envelope sourceEnvelope = transformQueryEnvelope( sourceCrs ); + return imageTransformer.transform( image, sourceEnvelope ); + } catch ( UnknownCRSException e ) { + handleTransformImageException( e ); + return image; + } catch ( TransformationException e ) { + handleTransformImageException( e ); + return image; + } + } + + private void checkIfImageTransformationIsPossible() { + if ( imageTransformer == null ) { + String msg = "Source tiles do not offer the requested coordinate system and transformation has not been implemented, yet"; + LOG.debug( msg ); + throw new RasterRenderingException( msg ); + } + } + + private Envelope transformQueryEnvelope( ICRS targetCrs ) + throws UnknownCRSException, TransformationException { + try { + return new GeometryTransformer( targetCrs ).transform( envelope ); + } catch ( TransformationException e ) { + LOG.warn( "Could not transform envelope: " + e.getMessage() ); + e.printStackTrace(); + throw e; + } catch ( UnknownCRSException e ) { + LOG.warn( "Could not transform envelope as CRS is unknown: " + e.getMessage() ); + e.printStackTrace(); + throw e; + } + } + + private void drawImage( BufferedImage image, Graphics g, AffineTransform worldToScreenTransform, Envelope env ) { int minx, miny, maxx, maxy; - Envelope env = tile.getEnvelope(); - Point2D.Double p = (Point2D.Double) worldToScreen.transform( new Point2D.Double( env.getMin().get0(), - env.getMin().get1() ), null ); - minx = MathUtils.round( p.x ); - miny = MathUtils.round( p.y ); - p = (Point2D.Double) worldToScreen.transform( new Point2D.Double( env.getMax().get0(), env.getMax().get1() ), - null ); - maxx = MathUtils.round( p.x ); - maxy = MathUtils.round( p.y ); + Point2D.Double minPoint = (Point2D.Double) worldToScreenTransform.transform( new Point2D.Double( + env.getMin().get0(), + env.getMin().get1() ), + null ); + minx = MathUtils.round( minPoint.x ); + miny = MathUtils.round( minPoint.y ); + Point2D.Double maxPoint = (Point2D.Double) worldToScreenTransform.transform( new Point2D.Double( + env.getMax().get0(), + env.getMax().get1() ), + null ); + maxx = MathUtils.round( maxPoint.x ); + maxy = MathUtils.round( maxPoint.y ); + + int minxCorrected = Math.min( minx, maxx ); + int minyCorrected = Math.min( miny, maxy ); + int maxxCorrected = Math.max( minx, maxx ); + int maxyCorrected = Math.max( miny, maxy ); + try { - graphics.drawImage( tile.getAsImage(), minx, miny, maxx - minx, maxy - miny, null ); + g.drawImage( image, minxCorrected, minyCorrected, maxxCorrected - minxCorrected, maxyCorrected + - minyCorrected, null ); } catch ( TileIOException e ) { - LOG.debug( "Error retrieving tile image: " + e.getMessage() ); - graphics.setColor( RED ); - graphics.fillRect( minx, miny, maxx - minx, maxy - miny ); + LOG.debug( "Error retrieving image: " + e.getMessage() ); + g.setColor( RED ); + g.fillRect( minx, miny, maxx - minx, maxy - miny ); } } -} + + private void handleWorldToScreenTransformException( Exception e ) { + LOG.warn( "Envelope could not be transformed to source CRS. World-To-Screen-Transformation of query envelope is used! Reason: " + + e.getMessage() ); + } + + private void handleTransformImageException( Exception e ) { + LOG.warn( "Envelope could not be transformed to source CRS. Image transformation is canceled! Reason: " + + e.getMessage() ); + } + +} \ No newline at end of file diff --git a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/TileRenderer.java b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/TileRenderer.java index aeaf5e4505..779d5bcb5a 100644 --- a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/TileRenderer.java +++ b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/TileRenderer.java @@ -42,6 +42,8 @@ Occam Labs UG (haftungsbeschränkt) import org.deegree.tile.Tile; +import java.util.Iterator; + /** * TileRenderer * @@ -53,6 +55,12 @@ Occam Labs UG (haftungsbeschränkt) public interface TileRenderer { - void render( Tile tile ); + /** + * Renders all tiles. + * + * @param tiles + * never null + */ + void render( Iterator tiles ); -} +} \ No newline at end of file diff --git a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/DefaultRenderContext.java b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/DefaultRenderContext.java index 6c9095a299..7339747921 100644 --- a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/DefaultRenderContext.java +++ b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/DefaultRenderContext.java @@ -35,6 +35,19 @@ ----------------------------------------------------------------------------*/ package org.deegree.rendering.r2d.context; +import org.deegree.rendering.r2d.Java2DLabelRenderer; +import org.deegree.rendering.r2d.Java2DRasterRenderer; +import org.deegree.rendering.r2d.Java2DRenderer; +import org.deegree.rendering.r2d.Java2DTextRenderer; +import org.deegree.rendering.r2d.Java2DTileRenderer; +import org.deegree.rendering.r2d.labelplacement.AutoLabelPlacement; +import org.deegree.style.utils.ImageUtils; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; + import static java.awt.RenderingHints.KEY_ANTIALIASING; import static java.awt.RenderingHints.KEY_INTERPOLATION; import static java.awt.RenderingHints.KEY_RENDERING; @@ -52,19 +65,6 @@ import static javax.imageio.ImageIO.write; import static org.apache.commons.io.IOUtils.closeQuietly; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.io.OutputStream; - -import org.deegree.rendering.r2d.Java2DRasterRenderer; -import org.deegree.rendering.r2d.Java2DRenderer; -import org.deegree.rendering.r2d.Java2DTextRenderer; -import org.deegree.rendering.r2d.Java2DLabelRenderer; -import org.deegree.rendering.r2d.Java2DTileRenderer; -import org.deegree.rendering.r2d.labelplacement.AutoLabelPlacement; -import org.deegree.style.utils.ImageUtils; - /** * * @author Andreas Schmitz @@ -81,7 +81,7 @@ public class DefaultRenderContext implements RenderContext { private Java2DRenderer renderer; private Java2DTextRenderer textRenderer; - + private Java2DLabelRenderer labelRenderer; private Java2DRasterRenderer rasterRenderer; @@ -102,7 +102,7 @@ public DefaultRenderContext( RenderingInfo info ) { textRenderer = new Java2DTextRenderer( renderer ); labelRenderer = new Java2DLabelRenderer( renderer, textRenderer ); rasterRenderer = new Java2DRasterRenderer( graphics ); - tileRenderer = new Java2DTileRenderer( graphics, info.getWidth(), info.getHeight(), info.getEnvelope() ); + tileRenderer = new Java2DTileRenderer( graphics, info.getWidth(), info.getHeight(), info.getEnvelope(), null ); } @Override @@ -134,19 +134,19 @@ public Java2DTileRenderer getTileRenderer() { public void setOutput( OutputStream out ) { this.out = out; } - + /** * To be called after all Renderings are done, to render and maybe optimize the labels. */ @Override public void optimizeAndDrawLabels() { - //Optimize Label Placement here, if pointplacement set to auto=true - try{ - new AutoLabelPlacement(labelRenderer.getLabels(), renderer ); + // Optimize Label Placement here, if pointplacement set to auto=true + try { + new AutoLabelPlacement( labelRenderer.getLabels(), renderer ); } catch ( Throwable e ) { e.printStackTrace(); } - labelRenderer.render( ); + labelRenderer.render(); } @Override diff --git a/deegree-core/deegree-core-rendering-2d/src/test/java/org/deegree/rendering/r2d/Java2DTileRendererTest.java b/deegree-core/deegree-core-rendering-2d/src/test/java/org/deegree/rendering/r2d/Java2DTileRendererTest.java new file mode 100644 index 0000000000..fb3722aa66 --- /dev/null +++ b/deegree-core/deegree-core-rendering-2d/src/test/java/org/deegree/rendering/r2d/Java2DTileRendererTest.java @@ -0,0 +1,52 @@ +package org.deegree.rendering.r2d; + +import org.deegree.geometry.primitive.Point; +import org.deegree.geometry.standard.DefaultEnvelope; +import org.deegree.tile.Tile; +import org.junit.Test; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; + +import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link org.deegree.rendering.r2d.Java2DTileRenderer}. + * + * @author Dirk Stenger + * @author last edited by: $Author: stenger $ + * + * @version $Revision$, $Date$ + */ +public class Java2DTileRendererTest { + + private final Java2DTileRenderer renderer = createRenderer(); + + @Test + public void testRenderWithEmptyIteratorShouldNotFail() + throws Exception { + Iterator emptyIterator = Collections.emptyIterator(); + renderer.render( emptyIterator ); + } + + @Test + public void testRenderWithIteratorWithNullValueShouldNotFail() + throws Exception { + ArrayList list = new ArrayList(); + list.add( null ); + Iterator iteratorWithNullValue = list.iterator(); + renderer.render( iteratorWithNullValue ); + } + + private Java2DTileRenderer createRenderer() { + BufferedImage image = new BufferedImage( 500, 500, TYPE_4BYTE_ABGR ); + Graphics2D graphics = (Graphics2D) image.getGraphics(); + return new Java2DTileRenderer( graphics, 500, 500, + new DefaultEnvelope( mock( Point.class ), mock( Point.class ) ), null ); + } + +} \ No newline at end of file diff --git a/deegree-datastores/deegree-tilestores/deegree-tilestore-geotiff/src/main/java/org/deegree/tile/persistence/geotiff/GeoTIFFTileDataLevel.java b/deegree-datastores/deegree-tilestores/deegree-tilestore-geotiff/src/main/java/org/deegree/tile/persistence/geotiff/GeoTIFFTileDataLevel.java index 72e77849fa..e2e1d714e0 100644 --- a/deegree-datastores/deegree-tilestores/deegree-tilestore-geotiff/src/main/java/org/deegree/tile/persistence/geotiff/GeoTIFFTileDataLevel.java +++ b/deegree-datastores/deegree-tilestores/deegree-tilestore-geotiff/src/main/java/org/deegree/tile/persistence/geotiff/GeoTIFFTileDataLevel.java @@ -40,14 +40,14 @@ Occam Labs UG (haftungsbeschränkt) ----------------------------------------------------------------------------*/ package org.deegree.tile.persistence.geotiff; -import java.io.File; - import org.apache.commons.pool.impl.GenericObjectPool; import org.deegree.geometry.Envelope; import org.deegree.geometry.GeometryFactory; import org.deegree.tile.TileDataLevel; import org.deegree.tile.TileMatrix; +import java.io.File; + /** * The GeoTIFFTileMatrix is a tile matrix handing out GeoTIFFTile tiles. It uses an object pool shared * among all tiles created by this matrix. diff --git a/deegree-layers/deegree-layers-tile/pom.xml b/deegree-layers/deegree-layers-tile/pom.xml index 69e60133cf..642bf739a3 100644 --- a/deegree-layers/deegree-layers-tile/pom.xml +++ b/deegree-layers/deegree-layers-tile/pom.xml @@ -56,6 +56,15 @@ deegree-tilestore-commons ${project.version} + + + junit + junit + + + org.mockito + mockito-core + diff --git a/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayer.java b/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayer.java index 5d103b17dd..f806da0409 100644 --- a/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayer.java +++ b/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayer.java @@ -40,17 +40,12 @@ Occam Labs UG (haftungsbeschränkt) ----------------------------------------------------------------------------*/ package org.deegree.layer.persistence.tile; -import static org.slf4j.LoggerFactory.getLogger; - -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - import org.deegree.commons.ows.exception.OWSException; import org.deegree.cs.coordinatesystems.ICRS; +import org.deegree.cs.exceptions.TransformationException; +import org.deegree.cs.exceptions.UnknownCRSException; import org.deegree.geometry.Envelope; +import org.deegree.geometry.GeometryTransformer; import org.deegree.layer.AbstractLayer; import org.deegree.layer.LayerData; import org.deegree.layer.LayerQuery; @@ -60,6 +55,16 @@ Occam Labs UG (haftungsbeschränkt) import org.deegree.tile.TileDataSet; import org.slf4j.Logger; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.deegree.commons.ows.exception.OWSException.NO_APPLICABLE_CODE; +import static org.deegree.layer.Utils.calcResolution; +import static org.slf4j.LoggerFactory.getLogger; + /** * A layer implementation based on a list of tile data sets. * @@ -88,19 +93,16 @@ public TileLayer( LayerMetadata md, List datasets ) { @Override public TileLayerData mapQuery( LayerQuery query, List headers ) throws OWSException { + double resolution = query.getResolution(); Envelope env = query.getEnvelope(); ICRS crs = env.getCoordinateSystem(); - String tds = coordinateSystems.get( crs ); - if ( tds == null ) { - String msg = "Tile layer " + getMetadata().getName() + " does not offer the coordinate system " - + crs.getAlias(); - LOG.debug( msg ); - throw new OWSException( msg, OWSException.INVALID_CRS ); + Iterator tiles; + if ( tds != null ) { + tiles = retrieveTiles( resolution, env, tds ); + } else { + tiles = retrieveTilesInSourceCrs( query, env, crs ); } - TileDataSet data = tileDataSets.get( tds ); - - Iterator tiles = data.getTiles( env, query.getResolution() ); return new TileLayerData( tiles ); } @@ -128,4 +130,65 @@ public Collection getTileDataSets() { public boolean isStyleApplicable( StyleRef style ) { return true; } -} + + /** + * Retrieves tiles in an available source crs. For that a crs is chosen from the coordinateSystems map, the bounding + * box and the resolution of the query are transformed into that crs and the matching tiles are returned. + * + * @param query + * never null + * @param env + * never null + * @param crs + * never null + * @return tile iterator + * @throws OWSException + */ + Iterator retrieveTilesInSourceCrs( LayerQuery query, Envelope env, ICRS crs ) + throws OWSException { + LOG.debug( "There are no source tiles for coordinate system " + crs.getAlias() ); + Iterator coordinateSystemsIter = coordinateSystems.keySet().iterator(); + if ( coordinateSystemsIter.hasNext() ) { + ICRS alternativeCrs = coordinateSystemsIter.next(); + return retrieveTilesInAlternativeCrs( query, env, alternativeCrs ); + } else { + String msg = "Tile layer " + getMetadata().getName() + " does not offer the coordinate system " + + crs.getAlias(); + LOG.debug( msg ); + throw new OWSException( msg, OWSException.INVALID_CRS ); + } + } + + private Iterator retrieveTilesInAlternativeCrs( LayerQuery query, Envelope env, ICRS alternativeCrs ) + throws OWSException { + LOG.debug( "Using coordinate system " + alternativeCrs.getAlias() + " instead to retrieve source tiles" ); + String alternativeTds = coordinateSystems.get( alternativeCrs ); + Envelope alternativeEnv = retrieveTransformedEnvelope( env, alternativeCrs ); + double alternativeResolution = calcResolution( alternativeEnv, query.getWidth(), query.getHeight() ); + return retrieveTiles( alternativeResolution, alternativeEnv, alternativeTds ); + } + + private Envelope retrieveTransformedEnvelope( Envelope env, ICRS alternativeCrs ) + throws OWSException { + try { + return new GeometryTransformer( alternativeCrs ).transform( env ); + } catch ( TransformationException e ) { + String msg = "Could not transform bounding box to new coordinate system: " + e.getMessage(); + LOG.warn( msg ); + e.printStackTrace(); + throw new OWSException( "Tiles cannot be retrieved: " + msg, NO_APPLICABLE_CODE ); + } catch ( UnknownCRSException e ) { + String msg = "Could not transform bounding box to new coordinate system as the CRS is not known: " + + e.getMessage(); + LOG.warn( msg ); + e.printStackTrace(); + throw new OWSException( "Tiles cannot be retrieved: " + msg, NO_APPLICABLE_CODE ); + } + } + + private Iterator retrieveTiles( double resolution, Envelope env, String tds ) { + TileDataSet data = tileDataSets.get( tds ); + return data.getTiles( env, resolution ); + } + +} \ No newline at end of file diff --git a/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayerData.java b/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayerData.java index 9094a45894..6446480378 100644 --- a/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayerData.java +++ b/deegree-layers/deegree-layers-tile/src/main/java/org/deegree/layer/persistence/tile/TileLayerData.java @@ -40,14 +40,14 @@ Occam Labs UG (haftungsbeschränkt) ----------------------------------------------------------------------------*/ package org.deegree.layer.persistence.tile; -import java.util.Iterator; - import org.deegree.feature.FeatureCollection; import org.deegree.layer.LayerData; import org.deegree.rendering.r2d.TileRenderer; import org.deegree.rendering.r2d.context.RenderContext; import org.deegree.tile.Tile; +import java.util.Iterator; + /** * TileLayerData * @@ -68,9 +68,7 @@ public TileLayerData( Iterator tiles ) { @Override public void render( RenderContext context ) { TileRenderer renderer = context.getTileRenderer(); - while ( tiles.hasNext() ) { - renderer.render( tiles.next() ); - } + renderer.render( tiles ); } @Override diff --git a/deegree-layers/deegree-layers-tile/src/test/org/deegree/layer/persistence/tile/TileLayerTest.java b/deegree-layers/deegree-layers-tile/src/test/org/deegree/layer/persistence/tile/TileLayerTest.java new file mode 100644 index 0000000000..c124c072c0 --- /dev/null +++ b/deegree-layers/deegree-layers-tile/src/test/org/deegree/layer/persistence/tile/TileLayerTest.java @@ -0,0 +1,105 @@ +package org.deegree.layer.persistence.tile; + +import org.deegree.cs.coordinatesystems.ICRS; +import org.deegree.cs.persistence.CRSManager; +import org.deegree.filter.OperatorFilter; +import org.deegree.geometry.Envelope; +import org.deegree.geometry.metadata.SpatialMetadata; +import org.deegree.geometry.precision.PrecisionModel; +import org.deegree.geometry.primitive.Point; +import org.deegree.geometry.standard.DefaultEnvelope; +import org.deegree.layer.LayerQuery; +import org.deegree.layer.metadata.LayerMetadata; +import org.deegree.rendering.r2d.context.MapOptionsMaps; +import org.deegree.style.StyleRef; +import org.deegree.tile.DefaultTileDataSet; +import org.deegree.tile.TileDataLevel; +import org.deegree.tile.TileDataSet; +import org.deegree.tile.TileMatrix; +import org.deegree.tile.TileMatrixSet; +import org.deegree.workspace.ResourceMetadata; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonList; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link org.deegree.layer.persistence.tile.TileLayer}. + * + * @author Dirk Stenger + * @author last edited by: $Author: stenger $ + * + * @version $Revision$, $Date$ + */ +public class TileLayerTest { + + private static List tileDataSets; + + @BeforeClass + public static void setupBeforeClass() + throws Exception { + tileDataSets = createTileDataSetsWithEpsg4326(); + } + + @Test + public void testMapQueryWithEpsg4326ShouldNotCallRetrieveTilesInSourceCrs() + throws Exception { + LayerQuery query = createLayerQuery( "EPSG:4326" ); + TileLayer tileLayer = spy( new TileLayer( mock( LayerMetadata.class ), tileDataSets ) ); + TileLayerData tileLayerData = tileLayer.mapQuery( query, mock( List.class ) ); + + assertThat( tileLayerData, is( notNullValue() ) ); + verify( tileLayer, never() ).retrieveTilesInSourceCrs( any( LayerQuery.class ), any( Envelope.class ), + any( ICRS.class ) ); + } + + @Test + public void testMapQueryWithEpsg25833ShouldCallRetrieveTilesInSourceCrs() + throws Exception { + LayerQuery query = createLayerQuery( "EPSG:25833" ); + TileLayer tileLayer = spy( new TileLayer( mock( LayerMetadata.class ), tileDataSets ) ); + TileLayerData tileLayerData = tileLayer.mapQuery( query, mock( List.class ) ); + + assertThat( tileLayerData, is( notNullValue() ) ); + verify( tileLayer, times( 1 ) ).retrieveTilesInSourceCrs( any( LayerQuery.class ), any( Envelope.class ), + any( ICRS.class ) ); + } + + private static List createTileDataSetsWithEpsg4326() + throws Exception { + List crsList = singletonList( CRSManager.lookup( "EPSG:4326" ) ); + SpatialMetadata spmd = new SpatialMetadata( mock( Envelope.class ), crsList ); + TileMatrixSet tms = new TileMatrixSet( "identifier", "wknScaleSet", mock( List.class ), spmd, + mock( ResourceMetadata.class ) ); + TileDataLevel tdl = mock( TileDataLevel.class ); + doReturn( mock( TileMatrix.class ) ).when( tdl ).getMetadata(); + List tdlList = singletonList( tdl ); + TileDataSet tds = spy( new DefaultTileDataSet( tdlList, tms, "format" ) ); + doReturn( mock( Iterator.class ) ).when( tds ).getTiles( any( Envelope.class ), anyDouble() ); + return singletonList( tds ); + } + + private LayerQuery createLayerQuery( String crs ) + throws Exception { + Envelope envelope = new DefaultEnvelope( "id", CRSManager.lookup( crs ), mock( PrecisionModel.class ), + mock( Point.class ), mock( Point.class ) ); + return new LayerQuery( envelope, 0, 0, mock( StyleRef.class ), mock( OperatorFilter.class ), mock( Map.class ), + mock( Map.class ), 0.0, mock( MapOptionsMaps.class ), envelope ); + } + +} \ No newline at end of file