001    //$HeadURL: $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    
039    package org.deegree.crs.components;
040    
041    import org.deegree.crs.Identifiable;
042    import org.deegree.crs.projections.ProjectionUtils;
043    
044    /**
045     * The <code>Ellipsoid</code> class hold all parameters which are necessary to define an Ellipsoid. Every Ellipsoid
046     * has a semi-major-axis and one of inverse_flattening, eccentricity or semi-minor-axis.
047     * 
048     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
049     * 
050     * @author last edited by: $Author:$
051     * 
052     * @version $Revision:$, $Date:$
053     * 
054     */
055    
056    public class Ellipsoid extends Identifiable {
057    
058        /**
059         * WGS 1984 ellipsoid. This ellipsoid is used in the GPS system and is the "default" Ellipsoid.
060         */
061        public static final Ellipsoid WGS84 = new Ellipsoid( 6378137.0,
062                                                             Unit.METRE,
063                                                             298.257223563,
064                                                             "EPSG:7030",
065                                                             "WGS84_Ellipsoid" );
066    
067        /**
068         * the larger one of the two (semi)axis of an ellipsoid
069         */
070        private double semiMajorAxis;
071    
072        /**
073         * the smaller one of the two (semi)axis of an ellipsoid
074         */
075        private double semiMinorAxis;
076    
077        /**
078         * the units of the axis
079         */
080        private 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 double flattening;
086    
087        /**
088         * Flattening f is normally given as 1/... value therefore an inverse is often given.
089         */
090        private 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 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 double squaredEccentricity;
102    
103        /**
104         * @param units
105         * @param semiMajorAxis
106         * @param semiMinorAxis
107         * @param identifiers
108         * @param names
109         * @param versions
110         * @param descriptions
111         * @param areasOfUse
112         */
113        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String[] identifiers, String[] names,
114                          String[] versions, String[] descriptions, String[] areasOfUse ) {
115            super( identifiers, names, versions, descriptions, areasOfUse );
116            this.units = units;
117            this.semiMajorAxis = semiMajorAxis;
118            this.semiMinorAxis = semiMinorAxis;
119            flattening = ( semiMajorAxis - semiMinorAxis ) / semiMajorAxis;
120            if ( Math.abs( flattening ) > 0.00001 ) {
121                inverseFlattening = 1. / flattening;
122            } else {
123                inverseFlattening = 0;
124            }
125            this.squaredEccentricity = calcSquaredEccentricity( flattening );
126            this.eccentricity = Math.sqrt( squaredEccentricity );
127        }
128    
129        /**
130         * @param units
131         * @param semiMajorAxis
132         * @param semiMinorAxis
133         * @param identifier
134         * @param name
135         * @param version
136         * @param description
137         * @param areaOfUse
138         */
139        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String identifier, String name,
140                          String version, String description, String areaOfUse ) {
141            this( units,
142                  semiMajorAxis,
143                  semiMinorAxis,
144                  new String[] { identifier },
145                  new String[] { name },
146                  new String[] { version },
147                  new String[] { description },
148                  new String[] { areaOfUse } );
149        }
150    
151        /**
152         * @param units
153         * @param semiMajorAxis
154         * @param semiMinorAxis
155         * @param identifiers
156         */
157        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String[] identifiers ) {
158            this( units, semiMajorAxis, semiMinorAxis, identifiers, null, null, null, null );
159        }
160    
161        /**
162         * @param units
163         * @param semiMajorAxis
164         * @param semiMinorAxis
165         * @param identifier
166         * @param name
167         */
168        public Ellipsoid( Unit units, double semiMajorAxis, double semiMinorAxis, String identifier, String name ) {
169            this( units, semiMajorAxis, semiMinorAxis, new String[] { identifier }, new String[] { name }, null, null, null );
170        }
171    
172        /**
173         * @param semiMajorAxis
174         * @param units
175         * @param inverseFlattening
176         * @param identifiers
177         * @param names
178         * @param versions
179         * @param descriptions
180         * @param areasOfUse
181         */
182        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String[] identifiers, String[] names,
183                          String[] versions, String[] descriptions, String[] areasOfUse ) {
184            super( identifiers, names, versions, descriptions, areasOfUse );
185            this.units = units;
186            this.semiMajorAxis = semiMajorAxis;
187            this.inverseFlattening = inverseFlattening;
188            if ( Math.abs( this.inverseFlattening ) > 0.00001 ) {
189                flattening = 1. / this.inverseFlattening;
190            } else {
191                flattening = 0;
192            }
193            this.squaredEccentricity = calcSquaredEccentricity( this.flattening );
194            eccentricity = Math.sqrt( squaredEccentricity );
195            this.semiMinorAxis = this.semiMajorAxis - ( flattening * this.semiMajorAxis );
196        }
197    
198        /**
199         * @param semiMajorAxis
200         * @param units
201         * @param inverseFlattening
202         * @param identifier
203         * @param name
204         * @param version
205         * @param description
206         * @param areaOfUse
207         */
208        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String identifier, String name,
209                          String version, String description, String areaOfUse ) {
210            this( semiMajorAxis,
211                  units,
212                  inverseFlattening,
213                  new String[] { identifier },
214                  new String[] { name },
215                  new String[] { version },
216                  new String[] { description },
217                  new String[] { areaOfUse } );
218        }
219    
220        /**
221         * @param semiMajorAxis
222         * @param units
223         * @param inverseFlattening
224         * @param identifiers
225         */
226        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String[] identifiers ) {
227            this( semiMajorAxis, units, inverseFlattening, identifiers, null, null, null, null );
228        }
229    
230        /**
231         * @param semiMajorAxis
232         * @param units
233         * @param inverseFlattening
234         * @param identifier
235         * @param name
236         */
237        public Ellipsoid( double semiMajorAxis, Unit units, double inverseFlattening, String identifier, String name ) {
238            this( semiMajorAxis,
239                  units,
240                  inverseFlattening,
241                  new String[] { identifier },
242                  new String[] { name },
243                  null,
244                  null,
245                  null );
246        }
247    
248        /**
249         * @param semiMajorAxis
250         * @param eccentricity
251         * @param units
252         * @param identifiers
253         * @param names
254         * @param versions
255         * @param descriptions
256         * @param areasOfUse
257         */
258        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String[] identifiers, String[] names,
259                          String[] versions, String[] descriptions, String[] areasOfUse ) {
260            super( identifiers, names, versions, descriptions, areasOfUse );
261            this.units = units;
262            this.semiMajorAxis = semiMajorAxis;
263            this.eccentricity = eccentricity;
264            this.squaredEccentricity = this.eccentricity * this.eccentricity;
265            this.flattening = calcFlattening( eccentricity );
266            if ( Math.abs( flattening ) > 0.00001 ) {
267                this.inverseFlattening = 1d / flattening;
268            } else {
269                this.inverseFlattening = 0;
270            }
271            this.semiMinorAxis = this.semiMajorAxis - ( flattening * this.semiMajorAxis );
272        }
273    
274        /**
275         * @param semiMajorAxis
276         * @param eccentricity
277         * @param units
278         * @param identifier
279         * @param name
280         * @param version
281         * @param description
282         * @param areaOfUse
283         */
284        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String identifier, String name,
285                          String version, String description, String areaOfUse ) {
286            this( semiMajorAxis,
287                  eccentricity,
288                  units,
289                  new String[] { identifier },
290                  new String[] { name },
291                  new String[] { version },
292                  new String[] { description },
293                  new String[] { areaOfUse } );
294        }
295    
296        /**
297         * @param semiMajorAxis
298         * @param eccentricity
299         * @param units
300         * @param identifiers
301         */
302        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String[] identifiers ) {
303            this( semiMajorAxis, eccentricity, units, identifiers, null, null, null, null );
304        }
305    
306        /**
307         * @param semiMajorAxis
308         * @param eccentricity
309         * @param units
310         * @param identifier
311         * @param name
312         */
313        public Ellipsoid( double semiMajorAxis, double eccentricity, Unit units, String identifier, String name ) {
314            this( semiMajorAxis, eccentricity, units, new String[] { identifier }, new String[] { name }, null, null, null );
315        }
316    
317        /**
318         * @return the eccentricity.
319         */
320        public final double getEccentricity() {
321            return eccentricity;
322        }
323    
324        /**
325         * @return the squared eccentricity of the ellipsoid-
326         */
327        public final double getSquaredEccentricity() {
328            return squaredEccentricity;
329        }
330    
331        /**
332         * @return the flattening.
333         */
334        public final double getFlattening() {
335            return flattening;
336        }
337    
338        /**
339         * @return the inverseFlattening.
340         */
341        public final double getInverseFlattening() {
342            return inverseFlattening;
343        }
344    
345        /**
346         * @return the semiMajorAxis.
347         */
348        public final double getSemiMajorAxis() {
349            return semiMajorAxis;
350        }
351    
352        /**
353         * @return the semiMinorAxis.
354         */
355        public final double getSemiMinorAxis() {
356            return semiMinorAxis;
357        }
358    
359        /**
360         * @return the units.
361         */
362        public final Unit getUnits() {
363            return units;
364        }
365    
366        /**
367         * @param other
368         *            another ellipsoid
369         * @return true if the other ellipsoid != null and its units, seminminoraxis and eccentricity are the same.
370         */
371        @Override
372        public boolean equals( Object other ) {
373            if ( other != null && other instanceof Ellipsoid ) {
374                final Ellipsoid that = (Ellipsoid) other;
375                return this.units.equals( that.units ) && ( Math.abs( this.semiMinorAxis - that.semiMinorAxis ) < ProjectionUtils.EPS11 )
376                       && ( Math.abs( this.eccentricity - that.eccentricity ) < ProjectionUtils.EPS11 );
377            }
378            return false;
379        }
380    
381        /**
382         * Calc the eccentricity from the flattening
383         * 
384         * @param flattening
385         *            given.
386         * @return the squared eccentricity which is given by e^2 = 2*f - f*f.
387         */
388        private double calcSquaredEccentricity( double flattening ) {
389            return ( 2. * flattening ) - ( flattening * flattening );
390        }
391    
392        /**
393         * calcs the flattening of an ellispoid using the eccentricity.
394         * 
395         * @param eccentricity
396         *            given
397         * @return 1-sqrt( 1- e^2) or 0 if e^1 > 1
398         */
399        private double calcFlattening( double eccentricity ) {
400            if ( eccentricity * eccentricity > 1 ) {
401                return 0;
402            }
403            return 1 - Math.sqrt( ( 1 - eccentricity * eccentricity ) );
404        }
405    
406        @Override
407        public String toString() {
408            StringBuilder sb = new StringBuilder( super.toString() );
409            sb.append( ", - Units: " ).append( units );
410            sb.append( ", - semi-major-axis(a): " ).append( semiMajorAxis );
411            sb.append( ", - semi-minor-axis(b): " ).append( semiMinorAxis );
412            sb.append( ", - inverse-flattening: " ).append( inverseFlattening );
413            sb.append( ", - eccentricity: " ).append( eccentricity );
414            return sb.toString();
415        }
416    
417        /**
418         * Implementation as proposed by Joshua Block in Effective Java (Addison-Wesley 2001), which supplies an even
419         * distribution and is relatively fast. It is created from field <b>f</b> as follows:
420         * <ul>
421         * <li>boolean -- code = (f ? 0 : 1)</li>
422         * <li>byte, char, short, int -- code = (int)f </li>
423         * <li>long -- code = (int)(f ^ (f &gt;&gt;&gt;32))</li>
424         * <li>float -- code = Float.floatToIntBits(f);</li>
425         * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l &gt;&gt;&gt; 32))</li>
426         * <li>all Objects, (where equals(&nbsp;) calls equals(&nbsp;) for this field) -- code = f.hashCode(&nbsp;)</li>
427         * <li>Array -- Apply above rules to each element</li>
428         * </ul>
429         * <p>
430         * Combining the hash code(s) computed above: result = 37 * result + code;
431         * </p>
432         * 
433         * @return (int) ( result >>> 32 ) ^ (int) result;
434         * 
435         * @see java.lang.Object#hashCode()
436         */
437        @Override
438        public int hashCode() {
439            // the 2nd millionth prime, :-)
440            long code = 32452843;
441            if ( units != null ) {
442                code = code * 37 + units.hashCode();
443            }
444            long tmp = Double.doubleToLongBits( semiMinorAxis );
445            code = code * 37 + (int) ( tmp ^ ( tmp >>> 32 ) );
446    
447            tmp = Double.doubleToLongBits( eccentricity );
448            code = code * 37 + (int) ( tmp ^ ( tmp >>> 32 ) );
449    
450            return (int) ( code >>> 32 ) ^ (int) code;
451        }
452    }