001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/ct/MapProjection.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/exse/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification 012 (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/) 013 SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite 014 Institut de Recherche pour le D�veloppement / US-Espace 015 mailto:seasnet@teledetection.fr 016 017 018 This library is free software; you can redistribute it and/or 019 modify it under the terms of the GNU Lesser General Public 020 License as published by the Free Software Foundation; either 021 version 2.1 of the License, or (at your option) any later version. 022 023 This library is distributed in the hope that it will be useful, 024 but WITHOUT ANY WARRANTY; without even the implied warranty of 025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 026 Lesser General Public License for more details. 027 028 You should have received a copy of the GNU Lesser General Public 029 License along with this library; if not, write to the Free Software 030 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 031 032 Contact: 033 034 Andreas Poth 035 lat/lon GmbH 036 Aennchenstr. 19 037 53115 Bonn 038 Germany 039 E-Mail: poth@lat-lon.de 040 041 Klaus Greve 042 Department of Geography 043 University of Bonn 044 Meckenheimer Allee 166 045 53115 Bonn 046 Germany 047 E-Mail: klaus.greve@uni-bonn.de 048 049 050 ---------------------------------------------------------------------------*/ 051 package org.deegree.model.csct.ct; 052 053 // Coordinates 054 import java.awt.Shape; 055 import java.awt.geom.Point2D; 056 057 import javax.media.jai.ParameterList; 058 059 import org.deegree.model.csct.cs.Projection; 060 import org.deegree.model.csct.pt.Latitude; 061 import org.deegree.model.csct.pt.Longitude; 062 import org.deegree.model.csct.resources.Geometry; 063 import org.deegree.model.csct.resources.css.ResourceKeys; 064 import org.deegree.model.csct.resources.css.Resources; 065 066 /** 067 * Provides transformation services between ellipsoidal and cartographic 068 * projections. Ellipsoidal height values remain unchanged. 069 * 070 * 071 * @version 1.0 072 * @author Andr� Gosselin 073 * @author Martin Desruisseaux 074 */ 075 abstract class MapProjection extends AbstractMathTransform implements MathTransform2D { 076 077 078 /** 079 * Marge de tol�rance pour les comparaisons de nombre r�els. 080 */ 081 static final double EPS = 1.0E-6; 082 083 /** 084 * Marge de tol�rance pour les calculs it�ratifs. 085 */ 086 static final double TOL = 1E-10; 087 088 /** 089 * Classification string for this projection 090 * (e.g. "Transverse_Mercator"). 091 */ 092 private final String classification; 093 094 /** 095 * Indique si le mod�le terrestre est sph�rique. La valeur <code>true</code> 096 * indique que le mod�le est sph�rique, c'est-�-dire que les champs {@link #a} 097 * et {@link #b} ont la m�me valeur. 098 */ 099 protected final boolean isSpherical; 100 101 /** 102 * Excentricit� de l'ellipse. L'excentricit� est 0 103 * si l'ellipso�de est sph�rique, c'est-�-dire si 104 * {@link #isSpherical} est <code>true</code>. 105 */ 106 protected final double e; 107 108 /** 109 * Carr� de l'excentricit� de l'ellipse: e� = (a�-b�)/a�. 110 */ 111 protected final double es; 112 113 /** 114 * Longueur de l'axe majeur de la terre, en m�tres. 115 * Sa valeur par d�faut d�pend de l'�llipso�de par 116 * d�faut (par exemple "WGS 1984"). 117 */ 118 protected final double a; 119 120 /** 121 * Longueur de l'axe mineur de la terre, en m�tres. 122 * Sa valeur par d�faut d�pend de l'�llipso�de par 123 * d�faut (par exemple "WGS 1984"). 124 */ 125 protected final double b; 126 127 /** 128 * Central longitude in <u>radians</u>. Default value is 0, the Greenwich 129 * meridian. <strong>Consider this field as final</strong>. It is not final 130 * only because {@link TransverseMercatorProjection} need to modify it at 131 * construction time. 132 */ 133 protected double centralMeridian; 134 135 /** 136 * Central latitude in <u>radians</u>. Default value is 0, the equator. 137 * <strong>Consider this field as final</strong>. It is not final only 138 * because some class need to modify it at construction time. 139 */ 140 protected double centralLatitude; 141 142 protected double false_easting; 143 144 protected double false_northing; 145 146 /** 147 * The inverse of this map projection. 148 * Will be created only when needed. 149 */ 150 private transient MathTransform inverse; 151 152 /** 153 * Construct a new map projection from the suplied parameters. 154 * 155 * @param parameters The parameter values in standard units. 156 * The following parameter are recognized: 157 * <ul> 158 * <li>"semi_major" (default to WGS 1984)</li> 159 * <li>"semi_minor" (default to WGS 1984)</li> 160 * <li>"central_meridian" (default to 0�)</li> 161 * <li>"latitude_of_origin" (default to 0�)</li> 162 * </ul> 163 * @throws MissingParameterException if a mandatory parameter is missing. 164 */ 165 protected MapProjection( final Projection parameters ) throws MissingParameterException { 166 this.classification = parameters.getClassName(); 167 this.a = parameters.getValue( "semi_major" ); 168 this.b = parameters.getValue( "semi_minor" ); 169 this.centralMeridian = longitudeToRadians( parameters.getValue( "central_meridian", 0 ), 170 true ); 171 this.centralLatitude = latitudeToRadians( parameters.getValue( "latitude_of_origin", 0 ), 172 true ); 173 this.isSpherical = ( a == b ); 174 this.es = 1.0 - ( b * b ) / ( a * a ); 175 this.e = Math.sqrt( es ); 176 177 false_easting = parameters.getValue( "false_easting" ); 178 false_northing = parameters.getValue( "false_northing" ); 179 180 } 181 182 /** 183 * Returns a human readable name localized for the specified locale. 184 */ 185 public abstract String getName(); 186 187 /** 188 * Gets the dimension of input points. 189 */ 190 public final int getDimSource() { 191 return 2; 192 } 193 194 /** 195 * Gets the dimension of output points. 196 */ 197 public final int getDimTarget() { 198 return 2; 199 } 200 201 /** 202 * Convertit en radians une longitude exprim�e en degr�s. Au passage, cette m�thode v�rifiera 203 * si la longitude est bien dans les limites permises (�180�). Cette m�thode est utile pour 204 * v�rifier la validit� des param�tres de la projection, comme {@link #setCentralLongitude}. 205 * 206 * @param x Longitude � v�rifier, en degr�s. 207 * @param edge <code>true</code> pour accepter les longitudes de �180�. 208 * @return Longitude en radians. 209 * @throws IllegalArgumentException si la longitude est invalide. 210 */ 211 static double longitudeToRadians( final double x, boolean edge ) 212 throws IllegalArgumentException { 213 if ( edge ? ( x >= Longitude.MIN_VALUE && x <= Longitude.MAX_VALUE ) 214 : ( x > Longitude.MIN_VALUE && x < Longitude.MAX_VALUE ) ) { 215 return Math.toRadians( x ); 216 } 217 throw new IllegalArgumentException( 218 Resources.format( 219 ResourceKeys.ERROR_LONGITUDE_OUT_OF_RANGE_$1, 220 new Longitude( x ) ) ); 221 } 222 223 /** 224 * Convertit en radians une latitude exprim�e en degr�s. Au passage, cette m�thode v�rifiera 225 * si la latitude est bien dans les limites permises (�90�). Cette m�thode est utile pour 226 * v�rifier la validit� des param�tres de la projection, comme {@link #setCentralLongitude}. 227 * 228 * @param y Latitude � v�rifier, en degr�s. 229 * @param edge <code>true</code> pour accepter les latitudes de �90�. 230 * @return Latitude en radians. 231 * @throws IllegalArgumentException si la latitude est invalide. 232 */ 233 static double latitudeToRadians( final double y, boolean edge ) 234 throws IllegalArgumentException { 235 if ( edge ? ( y >= Latitude.MIN_VALUE && y <= Latitude.MAX_VALUE ) 236 : ( y > Latitude.MIN_VALUE && y < Latitude.MAX_VALUE ) ) { 237 return Math.toRadians( y ); 238 } 239 throw new IllegalArgumentException( 240 Resources.format( 241 ResourceKeys.ERROR_LATITUDE_OUT_OF_RANGE_$1, 242 new Latitude( y ) ) ); 243 } 244 245 246 ////////////////////////////////////////////////////////////////////// 247 //// //// 248 //// TRANSFORMS //// 249 //// //// 250 ////////////////////////////////////////////////////////////////////// 251 /** 252 * Transforms the specified coordinate and stores the result in <code>ptDst</code>. 253 * This method is guaranteed to be invoked with values of <var>x</var> in the range 254 * <code>[-PI..PI]</code> and values of <var>y</var> in the range <code>[-PI/2..PI/2]</code>. 255 * 256 * @param x The longitude of the coordinate, in <strong>radians</strong>. 257 * @param y The latitude of the coordinate, in <strong>radians</strong>. 258 * @param ptDst the specified coordinate point that stores the 259 * result of transforming <code>ptSrc</code>, or 260 * <code>null</code>. Ordinates will be in metres. 261 * @return the coordinate point after transforming <code>ptSrc</code> 262 * and stroring the result in <code>ptDst</code>. 263 * @throws TransformException if the point can't be transformed. 264 */ 265 protected abstract Point2D transform( double x, double y, final Point2D ptDst ) 266 throws TransformException; 267 268 /** 269 * Transforms the specified <code>ptSrc</code> 270 * and stores the result in <code>ptDst</code>. 271 * 272 * @param ptSrc the specified coordinate point to be transformed. 273 * Ordinates must be in degrees. 274 * @param ptDst the specified coordinate point that stores the 275 * result of transforming <code>ptSrc</code>, or 276 * <code>null</code>. Ordinates will be in metres. 277 * @return the coordinate point after transforming <code>ptSrc</code> 278 * and stroring the result in <code>ptDst</code>. 279 * @throws TransformException if the point can't be transformed. 280 */ 281 public final Point2D transform( final Point2D ptSrc, Point2D ptDst ) 282 throws TransformException { 283 final double x = ptSrc.getX(); 284 final double y = ptSrc.getY(); 285 if ( !( x >= Longitude.MIN_VALUE && x <= Longitude.MAX_VALUE ) ) { 286 throw new TransformException( 287 Resources.format( 288 ResourceKeys.ERROR_LONGITUDE_OUT_OF_RANGE_$1, 289 new Longitude( x ) ) ); 290 } 291 if ( !( y >= Latitude.MIN_VALUE && y <= Latitude.MAX_VALUE ) ) { 292 throw new TransformException( 293 Resources.format( 294 ResourceKeys.ERROR_LATITUDE_OUT_OF_RANGE_$1, 295 new Latitude( y ) ) ); 296 } 297 ptDst = transform( Math.toRadians( x ), Math.toRadians( y ), ptDst ); 298 return ptDst; 299 } 300 301 /** 302 * Transforms a list of coordinate point ordinal values. 303 * Ordinates must be (<var>longitude</var>,<var>latitude</var>) 304 * pairs in degrees. 305 * 306 * @throws TransformException if a point can't be transformed. This method try 307 * to transform every points even if some of them can't be transformed. 308 * Non-transformable points will have value {@link Double#NaN}. If more 309 * than one point can't be transformed, then this exception may be about 310 * an arbitrary point. 311 */ 312 public final void transform( final double[] src, int srcOffset, final double[] dest, 313 int dstOffset, int numPts ) 314 throws TransformException { 315 /* 316 * V�rifie s'il faudra parcourir le tableau en sens inverse. 317 * Ce sera le cas si les tableaux source et destination se 318 * chevauchent et que la destination est apr�s la source. 319 */ 320 final boolean reverse = ( src == dest && srcOffset < dstOffset && srcOffset + ( 2 * numPts ) > dstOffset ); 321 if ( reverse ) { 322 srcOffset += 2 * numPts; 323 dstOffset += 2 * numPts; 324 } 325 final Point2D.Double point = new Point2D.Double(); 326 TransformException firstException = null; 327 while ( --numPts >= 0 ) { 328 try { 329 point.x = src[srcOffset++]; 330 point.y = src[srcOffset++]; 331 transform( point, point ); 332 dest[dstOffset++] = point.x; 333 dest[dstOffset++] = point.y; 334 } catch ( TransformException exception ) { 335 dest[dstOffset++] = Double.NaN; 336 dest[dstOffset++] = Double.NaN; 337 if ( firstException == null ) { 338 firstException = exception; 339 } 340 } 341 if ( reverse ) { 342 srcOffset -= 4; 343 dstOffset -= 4; 344 } 345 } 346 if ( firstException != null ) 347 throw firstException; 348 } 349 350 /** 351 * Transforms a list of coordinate point ordinal values. 352 * Ordinates must be (<var>longitude</var>,<var>latitude</var>) 353 * pairs in degrees. 354 * 355 * @throws TransformException if a point can't be transformed. This method try 356 * to transform every points even if some of them can't be transformed. 357 * Non-transformable points will have value {@link Float#NaN}. If more 358 * than one point can't be transformed, then this exception may be about 359 * an arbitrary point. 360 */ 361 public final void transform( final float[] src, int srcOffset, final float[] dest, 362 int dstOffset, int numPts ) 363 throws TransformException { 364 final boolean reverse = ( src == dest && srcOffset < dstOffset && srcOffset 365 + ( numPts << 1 ) > dstOffset ); 366 if ( reverse ) { 367 srcOffset += 2 * numPts; 368 dstOffset += 2 * numPts; 369 } 370 final Point2D.Double point = new Point2D.Double(); 371 TransformException firstException = null; 372 while ( --numPts >= 0 ) { 373 try { 374 point.x = src[srcOffset++]; 375 point.y = src[srcOffset++]; 376 transform( point, point ); 377 dest[dstOffset++] = (float) point.x; 378 dest[dstOffset++] = (float) point.y; 379 } catch ( TransformException exception ) { 380 dest[dstOffset++] = Float.NaN; 381 dest[dstOffset++] = Float.NaN; 382 if ( firstException == null ) { 383 firstException = exception; 384 } 385 } 386 if ( reverse ) { 387 srcOffset -= 4; 388 dstOffset -= 4; 389 } 390 } 391 if ( firstException != null ) 392 throw firstException; 393 } 394 395 /** 396 * Transforme la forme g�om�trique <code>shape</code> sp�cifi�e. 397 * Cette projection peut remplacer certaines lignes droites 398 * par des courbes. Tous les points de la forme g�om�trique 399 * seront copi�s. Cette m�thode n'est donc pas � conseiller 400 * si <code>shape</code> est volumineux, par exemple s'il 401 * repr�sente une bathym�trie enti�re. 402 * 403 * @param shape Forme g�om�trique � transformer. Les coordonn�es des points 404 * de cette forme doivent �tre exprim�es en degr�s de latitudes 405 * et de longitudes. 406 * @return Forme g�om�trique transform�e. Les coordonn�es des points de 407 * cette forme seront exprim�es en m�tres. 408 * @throws TransformException si une transformation a �chou�e. 409 */ 410 public final Shape createTransformedShape( final Shape shape ) 411 throws TransformException { 412 return createTransformedShape( shape, null, null, Geometry.HORIZONTAL ); 413 } 414 415 ////////////////////////////////////////////////////////////////////// 416 //// //// 417 //// INVERSE TRANSFORMS //// 418 //// //// 419 ////////////////////////////////////////////////////////////////////// 420 421 /** 422 * Transforms the specified coordinate and stores the result in <code>ptDst</code>. 423 * This method shall returns <var>x</var> values in the range <code>[-PI..PI]</code> 424 * and <var>y</var> values in the range <code>[-PI/2..PI/2]</code>. It will be checked 425 * by the caller, so this method doesn't need to performs this check. 426 * 427 * @param x The longitude of the coordinate, in metres. 428 * @param y The latitude of the coordinate, in metres. 429 * @param ptDst the specified coordinate point that stores the 430 * result of transforming <code>ptSrc</code>, or 431 * <code>null</code>. Ordinates will be in <strong>radians</strong>. 432 * @return the coordinate point after transforming <code>ptSrc</code> 433 * and stroring the result in <code>ptDst</code>. 434 * @throws TransformException if the point can't be transformed. 435 */ 436 protected abstract Point2D inverseTransform( double x, double y, final Point2D ptDst ) 437 throws TransformException; 438 439 /** 440 * Inverse transforms the specified <code>ptSrc</code> 441 * and stores the result in <code>ptDst</code>. 442 * 443 * @param ptSrc the specified coordinate point to be transformed. 444 * Ordinates must be in metres. 445 * @param ptDst the specified coordinate point that stores the 446 * result of transforming <code>ptSrc</code>, or 447 * <code>null</code>. Ordinates will be in degrees. 448 * @return the coordinate point after transforming <code>ptSrc</code> 449 * and stroring the result in <code>ptDst</code>. 450 * @throws TransformException if the point can't be transformed. 451 */ 452 public final Point2D inverseTransform( final Point2D ptSrc, Point2D ptDst ) 453 throws TransformException { 454 final double x0 = ptSrc.getX(); 455 final double y0 = ptDst.getY(); 456 ptDst = inverseTransform( x0, y0, ptDst ); 457 final double x = Math.toDegrees( ptDst.getX() ); 458 final double y = Math.toDegrees( ptDst.getY() ); 459 ptDst.setLocation( x, y ); 460 if ( !( x >= Longitude.MIN_VALUE && x <= Longitude.MAX_VALUE ) ) { 461 throw new TransformException( 462 Resources.format( 463 ResourceKeys.ERROR_LONGITUDE_OUT_OF_RANGE_$1, 464 new Longitude( x ) ) ); 465 } 466 if ( !( y >= Latitude.MIN_VALUE && y <= Latitude.MAX_VALUE ) ) { 467 throw new TransformException( 468 Resources.format( 469 ResourceKeys.ERROR_LATITUDE_OUT_OF_RANGE_$1, 470 new Latitude( y ) ) ); 471 } 472 return ptDst; 473 } 474 475 /** 476 * Inverse transforms a list of coordinate point ordinal values. 477 * Ordinates must be (<var>x</var>,<var>y</var>) pairs in metres. 478 * 479 * @throws TransformException if a point can't be transformed. This method try 480 * to transform every points even if some of them can't be transformed. 481 * Non-transformable points will have value {@link Double#NaN}. If more 482 * than one point can't be transformed, then this exception may be about 483 * an arbitrary point. 484 */ 485 public final void inverseTransform( final double[] src, int srcOffset, final double[] dest, 486 int dstOffset, int numPts ) 487 throws TransformException { 488 /* 489 * V�rifie s'il faudra parcourir le tableau en sens inverse. 490 * Ce sera le cas si les tableaux source et destination se 491 * chevauchent et que la destination est apr�s la source. 492 */ 493 final boolean reverse = ( src == dest && srcOffset < dstOffset && srcOffset 494 + ( numPts << 1 ) > dstOffset ); 495 if ( reverse ) { 496 srcOffset += src.length * numPts; 497 dstOffset += src.length * numPts; 498 } 499 final Point2D.Double point = new Point2D.Double(); 500 TransformException firstException = null; 501 while ( --numPts >= 0 ) { 502 try { 503 point.x = src[srcOffset++]; 504 point.y = src[srcOffset++]; 505 inverseTransform( point, point ); 506 dest[dstOffset++] = point.x; 507 dest[dstOffset++] = point.y; 508 } catch ( TransformException exception ) { 509 dest[dstOffset++] = Double.NaN; 510 dest[dstOffset++] = Double.NaN; 511 if ( firstException == null ) { 512 firstException = exception; 513 } 514 } 515 if ( reverse ) { 516 srcOffset -= 4; 517 dstOffset -= 4; 518 } 519 } 520 if ( firstException != null ) 521 throw firstException; 522 } 523 524 /** 525 * Inverse transforms a list of coordinate point ordinal values. 526 * Ordinates must be (<var>x</var>,<var>y</var>) pairs in metres. 527 * 528 * @throws TransformException if a point can't be transformed. This method try 529 * to transform every points even if some of them can't be transformed. 530 * Non-transformable points will have value {@link Float#NaN}. If more 531 * than one point can't be transformed, then this exception may be about 532 * an arbitrary point. 533 */ 534 public final void inverseTransform( final float[] src, int srcOffset, final float[] dest, 535 int dstOffset, int numPts ) 536 throws TransformException { 537 final boolean reverse = ( src == dest && srcOffset < dstOffset && srcOffset 538 + ( numPts << 1 ) > dstOffset ); 539 if ( reverse ) { 540 srcOffset += 2 * numPts; 541 dstOffset += 2 * numPts; 542 } 543 final Point2D.Double point = new Point2D.Double(); 544 TransformException firstException = null; 545 while ( --numPts >= 0 ) { 546 try { 547 point.x = src[srcOffset++]; 548 point.y = src[srcOffset++]; 549 inverseTransform( point, point ); 550 dest[dstOffset++] = (float) point.x; 551 dest[dstOffset++] = (float) point.y; 552 } catch ( TransformException exception ) { 553 dest[dstOffset++] = Float.NaN; 554 dest[dstOffset++] = Float.NaN; 555 if ( firstException == null ) { 556 firstException = exception; 557 } 558 } 559 if ( reverse ) { 560 srcOffset -= 4; 561 dstOffset -= 4; 562 } 563 } 564 if ( firstException != null ) 565 throw firstException; 566 } 567 568 ////////////////////////////////////////////////////////////////////// 569 //// //// 570 //// INTERNAL COMPUTATIONS FOR SUBCLASSES //// 571 //// //// 572 ////////////////////////////////////////////////////////////////////// 573 574 /** 575 * Iteratively solve equation (7-9) from Snyder. 576 */ 577 final double cphi2( final double ts ) 578 throws TransformException { 579 final double eccnth = 0.5 * e; 580 double phi = ( Math.PI / 2 ) - 2.0 * Math.atan( ts ); 581 for ( int i = 0; i < 16; i++ ) { 582 final double con = e * Math.sin( phi ); 583 final double dphi = ( Math.PI / 2 ) - 2.0 584 * Math.atan( ts * Math.pow( ( 1 - con ) / ( 1 + con ), eccnth ) ) 585 - phi; 586 phi += dphi; 587 if ( Math.abs( dphi ) <= TOL ) 588 return phi; 589 } 590 throw new TransformException( Resources.format( ResourceKeys.ERROR_NO_CONVERGENCE ) ); 591 } 592 593 /** 594 * Compute function <code>f(s,c,es) = c/sqrt(1 - s�*es)</code> 595 * needed for the true scale latitude (Snyder, p. 47), where 596 * <var>s</var> and <var>c</var> are the sine and cosine of 597 * the true scale latitude, and {@link #es} the eccentricity 598 * squared. 599 */ 600 final double msfn( final double s, final double c ) { 601 return c / Math.sqrt( 1.0 - s * s * es ); 602 } 603 604 /** 605 * Compute function (15-9) from Snyder 606 * equivalent to negative of function (7-7). 607 */ 608 final double tsfn( final double phi, double sinphi ) { 609 sinphi *= e; 610 /* 611 * NOTE: change sign to get the equivalent of Snyder (7-7). 612 */ 613 return Math.tan( 0.5 * ( ( Math.PI / 2d ) - phi ) ) 614 / Math.pow( ( 1 - sinphi ) / ( 1 + sinphi ), 0.5 * e ); 615 } 616 617 ////////////////////////////////////////////////////////////////////// 618 //// //// 619 //// MISCELLANEOUS //// 620 //// //// 621 ////////////////////////////////////////////////////////////////////// 622 623 /** 624 * Returns the inverse of this map projection. 625 */ 626 public final synchronized MathTransform inverse() { 627 if ( inverse == null ) 628 inverse = new Inverse(); 629 return inverse; 630 } 631 632 /** 633 * Returns <code>false</code> since map 634 * projections are not identity transforms. 635 */ 636 public final boolean isIdentity() { 637 return false; 638 } 639 640 /** 641 * Returns a hash value for this map projection. 642 */ 643 public int hashCode() { 644 long code = Double.doubleToLongBits( a ); 645 code = code * 37 + Double.doubleToLongBits( b ); 646 code = code * 37 + Double.doubleToLongBits( centralMeridian ); 647 code = code * 37 + Double.doubleToLongBits( centralLatitude ); 648 return (int) code ^ (int) ( code >>> 32 ); 649 } 650 651 /** 652 * Compares the specified object with 653 * this map projection for equality. 654 */ 655 public boolean equals( final Object object ) { 656 // Do not check 'object==this' here, since this 657 // optimization is usually done in subclasses. 658 if ( super.equals( object ) ) { 659 final MapProjection that = (MapProjection) object; 660 return Double.doubleToLongBits( this.a ) == Double.doubleToLongBits( that.a ) 661 && Double.doubleToLongBits( this.b ) == Double.doubleToLongBits( that.b ) 662 && Double.doubleToLongBits( this.centralMeridian ) == Double.doubleToLongBits( that.centralMeridian ) 663 && Double.doubleToLongBits( this.centralLatitude ) == Double.doubleToLongBits( that.centralLatitude ); 664 } 665 return false; 666 } 667 668 /** 669 * Retourne une cha�ne de caract�res repr�sentant cette projection cartographique. 670 * Cette cha�ne de caract�res contiendra entre autres le nom de la projection, les 671 * coordonn�es du centre et celles de l'origine. 672 */ 673 public final String toString() { 674 final StringBuffer buffer = paramMT( classification ); 675 toString( buffer ); 676 buffer.append( ']' ); 677 return buffer.toString(); 678 } 679 680 /** 681 * Impl�mentation de la partie entre crochets 682 * de la cha�ne retourn�e par {@link #toString()}. 683 */ 684 void toString( final StringBuffer buffer ) { 685 addParameter( buffer, "semi_major", a ); 686 addParameter( buffer, "semi_minor", b ); 687 addParameter( buffer, "central_meridian", Math.toDegrees( centralMeridian ) ); 688 addParameter( buffer, "latitude_of_origin", Math.toDegrees( centralLatitude ) ); 689 } 690 691 /** 692 * Inverse of a map projection. 693 * 694 * @version 1.0 695 * @author Martin Desruisseaux 696 */ 697 private final class Inverse extends AbstractMathTransform.Inverse implements MathTransform2D { 698 public Inverse() { 699 MapProjection.this.super(); 700 } 701 702 public Point2D transform( final Point2D source, final Point2D dest ) 703 throws TransformException { 704 return MapProjection.this.inverseTransform( source, dest ); 705 } 706 707 public void transform( final double[] source, final int srcOffset, final double[] dest, 708 final int dstOffset, final int length ) 709 throws TransformException { 710 MapProjection.this.inverseTransform( source, srcOffset, dest, dstOffset, length ); 711 } 712 713 public void transform( final float[] source, final int srcOffset, final float[] dest, 714 final int dstOffset, final int length ) 715 throws TransformException { 716 MapProjection.this.inverseTransform( source, srcOffset, dest, dstOffset, length ); 717 } 718 719 public Shape createTransformedShape( final Shape shape ) 720 throws TransformException { 721 return this.createTransformedShape( shape, null, null, Geometry.HORIZONTAL ); 722 } 723 } 724 725 /** 726 * Informations about a {@link MapProjection}. 727 * 728 * @version 1.0 729 * @author Martin Desruisseaux 730 */ 731 static abstract class Provider extends MathTransformProvider { 732 /** 733 * Construct a new provider. 734 * 735 * @param classname The classification name. 736 * @param nameKey Resources key for a human readable name. 737 * This is used for {@link #getName} implementation. 738 */ 739 protected Provider( final String classname, final int nameKey ) { 740 super( classname, nameKey, DEFAULT_PROJECTION_DESCRIPTOR ); 741 } 742 743 /** 744 * Create a new map projection for a parameter list. 745 */ 746 public final MathTransform create( final ParameterList parameters ) { 747 return (MathTransform) create( new Projection( "Generated", getClassName(), parameters ) ); 748 } 749 750 /** 751 * Create a new map projection. NOTE: The returns type should 752 * be {@link MathTransform}, but as of JDK 1.4-beta3, it force 753 * class loading for all projection classes (MercatorProjection, 754 * etc.) before than necessary. Changing the returns type to 755 * Object is a trick to avoid too early class loading... 756 */ 757 protected abstract Object create( final Projection parameters ); 758 } 759 }