001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/pt/Angle.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.pt;
052    
053    // Miscellaneous
054    import java.io.Serializable;
055    import java.lang.ref.Reference;
056    import java.lang.ref.SoftReference;
057    import java.text.Format;
058    import java.text.ParseException;
059    import java.util.Locale;
060    
061    import org.deegree.model.csct.resources.ClassChanger;
062    
063    /**
064     * An angle in degrees. An angle is the amount of rotation needed to bring one line or plane into
065     * coincidence with another, generally measured in degrees, sexagesimal degrees or grads.
066     * 
067     * @version 1.0
068     * @author Martin Desruisseaux
069     * 
070     * @see Latitude
071     * @see Longitude
072     * @see AngleFormat
073     */
074    public class Angle implements Comparable, Serializable {
075        /**
076         * Serial number for interoperability with different versions.
077         */
078        private static final long serialVersionUID = 1158747349433104534L;
079    
080        /**
081         * A shared instance of {@link AngleFormat}.
082         */
083        private static Reference format;
084    
085        /**
086         * Define how angle can be converted to {@link Number} objects.
087         */
088        static {
089            ClassChanger.register( new ClassChanger( Angle.class, Double.class ) {
090                protected Number convert( final Comparable o ) {
091                    return new Double( ( (Angle) o ).theta );
092                }
093    
094                protected Comparable inverseConvert( final Number value ) {
095                    return new Angle( value.doubleValue() );
096                }
097            } );
098        }
099    
100        /**
101         * Angle value in degres.
102         */
103        private final double theta;
104    
105        /**
106         * Contruct a new angle with the specified value.
107         * 
108         * @param theta
109         *            Angle in degrees.
110         */
111        public Angle( final double theta ) {
112            this.theta = theta;
113        }
114    
115        /**
116         * Constructs a newly allocated <code>Angle</code> object that represents the angle value
117         * represented by the string. The string should represents an angle in either fractional degrees
118         * (e.g. 45.5�) or degrees with minutes and seconds (e.g. 45�30').
119         * 
120         * @param string
121         *            A string to be converted to an <code>Angle</code>.
122         * @throws NumberFormatException
123         *             if the string does not contain a parsable angle.
124         */
125        public Angle( final String string ) throws NumberFormatException {
126            try {
127                final Angle theta = (Angle) getAngleFormat().parseObject( string );
128                if ( getClass().isAssignableFrom( theta.getClass() ) ) {
129                    this.theta = theta.theta;
130                } else
131                    throw new NumberFormatException();
132            } catch ( ParseException exception ) {
133                NumberFormatException e = new NumberFormatException( exception.getLocalizedMessage() );
134                e.initCause( exception );
135                throw e;
136            }
137        }
138    
139        /**
140         * Returns the angle value in degrees.
141         */
142        public double degrees() {
143            return theta;
144        }
145    
146        /**
147         * Returns the angle value in radians.
148         */
149        public double radians() {
150            return Math.toRadians( theta );
151        }
152    
153        /**
154         * Returns a hash code for this <code>Angle</code> object.
155         */
156        public int hashCode() {
157            final long code = Double.doubleToLongBits( theta );
158            return (int) code ^ (int) ( code >>> 32 );
159        }
160    
161        /**
162         * Compares the specified object with this angle for equality.
163         */
164        public boolean equals( final Object that ) {
165            if ( that == this )
166                return true;
167            if ( that != null && getClass().equals( that.getClass() ) ) {
168                return Double.doubleToLongBits( theta ) == Double.doubleToLongBits( ( (Angle) that ).theta );
169            }
170            return false;
171        }
172    
173        /**
174         * Compares two <code>Angle</code> objects numerically. The comparaison is done as if by the
175         * {@link Double#compare(double,double)} method.
176         */
177        public int compareTo( final Object that ) {
178            final double d1 = this.theta;
179            final double d2 = ( (Angle) that ).theta;
180            if ( d1 < d2 )
181                return -1;
182            if ( d1 > d2 )
183                return +1;
184            if ( d1 == d2 )
185                return 0;
186            final long bits1 = Double.doubleToLongBits( d1 );
187            final long bits2 = Double.doubleToLongBits( d2 );
188            if ( bits1 < bits2 )
189                return -1; // (-0.0, 0.0) or (!NaN, NaN)
190            if ( bits1 > bits2 )
191                return +1; // (0.0, -0.0) or (NaN, !NaN)
192            return 0;
193    
194        }
195    
196        /**
197         * Returns a string representation of this <code>Angle</code> object.
198         */
199        public String toString() {
200            return getAngleFormat().format( this, new StringBuffer(), null ).toString();
201        }
202    
203        /**
204         * Returns a shared instance of {@link AngleFormat}. The return type is {@link Format} in order
205         * to avoid class loading before necessary.
206         */
207        private static synchronized Format getAngleFormat() {
208            if ( format != null ) {
209                final Format angleFormat = (Format) format.get();
210                if ( angleFormat != null )
211                    return angleFormat;
212            }
213            final Format newFormat = new AngleFormat( "D�MM.m'", Locale.US );
214            format = new SoftReference<Format>( newFormat );
215            return newFormat;
216        }
217    }