001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/crs/GeoTransformer.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 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.RenderingHints;
045 import java.awt.image.BufferedImage;
046 import java.awt.image.renderable.ParameterBlock;
047 import java.security.InvalidParameterException;
048 import java.util.ArrayList;
049 import java.util.List;
050
051 import javax.media.jai.ImageLayout;
052 import javax.media.jai.Interpolation;
053 import javax.media.jai.InterpolationNearest;
054 import javax.media.jai.JAI;
055 import javax.media.jai.WarpPolynomial;
056 import javax.vecmath.Point3d;
057
058 import org.deegree.crs.components.Unit;
059 import org.deegree.crs.configuration.CRSConfiguration;
060 import org.deegree.crs.configuration.CRSProvider;
061 import org.deegree.crs.coordinatesystems.CoordinateSystem;
062 import org.deegree.crs.exceptions.TransformationException;
063 import org.deegree.crs.projections.ProjectionUtils;
064 import org.deegree.crs.transformations.CRSTransformation;
065 import org.deegree.crs.transformations.TransformationFactory;
066 import org.deegree.graphics.transformation.GeoTransform;
067 import org.deegree.graphics.transformation.WorldToScreenTransform;
068 import org.deegree.i18n.Messages;
069 import org.deegree.model.coverage.grid.AbstractGridCoverage;
070 import org.deegree.model.coverage.grid.GridCoverage;
071 import org.deegree.model.coverage.grid.ImageGridCoverage;
072 import org.deegree.model.feature.Feature;
073 import org.deegree.model.feature.FeatureCollection;
074 import org.deegree.model.feature.FeatureProperty;
075 import org.deegree.model.spatialschema.Curve;
076 import org.deegree.model.spatialschema.CurveSegment;
077 import org.deegree.model.spatialschema.Envelope;
078 import org.deegree.model.spatialschema.Geometry;
079 import org.deegree.model.spatialschema.GeometryException;
080 import org.deegree.model.spatialschema.GeometryFactory;
081 import org.deegree.model.spatialschema.MultiCurve;
082 import org.deegree.model.spatialschema.MultiPoint;
083 import org.deegree.model.spatialschema.MultiSurface;
084 import org.deegree.model.spatialschema.Point;
085 import org.deegree.model.spatialschema.Position;
086 import org.deegree.model.spatialschema.Surface;
087 import org.deegree.model.spatialschema.SurfacePatch;
088 import org.deegree.ogcbase.OGCException;
089 import org.deegree.ogcwebservices.wcs.WCSException;
090 import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering;
091 import org.deegree.ogcwebservices.wcs.describecoverage.DomainSet;
092 import org.opengis.pt.PT_CoordinatePoint;
093
094 /**
095 * class for transforming deegree geometries to new coordinate reference systems.
096 *
097 * <p>
098 * ------------------------------------------------------------
099 * </p>
100 *
101 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
102 * @author last edited by: $Author: rbezema $
103 *
104 * @version $Revision: 11341 $, $Date: 2008-04-22 12:08:33 +0200 (Di, 22 Apr 2008) $
105 */
106 public class GeoTransformer {
107
108 private final CRSProvider crsProvider;
109
110 private CoordinateSystem targetCRS = null;
111
112 private org.deegree.model.crs.CoordinateSystem targetCRSWrapper = null;
113
114 /**
115 * Private constructor will get an instance of the CRSConfiguration, and from it the CRSProvider.
116 */
117 private GeoTransformer() {
118 CRSConfiguration crsConfig = CRSConfiguration.getCRSConfiguration();
119 crsProvider = crsConfig.getProvider();
120 }
121
122 /**
123 * Creates a new GeoTransformer object.
124 *
125 * @param targetCRS
126 * @throws InvalidParameterException
127 * if the given parameter is null.
128 */
129 public GeoTransformer( org.deegree.model.crs.CoordinateSystem targetCRS ) throws InvalidParameterException {
130 this();
131 if ( targetCRS == null ) {
132 throw new InvalidParameterException( Messages.getMessage( "CRS_PARAMETER_NOT_NULL",
133 "GeoTransformer(CoordinateSystem)", "targetCRS" ) );
134 }
135 this.targetCRS = targetCRS.getCRS();
136 targetCRSWrapper = targetCRS;
137 }
138
139 /**
140 * Creates a new GeoTransformer object, with the given id as the target CRS.
141 *
142 * @param targetCRS
143 * an identifier to which all other CRS's shall be transformed.
144 * @throws UnknownCRSException
145 * if the given crs name could not be mapped to a valid (configured) crs.
146 * @throws InvalidParameterException
147 * if the given parameter is null.
148 */
149 public GeoTransformer( String targetCRS ) throws UnknownCRSException, InvalidParameterException {
150 this();
151 if ( targetCRS == null ) {
152 throw new InvalidParameterException( Messages.getMessage( "CRS_PARAMETER_NOT_NULL",
153 "GeoTransformer(String)", "targetCRS" ) );
154 }
155 this.targetCRS = crsProvider.getCRSByID( targetCRS );
156 if ( this.targetCRS == null ) {
157 throw new UnknownCRSException( targetCRS );
158 }
159 targetCRSWrapper = new org.deegree.model.crs.CoordinateSystem( this.targetCRS, targetCRS );
160 }
161
162 /**
163 * transforms a GridCoverage into another coordinate reference system.
164 *
165 * @param coverage
166 * grid coverage to transform
167 * @param targetBBOX
168 * envelope for the target coverage
169 * @param dstWidth
170 * width of the output coverage in pixel
171 * @param dstHeight
172 * height of the output coverage in pixel
173 * @param refPointsGridSize
174 * size of the grid used to calculate polynoms coefficients. E.g. 2 -≶ 4 points, 3 -≶ 9 points ...<br>
175 * Must be ≶= 2. Accuracy of coefficients increase with size of the grid. Speed decreases with size of
176 * the grid.
177 * @param degree
178 * The degree of the polynomial is supplied as an argument.
179 * @param interpolation
180 * interpolation method for warping the passed coverage. Can be <code>null</code>. In this case
181 * 'Nearest Neighbor' will be used as default
182 * @return a transformed GridCoverage.
183 * @throws CRSTransformationException
184 * if the gridCoverage could not be created or the transformation failed
185 */
186 public GridCoverage transform( AbstractGridCoverage coverage, Envelope targetBBOX, int dstWidth, int dstHeight,
187 int refPointsGridSize, int degree, Interpolation interpolation )
188 throws CRSTransformationException {
189
190 BufferedImage img = coverage.getAsImage( -1, -1 );
191 PT_CoordinatePoint min = coverage.getEnvelope().minCP;
192 PT_CoordinatePoint max = coverage.getEnvelope().maxCP;
193
194 // create transformation object to transform reference points
195 // from the target CRS to the source (native) CRS
196 org.deegree.model.crs.CoordinateSystem crs = coverage.getCoordinateReferenceSystem();
197 org.deegree.model.crs.CoordinateSystem targetMCRS = new org.deegree.model.crs.CoordinateSystem( targetCRS, null );
198
199 Envelope sourceBBOX = GeometryFactory.createEnvelope( min.ord[0], min.ord[1], max.ord[0], max.ord[1], crs );
200
201 GeoTransform sourceGT = new WorldToScreenTransform( sourceBBOX.getMin().getX(), sourceBBOX.getMin().getY(),
202 sourceBBOX.getMax().getX(), sourceBBOX.getMax().getY(), 0,
203 0, img.getWidth() - 1, img.getHeight() - 1 );
204 GeoTransform targetGT = new WorldToScreenTransform( targetBBOX.getMin().getX(), targetBBOX.getMin().getY(),
205 targetBBOX.getMax().getX(), targetBBOX.getMax().getY(), 0,
206 0, dstWidth - 1, dstHeight - 1 );
207
208 // create/calculate reference points
209 float dx = ( dstWidth - 1 ) / (float) ( refPointsGridSize - 1 );
210 float dy = ( dstHeight - 1 ) / (float) ( refPointsGridSize - 1 );
211 float[] srcCoords = new float[refPointsGridSize * refPointsGridSize * 2];
212 float[] targetCoords = new float[refPointsGridSize * refPointsGridSize * 2];
213 int k = 0;
214
215 GeoTransformer sourceCoordGT = new GeoTransformer( crs );
216 for ( int i = 0; i < refPointsGridSize; i++ ) {
217 for ( int j = 0; j < refPointsGridSize; j++ ) {
218 targetCoords[k] = i * dx;
219 targetCoords[k + 1] = j * dy;
220 double x = targetGT.getSourceX( targetCoords[k] );
221 double y = targetGT.getSourceY( targetCoords[k + 1] );
222 Point point = GeometryFactory.createPoint( x, y, targetMCRS );
223 point = (Point) sourceCoordGT.transform( point );
224 srcCoords[k] = (float) sourceGT.getDestX( point.getX() );
225 srcCoords[k + 1] = (float) sourceGT.getDestY( point.getY() );
226 // LOG.logDebug( String.format( "%.4f %.4f -> %.4f %.4f ",
227 // srcCoords[k], srcCoords[k+1],
228 // targetCoords[k], targetCoords[k+1]) );
229 k += 2;
230 }
231 }
232
233 // create warp object from reference points and desired interpolation
234 WarpPolynomial warp = WarpPolynomial.createWarp( srcCoords, 0, targetCoords, 0, srcCoords.length, 1f, 1f, 1f,
235 1f, degree );
236
237 if ( interpolation == null ) {
238 interpolation = new InterpolationNearest();
239 }
240
241 // Create and perform the warp operation.
242 ParameterBlock pb = new ParameterBlock();
243 pb.addSource( img );
244 pb.add( warp );
245 pb.add( interpolation );
246
247 // Limit output size, otherwise the result will be a bit larger
248 // (the polynomial warp will overlap the correct image border)
249 ImageLayout layout = new ImageLayout();
250 layout.setMinX( 0 );
251 layout.setMinY( 0 );
252 layout.setWidth( dstWidth );
253 layout.setHeight( dstHeight );
254 RenderingHints rh = new RenderingHints( JAI.KEY_IMAGE_LAYOUT, layout );
255
256 img = JAI.create( "warp", pb, rh ).getAsBufferedImage();
257
258 // create a new GridCoverage from the warp result.
259 // because warping only can be performed on images the
260 // resulting GridCoverage will be an instance of ImageGridCoverage
261 CoverageOffering oldCO = coverage.getCoverageOffering();
262 CoverageOffering coverageOffering = null;
263 if ( oldCO != null ) {
264 try {
265 DomainSet ds = oldCO.getDomainSet();
266 ds.getSpatialDomain().setEnvelops( new Envelope[] { targetBBOX } );
267 coverageOffering = new CoverageOffering( oldCO.getName(), oldCO.getLabel(), oldCO.getDescription(),
268 oldCO.getMetadataLink(), oldCO.getLonLatEnvelope(),
269 oldCO.getKeywords(), ds, oldCO.getRangeSet(),
270 oldCO.getSupportedCRSs(), oldCO.getSupportedFormats(),
271 oldCO.getSupportedInterpolations(), oldCO.getExtension() );
272 } catch ( WCSException e ) {
273 throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
274 crs.getIdentifier(),
275 targetCRS.getIdentifier(), e.getMessage() ),
276 e );
277 } catch ( OGCException e ) {
278 throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
279 crs.getIdentifier(),
280 targetCRS.getIdentifier(), e.getMessage() ),
281 e );
282 }
283 }
284
285 return new ImageGridCoverage( coverageOffering, sourceBBOX, img );
286 }
287
288 /**
289 * transforms a GridCoverage into another coordinate reference system.
290 *
291 * @param coverage
292 * grid coverage to transform
293 * @param refPointsGridSize
294 * size of the grid used to calculate polynoms coefficients. E.g. 2 -≶ 4 points, 3 -≶ 9 points ...<br>
295 * Must be ≶= 2. Accuracy of coefficients increase with size of the grid. Speed decreases with size of
296 * the grid.
297 * @param degree
298 * The degree of the polynomial is supplied as an argument.
299 * @param interpolation
300 * interpolation method for warping the passed coverage. Can be <code>null</code>. In this case
301 * 'Nearest Neighbor' will be used as default
302 * @return a transformed GridCoverage.
303 * @throws CRSTransformationException
304 * if the gridCoverage could not be created or the transformation failed
305 */
306 @Deprecated
307 public GridCoverage transform( AbstractGridCoverage coverage, int refPointsGridSize, int degree,
308 Interpolation interpolation )
309 throws CRSTransformationException {
310
311 BufferedImage img = coverage.getAsImage( -1, -1 );
312 PT_CoordinatePoint min = coverage.getEnvelope().minCP;
313 PT_CoordinatePoint max = coverage.getEnvelope().maxCP;
314
315 // create transformation object to transform reference points
316 // from the source (native) CRS to the target CRS
317 org.deegree.model.crs.CoordinateSystem crs = coverage.getCoordinateReferenceSystem();
318 Envelope sourceBBOX = GeometryFactory.createEnvelope( min.ord[0], min.ord[1], max.ord[0], max.ord[1], crs );
319 Envelope targetBBOX = transform( sourceBBOX, crs );
320
321 GeoTransform sourceGT = new WorldToScreenTransform( sourceBBOX.getMin().getX(), sourceBBOX.getMin().getY(),
322 sourceBBOX.getMax().getX(), sourceBBOX.getMax().getY(), 0,
323 0, img.getWidth() - 1, img.getHeight() - 1 );
324 GeoTransform targetGT = new WorldToScreenTransform( targetBBOX.getMin().getX(), targetBBOX.getMin().getY(),
325 targetBBOX.getMax().getX(), targetBBOX.getMax().getY(), 0,
326 0, img.getWidth() - 1, img.getHeight() - 1 );
327
328 // create/calculate reference points
329 float dx = img.getWidth() / (float) ( refPointsGridSize - 1 );
330 float dy = img.getHeight() / (float) ( refPointsGridSize - 1 );
331 float[] srcCoords = new float[refPointsGridSize * refPointsGridSize * 2];
332 float[] targetCoords = new float[refPointsGridSize * refPointsGridSize * 2];
333 int k = 0;
334 for ( int i = 0; i < refPointsGridSize; i++ ) {
335 for ( int j = 0; j < refPointsGridSize; j++ ) {
336 srcCoords[k] = i * dx;
337 srcCoords[k + 1] = j * dy;
338 double x = sourceGT.getSourceX( srcCoords[k] );
339 double y = sourceGT.getSourceY( srcCoords[k + 1] );
340 Point point = GeometryFactory.createPoint( x, y, crs );
341 point = (Point) transform( point );
342 targetCoords[k] = (float) targetGT.getDestX( point.getX() );
343 targetCoords[k + 1] = (float) targetGT.getDestY( point.getY() );
344 k += 2;
345 }
346 }
347
348 // create warp object from reference points and desired interpolation
349 WarpPolynomial warp = WarpPolynomial.createWarp( srcCoords, 0, targetCoords, 0, srcCoords.length, 1f, 1f, 1f,
350 1f, degree );
351
352 if ( interpolation == null ) {
353 interpolation = new InterpolationNearest();
354 }
355
356 // Create and perform the warp operation.
357 ParameterBlock pb = new ParameterBlock();
358 pb.addSource( img );
359 pb.add( warp );
360 pb.add( interpolation );
361
362 img = JAI.create( "warp", pb ).getAsBufferedImage();
363
364 // create a new GridCoverage from the warp result.
365 // because warping only can be performed on images the
366 // resulting GridCoverage will be an instance of ImageGridCoverage
367 CoverageOffering oldCO = coverage.getCoverageOffering();
368 CoverageOffering coverageOffering = null;
369 if ( oldCO != null ) {
370 try {
371 DomainSet ds = oldCO.getDomainSet();
372 ds.getSpatialDomain().setEnvelops( new Envelope[] { targetBBOX } );
373 coverageOffering = new CoverageOffering( oldCO.getName(), oldCO.getLabel(), oldCO.getDescription(),
374 oldCO.getMetadataLink(), oldCO.getLonLatEnvelope(),
375 oldCO.getKeywords(), ds, oldCO.getRangeSet(),
376 oldCO.getSupportedCRSs(), oldCO.getSupportedFormats(),
377 oldCO.getSupportedInterpolations(), oldCO.getExtension() );
378 } catch ( WCSException e ) {
379 throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
380 crs.getIdentifier(),
381 targetCRS.getIdentifier(), e.getMessage() ),
382 e );
383 } catch ( OGCException e ) {
384 throw new CRSTransformationException( Messages.getMessage( "CRS_CO_CREATION_ERROR",
385 crs.getIdentifier(),
386 targetCRS.getIdentifier(), e.getMessage() ),
387 e );
388 }
389 }
390 return new ImageGridCoverage( coverageOffering, sourceBBOX, img );
391 }
392
393 /**
394 * transforms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
395 *
396 * @param envelope
397 * to transform
398 * @param sourceCRS
399 * CRS of the envelope
400 * @return the transformed envelope
401 * @throws CRSTransformationException
402 */
403 public Envelope transform( Envelope envelope, org.deegree.model.crs.CoordinateSystem sourceCRS )
404 throws CRSTransformationException {
405
406 Point min = GeometryFactory.createPoint( envelope.getMin().getX(), envelope.getMin().getY(), sourceCRS );
407 Point max = GeometryFactory.createPoint( envelope.getMax().getX(), envelope.getMax().getY(), sourceCRS );
408 min = (Point) transform( min );
409 max = (Point) transform( max );
410 // create bounding box with coordinates
411 return GeometryFactory.createEnvelope( min.getX(), min.getY(), max.getX(), max.getY(), targetCRSWrapper );
412 }
413
414 /**
415 * transfroms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
416 *
417 * This transformation takes rotation and distortion into account when regardDistortion is true. Otherwise the
418 * transformed envelope may not contain the whole input envelope.
419 *
420 * @param envelope
421 * to transform
422 * @param sourceCRS
423 * CRS of the envelope
424 * @param regardDistortion
425 *
426 * @return the transformed envelope
427 * @throws CRSTransformationException
428 */
429 public Envelope transform( Envelope envelope, org.deegree.model.crs.CoordinateSystem sourceCRS,
430 boolean regardDistortion )
431 throws CRSTransformationException {
432
433 if ( !regardDistortion ) {
434 return transform( envelope, sourceCRS );
435 }
436 double x1 = envelope.getMin().getX();
437 double y1 = envelope.getMin().getY();
438 double x2 = envelope.getMax().getX();
439 double y2 = envelope.getMax().getY();
440
441 double width = envelope.getWidth();
442 double height = envelope.getHeight();
443
444 // transform with 8 points instead of only the min and max point
445 double[] coords = new double[] { x1, y1, x1, y2, x2, y2, x2, y1, x1, y1 + height, x1 + width, y1, x2,
446 y1 + height, x1 + width, y2 };
447
448 Geometry envelopeGeom = null;
449 try {
450 envelopeGeom = GeometryFactory.createCurve( coords, 2, sourceCRS );
451 } catch ( GeometryException e ) {
452 throw new CRSTransformationException( Messages.getMessage( "CRS_TRANSFORMATION_ERROR",
453 sourceCRS.getIdentifier(),
454 targetCRS.getIdentifier(), e.getMessage() ), e );
455 }
456 envelopeGeom = transform( envelopeGeom );
457
458 return envelopeGeom.getEnvelope();
459
460 }
461
462 /**
463 * Transforms a <code>Envelope</code> to the target crs of the <code>GeoTransformer</code> instance
464 *
465 * @param envelope
466 * to transform
467 * @param sourceCRS
468 * CRS of the envelope
469 * @return the transformed envelope
470 * @throws CRSTransformationException
471 * if the transformation did not succeed.
472 * @throws UnknownCRSException
473 * if the given string is unknown to the CRSProvider
474 */
475 public Envelope transform( Envelope envelope, String sourceCRS )
476 throws CRSTransformationException, UnknownCRSException {
477
478 org.deegree.model.crs.CoordinateSystem crs = CRSFactory.create( sourceCRS );
479 return transform( envelope, crs );
480 }
481
482 /**
483 * transforms all geometries contained within the passed {@link Feature} into the target CRS of a GeoTransformer
484 * instance. If a geometry was transformed the {@link Feature#setEnvelopesUpdated()} method will be called.
485 *
486 * @param feature
487 * @return the transformed geometries in the given Feature.
488 * @throws CRSTransformationException
489 */
490 public Feature transform( Feature feature )
491 throws CRSTransformationException {
492 if ( feature != null ) {
493 FeatureProperty[] featureProperties = feature.getProperties();
494 if ( featureProperties != null ) {
495 for ( FeatureProperty fp : featureProperties ) {
496 if ( fp != null ) {
497 Object value = fp.getValue();
498 if ( value != null ) {
499 if ( value instanceof Geometry ) {
500 Geometry geom = (Geometry) value;
501 if ( !targetCRSWrapper.equals( geom.getCoordinateSystem() ) ) {
502 fp.setValue( transform( geom ) );
503 feature.setEnvelopesUpdated();
504 }
505 } else if ( value instanceof Feature ) {
506 transform( (Feature) value );
507 }
508 }
509 }
510 }
511 }
512 }
513 return feature;
514 }
515
516 /**
517 * transforms all geometries contained within the passed {@link FeatureCollection} into the target CRS of a
518 * GeoTransformer instance.
519 *
520 * @param fc
521 * the collection to transform
522 * @return the transformed geometries in the FeatureCollection
523 * @throws CRSTransformationException
524 * if the transformation cannot be created or processed.
525 */
526 public FeatureCollection transform( FeatureCollection fc )
527 throws CRSTransformationException {
528 for ( int i = 0; i < fc.size(); i++ ) {
529 transform( fc.getFeature( i ) );
530 }
531 // signal that the envelope properties might have been updated.
532 fc.setEnvelopesUpdated();
533 return fc;
534 }
535
536 /**
537 * transforms the coodinates of a deegree geometry to the target coordinate reference system.
538 *
539 * @param geo
540 * to be transformed
541 * @return the same geometry in a different crs.
542 * @throws CRSTransformationException
543 * if the transformation between the source and target crs cannot be created.
544 * @throws IllegalArgumentException
545 * if the coordinates system of the geometry is <code>null</code>
546 */
547 public Geometry transform( Geometry geo )
548 throws CRSTransformationException, IllegalArgumentException {
549
550 if ( geo.getCoordinateSystem() == null ) {
551 throw new IllegalArgumentException( Messages.getMessage( "CRS_GEOMETRY_HAS_NO_CRS" ) );
552 }
553 CoordinateSystem sourceCRS = geo.getCoordinateSystem().getCRS();
554
555 TransformationFactory factory = TransformationFactory.getInstance();
556 CRSTransformation trans = null;
557 try {
558 trans = factory.createFromCoordinateSystems( sourceCRS, targetCRS );
559 } catch ( TransformationException e ) {
560 throw new CRSTransformationException( e );
561 }
562 try {
563 if ( geo instanceof Point ) {
564 geo = transform( (Point) geo, trans );
565 } else if ( geo instanceof Curve ) {
566 geo = transform( (Curve) geo, trans );
567 } else if ( geo instanceof Surface ) {
568 geo = transform( (Surface) geo, trans );
569 } else if ( geo instanceof MultiPoint ) {
570 geo = transform( (MultiPoint) geo, trans );
571 } else if ( geo instanceof MultiCurve ) {
572 geo = transform( (MultiCurve) geo, trans );
573 } else if ( geo instanceof MultiSurface ) {
574 geo = transform( (MultiSurface) geo, trans );
575 }
576 } catch ( GeometryException ge ) {
577 throw new CRSTransformationException( Messages.getMessage( "CRS_TRANSFORMATION_ERROR",
578 sourceCRS.getIdentifier(),
579 targetCRSWrapper.getIdentifier(),
580 ge.getMessage() ), ge );
581 }
582 return geo;
583 }
584
585 /**
586 * transforms the submitted curve to the target coordinate reference system
587 *
588 * @throws GeometryException
589 */
590 private Geometry transform( Curve geo, CRSTransformation trans )
591 throws GeometryException {
592 CurveSegment[] newcus = new CurveSegment[geo.getNumberOfCurveSegments()];
593 for ( int i = 0; i < geo.getNumberOfCurveSegments(); i++ ) {
594 CurveSegment cus = geo.getCurveSegmentAt( i );
595 Position[] pos = cus.getPositions();
596 pos = transform( pos, trans );
597 newcus[i] = GeometryFactory.createCurveSegment( pos, targetCRSWrapper );
598 }
599 return GeometryFactory.createCurve( newcus );
600 }
601
602 /**
603 * transforms the submitted multi curve to the target coordinate reference system
604 *
605 * @throws GeometryException
606 */
607 private Geometry transform( MultiCurve geo, CRSTransformation trans )
608 throws GeometryException {
609 Curve[] curves = new Curve[geo.getSize()];
610 for ( int i = 0; i < geo.getSize(); i++ ) {
611 curves[i] = (Curve) transform( geo.getCurveAt( i ), trans );
612 }
613 return GeometryFactory.createMultiCurve( curves );
614 }
615
616 /**
617 * transforms the submitted multi point to the target coordinate reference system
618 */
619 private Geometry transform( MultiPoint geo, CRSTransformation trans ) {
620 Point[] points = new Point[geo.getSize()];
621 for ( int i = 0; i < geo.getSize(); i++ ) {
622 points[i] = (Point) transform( geo.getPointAt( i ), trans );
623 }
624 return GeometryFactory.createMultiPoint( points );
625 }
626
627 /**
628 * transforms the submitted multi surface to the target coordinate reference system
629 *
630 * @throws GeometryException
631 */
632 private Geometry transform( MultiSurface geo, CRSTransformation trans )
633 throws GeometryException {
634 Surface[] surfaces = new Surface[geo.getSize()];
635 for ( int i = 0; i < geo.getSize(); i++ ) {
636 surfaces[i] = (Surface) transform( geo.getSurfaceAt( i ), trans );
637 }
638 return GeometryFactory.createMultiSurface( surfaces );
639 }
640
641 /**
642 * transforms the submitted point to the target coordinate reference system
643 */
644 private Geometry transform( Point geo, CRSTransformation trans ) {
645
646 double[] din = geo.getAsArray();
647 /**
648 * Normalize points to fit in -180:180 and -90:90 if they are in degrees.
649 */
650 if ( geo.getCoordinateSystem().getCRS().getUnits().equals( Unit.RADIAN ) ) {
651 din[0] = ProjectionUtils.normalizeLongitude( Math.toRadians( din[0] ) );
652 din[1] = ProjectionUtils.normalizeLatitude( Math.toRadians( din[1] ) );
653 }
654
655 Point3d coord = new Point3d( din[0], din[1], ( geo.getCoordinateSystem().getDimension() == 3 ) ? din[2]
656 : Double.NaN );
657 Point3d result = trans.doTransform( coord );
658 if ( targetCRS.getUnits().equals( Unit.RADIAN ) ) {
659 result.x = Math.toDegrees( result.x );
660 result.y = Math.toDegrees( result.y );
661 }
662 return GeometryFactory.createPoint( result.x, result.y, ( targetCRS.getDimension() == 3 ) ? result.z
663 : Double.NaN,
664 targetCRSWrapper );
665 }
666
667 /**
668 * transforms an array of Positions to the target coordinate reference system
669 */
670 private Position[] transform( Position[] pos, CRSTransformation trans ) {
671
672 Position[] newpos = new Position[pos.length];
673 boolean srcRad = trans.getSourceCRS().getUnits().equals( Unit.RADIAN );
674 boolean targetRad = trans.getTargetCRS().getUnits().equals( Unit.RADIAN );
675
676 List<Point3d> coords = new ArrayList<Point3d>( pos.length );
677 for ( Position p : pos ) {
678 if ( srcRad ) {
679 coords.add( new Point3d( Math.toRadians( p.getX() ), Math.toRadians( p.getY() ), p.getZ() ) );
680 } else {
681 coords.add( new Point3d( p.getX(), p.getY(), p.getZ() ) );
682 }
683 }
684 List<Point3d> result = trans.doTransform( coords );
685 for ( int i = 0; i < result.size(); ++i ) {
686 Point3d p = result.get( i );
687 if ( p != null && i < newpos.length ) {
688 if ( targetRad ) {
689 newpos[i] = GeometryFactory.createPosition( Math.toDegrees( p.x ), Math.toDegrees( p.y ),
690 Double.NaN );
691 } else {
692 newpos[i] = GeometryFactory.createPosition( p.x, p.y, ( targetCRS.getDimension() == 3 ) ? p.z
693 : Double.NaN );
694 }
695 }
696 }
697 return newpos;
698 }
699
700 /**
701 * transforms the submitted surface to the target coordinate reference system
702 *
703 * @throws GeometryException
704 * if the surface cannot be retrieved or if the surface patch could not be created.
705 */
706 private Geometry transform( Surface geo, CRSTransformation trans )
707 throws GeometryException {
708 int cnt = geo.getNumberOfSurfacePatches();
709 SurfacePatch[] patches = new SurfacePatch[cnt];
710
711 for ( int i = 0; i < cnt; i++ ) {
712 SurfacePatch p = geo.getSurfacePatchAt( i );
713 Position[] ex = p.getExteriorRing();
714 ex = transform( ex, trans );
715
716 Position[][] innerRings = p.getInteriorRings();
717 Position[][] transformedInnerRings = null;
718
719 if ( innerRings != null ) {
720 transformedInnerRings = new Position[innerRings.length][];
721
722 for ( int k = 0; k < innerRings.length; k++ ) {
723 transformedInnerRings[k] = transform( innerRings[k], trans );
724 }
725 }
726
727 patches[i] = GeometryFactory.createSurfacePatch( ex, transformedInnerRings, p.getInterpolation(),
728 this.targetCRSWrapper );
729 }
730
731 // at the moment only polygons made of one patch are supported
732 return GeometryFactory.createSurface( patches[0] );
733 }
734
735 }