001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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 -≶ 4 points, 3 -≶ 9 points ...<br>
184 * Must be ≶= 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 -≶ 4 points, 3 -≶ 9 points ...<br>
256 * Must be ≶= 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 -≶ 4 points, 3 -≶ 9 points ...<br>
343 * Must be ≶= 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 }