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 }