    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/model/crs/GeoTransformer.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005       Department of Geography, University of Bonn
    006     and
    007       lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035    ----------------------------------------------------------------------------*/
    036    package org.deegree.model.crs;
    037
    038    import java.awt.RenderingHints;
    039    import java.awt.image.BufferedImage;
    040    import java.awt.image.renderable.ParameterBlock;
    041    import java.security.InvalidParameterException;
    042    import java.util.ArrayList;
    043    import java.util.List;
    044    import java.util.Map;
    045    import java.util.Set;
    046
    047    import javax.media.jai.ImageLayout;
    048    import javax.media.jai.Interpolation;
    049    import javax.media.jai.InterpolationNearest;
    050    import javax.media.jai.JAI;
    051    import javax.media.jai.WarpPolynomial;
    052    import javax.vecmath.Point3d;
    053
    054    import org.deegree.crs.coordinatesystems.CoordinateSystem;
    055    import org.deegree.crs.exceptions.TransformationException;
    056    import org.deegree.crs.transformations.Transformation;
    057    import org.deegree.crs.transformations.TransformationFactory;
    058    import org.deegree.framework.log.ILogger;
    059    import org.deegree.framework.log.LoggerFactory;
    060    import org.deegree.graphics.transformation.GeoTransform;
    061    import org.deegree.graphics.transformation.WorldToScreenTransform;
    062    import org.deegree.i18n.Messages;
    063    import org.deegree.model.coverage.grid.AbstractGridCoverage;
    064    import org.deegree.model.coverage.grid.GridCoverage;
    065    import org.deegree.model.coverage.grid.ImageGridCoverage;
    066    import org.deegree.model.feature.Feature;
    067    import org.deegree.model.feature.FeatureCollection;
    068    import org.deegree.model.feature.FeatureProperty;
    069    import org.deegree.model.spatialschema.Curve;
    070    import org.deegree.model.spatialschema.CurveSegment;
    071    import org.deegree.model.spatialschema.Envelope;
    072    import org.deegree.model.spatialschema.Geometry;
    073    import org.deegree.model.spatialschema.GeometryException;
    074    import org.deegree.model.spatialschema.GeometryFactory;
    075    import org.deegree.model.spatialschema.MultiCurve;
    076    import org.deegree.model.spatialschema.MultiPoint;
    077    import org.deegree.model.spatialschema.MultiSurface;
    078    import org.deegree.model.spatialschema.Point;
    079    import org.deegree.model.spatialschema.Position;
    080    import org.deegree.model.spatialschema.Surface;
    081    import org.deegree.model.spatialschema.SurfacePatch;
    082    import org.deegree.ogcbase.OGCException;
    083    import org.deegree.ogcwebservices.wcs.WCSException;
    084    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
    085    import org.deegree.ogcwebservices.wcs.describecoverage.DomainSet;
    086
    087    /**
    088     * class for transforming deegree geometries to new coordinate reference systems.
    089     *
    090     * <p>
    091     * ------------------------------------------------------------
    092     * </p>
    093     *
    094     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    095     * @author last edited by: $Author: mschneider $
    096     *
    097     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    098     */
    099    public class GeoTransformer {
    100
    101        private static ILogger LOG_TRANSORM = LoggerFactory.getLogger( GeoTransformer.class.getCanonicalName()
    102                                                                       + ".TransformLogger" );
    103
    104        private static ILogger LOG = LoggerFactory.getLogger( GeoTransformer.class );
    105
    106        private CoordinateSystem targetCRS = null;
    107
    108        private org.deegree.model.crs.CoordinateSystem targetCRSWrapper = null;
    109
    110        private Transformation definedTransformation = null;
    111
    112        /**
    113         * Creates a new GeoTransformer object.
    114         *
    115         * @param targetCRS
    116         * @throws InvalidParameterException
    117         *             if the given parameter is null.
    118         */
    119        public GeoTransformer( org.deegree.model.crs.CoordinateSystem targetCRS ) throws InvalidParameterException {
    120            if ( targetCRS == null ) {
    121                throw new InvalidParameterException( Messages.getMessage( "CRS_PARAMETER_NOT_NULL",
    122                                                                          "GeoTransformer(CoordinateSystem)", "targetCRS" ) );
    123            }
    124            this.targetCRS = targetCRS.getCRS();
    125            targetCRSWrapper = targetCRS;
    126        }
    127
    128        /**
    129         * Creates a new GeoTransformer object.
    130         *
    131         * @param targetCRS
    132         * @throws InvalidParameterException
    133         *             if the given parameter is null.
    134         */
    135        public GeoTransformer( CoordinateSystem targetCRS ) {
    136            if ( targetCRS == null ) {
    137                throw new InvalidParameterException( Messages.getMessage( "CRS_PARAMETER_NOT_NULL",
    138                                                                          "GeoTransformer(CoordinateSystem)", "targetCRS" ) );
    139            }
    140            this.targetCRS = targetCRS;
    141        }
    142
    143        /**
    144         * @param definedTransformation
    145         *            to use instead of the CRSFactory.
    146         */
    147        public GeoTransformer( Transformation definedTransformation ) {
    148            if ( definedTransformation == null ) {
    149                throw new InvalidParameterException( Messages.getMessage( "CRS_PARAMETER_NOT_NULL",
    150                                                                          "GeoTransformer(CRSTransformation)", "targetCRS" ) );
    151            }
    152            targetCRS = definedTransformation.getTargetCRS();
    153            targetCRSWrapper = new org.deegree.model.crs.CoordinateSystem( targetCRS );
    154            this.definedTransformation = definedTransformation;
    155        }
    156
    157        /**
    158         * Creates a new GeoTransformer object, with the given id as the target CRS.
    159         *
    160         * @param targetCRS
    161         *            an identifier to which all other CRS's shall be transformed.
    162         * @throws UnknownCRSException
    163         *             if the given crs name could not be mapped to a valid (configured) crs.
    164         * @throws InvalidParameterException
    165         *             if the given parameter is null.
    166         */
    167        public GeoTransformer( String targetCRS ) throws UnknownCRSException, InvalidParameterException {
    168            this( CRSFactory.create( targetCRS ) );
    169        }
    170
    171        /**
    172         * transforms a GridCoverage into another coordinate reference system.
    173         *
    174         * @param coverage
    175         *            grid coverage to definedTransformation
    176         * @param targetBBOX
    177         *            envelope for the target coverage
    178         * @param dstWidth
    179         *            width of the output coverage in pixel
    180         * @param dstHeight
    181         *            height of the output coverage in pixel
    182         * @param refPointsGridSize
    183         *            size of the grid used to calculate polynoms coefficients. E.g. 2 -&lg; 4 points, 3 -&lg; 9 points ...<br>
    184         *            Must be &lg;= 2. Accuracy of coefficients increase with size of the grid. Speed decreases with size of
    185         *            the grid.
    186         * @param degree
    187         *            The degree of the polynomial is supplied as an argument.
    188         * @param interpolation
    189         *            interpolation method for warping the passed coverage. Can be <code>null</code>. In this case 'Nearest
    190         *            Neighbor' will be used as default
    191         * @return a transformed GridCoverage.
    192         * @throws CRSTransformationException
    193         *             if the gridCoverage could not be created or the transformation failed
    194         */
    195        public GridCoverage transform( AbstractGridCoverage coverage, Envelope targetBBOX, int dstWidth, int dstHeight,
    196                                       int refPointsGridSize, int degree, Interpolation interpolation )
    197                                throws CRSTransformationException {
    198
    199            BufferedImage img = coverage.getAsImage( -1, -1 );
    200            Position min = coverage.getEnvelope().getMin();
    201            Position max = coverage.getEnvelope().getMax();
    202
    203            // create transformation object to definedTransformation reference points
    204            // from the target CRS to the source (native) CRS
    205            org.deegree.model.crs.CoordinateSystem crs = coverage.getCoordinateReferenceSystem();
    206
    207            Envelope sourceBBOX = GeometryFactory.createEnvelope( min.getX(), min.getY(), max.getX(), max.getY(), crs );
    208
    209            img = transform( img, sourceBBOX, targetBBOX, dstWidth, dstHeight, refPointsGridSize, degree, interpolation );
    210
    211            // create a new GridCoverage from the warp result.
    212            // because warping only can be performed on images the
    213            // resulting GridCoverage will be an instance of ImageGridCoverage
    214            CoverageOffering oldCO = coverage.getCoverageOffering();
    215            CoverageOffering coverageOffering = null;
    216            if ( oldCO != null ) {
    217                try {
    218                    DomainSet ds = oldCO.getDomainSet();
    219                    ds.getSpatialDomain().setEnvelops( new Envelope[] { targetBBOX } );
    220                    coverageOffering = new CoverageOffering( oldCO.getName(), oldCO.getLabel(), oldCO.getDescription(),
    221                                                             oldCO.getMetadataLink(), oldCO.getLonLatEnvelope(),
    222                                                             oldCO.getKeywords(), ds, oldCO.getRangeSet(),
    223                                                             oldCO.getSupportedCRSs(), oldCO.getSupportedFormats(),
    224                                                             oldCO.getSupportedInterpolations(), oldCO.getExtension() );
    225                } catch ( WCSException e ) {
    226                    throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
    227                                                                               crs.getIdentifier(),
    228                                                                               targetCRS.getIdentifier(), e.getMessage() ),
    229                                                          e );
    230                } catch ( OGCException e ) {
    231                    throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
    232                                                                               crs.getIdentifier(),
    233                                                                               targetCRS.getIdentifier(), e.getMessage() ),
    234                                                          e );
    235                }
    236            }
    237
    238            return new ImageGridCoverage( coverageOffering, targetBBOX, img );
    239        }
    240
    241        /**
    242         * transforms an image into another coordinate reference system.
    243         *
    244         * @param img
    245         *            the image to definedTransformation
    246         * @param sourceBBOX
    247         *            envelope of the source image
    248         * @param targetBBOX
    249         *            envelope for the target image
    250         * @param dstWidth
    251         *            width of the output image in pixel
    252         * @param dstHeight
    253         *            height of the output image in pixel
    254         * @param refPointsGridSize
    255         *            size of the grid used to calculate polynoms coefficients. E.g. 2 -&lg; 4 points, 3 -&lg; 9 points ...<br>
    256         *            Must be &lg;= 2. Accuracy of coefficients increase with size of the grid. Speed decreases with size of
    257         *            the grid.
    258         * @param degree
    259         *            The degree of the polynomial is supplied as an argument.
    260         * @param interpolation
    261         *            interpolation method for warping the passed image. Can be <code>null</code>. In this case 'Nearest
    262         *            Neighbor' will be used as default
    263         * @return a transformed image.
    264         * @throws CRSTransformationException
    265         *             if the image could not be created or the transformation failed
    266         */
    267        public BufferedImage transform( BufferedImage img, Envelope sourceBBOX, Envelope targetBBOX, int dstWidth,
    268                                        int dstHeight, int refPointsGridSize, int degree, Interpolation interpolation )
    269                                throws CRSTransformationException {
    270
    271            // create transformation object to definedTransformation reference points
    272            // from the target CRS to the source (native) CRS
    273            org.deegree.model.crs.CoordinateSystem crs = sourceBBOX.getCoordinateSystem();
    274            // org.deegree.model.crs.CoordinateSystem targetMCRS = new org.deegree.model.crs.CoordinateSystem( targetCRS,
    275            // null );
    276
    277            GeoTransform sourceGT = new WorldToScreenTransform( sourceBBOX.getMin().getX(), sourceBBOX.getMin().getY(),
    278                                                                sourceBBOX.getMax().getX(), sourceBBOX.getMax().getY(), 0,
    279                                                                0, img.getWidth() - 1, img.getHeight() - 1 );
    280            GeoTransform targetGT = new WorldToScreenTransform( targetBBOX.getMin().getX(), targetBBOX.getMin().getY(),
    281                                                                targetBBOX.getMax().getX(), targetBBOX.getMax().getY(), 0,
    282                                                                0, dstWidth - 1, dstHeight - 1 );
    283
    284            // create/calculate reference points
    285            float dx = ( dstWidth - 1 ) / (float) ( refPointsGridSize - 1 );
    286            float dy = ( dstHeight - 1 ) / (float) ( refPointsGridSize - 1 );
    287            float[] srcCoords = new float[refPointsGridSize * refPointsGridSize * 2];
    288            float[] targetCoords = new float[refPointsGridSize * refPointsGridSize * 2];
    289            int k = 0;
    290
    291            GeoTransformer sourceCoordGT = new GeoTransformer( crs );
    292            for ( int i = 0; i < refPointsGridSize; i++ ) {
    293                for ( int j = 0; j < refPointsGridSize; j++ ) {
    294                    targetCoords[k] = i * dx;
    295                    targetCoords[k + 1] = j * dy;
    296                    double x = targetGT.getSourceX( targetCoords[k] );
    297                    double y = targetGT.getSourceY( targetCoords[k + 1] );
    298                    Point point = GeometryFactory.createPoint( x, y, this.targetCRSWrapper );
    299                    point = (Point) sourceCoordGT.transform( point );
    300                    srcCoords[k] = (float) sourceGT.getDestX( point.getX() );
    301                    srcCoords[k + 1] = (float) sourceGT.getDestY( point.getY() );
    302                    // LOG.logDebug( String.format( "%.4f %.4f -> %.4f %.4f ",
    303                    // srcCoords[k], srcCoords[k+1],
    304                    // targetCoords[k], targetCoords[k+1]) );
    305                    k += 2;
    306                }
    307            }
    308
    309            // create warp object from reference points and desired interpolation
    310            WarpPolynomial warp = WarpPolynomial.createWarp( srcCoords, 0, targetCoords, 0, srcCoords.length, 1f, 1f, 1f,
    311                                                             1f, degree );
    312
    313            if ( interpolation == null ) {
    314                interpolation = new InterpolationNearest();
    315            }
    316
    317            // Create and perform the warp operation.
    318            ParameterBlock pb = new ParameterBlock();
    319            pb.addSource( img );
    320            pb.add( warp );
    321            pb.add( interpolation );
    322            pb.add( new double[] { 0 } );
    323
    324            // Limit output size, otherwise the result will be a bit larger
    325            // (the polynomial warp will overlap the correct image border)
    326            ImageLayout layout = new ImageLayout();
    327            layout.setMinX( 0 );
    328            layout.setMinY( 0 );
    329            layout.setWidth( dstWidth );
    330            layout.setHeight( dstHeight );
    331            RenderingHints rh = new RenderingHints( JAI.KEY_IMAGE_LAYOUT, layout );
    332
    333            return JAI.create( "warp", pb, rh ).getAsBufferedImage();
    334        }
    335
    336        /**
    337         * transforms a GridCoverage into another coordinate reference system.
    338         *
    339         * @param coverage
    340         *            grid coverage to definedTransformation
    341         * @param refPointsGridSize
    342         *            size of the grid used to calculate polynoms coefficients. E.g. 2 -&lg; 4 points, 3 -&lg; 9 points ...<br>
    343         *            Must be &lg;= 2. Accuracy of coefficients increase with size of the grid. Speed decreases with size of
    344         *            the grid.
    345         * @param degree
    346         *            The degree of the polynomial is supplied as an argument.
    347         * @param interpolation
    348         *            interpolation method for warping the passed coverage. Can be <code>null</code>. In this case 'Nearest
    349         *            Neighbor' will be used as default
    350         * @return a transformed GridCoverage.
    351         * @throws CRSTransformationException
    352         *             if the gridCoverage could not be created or the transformation failed
    353         */
    354        @Deprecated
    355        public GridCoverage transform( AbstractGridCoverage coverage, int refPointsGridSize, int degree,
    356                                       Interpolation interpolation )
    357                                throws CRSTransformationException {
    358
    359            BufferedImage img = coverage.getAsImage( -1, -1 );
    360            Position min = coverage.getEnvelope().getMin();
    361            Position max = coverage.getEnvelope().getMax();
    362
    363            // create transformation object to definedTransformation reference points
    364            // from the source (native) CRS to the target CRS
    365            org.deegree.model.crs.CoordinateSystem crs = coverage.getCoordinateReferenceSystem();
    366            Envelope sourceBBOX = GeometryFactory.createEnvelope( min.getX(), min.getY(), max.getX(), max.getY(), crs );
    367            Envelope targetBBOX = transform( sourceBBOX, crs );
    368
    369            GeoTransform sourceGT = new WorldToScreenTransform( sourceBBOX.getMin().getX(), sourceBBOX.getMin().getY(),
    370                                                                sourceBBOX.getMax().getX(), sourceBBOX.getMax().getY(), 0,
    371                                                                0, img.getWidth() - 1, img.getHeight() - 1 );
    372            GeoTransform targetGT = new WorldToScreenTransform( targetBBOX.getMin().getX(), targetBBOX.getMin().getY(),
    373                                                                targetBBOX.getMax().getX(), targetBBOX.getMax().getY(), 0,
    374                                                                0, img.getWidth() - 1, img.getHeight() - 1 );
    375
    376            // create/calculate reference points
    377            float dx = img.getWidth() / (float) ( refPointsGridSize - 1 );
    378            float dy = img.getHeight() / (float) ( refPointsGridSize - 1 );
    379            float[] srcCoords = new float[refPointsGridSize * refPointsGridSize * 2];
    380            float[] targetCoords = new float[refPointsGridSize * refPointsGridSize * 2];
    381            int k = 0;
    382            for ( int i = 0; i < refPointsGridSize; i++ ) {
    383                for ( int j = 0; j < refPointsGridSize; j++ ) {
    384                    srcCoords[k] = i * dx;
    385                    srcCoords[k + 1] = j * dy;
    386                    double x = sourceGT.getSourceX( srcCoords[k] );
    387                    double y = sourceGT.getSourceY( srcCoords[k + 1] );
    388                    Point point = GeometryFactory.createPoint( x, y, crs );
    389                    point = (Point) transform( point );
    390                    targetCoords[k] = (float) targetGT.getDestX( point.getX() );
    391                    targetCoords[k + 1] = (float) targetGT.getDestY( point.getY() );
    392                    k += 2;
    393                }
    394            }
    395
    396            // create warp object from reference points and desired interpolation
    397            WarpPolynomial warp = WarpPolynomial.createWarp( srcCoords, 0, targetCoords, 0, srcCoords.length, 1f, 1f, 1f,
    398                                                             1f, degree );
    399
    400            if ( interpolation == null ) {
    401                interpolation = new InterpolationNearest();
    402            }
    403
    404            // Create and perform the warp operation.
    405            ParameterBlock pb = new ParameterBlock();
    406            pb.addSource( img );
    407            pb.add( warp );
    408            pb.add( interpolation );
    409
    410            img = JAI.create( "warp", pb ).getAsBufferedImage();
    411
    412            // create a new GridCoverage from the warp result.
    413            // because warping only can be performed on images the
    414            // resulting GridCoverage will be an instance of ImageGridCoverage
    415            CoverageOffering oldCO = coverage.getCoverageOffering();
    416            CoverageOffering coverageOffering = null;
    417            if ( oldCO != null ) {
    418                try {
    419                    DomainSet ds = oldCO.getDomainSet();
    420                    ds.getSpatialDomain().setEnvelops( new Envelope[] { targetBBOX } );
    421                    coverageOffering = new CoverageOffering( oldCO.getName(), oldCO.getLabel(), oldCO.getDescription(),
    422                                                             oldCO.getMetadataLink(), oldCO.getLonLatEnvelope(),
    423                                                             oldCO.getKeywords(), ds, oldCO.getRangeSet(),
    424                                                             oldCO.getSupportedCRSs(), oldCO.getSupportedFormats(),
    425                                                             oldCO.getSupportedInterpolations(), oldCO.getExtension() );
    426                } catch ( WCSException e ) {
    427                    throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
    428                                                                               crs.getIdentifier(),
    429                                                                               targetCRS.getIdentifier(), e.getMessage() ),
    430                                                          e );
    431                } catch ( OGCException e ) {
    432                    throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
    433                                                                               crs.getIdentifier(),
    434                                                                               targetCRS.getIdentifier(), e.getMessage() ),
    435                                                          e );
    436                }
    437            }
    438            return new ImageGridCoverage( coverageOffering, sourceBBOX, img );
    439        }
    440
    441        /**
    442         * Transforms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
    443         *
    444         * @param envelope
    445         *            to definedTransformation
    446         * @param sourceCRS
    447         *            CRS of the envelope
    448         * @return the transformed envelope
    449         * @throws CRSTransformationException
    450         */
    451        public Envelope transform( Envelope envelope, org.deegree.model.crs.CoordinateSystem sourceCRS )
    452                                throws CRSTransformationException {
    453
    454            Point min = GeometryFactory.createPoint( envelope.getMin().getX(), envelope.getMin().getY(), sourceCRS );
    455            Point max = GeometryFactory.createPoint( envelope.getMax().getX(), envelope.getMax().getY(), sourceCRS );
    456            min = (Point) transform( min );
    457            max = (Point) transform( max );
    458            // create bounding box with coordinates
    459            return GeometryFactory.createEnvelope( min.getX(), min.getY(), max.getX(), max.getY(), targetCRSWrapper );
    460        }
    461
    462        /**
    463         * Transforms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
    464         *
    465         * This transformation takes rotation and distortion into account when regardDistortion is true. Otherwise the
    466         * transformed envelope may not contain the whole input envelope.
    467         *
    468         * @param envelope
    469         *            to definedTransformation
    470         * @param sourceCRS
    471         *            CRS of the envelope
    472         * @param regardDistortion
    473         *
    474         * @return the transformed envelope
    475         * @throws CRSTransformationException
    476         * @throws UnknownCRSException
    477         */
    478        public Envelope transform( Envelope envelope, String sourceCRS, boolean regardDistortion )
    479                                throws CRSTransformationException, UnknownCRSException {
    480            org.deegree.model.crs.CoordinateSystem crs = CRSFactory.create( sourceCRS );
    481            return transform( envelope, crs, regardDistortion );
    482        }
    483
    484        /**
    485         * Transforms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
    486         *
    487         * This transformation takes rotation and distortion into account when regardDistortion is true. Otherwise the
    488         * transformed envelope may not contain the whole input envelope.
    489         *
    490         * @param envelope
    491         *            to definedTransformation
    492         * @param sourceCRS
    493         *            CRS of the envelope
    494         * @param regardDistortion
    495         *
    496         * @return the transformed envelope
    497         * @throws CRSTransformationException
    498         */
    499        public Envelope transform( Envelope envelope, org.deegree.model.crs.CoordinateSystem sourceCRS,
    500                                   boolean regardDistortion )
    501                                throws CRSTransformationException {
    502
    503            if ( !regardDistortion ) {
    504                return transform( envelope, sourceCRS );
    505            }
    506            double x1 = envelope.getMin().getX();
    507            double y1 = envelope.getMin().getY();
    508            double x2 = envelope.getMax().getX();
    509            double y2 = envelope.getMax().getY();
    510
    511            double width = envelope.getWidth();
    512            double height = envelope.getHeight();
    513
    514            // definedTransformation with 8 points instead of only the min and max point
    515            double[] coords = new double[] { x1, y1, x1, y2, x2, y2, x2, y1, x1, y1 + height, x1 + width, y1, x2,
    516                                            y1 + height, x1 + width, y2 };
    517
    518            Geometry envelopeGeom = null;
    519            try {
    520                envelopeGeom = GeometryFactory.createCurve( coords, 2, sourceCRS );
    521            } catch ( GeometryException e ) {
    522                throw new CRSTransformationException( Messages.getMessage( "CRS_TRANSFORMATION_ERROR",
    523                                                                           sourceCRS.getIdentifier(),
    524                                                                           targetCRS.getIdentifier(), e.getMessage() ), e );
    525            }
    526            envelopeGeom = transform( envelopeGeom );
    527
    528            return envelopeGeom.getEnvelope();
    529
    530        }
    531
    532        /**
    533         *
    534         * @param sourceCRS
    535         *            in which the given points are referenced.
    536         * @param points
    537         *            to definedTransformation.
    538         * @return a list of transformed point3d's or an empty list if something went wrong, never <code>null</code>
    539         * @throws CRSTransformationException
    540         *             if no transformation could be created for the given source and target crs.
    541         * @throws IllegalArgumentException
    542         *             if the sourceCRS is <code>null</code>
    543         */
    544        public List<Point3d> transform( org.deegree.model.crs.CoordinateSystem sourceCRS, List<Point3d> points )
    545                                throws CRSTransformationException, IllegalArgumentException {
    546            if ( points == null || points.size() == 0 ) {
    547                return new ArrayList<Point3d>();
    548            }
    549            if ( sourceCRS == null ) {
    550                throw new IllegalArgumentException( Messages.getMessage( "CRS_NO_SOURCE_CRS" ) );
    551            }
    552            CoordinateSystem sCRS = sourceCRS.getCRS();
    553            Transformation trans = checkOrCreateTransformation( sCRS );
    554
    555            List<Point3d> result = new ArrayList<Point3d>( points.size() );
    556            TransformationException exception = null;
    557            try {
    558                result = trans.doTransform( points );
    559            } catch ( TransformationException te ) {
    560                List<Point3d> tResult = te.getTransformedPoints();
    561                if ( tResult != null && tResult.size() > 0 ) {
    562                    result = tResult;
    563                }
    564                exception = te;
    565            }
    566
    567            if ( LOG_TRANSORM.isDebug() || LOG.isDebug() ) {
    568                Map<Integer, String> errorMessages = null;
    569                if ( exception != null ) {
    570                    errorMessages = exception.getTransformErrors();
    571                }
    572                for ( int i = 0; i < points.size(); ++i ) {
    573                    StringBuilder sb = new StringBuilder( 1000 );
    574                    Point3d coord = points.get( i );
    575                    Point3d resultCoord = result.get( i );
    576                    if ( resultCoord == null ) {
    577                        resultCoord = new Point3d( coord );
    578                    }
    579                    sb.append( trans.getSourceCRS().getIdentifier() );
    580                    sb.append( ";" );
    581                    sb.append( coord.x );
    582                    sb.append( ";" );
    583                    sb.append( coord.y );
    584                    sb.append( ";" );
    585                    if ( trans.getSourceDimension() == 3 ) {
    586                        sb.append( coord.z );
    587                        sb.append( ";" );
    588                    }
    589                    sb.append( trans.getTargetCRS().getIdentifier() );
    590                    sb.append( ";" );
    591                    sb.append( resultCoord.x );
    592                    sb.append( ";" );
    593                    sb.append( resultCoord.y );
    594                    sb.append( ";" );
    595                    if ( trans.getTargetDimension() == 3 ) {
    596                        sb.append( resultCoord.z );
    597                        sb.append( ";" );
    598                    }
    599                    String successString = "Success";
    600                    if ( errorMessages != null ) {
    601                        String tmp = errorMessages.get( new Integer( i ) );
    602                        if ( tmp != null && !"".equals( tmp.trim() ) ) {
    603                            successString = tmp;
    604                        }
    605                    }
    606                    sb.append( successString );
    607                    LOG_TRANSORM.logDebug( sb.toString() );
    608                    // LOG.logDebug( sb.toString() );
    609                }
    610            }
    611            if ( result == null ) {
    612                result = new ArrayList<Point3d>();
    613            }
    614            return result;
    615        }
    616
    617        /**
    618         * Transforms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
    619         *
    620         * @param envelope
    621         *            to definedTransformation
    622         * @param sourceCRS
    623         *            CRS of the envelope
    624         * @return the transformed envelope
    625         * @throws CRSTransformationException
    626         *             if the transformation did not succeed.
    627         * @throws UnknownCRSException
    628         *             if the given string is unknown to the CRSProvider
    629         */
    630        public Envelope transform( Envelope envelope, String sourceCRS )
    631                                throws CRSTransformationException, UnknownCRSException {
    632
    633            org.deegree.model.crs.CoordinateSystem crs = CRSFactory.create( sourceCRS );
    634            return transform( envelope, crs );
    635        }
    636
    637        /**
    638         * transforms all geometries contained within the passed {@link Feature} into the target CRS of a GeoTransformer
    639         * instance. If a geometry was transformed the {@link Feature#setEnvelopesUpdated()} method will be called.
    640         *
    641         * @param feature
    642         * @return the transformed geometries in the given Feature.
    643         * @throws CRSTransformationException
    644         */
    645        public Feature transform( Feature feature )
    646                                throws CRSTransformationException {
    647            if ( feature != null ) {
    648                FeatureProperty[] featureProperties = feature.getProperties();
    649                if ( featureProperties != null ) {
    650                    for ( FeatureProperty fp : featureProperties ) {
    651                        if ( fp != null ) {
    652                            Object value = fp.getValue();
    653                            if ( value != null ) {
    654                                if ( value instanceof Geometry ) {
    655                                    Geometry geom = (Geometry) value;
    656                                    if ( !targetCRSWrapper.equals( geom.getCoordinateSystem() ) ) {
    657                                        fp.setValue( transform( geom ) );
    658                                        feature.setEnvelopesUpdated();
    659                                    }
    660                                } else if ( value instanceof Feature ) {
    661                                    transform( (Feature) value );
    662                                }
    663                            }
    664                        }
    665                    }
    666                }
    667            }
    668            return feature;
    669        }
    670
    671        /**
    672         * transforms all geometries contained within the passed {@link FeatureCollection} into the target CRS of a
    673         * GeoTransformer instance.
    674         *
    675         * @param fc
    676         *            the collection to definedTransformation
    677         * @return the transformed geometries in the FeatureCollection
    678         * @throws CRSTransformationException
    679         *             if the transformation cannot be created or processed.
    680         */
    681        public FeatureCollection transform( FeatureCollection fc )
    682                                throws CRSTransformationException {
    683            for ( int i = 0; i < fc.size(); i++ ) {
    684                transform( fc.getFeature( i ) );
    685            }
    686            // signal that the envelope properties might have been updated.
    687            fc.setEnvelopesUpdated();
    688            return fc;
    689        }
    690
    691        /**
    692         * transforms the coordinates of a deegree geometry to the target coordinate reference system.
    693         *
    694         * @param geo
    695         *            to be transformed
    696         * @return the same geometry in a different crs.
    697         * @throws CRSTransformationException
    698         *             if the transformation between the source and target crs cannot be created.
    699         * @throws IllegalArgumentException
    700         *             if the coordinates system of the geometry is <code>null</code>
    701         */
    702        public Geometry transform( Geometry geo )
    703                                throws CRSTransformationException, IllegalArgumentException {
    704
    705            if ( geo.getCoordinateSystem() == null ) {
    706                throw new IllegalArgumentException( Messages.getMessage( "CRS_GEOMETRY_HAS_NO_CRS" ) );
    707            }
    708
    709            return transform( geo, geo.getCoordinateSystem().getCRS() );
    710        }
    711
    712        /**
    713         * @param geo
    714         * @param sourceCRS
    715         * @return a transformed geometry
    716         * @throws CRSTransformationException
    717         */
    718        public Geometry transform( Geometry geo, CoordinateSystem sourceCRS )
    719                                throws CRSTransformationException {
    720            Transformation trans = checkOrCreateTransformation( sourceCRS );
    721
    722            try {
    723                if ( geo instanceof Point ) {
    724                    geo = transform( (Point) geo, trans );
    725                } else if ( geo instanceof Curve ) {
    726                    geo = transform( (Curve) geo, trans );
    727                } else if ( geo instanceof Surface ) {
    728                    geo = transform( (Surface) geo, trans );
    729                } else if ( geo instanceof MultiPoint ) {
    730                    geo = transform( (MultiPoint) geo, trans );
    731                } else if ( geo instanceof MultiCurve ) {
    732                    geo = transform( (MultiCurve) geo, trans );
    733                } else if ( geo instanceof MultiSurface ) {
    734                    geo = transform( (MultiSurface) geo, trans );
    735                }
    736            } catch ( GeometryException ge ) {
    737                throw new CRSTransformationException( Messages.getMessage( "CRS_TRANSFORMATION_ERROR",
    738                                                                           sourceCRS.getIdentifier(),
    739                                                                           targetCRSWrapper.getIdentifier(),
    740                                                                           ge.getMessage() ), ge );
    741            }
    742            return geo;
    743        }
    744
    745        /**
    746         * transforms the submitted curve to the target coordinate reference system
    747         *
    748         * @throws GeometryException
    749         */
    750        private Geometry transform( Curve geo, Transformation trans )
    751                                throws GeometryException {
    752            CurveSegment[] newcus = new CurveSegment[geo.getNumberOfCurveSegments()];
    753            for ( int i = 0; i < geo.getNumberOfCurveSegments(); i++ ) {
    754                CurveSegment cus = geo.getCurveSegmentAt( i );
    755                Position[] pos = cus.getPositions();
    756                pos = transform( pos, trans );
    757                newcus[i] = GeometryFactory.createCurveSegment( pos, targetCRSWrapper );
    758            }
    759            return GeometryFactory.createCurve( newcus );
    760        }
    761
    762        /**
    763         * transforms the submitted multi curve to the target coordinate reference system
    764         *
    765         * @throws GeometryException
    766         */
    767        private Geometry transform( MultiCurve geo, Transformation trans )
    768                                throws GeometryException {
    769            Curve[] curves = new Curve[geo.getSize()];
    770            for ( int i = 0; i < geo.getSize(); i++ ) {
    771                curves[i] = (Curve) transform( geo.getCurveAt( i ), trans );
    772            }
    773            return GeometryFactory.createMultiCurve( curves );
    774        }
    775
    776        /**
    777         * transforms the submitted multi point to the target coordinate reference system
    778         */
    779        private Geometry transform( MultiPoint geo, Transformation trans ) {
    780            Point[] points = new Point[geo.getSize()];
    781            for ( int i = 0; i < geo.getSize(); i++ ) {
    782                points[i] = (Point) transform( geo.getPointAt( i ), trans );
    783            }
    784            return GeometryFactory.createMultiPoint( points );
    785        }
    786
    787        /**
    788         * transforms the submitted multi surface to the target coordinate reference system
    789         *
    790         * @throws GeometryException
    791         */
    792        private Geometry transform( MultiSurface geo, Transformation trans )
    793                                throws GeometryException {
    794            Surface[] surfaces = new Surface[geo.getSize()];
    795            for ( int i = 0; i < geo.getSize(); i++ ) {
    796                surfaces[i] = (Surface) transform( geo.getSurfaceAt( i ), trans );
    797            }
    798            return GeometryFactory.createMultiSurface( surfaces );
    799        }
    800
    801        /**
    802         * transforms the submitted point to the target coordinate reference system
    803         */
    804        private Geometry transform( Point geo, Transformation trans ) {
    805            Point3d coord = geo.getPosition().getAsPoint3d();
    806            Point3d result = new Point3d( coord );
    807            TransformationException exception = null;
    808            try {
    809                result = trans.doTransform( coord );
    810            } catch ( TransformationException te ) {
    811                List<Point3d> tResult = te.getTransformedPoints();
    812                if ( tResult != null && tResult.size() > 0 ) {
    813                    result = tResult.get( 0 );
    814                }
    815                exception = te;
    816            }
    817
    818            if ( LOG_TRANSORM.isDebug() || LOG.isDebug() ) {
    819                StringBuilder sb = new StringBuilder( trans.getSourceCRS().getIdentifier() );
    820                sb.append( ";" );
    821                sb.append( coord.x );
    822                sb.append( ";" );
    823                sb.append( coord.y );
    824                sb.append( ";" );
    825                if ( trans.getSourceDimension() == 3 ) {
    826                    sb.append( coord.z );
    827                    sb.append( ";" );
    828                }
    829                sb.append( trans.getTargetCRS().getIdentifier() );
    830                sb.append( ";" );
    831                sb.append( result.x );
    832                sb.append( ";" );
    833                sb.append( result.y );
    834                sb.append( ";" );
    835                if ( trans.getTargetDimension() == 3 ) {
    836                    sb.append( result.z );
    837                    sb.append( ";" );
    838                }
    839                if ( exception != null ) {
    840                    Map<Integer, String> messages = exception.getTransformErrors();
    841                    if ( !messages.isEmpty() ) {
    842                        Set<Integer> keys = messages.keySet();
    843                        for ( Integer key : keys ) {
    844                            sb.append( messages.get( key ) );
    845                        }
    846                    }
    847                } else {
    848                    sb.append( "Success" );
    849                }
    850                LOG_TRANSORM.logDebug( sb.toString() );
    851                // LOG.logDebug( sb.toString() );
    852            }
    853            return GeometryFactory.createPoint( result.x, result.y, ( targetCRS.getDimension() == 3 ) ? result.z
    854                                                                                                     : Double.NaN,
    855                                                targetCRSWrapper );
    856        }
    857
    858        /**
    859         * transforms an array of Positions to the target coordinate reference system
    860         */
    861        private Position[] transform( Position[] pos, Transformation trans ) {
    862
    863            Position[] newpos = new Position[pos.length];
    864            // boolean srcRad = trans.getSourceCRS().getUnits().equals( Unit.DEGREE );
    865            // boolean targetRad = trans.getTargetCRS().getUnits().equals( Unit.DEGREE );
    866
    867            List<Point3d> coords = new ArrayList<Point3d>( pos.length );
    868            for ( Position p : pos ) {
    869                // if ( srcRad ) {
    870                // coords.add( new Point3d( Math.toRadians( p.getX() ), Math.toRadians( p.getY() ),
    871                // p.getZ() ) );
    872                // } else {
    873                coords.add( new Point3d( p.getAsPoint3d() ) );
    874                // }
    875            }
    876            // List<Point3d> result = trans.doTransform( coords );
    877            List<Point3d> result = new ArrayList<Point3d>( coords.size() );
    878            TransformationException exception = null;
    879            try {
    880                result = trans.doTransform( coords );
    881            } catch ( TransformationException te ) {
    882                List<Point3d> tResult = te.getTransformedPoints();
    883                if ( tResult != null && tResult.size() > 0 ) {
    884                    result = tResult;
    885                }
    886                exception = te;
    887            }
    888
    889            if ( LOG_TRANSORM.isDebug() || LOG.isDebug() ) {
    890                Map<Integer, String> errorMessages = null;
    891                if ( exception != null ) {
    892                    errorMessages = exception.getTransformErrors();
    893                }
    894
    895                for ( int i = 0; i < coords.size(); ++i ) {
    896                    StringBuilder sb = new StringBuilder( 1000 );
    897                    Point3d coord = coords.get( i );
    898                    Point3d resultCoord = result.get( i );
    899                    if ( resultCoord == null ) {
    900                        resultCoord = new Point3d( coord );
    901                    }
    902                    sb.append( trans.getSourceCRS().getIdentifier() );
    903                    sb.append( ";" );
    904                    sb.append( coord.x );
    905                    sb.append( ";" );
    906                    sb.append( coord.y );
    907                    sb.append( ";" );
    908                    if ( trans.getSourceDimension() == 3 ) {
    909                        sb.append( coord.z );
    910                        sb.append( ";" );
    911                    }
    912                    sb.append( trans.getTargetCRS().getIdentifier() );
    913                    sb.append( ";" );
    914                    sb.append( resultCoord.x );
    915                    sb.append( ";" );
    916                    sb.append( resultCoord.y );
    917                    sb.append( ";" );
    918                    if ( trans.getTargetDimension() == 3 ) {
    919                        sb.append( resultCoord.z );
    920                        sb.append( ";" );
    921                    }
    922                    String successString = "Success";
    923                    if ( errorMessages != null ) {
    924                        String tmp = errorMessages.get( new Integer( i ) );
    925                        if ( tmp != null && !"".equals( tmp.trim() ) ) {
    926                            successString = tmp;
    927                        }
    928                    }
    929                    sb.append( successString );
    930                    LOG_TRANSORM.logDebug( sb.toString() );
    931                    // LOG.logDebug( sb.toString() );
    932                }
    933
    934            }
    935            for ( int i = 0; i < result.size(); ++i ) {
    936                Point3d p = result.get( i );
    937                if ( p != null && i < newpos.length ) {
    938                    newpos[i] = GeometryFactory.createPosition( p );
    939                }
    940            }
    941            return newpos;
    942        }
    943
    944        /**
    945         * transforms the submitted surface to the target coordinate reference system
    946         *
    947         * @throws GeometryException
    948         *             if the surface cannot be retrieved or if the surface patch could not be created.
    949         */
    950        private Geometry transform( Surface geo, Transformation trans )
    951                                throws GeometryException {
    952            int cnt = geo.getNumberOfSurfacePatches();
    953            SurfacePatch[] patches = new SurfacePatch[cnt];
    954
    955            for ( int i = 0; i < cnt; i++ ) {
    956                SurfacePatch p = geo.getSurfacePatchAt( i );
    957                Position[] ex = p.getExteriorRing();
    958                ex = transform( ex, trans );
    959
    960                Position[][] innerRings = p.getInteriorRings();
    961                Position[][] transformedInnerRings = null;
    962
    963                if ( innerRings != null ) {
    964                    transformedInnerRings = new Position[innerRings.length][];
    965
    966                    for ( int k = 0; k < innerRings.length; k++ ) {
    967                        transformedInnerRings[k] = transform( innerRings[k], trans );
    968                    }
    969                }
    970
    971                patches[i] = GeometryFactory.createSurfacePatch( ex, transformedInnerRings, p.getInterpolation(),
    972                                                                 this.targetCRSWrapper );
    973            }
    974
    975            // at the moment only polygons made of one patch are supported
    976            return GeometryFactory.createSurface( patches[0] );
    977        }
    978
    979        /**
    980         * Simple method to check for the CRS transformation to use.
    981         *
    982         * @param sourceCRS
    983         * @throws CRSTransformationException
    984         */
    985        private synchronized Transformation checkOrCreateTransformation(
    986                                                                         org.deegree.crs.coordinatesystems.CoordinateSystem sourceCRS )
    987                                throws CRSTransformationException {
    988            if ( definedTransformation == null
    989                 || !( definedTransformation.getSourceCRS().equals( sourceCRS ) && definedTransformation.getTargetCRS().equals(
    990                                                                                                                                targetCRS ) ) ) {
    991                try {
    992                    definedTransformation = TransformationFactory.getInstance().createFromCoordinateSystems( sourceCRS,
    993                                                                                                             targetCRS );
    994                } catch ( TransformationException e ) {
    995                    throw new CRSTransformationException( e );
    996                }
    997            }
    998            return definedTransformation;
    999        }
    1000    }