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 }