001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/crs/GeoTransformer.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2007 by:
006     lat/lon GmbH
007     http://www.lat-lon.de
008    
009     This library is free software; you can redistribute it and/or
010     modify it under the terms of the GNU Lesser General Public
011     License as published by the Free Software Foundation; either
012     version 2.1 of the License, or (at your option) any later version.
013    
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
017     Lesser General Public License for more details.
018    
019     You should have received a copy of the GNU Lesser General Public
020     License along with this library; if not, write to the Free Software
021     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
022    
023     Contact:
024    
025     Andreas Poth
026     lat/lon GmbH
027     Aennchenstr. 19
028     53115 Bonn
029     Germany
030     E-Mail: poth@lat-lon.de
031    
032     Klaus Greve
033     Department of Geography
034     University of Bonn
035     Meckenheimer Allee 166
036     53115 Bonn
037     Germany
038     E-Mail: klaus.greve@uni-bonn.de
039    
040     
041     ---------------------------------------------------------------------------*/
042    package org.deegree.model.crs;
043    
044    import java.awt.image.BufferedImage;
045    import java.awt.image.renderable.ParameterBlock;
046    
047    import javax.media.jai.Interpolation;
048    import javax.media.jai.InterpolationNearest;
049    import javax.media.jai.JAI;
050    import javax.media.jai.WarpPolynomial;
051    
052    import org.deegree.graphics.transformation.GeoTransform;
053    import org.deegree.graphics.transformation.WorldToScreenTransform;
054    import org.deegree.i18n.Messages;
055    import org.deegree.model.coverage.grid.AbstractGridCoverage;
056    import org.deegree.model.coverage.grid.GridCoverage;
057    import org.deegree.model.coverage.grid.ImageGridCoverage;
058    import org.deegree.model.csct.cs.CoordinateSystem;
059    import org.deegree.model.csct.ct.CoordinateTransformationFactory;
060    import org.deegree.model.csct.ct.MathTransform;
061    import org.deegree.model.feature.Feature;
062    import org.deegree.model.feature.FeatureCollection;
063    import org.deegree.model.feature.FeatureProperty;
064    import org.deegree.model.spatialschema.Curve;
065    import org.deegree.model.spatialschema.CurveSegment;
066    import org.deegree.model.spatialschema.Envelope;
067    import org.deegree.model.spatialschema.Geometry;
068    import org.deegree.model.spatialschema.GeometryException;
069    import org.deegree.model.spatialschema.GeometryFactory;
070    import org.deegree.model.spatialschema.MultiCurve;
071    import org.deegree.model.spatialschema.MultiPoint;
072    import org.deegree.model.spatialschema.MultiSurface;
073    import org.deegree.model.spatialschema.Point;
074    import org.deegree.model.spatialschema.Position;
075    import org.deegree.model.spatialschema.Surface;
076    import org.deegree.model.spatialschema.SurfacePatch;
077    import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
078    import org.deegree.ogcwebservices.wcs.describecoverage.DomainSet;
079    import org.opengis.pt.PT_CoordinatePoint;
080    
081    
082    /**
083     * class for transforming deegree geometries to new coordinate reference systems.
084     * 
085     * <p>
086     * ------------------------------------------------------------
087     * </p>
088     * 
089     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
090     * @author last edited by: $Author: apoth $
091     * 
092     * @version $Revision: 7637 $, $Date: 2007-06-25 13:00:05 +0200 (Mo, 25 Jun 2007) $
093     */
094    public class GeoTransformer implements IGeoTransformer {
095    
096        private CoordinateSystem targetCS = null;
097        
098        private String csAccess = CSAccessFactory.CSA_DEFAULT;
099        
100        /**
101         * Creates a new GeoTransformer object.
102         * 
103         * @param targetCS
104         * @throws CRSException 
105         */
106        public GeoTransformer( String targetCS ) throws CRSException {
107            this.targetCS = CSAccessFactory.getCSAccess( csAccess ).getCSByCode( targetCS, null );        
108        }
109    
110        /**
111         * Creates a new GeoTransformer object.
112         * 
113         * @param targetCRS
114         * @throws CRSException 
115         */
116        public GeoTransformer( org.deegree.model.crs.CoordinateSystem targetCRS ) throws CRSException {
117            this.targetCS = CSAccessFactory.getCSAccess( csAccess ).getCSByCode( targetCRS.getName(), null );
118        }
119        
120        /**
121         * Creates a new GeoTransformer object.
122         * 
123         * @param targetCS
124         * @throws CRSException 
125         */
126        public GeoTransformer( String targetCS, String csAccess ) throws CRSException {
127            this.csAccess = csAccess;
128            this.targetCS = CSAccessFactory.getCSAccess( csAccess ).getCSByCode( targetCS, null );        
129        }
130    
131        /**
132         * Creates a new GeoTransformer object.
133         * 
134         * @param targetCRS
135         * @throws CRSException 
136         */
137        public GeoTransformer( org.deegree.model.crs.CoordinateSystem targetCRS, String csAccess  ) throws CRSException {
138            this.csAccess = csAccess;
139            this.targetCS = CSAccessFactory.getCSAccess( csAccess ).getCSByCode( targetCRS.getName(), null );
140        }
141        
142        /*
143         * (non-Javadoc)
144         * 
145         * @see org.deegree.model.crs.IGeoTransformer#transform(org.deegree.model.spatialschema.Geometry)
146         */
147        public Geometry transform( Geometry geo )
148                                throws CRSTransformationException, CRSException {
149    
150            String csname = geo.getCoordinateSystem().getName();        
151            CoordinateSystem cs = CSAccessFactory.getCSAccess( csAccess ).getCSByCode( csname, null );        
152    
153            CoordinateTransformationFactory ctfc = CoordinateTransformationFactory.getDefault();
154            MathTransform trans = null;
155    
156            try {
157                trans = ctfc.createFromCoordinateSystems( cs, targetCS ).getMathTransform();
158            } catch ( Exception e ) {
159                e.printStackTrace();
160                String s = Messages.getMessage( "CRS_NOT_SUPPORTED_TRANSFORMATION",
161                                                geo.getCoordinateSystem(), targetCS, e.getMessage() );
162                throw new CRSTransformationException( s );
163            }
164    
165            if ( geo instanceof Point ) {
166                geo = transform( (Point) geo, trans );
167            } else if ( geo instanceof Curve ) {
168                geo = transform( (Curve) geo, trans );
169            } else if ( geo instanceof Surface ) {
170                geo = transform( (Surface) geo, trans );
171            } else if ( geo instanceof MultiPoint ) {
172                geo = transform( (MultiPoint) geo, trans );
173            } else if ( geo instanceof MultiCurve ) {
174                geo = transform( (MultiCurve) geo, trans );
175            } else if ( geo instanceof MultiSurface ) {
176                geo = transform( (MultiSurface) geo, trans );
177            }
178    
179            return geo;
180        }
181    
182        /**
183         * transforms the submitted point to the target coordinate reference system
184         */
185        private Geometry transform( Point geo, MathTransform trans )
186                                throws CRSTransformationException {
187    
188            try {
189                double[] din = geo.getAsArray();
190    
191                if ( geo.getCoordinateSystem().getUnits().equals( "°" ) ) {
192                    if ( din[0] <= -179.999 )
193                        din[0] = -179.999;
194                    else if ( din[0] >= 179.999 )
195                        din[0] = 179.999;
196                    if ( din[1] <= -89.999 )
197                        din[1] = -89.999;
198                    else if ( din[1] >= 89.999 )
199                        din[1] = 89.999;
200                }
201    
202                double[] di = new double[] { din[0], din[1] };
203                double[] dou = new double[2];
204    
205                trans.transform( di, 0, dou, 0, di.length - 1 );
206    
207                org.deegree.model.crs.CoordinateSystem crs = CRSFactory.create( targetCS.getName() );
208                geo = GeometryFactory.createPoint( dou[0], dou[1], din[2], crs );
209            } catch ( Exception e ) {
210                e.printStackTrace();
211                throw new CRSTransformationException( e );
212            }
213    
214            return geo;
215        }
216    
217        /**
218         * transforms the submitted curve to the target coordinate reference system
219         */
220        private Geometry transform( Curve geo, MathTransform trans )
221                                throws CRSTransformationException {
222    
223            try {
224                CurveSegment[] newcus = new CurveSegment[geo.getNumberOfCurveSegments()];
225    
226                org.deegree.model.crs.CoordinateSystem crs = CRSFactory.create( targetCS.getName() );
227                for ( int i = 0; i < geo.getNumberOfCurveSegments(); i++ ) {
228                    CurveSegment cus = geo.getCurveSegmentAt( i );
229                    Position[] pos = cus.getPositions();
230                    pos = transform( pos, trans );
231                    newcus[i] = GeometryFactory.createCurveSegment( pos, crs );
232                }
233    
234                geo = GeometryFactory.createCurve( newcus );
235            } catch ( Exception e ) {
236                throw new CRSTransformationException( e );
237            }
238    
239            return geo;
240        }
241    
242        /**
243         * transforms the submitted surface to the target coordinate reference system
244         */
245        private Geometry transform( Surface geo, MathTransform trans )
246                                throws CRSTransformationException {
247    
248            try {
249                int cnt = geo.getNumberOfSurfacePatches();
250                SurfacePatch[] patches = new SurfacePatch[cnt];
251    
252                org.deegree.model.crs.CoordinateSystem crs = CRSFactory.create( targetCS.getName() );
253                for ( int i = 0; i < cnt; i++ ) {
254                    SurfacePatch p = geo.getSurfacePatchAt( i );
255                    Position[] ex = p.getExteriorRing();
256                    ex = transform( ex, trans );
257    
258                    Position[][] in = p.getInteriorRings();
259                    Position[][] inn = null;
260    
261                    if ( in != null ) {
262                        inn = new Position[in.length][];
263    
264                        for ( int k = 0; k < in.length; k++ ) {
265                            inn[k] = transform( in[k], trans );
266                        }
267                    }
268    
269                    patches[i] = GeometryFactory.createSurfacePatch( ex, inn, p.getInterpolation(), crs );
270                }
271    
272                // at the moment only polygons made of one patch are supported
273                geo = GeometryFactory.createSurface( patches[0] );
274            } catch ( Exception e ) {
275                e.printStackTrace();
276                throw new CRSTransformationException( e );
277            }
278    
279            return geo;
280        }
281    
282        /**
283         * transforms the submitted multi point to the target coordinate reference system
284         */
285        private Geometry transform( MultiPoint geo, MathTransform trans )
286                                throws CRSTransformationException {
287    
288            try {
289                Point[] points = new Point[geo.getSize()];
290    
291                for ( int i = 0; i < geo.getSize(); i++ ) {
292                    points[i] = (Point) transform( geo.getPointAt( i ), trans );
293                }
294    
295                geo = GeometryFactory.createMultiPoint( points );
296            } catch ( Exception e ) {
297                throw new CRSTransformationException( e );
298            }
299    
300            return geo;
301        }
302    
303        /**
304         * transforms the submitted multi curve to the target coordinate reference system
305         */
306        private Geometry transform( MultiCurve geo, MathTransform trans )
307                                throws CRSTransformationException {
308    
309            try {
310                Curve[] curves = new Curve[geo.getSize()];
311    
312                for ( int i = 0; i < geo.getSize(); i++ ) {
313                    curves[i] = (Curve) transform( geo.getCurveAt( i ), trans );
314                }
315    
316                geo = GeometryFactory.createMultiCurve( curves );
317            } catch ( Exception e ) {
318                throw new CRSTransformationException( e );
319            }
320    
321            return geo;
322        }
323    
324        /**
325         * transforms the submitted multi surface to the target coordinate reference system
326         */
327        private Geometry transform( MultiSurface geo, MathTransform trans )
328                                throws CRSTransformationException {
329    
330            Surface[] surfaces = new Surface[geo.getSize()];
331    
332            for ( int i = 0; i < geo.getSize(); i++ ) {
333                surfaces[i] = (Surface) transform( geo.getSurfaceAt( i ), trans );
334            }
335    
336            geo = GeometryFactory.createMultiSurface( surfaces );
337    
338            return geo;
339        }
340    
341        /**
342         * transfroms an array of Positions to the target coordinate reference system
343         */
344        private Position[] transform( Position[] pos, MathTransform trans )
345                                throws Exception {
346    
347            Position[] newpos = new Position[pos.length];
348    
349            for ( int k = 0; k < pos.length; k++ ) {
350                double[] din = pos[k].getAsArray();
351                double[] di = new double[] { din[0], din[1] };
352                double[] dou = new double[2];
353                trans.transform( di, 0, dou, 0, di.length - 1 );
354                newpos[k] = GeometryFactory.createPosition( dou[0], dou[1], din[2] );
355            }
356    
357            return newpos;
358        }
359    
360        /*
361         * (non-Javadoc)
362         * 
363         * @see org.deegree.model.crs.IGeoTransformer#transform(org.deegree.model.spatialschema.Envelope,
364         *      java.lang.String)
365         */
366        public Envelope transform( Envelope envelope, String sourceCRS )
367                                throws CRSTransformationException, CRSException {
368    
369            org.deegree.model.crs.CoordinateSystem crs = null;
370            try {
371                crs = CRSFactory.create( sourceCRS );
372            } catch ( Exception e ) {
373                e.printStackTrace();
374            }
375            return transform( envelope, crs );
376        }
377    
378        /*
379         * (non-Javadoc)
380         * 
381         * @see org.deegree.model.crs.IGeoTransformer#transform(org.deegree.model.spatialschema.Envelope,
382         *      org.deegree.model.crs.CoordinateSystem)
383         */
384        public Envelope transform( Envelope envelope, org.deegree.model.crs.CoordinateSystem sourceCRS )
385                                throws CRSTransformationException, CRSException {
386    
387            Point min = GeometryFactory.createPoint( envelope.getMin().getX(),
388                                                     envelope.getMin().getY(), sourceCRS );
389            Point max = GeometryFactory.createPoint( envelope.getMax().getX(),
390                                                     envelope.getMax().getY(), sourceCRS );
391            min = (Point) transform( min );
392            max = (Point) transform( max );
393    
394            org.deegree.model.crs.CoordinateSystem crs = null;
395            try {
396                crs = CRSFactory.create( targetCS.getName() );
397            } catch ( Exception e ) {
398                e.printStackTrace();
399            }
400            // create bounding box with coordinates
401            return GeometryFactory.createEnvelope( min.getX(), min.getY(), max.getX(), max.getY(), crs );
402        }
403    
404        /*
405         * (non-Javadoc)
406         * 
407         * @see org.deegree.model.crs.IGeoTransformer#transform(org.deegree.model.feature.FeatureCollection)
408         */
409        public FeatureCollection transform( FeatureCollection fc )
410                                throws CRSTransformationException, GeometryException, CRSException {
411    
412            for ( int i = 0; i < fc.size(); i++ ) {
413                transform( fc.getFeature( i ) );
414            }
415    
416            return fc;
417        }
418    
419        /*
420         * (non-Javadoc)
421         * 
422         * @see org.deegree.model.crs.IGeoTransformer#transform(org.deegree.model.feature.Feature)
423         */
424        public Feature transform( Feature feature )
425                                throws CRSTransformationException, GeometryException, CRSException {
426    
427            org.deegree.model.crs.CoordinateSystem crs = null;
428            try {
429                crs = CRSFactory.create( targetCS.getName() );
430            } catch ( Exception e ) {
431            }
432            FeatureProperty[] fp = feature.getProperties();
433            for ( int i = 0; i < fp.length; i++ ) {
434    
435                if ( fp[i].getValue() instanceof Geometry ) {
436                    Geometry geom = (Geometry) fp[i].getValue();
437                    if ( !crs.equals( geom.getCoordinateSystem() ) ) {
438                        fp[i].setValue( transform( (Geometry) fp[i].getValue() ) );
439                    }
440                } else if ( fp[i].getValue() instanceof Feature ) {
441                    transform( (Feature) fp[i].getValue() );
442                }
443    
444            }
445    
446            return feature;
447        }
448    
449        /*
450         * (non-Javadoc)
451         * 
452         * @see org.deegree.model.crs.IGeoTransformer#transform(GridCoverage coverage, int
453         *      refPointsGridSize, int degree)
454         */
455        public GridCoverage transform( AbstractGridCoverage coverage, int refPointsGridSize,
456                                       int degree, Interpolation interpolation )
457                                throws CRSTransformationException, CRSException {
458    
459            BufferedImage img = coverage.getAsImage( -1, -1 );
460            PT_CoordinatePoint min = coverage.getEnvelope().minCP;
461            PT_CoordinatePoint max = coverage.getEnvelope().maxCP;
462    
463            // create transformation object to transform reference points
464            // from the source (native) CRS to the target CRS
465            org.deegree.model.crs.CoordinateSystem crs = coverage.getCoordinateReferenceSystem();
466            Envelope sourceBBOX = GeometryFactory.createEnvelope( min.ord[0], min.ord[1], max.ord[0],
467                                                                  max.ord[1], crs );
468            Envelope targetBBOX = transform( sourceBBOX, crs );
469    
470            GeoTransform sourceGT = new WorldToScreenTransform( sourceBBOX.getMin().getX(),
471                                                                sourceBBOX.getMin().getY(),
472                                                                sourceBBOX.getMax().getX(),
473                                                                sourceBBOX.getMax().getY(), 0, 0,
474                                                                img.getWidth() - 1, img.getHeight() - 1 );
475            GeoTransform targetGT = new WorldToScreenTransform( targetBBOX.getMin().getX(),
476                                                                targetBBOX.getMin().getY(),
477                                                                targetBBOX.getMax().getX(),
478                                                                targetBBOX.getMax().getY(), 0, 0,
479                                                                img.getWidth() - 1, img.getHeight() - 1 );
480    
481            // create/calculate reference points
482            float dx = img.getWidth() / (float) ( refPointsGridSize - 1 );
483            float dy = img.getHeight() / (float) ( refPointsGridSize - 1 );
484            float[] srcCoords = new float[refPointsGridSize * refPointsGridSize * 2];
485            float[] targetCoords = new float[refPointsGridSize * refPointsGridSize * 2];
486            int k = 0;
487            for ( int i = 0; i < refPointsGridSize; i++ ) {
488                for ( int j = 0; j < refPointsGridSize; j++ ) {
489                    srcCoords[k] = i * dx;
490                    srcCoords[k + 1] = j * dy;
491                    double x = sourceGT.getSourceX( srcCoords[k] );
492                    double y = sourceGT.getSourceY( srcCoords[k + 1] );
493                    Point point = GeometryFactory.createPoint( x, y, crs );
494                    point = (Point) transform( point );
495                    targetCoords[k] = (float) targetGT.getDestX( point.getX() );
496                    targetCoords[k + 1] = (float) targetGT.getDestY( point.getY() );
497                    k += 2;
498                }
499            }
500    
501            // create warp object from reference points and desired interpolation
502            WarpPolynomial warp = WarpPolynomial.createWarp( srcCoords, 0, targetCoords, 0,
503                                                             srcCoords.length, 1f, 1f, 1f, 1f, degree );
504    
505            if ( interpolation == null ) {
506                interpolation = new InterpolationNearest();
507            }
508    
509            // Create and perform the warp operation.
510            ParameterBlock pb = new ParameterBlock();
511            pb.addSource( img );
512            pb.add( warp );
513            pb.add( interpolation );
514    
515            img = JAI.create( "warp", pb ).getAsBufferedImage();
516    
517            // create a new GridCoverage from the warp result.
518            // because warping only can be performed on images the
519            // resulting GridCoverage will be an instance of ImageGridCoverage
520            CoverageOffering oldCO = coverage.getCoverageOffering();
521            CoverageOffering coverageOffering = null;
522            if ( oldCO != null ) {
523                try {
524                    DomainSet ds = oldCO.getDomainSet();
525                    ds.getSpatialDomain().setEnvelops( new Envelope[] { targetBBOX } );
526                    coverageOffering = new CoverageOffering( oldCO.getName(), oldCO.getLabel(),
527                                                             oldCO.getDescription(),
528                                                             oldCO.getMetadataLink(),
529                                                             oldCO.getLonLatEnvelope(),
530                                                             oldCO.getKeywords(), ds,
531                                                             oldCO.getRangeSet(),
532                                                             oldCO.getSupportedCRSs(),
533                                                             oldCO.getSupportedFormats(),
534                                                             oldCO.getSupportedInterpolations(),
535                                                             oldCO.getExtension() );
536                } catch ( Exception e ) {
537                    String s = Messages.getMessage( "CRS_CO_CREATION_ERROR", e.getMessage() );
538                    throw new CRSTransformationException( s );
539                }
540            }
541            return new ImageGridCoverage( coverageOffering, sourceBBOX, img );
542        }
543    
544    }