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 }