//$HeadURL$ /*---------------------------------------------------------------------------- This file is part of deegree, http://deegree.org/ Copyright (C) 2001-2011 by: - Department of Geography, University of Bonn - and - lat/lon GmbH - This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact information: lat/lon GmbH Aennchenstr. 19, 53177 Bonn Germany http://lat-lon.de/ Department of Geography, University of Bonn Prof. Dr. Klaus Greve Postfach 1147, 53001 Bonn Germany http://www.geographie.uni-bonn.de/deegree/ e-mail: info@deegree.org ----------------------------------------------------------------------------*/ package org.deegree.securityproxy.wcs.responsefilter.clipping; import static java.io.File.createTempFile; import static org.apache.commons.io.IOUtils.copy; import static org.deegree.securityproxy.service.commons.responsefilter.clipping.ClippingUtils.calculateGeometryVisibleAfterClipping; import static org.deegree.securityproxy.service.commons.responsefilter.clipping.ClippingUtils.isClippingRequired; import static org.deegree.securityproxy.service.commons.responsefilter.clipping.ClippingUtils.transformGeometry; import static org.geotools.referencing.CRS.decode; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.imageio.metadata.IIOMetadataNode; import org.apache.log4j.Logger; import org.deegree.securityproxy.request.OwsRequest; import org.deegree.securityproxy.responsefilter.logging.ResponseClippingReport; import org.deegree.securityproxy.service.commons.responsefilter.clipping.ImageClipper; import org.deegree.securityproxy.service.commons.responsefilter.clipping.exception.ClippingException; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.processing.operation.Crop; import org.geotools.coverage.processing.operation.Resample; import org.geotools.coverage.processing.operation.Rescale; import org.geotools.gce.geotiff.GeoTiffReader; import org.geotools.geometry.GeneralEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; /** * Concrete implementation to clip geotiffs. The image dimension is kept as in the original image as well as the geotiff * header values. Invisible areas are cropped and in the image stored as no data values. * * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> * @author <a href="mailto:stenger@lat-lon.de">Dirk Stenger</a> * @author last edited by: $Author: lyn $ * * @version $Revision: $, $Date: $ */ public class GeotiffClipper implements ImageClipper { private static final Logger LOG = Logger.getLogger( GeotiffClipper.class ); private final CoordinateReferenceSystem visibleAreaCrs; public GeotiffClipper() throws NoSuchAuthorityCodeException, FactoryException { visibleAreaCrs = decode( "EPSG:4326", true ); } @Override public ResponseClippingReport calculateClippedImage( InputStream imageToClip, Geometry visibleArea, OutputStream destination, OwsRequest request ) throws IllegalArgumentException, ClippingException { checkRequiredParameters( imageToClip, destination ); try { File imageToClipAsFile = writeToTempFile( imageToClip ); GeoTiffReader reader = new GeoTiffReader( imageToClipAsFile ); CoordinateReferenceSystem imgCrs = reader.getCrs(); Geometry visibleAreaInImageCrs = transformGeometry( visibleAreaCrs, visibleArea, imgCrs ); GridCoverage2D geotiff = reader.read( null ); IIOMetadataNode metadataRootNode = reader.getMetadata().getRootNode(); GeoTiffWriterModified writer = new GeoTiffWriterModified( destination ); writer.setIIOMetadata( metadataRootNode ); Geometry imgEnvelope = convertImageEnvelopeToGeometry( reader ); if ( isClippingRequired( imgEnvelope, visibleAreaInImageCrs ) ) { LOG.debug( "Clipping is required!" ); GridCoverage2D clippedGeotiff = calculateClippedGeotiff( visibleAreaInImageCrs, geotiff, imgEnvelope ); writer.write( clippedGeotiff, null ); Geometry imageGeometry = convertImageEnvelopeToGeometry( reader ); Geometry visibleAreaAfterClipping = calculateGeometryVisibleAfterClipping( imageGeometry, visibleAreaInImageCrs ); LOG.debug( "Visible area after clipping: " + visibleAreaAfterClipping ); Geometry visibleAreaAfterClippingInOriginalCrs = transformGeometry( visibleAreaCrs, visibleAreaAfterClipping, imgCrs ); return new ResponseClippingReport( visibleAreaAfterClippingInOriginalCrs, true ); } else { LOG.debug( "Clipping is not required!" ); writer.write( geotiff, null ); Geometry imageEnvelopeInOriginalCrs = transformGeometry( visibleAreaCrs, imgEnvelope, imgCrs ); return new ResponseClippingReport( imageEnvelopeInOriginalCrs, false ); } } catch ( Exception e ) { LOG.error( "An error occured during clipping the image!", e ); throw new ClippingException( e ); } } // TODO: dirty hack! coverage should be read from input stream directly private File writeToTempFile( InputStream coverageToClip ) throws IOException { File tempFile = createTempFile( "imageToClip", ".tif" ); LOG.trace( "Response image was written into file: " + tempFile.getAbsolutePath() ); FileOutputStream output = new FileOutputStream( tempFile ); copy( coverageToClip, output ); output.close(); return tempFile; } private void checkRequiredParameters( InputStream imageToClip, OutputStream toWriteImage ) throws IllegalArgumentException { if ( imageToClip == null ) throw new IllegalArgumentException( "Image to clip must not be null!" ); if ( toWriteImage == null ) throw new IllegalArgumentException( "Output stream to write image to must not be null!" ); } private GridCoverage2D calculateClippedGeotiff( Geometry visibleAreaInImageCrs, GridCoverage2D geotiffToWrite, Geometry imageEnvelope ) { GridCoverage2D modifiedCoverageToWrite; if ( visibleAreaInImageCrs.intersects( imageEnvelope ) ) { modifiedCoverageToWrite = executeCropping( geotiffToWrite, visibleAreaInImageCrs ); } else { modifiedCoverageToWrite = executeRescalingToNull( geotiffToWrite ); } return executeResampling( modifiedCoverageToWrite, geotiffToWrite ); } private GridCoverage2D executeCropping( GridCoverage2D coverageToWrite, Geometry croppingArea ) { Crop crop = new Crop(); ParameterValueGroup cropParameters = crop.getParameters(); cropParameters.parameter( "Source" ).setValue( coverageToWrite ); cropParameters.parameter( "ROI" ).setValue( croppingArea ); return (GridCoverage2D) crop.doOperation( cropParameters, null ); } private GridCoverage2D executeResampling( GridCoverage2D coverageToWrite, GridCoverage2D coverageToGetGeometry ) { Resample resample = new Resample(); ParameterValueGroup resampleParameters = resample.getParameters(); resampleParameters.parameter( "Source" ).setValue( coverageToWrite ); resampleParameters.parameter( "GridGeometry" ).setValue( coverageToGetGeometry.getGridGeometry() ); return (GridCoverage2D) resample.doOperation( resampleParameters, null ); } private GridCoverage2D executeRescalingToNull( GridCoverage2D coverageToWrite ) { Rescale rescale = new Rescale(); ParameterValueGroup rescaleParameters = rescale.getParameters(); rescaleParameters.parameter( "Source" ).setValue( coverageToWrite ); rescaleParameters.parameter( "constants" ).setValue( new double[] { 0 } ); return (GridCoverage2D) rescale.doOperation( rescaleParameters, null ); } private Geometry convertImageEnvelopeToGeometry( GeoTiffReader reader ) { GeometryFactory geometryFactory = new GeometryFactory(); GeneralEnvelope imageEnvelope = reader.getOriginalEnvelope(); ReferencedEnvelope envelope = new ReferencedEnvelope( imageEnvelope ); return geometryFactory.toGeometry( envelope ); } }