001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/crs/components/Ellipsoid.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    
037    package org.deegree.crs.components;
038    
039    import static org.deegree.crs.projections.ProjectionUtils.EPS11;
040    
041    import java.io.Serializable;
042    
043    import org.deegree.crs.Identifiable;
044    
045    /**
046     * The <code>Ellipsoid</code> class hold all parameters which are necessary to define an Ellipsoid. Every Ellipsoid has
047     * a semi-major-axis and one of inverse_flattening, eccentricity or semi-minor-axis.
048     * 
049     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
050     * 
051     * @author last edited by: $Author: rbezema $
052     * 
053     * @version $Revision: 19653 $, $Date: 2009-09-15 14:56:30 +0200 (Di, 15 Sep 2009) $
054     * 
055     */
056    
057    public class Ellipsoid extends Identifiable implements Serializable {
058    
059        private static final long serialVersionUID = -2734558237905436138L;
060    
061        /**
062         * WGS 1984 ellipsoid. This ellipsoid is used in the GPS system and is the "default" Ellipsoid.
063         */
064        public static final Ellipsoid WGS84 = new Ellipsoid( 6378137.0, Unit.METRE, 298.257223563, "EPSG:7030",
065                                                             "WGS84_Ellipsoid" );
066    
067        /**
068         * the larger one of the two (semi)axis of an ellipsoid
069         */
070        private final double semiMajorAxis;
071    
072        /**
073         * the smaller one of the two (semi)axis of an ellipsoid
074         */
075        private final double semiMinorAxis;
076    
077        /**
078         * the units of the axis
079         */
080        private final Unit units;
081    
082        /**
083         * Flattening f is dependent on both the semi-major axis a and the semi-minor axis b. f = (a - b) / a
084         */
085        private final double flattening;
086    
087        /**
088         * Flattening f is normally given as 1/... value therefore an inverse is often given.
089         */
090        private final double inverseFlattening;
091    
092        /**
093         * The ellipsoid may also be defined by its semi-major axis a and eccentricity e, which is given by: e*e = 2f - f*f
094         */
095        private final double eccentricity;
096    
097        /**
098         * The ellipsoid may also be defined by its semi-major axis a and eccentricity e, which is given by: e*e = 2f - f*f,
099         * this is it's squared value.
100         */
101        private final double squaredEccentricity;
102    
103        /**
104         * @param units
105         * @param semiMajorAxis
106         * @param semiMinorAxis
107         * @param id
108         *            containing the relevant information
109         */
110        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, Identifiable id ) {
111            super( id );
112            this.units = units;
113            this.semiMajorAxis = semiMajorAxis;
114            this.semiMinorAxis = semiMinorAxis;
115            flattening = ( semiMajorAxis - semiMinorAxis ) / semiMajorAxis;
116            if ( Math.abs( flattening ) > 0.00001 ) {
117                inverseFlattening = 1. / flattening;
118            } else {
119                inverseFlattening = 0;
120            }
121            this.squaredEccentricity = calcSquaredEccentricity( flattening );
122            this.eccentricity = Math.sqrt( squaredEccentricity );
123        }
124    
125        /**
126         * @param units
127         * @param semiMajorAxis
128         * @param semiMinorAxis
129         * @param identifiers
130         * @param names
131         * @param versions
132         * @param descriptions
133         * @param areasOfUse
134         */
135        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String[] identifiers, String[] names,
136                          String[] versions, String[] descriptions, String[] areasOfUse ) {
137            this( units, semiMajorAxis, semiMinorAxis, new Identifiable( identifiers, names, versions, descriptions,
138                                                                         areasOfUse ) );
139        }
140    
141        /**
142         * @param units
143         * @param semiMajorAxis
144         * @param semiMinorAxis
145         * @param identifier
146         * @param name
147         * @param version
148         * @param description
149         * @param areaOfUse
150         */
151        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String identifier, String name,
152                          String version, String description, String areaOfUse ) {
153            this( units, semiMajorAxis, semiMinorAxis, new String[] { identifier }, new String[] { name },
154                  new String[] { version }, new String[] { description }, new String[] { areaOfUse } );
155        }
156    
157        /**
158         * @param units
159         * @param semiMajorAxis
160         * @param semiMinorAxis
161         * @param identifiers
162         */
163        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String[] identifiers ) {
164            this( units, semiMajorAxis, semiMinorAxis, identifiers, null, null, null, null );
165        }
166    
167        /**
168         * @param units
169         * @param semiMajorAxis
170         * @param semiMinorAxis
171         * @param identifier
172         * @param name
173         */
174        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String identifier, String name ) {
175            this( units, semiMajorAxis, semiMinorAxis, new String[] { identifier }, new String[] { name }, null, null, null );
176        }
177    
178        /**
179         * @param semiMajorAxis
180         * @param units
181         * @param inverseFlattening
182         * @param id
183         *            containing all id relevant data.
184         */
185        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, Identifiable id ) {
186            super( id );
187            this.units = units;
188            this.semiMajorAxis = semiMajorAxis;
189            this.inverseFlattening = inverseFlattening;
190            if ( Math.abs( this.inverseFlattening ) > 0.00001 ) {
191                flattening = 1. / this.inverseFlattening;
192            } else {
193                flattening = 0;
194            }
195            this.squaredEccentricity = calcSquaredEccentricity( this.flattening );
196            eccentricity = Math.sqrt( squaredEccentricity );
197            this.semiMinorAxis = this.semiMajorAxis - ( flattening * this.semiMajorAxis );
198        }
199    
200        /**
201         * @param semiMajorAxis
202         * @param units
203         * @param inverseFlattening
204         * @param identifiers
205         * @param names
206         * @param versions
207         * @param descriptions
208         * @param areasOfUse
209         */
210        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String[] identifiers, String[] names,
211                          String[] versions, String[] descriptions, String[] areasOfUse ) {
212            this( semiMajorAxis, units, inverseFlattening, new Identifiable( identifiers, names, versions, descriptions,
213                                                                             areasOfUse ) );
214        }
215    
216        /**
217         * @param semiMajorAxis
218         * @param units
219         * @param inverseFlattening
220         * @param identifier
221         * @param name
222         * @param version
223         * @param description
224         * @param areaOfUse
225         */
226        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String identifier, String name,
227                          String version, String description, String areaOfUse ) {
228            this( semiMajorAxis, units, inverseFlattening, new String[] { identifier }, new String[] { name },
229                  new String[] { version }, new String[] { description }, new String[] { areaOfUse } );
230        }
231    
232        /**
233         * @param semiMajorAxis
234         * @param units
235         * @param inverseFlattening
236         * @param identifiers
237         */
238        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String[] identifiers ) {
239            this( semiMajorAxis, units, inverseFlattening, identifiers, null, null, null, null );
240        }
241    
242        /**
243         * @param semiMajorAxis
244         * @param units
245         * @param inverseFlattening
246         * @param identifier
247         * @param name
248         */
249        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String identifier, String name ) {
250            this( semiMajorAxis, units, inverseFlattening, new String[] { identifier }, new String[] { name }, null, null,
251                  null );
252        }
253    
254        /**
255         * @param semiMajorAxis
256         * @param eccentricity
257         * @param units
258         * @param id
259         *            containing all id relevant data.
260         */
261        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, Identifiable id ) {
262            super( id );
263            this.units = units;
264            this.semiMajorAxis = semiMajorAxis;
265            this.eccentricity = eccentricity;
266            this.squaredEccentricity = this.eccentricity * this.eccentricity;
267            this.flattening = calcFlattening( eccentricity );
268            if ( Math.abs( flattening ) > 0.00001 ) {
269                this.inverseFlattening = 1d / flattening;
270            } else {
271                this.inverseFlattening = 0;
272            }
273            this.semiMinorAxis = this.semiMajorAxis - ( flattening * this.semiMajorAxis );
274        }
275    
276        /**
277         * @param semiMajorAxis
278         * @param eccentricity
279         * @param units
280         * @param identifiers
281         * @param names
282         * @param versions
283         * @param descriptions
284         * @param areasOfUse
285         */
286        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String[] identifiers, String[] names,
287                          String[] versions, String[] descriptions, String[] areasOfUse ) {
288            this( semiMajorAxis, eccentricity, units, new Identifiable( identifiers, names, versions, descriptions,
289                                                                        areasOfUse ) );
290        }
291    
292        /**
293         * @param semiMajorAxis
294         * @param eccentricity
295         * @param units
296         * @param identifier
297         * @param name
298         * @param version
299         * @param description
300         * @param areaOfUse
301         */
302        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String identifier, String name,
303                          String version, String description, String areaOfUse ) {
304            this( semiMajorAxis, eccentricity, units, new String[] { identifier }, new String[] { name },
305                  new String[] { version }, new String[] { description }, new String[] { areaOfUse } );
306        }
307    
308        /**
309         * @param semiMajorAxis
310         * @param eccentricity
311         * @param units
312         * @param identifiers
313         */
314        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String[] identifiers ) {
315            this( semiMajorAxis, eccentricity, units, identifiers, null, null, null, null );
316        }
317    
318        /**
319         * @param semiMajorAxis
320         * @param eccentricity
321         * @param units
322         * @param identifier
323         * @param name
324         */
325        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String identifier, String name ) {
326            this( semiMajorAxis, eccentricity, units, new String[] { identifier }, new String[] { name }, null, null, null );
327        }
328    
329        /**
330         * @return the eccentricity.
331         */
332        public final double getEccentricity() {
333            return eccentricity;
334        }
335    
336        /**
337         * @return the squared eccentricity of the ellipsoid-
338         */
339        public final double getSquaredEccentricity() {
340            return squaredEccentricity;
341        }
342    
343        /**
344         * @return the flattening.
345         */
346        public final double getFlattening() {
347            return flattening;
348        }
349    
350        /**
351         * @return the inverseFlattening.
352         */
353        public final double getInverseFlattening() {
354            return inverseFlattening;
355        }
356    
357        /**
358         * @return the semiMajorAxis.
359         */
360        public final double getSemiMajorAxis() {
361            return semiMajorAxis;
362        }
363    
364        /**
365         * @return the semiMinorAxis.
366         */
367        public final double getSemiMinorAxis() {
368            return semiMinorAxis;
369        }
370    
371        /**
372         * @return the units.
373         */
374        public final Unit getUnits() {
375            return units;
376        }
377    
378        /**
379         * @param other
380         *            another ellipsoid
381         * @return true if the other ellipsoid != null and its units, semi-major-axis and eccentricity are the same.
382         */
383        @Override
384        public boolean equals( Object other ) {
385            if ( other != null && other instanceof Ellipsoid ) {
386                final Ellipsoid that = (Ellipsoid) other;
387                return this.units.equals( that.units ) && ( Math.abs( this.semiMajorAxis - that.semiMajorAxis ) < EPS11 )
388                       && ( Math.abs( this.eccentricity - that.eccentricity ) < EPS11 ) && super.equals( that );
389            }
390            return false;
391        }
392    
393        /**
394         * Calc the eccentricity from the flattening
395         * 
396         * @param flattening
397         *            given.
398         * @return the squared eccentricity which is given by e^2 = 2*f - f*f.
399         */
400        private double calcSquaredEccentricity( double flattening ) {
401            return ( 2. * flattening ) - ( flattening * flattening );
402        }
403    
404        /**
405         * calcs the flattening of an ellispoid using the eccentricity.
406         * 
407         * @param eccentricity
408         *            given
409         * @return 1-sqrt( 1- e^2) or 0 if e^1 > 1
410         */
411        private double calcFlattening( double eccentricity ) {
412            if ( eccentricity * eccentricity > 1 ) {
413                return 0;
414            }
415            return 1 - Math.sqrt( ( 1 - eccentricity * eccentricity ) );
416        }
417    
418        @Override
419        public String toString() {
420            StringBuilder sb = new StringBuilder( super.toString() );
421            sb.append( ", - Units: " ).append( units );
422            sb.append( ", - semi-major-axis(a): " ).append( semiMajorAxis );
423            sb.append( ", - semi-minor-axis(b): " ).append( semiMinorAxis );
424            sb.append( ", - inverse-flattening: " ).append( inverseFlattening );
425            sb.append( ", - eccentricity: " ).append( eccentricity );
426            return sb.toString();
427        }
428    
429        /**
430         * Implementation as proposed by Joshua Block in Effective Java (Addison-Wesley 2001), which supplies an even
431         * distribution and is relatively fast. It is created from field <b>f</b> as follows:
432         * <ul>
433         * <li>boolean -- code = (f ? 0 : 1)</li>
434         * <li>byte, char, short, int -- code = (int)f</li>
435         * <li>long -- code = (int)(f ^ (f &gt;&gt;&gt;32))</li>
436         * <li>float -- code = Float.floatToIntBits(f);</li>
437         * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l &gt;&gt;&gt; 32))</li>
438         * <li>all Objects, (where equals(&nbsp;) calls equals(&nbsp;) for this field) -- code = f.hashCode(&nbsp;)</li>
439         * <li>Array -- Apply above rules to each element</li>
440         * </ul>
441         * <p>
442         * Combining the hash code(s) computed above: result = 37 * result + code;
443         * </p>
444         * 
445         * @return (int) ( result >>> 32 ) ^ (int) result;
446         * 
447         * @see java.lang.Object#hashCode()
448         */
449        @Override
450        public int hashCode() {
451            // the 2nd millionth prime, :-)
452            long code = 32452843;
453            if ( units != null ) {
454                code = code * 37 + units.hashCode();
455            }
456            long tmp = Double.doubleToLongBits( semiMajorAxis );
457            code = code * 37 + (int) ( tmp ^ ( tmp >>> 32 ) );
458    
459            tmp = Double.doubleToLongBits( eccentricity );
460            code = code * 37 + (int) ( tmp ^ ( tmp >>> 32 ) );
461    
462            return (int) ( code >>> 32 ) ^ (int) code;
463        }
464    
465        /**
466         * @return true if this ellipsoid has no eccentricity.
467         */
468        public boolean isSphere() {
469            return eccentricity == 0;
470        }
471    }