001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/crs/components/Unit.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    package org.deegree.crs.components;
037    
038    import static org.deegree.crs.projections.ProjectionUtils.DTR;
039    import static org.deegree.crs.utilities.MappingUtils.matchEPSGString;
040    
041    import java.io.Serializable;
042    
043    import org.deegree.crs.Identifiable;
044    
045    /**
046     * The <code>Unit</code> class defines a mechanism to convert between different measurements units, such as british_yard
047     * and meter.
048     *
049     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
050     *
051     * @author last edited by: $Author: mschneider $
052     *
053     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
054     *
055     */
056    
057    public final class Unit extends Identifiable implements Serializable {
058    
059        private static final long serialVersionUID = 4176657042228182638L;
060    
061        /**
062         * Unit of angle.
063         */
064        public static final Unit RADIAN = new Unit( "rad", "Radian" );
065    
066        /**
067         * Unit of angle.
068         */
069        public static final Unit DEGREE = new Unit( "°", "Degree", DTR, RADIAN );
070    
071        /**
072         * Unit of angle, which is defined to be 1/3600 of a degree, or PI/(180*3600) Radian.
073         */
074        public static final Unit ARC_SEC = new Unit( "\"", "Arcsecond", DTR * ( 1. / 3600 ), RADIAN );
075    
076        /**
077         * Base unit of length.
078         */
079        public static final Unit METRE = new Unit( "m", "Metre" );
080    
081        /**
082         * British yard; unit of length.
083         */
084        public static final Unit BRITISHYARD = new Unit( "y", "britishyard", 0.9144, METRE );
085    
086        /**
087         * US foot; unit of length, with base unit of 0.304 meter.
088         */
089        public static final Unit USFOOT = new Unit( "ft", "usfoot", 0.3048006096012192, METRE );
090    
091        /**
092         * Base unit of time.
093         */
094        public static final Unit SECOND = new Unit( "s", "Second" );
095    
096        /**
097         * Unit of time.
098         */
099        public static final Unit MILLISECOND = new Unit( "ms", "milli second", 0.001, SECOND );
100    
101        /**
102         * Unit of time.
103         */
104        public static final Unit DAY = new Unit( "day", "day", 24 * 60 * 60, SECOND );
105    
106        /**
107         * The unit's symbol.
108         */
109        private final String symbol;
110    
111        /**
112         * The scale factor.
113         */
114        private final double scale;
115    
116        /**
117         * Base unit, or <code>this</code> if none.
118         */
119        private final Unit baseType;
120    
121        /**
122         * Unit constructor.
123         *
124         * @param symbol
125         * @param name
126         *            of the unit, e.g. metre
127         * @param id
128         *            of the unit
129         */
130        public Unit( final String symbol, final String name, final String id ) {
131            super( new Identifiable( new String[] { id }, new String[] { name }, null, null, null ) );
132            this.symbol = symbol;
133            this.scale = 1;
134            this.baseType = this;
135        }
136    
137        /**
138         * Unit constructor.
139         *
140         * @param symbol
141         * @param name
142         *            of the unit, e.g. metre
143         */
144        public Unit( final String symbol, final String name ) {
145            this( symbol, name, name );
146        }
147    
148        /**
149         * Unit constructor, which uses the name as the id.
150         *
151         * @param symbol
152         *            of the units, e.g. 'm'
153         * @param name
154         *            human readable name, e.g. metre
155         * @param scale
156         *            to convert to the base type.
157         * @param baseType
158         *            the baseType
159         */
160        public Unit( final String symbol, String name, final double scale, final Unit baseType ) {
161            this( symbol, name, name, scale, baseType );
162        }
163    
164        /**
165         * Unit constructor.
166         *
167         * @param symbol
168         *            of the units, e.g. 'm'
169         * @param name
170         *            human readable name, e.g. metre
171         * @param id
172         *            of the unit.
173         * @param scale
174         *            to convert to the base type.
175         * @param baseType
176         *            the baseType
177         */
178        public Unit( final String symbol, String name, String id, final double scale, final Unit baseType ) {
179            super( new Identifiable( new String[] { id }, new String[] { name }, null, null, null ) );
180            this.symbol = symbol;
181            this.scale = scale;
182            this.baseType = baseType;
183        }
184    
185        /**
186         * Will create a unit from the given String. If no appropriate unit was found <code>null<code> will be returned.
187         *
188         * @param unit
189         *            to convert to an actual unit.
190         * @return a unit or <code>null</code>
191         */
192        public static Unit createUnitFromString( final String unit ) {
193            if ( unit != null && !"".equals( unit.trim() ) ) {
194                String t = unit.trim().toUpperCase();
195                if ( "METRE".equals( t ) || "METER".equals( t ) || "M".equals( t ) || matchEPSGString( unit, "uom", "9001" ) ) {
196                    return METRE;
197                } else if ( "BRITISHYARD".equals( t ) || "Y".equals( t ) || matchEPSGString( unit, "uom", "9060" ) ) {
198                    return BRITISHYARD;
199                } else if ( "USFOOT".equals( t ) || "FT".equals( t ) || matchEPSGString( unit, "uom", "9003" ) ) {
200                    return USFOOT;
201                } else if ( "DEGREE".equals( t ) || "°".equals( t ) || matchEPSGString( unit, "uom", "9102" ) ) {
202                    return DEGREE;
203                } else if ( "RADIAN".equals( t ) || "rad".equals( t ) || matchEPSGString( unit, "uom", "9101" ) ) {
204                    return RADIAN;
205                } else if ( "SECOND".equals( t ) || "S".equals( t ) ) {
206                    return SECOND;
207                } else if ( "MILLISECOND".equals( t ) || "MS".equals( t ) ) {
208                    return MILLISECOND;
209                } else if ( "DAY".equals( t ) || "D".equals( t ) ) {
210                    return DAY;
211                } else if ( "Arcsecond".equalsIgnoreCase( t ) || matchEPSGString( unit, "uom", "9104" ) ) {
212                    return ARC_SEC;
213                }
214    
215            }
216            return null;
217        }
218    
219        /**
220         * Check if amount of the specified unit can be converted into amount of this unit.
221         *
222         * @param other
223         * @return true if this unit can be converted into the other unit
224         */
225        public boolean canConvert( final Unit other ) {
226            return ( baseType == other.baseType ) || ( baseType != null && baseType.equals( other.baseType ) );
227        }
228    
229        /**
230         * Convert a value in this unit to the given unit if possible.
231         *
232         * @param value
233         *            to be converted
234         * @param targetUnit
235         *            to convert to
236         * @return the converted value or the same value if this unit equals given unit.
237         * @throws IllegalArgumentException
238         *             if no conversion can be applied.
239         */
240        public final double convert( final double value, final Unit targetUnit ) {
241            if ( this.equals( targetUnit ) ) {
242                return value;
243            }
244            if ( canConvert( targetUnit ) ) {
245                return ( value * scale ) / targetUnit.scale;
246            }
247            throw new IllegalArgumentException( "Can't convert from \"" + this + "\" to \"" + targetUnit + "\"." );
248        }
249    
250        /**
251         * Convert a value in this unit to the base unit, e.g. degree->radians
252         *
253         * @param value
254         *            to be converted
255         * @return the converted value or the same value if this unit is a base unit.
256         */
257        public final double toBaseUnits( final double value ) {
258            if ( isBaseType() ) {
259                return value;
260            }
261            return value * scale;
262        }
263    
264        /**
265         * @return the symbol of this unit.
266         */
267        @Override
268        public String toString() {
269            return symbol;
270        }
271    
272        /**
273         * Compare this unit symbol with the specified object for equality. Only symbols are compared; other parameters are
274         * ignored.
275         */
276        @Override
277        public boolean equals( final Object object ) {
278            if ( object != null && object instanceof Unit ) {
279                final Unit that = (Unit) object;
280                return symbol.equals( that.symbol ) && ( Math.abs( this.scale - that.scale ) < 1E-10 );
281            }
282            return false;
283        }
284    
285        /**
286         * Implementation as proposed by Joshua Block in Effective Java (Addison-Wesley 2001), which supplies an even
287         * distribution and is relatively fast. It is created from field <b>f</b> as follows:
288         * <ul>
289         * <li>boolean -- code = (f ? 0 : 1)</li>
290         * <li>byte, char, short, int -- code = (int)f</li>
291         * <li>long -- code = (int)(f ^ (f &gt;&gt;&gt;32))</li>
292         * <li>float -- code = Float.floatToIntBits(f);</li>
293         * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l &gt;&gt;&gt; 32))</li>
294         * <li>all Objects, (where equals(&nbsp;) calls equals(&nbsp;) for this field) -- code = f.hashCode(&nbsp;)</li>
295         * <li>Array -- Apply above rules to each element</li>
296         * </ul>
297         * <p>
298         * Combining the hash code(s) computed above: result = 37 * result + code;
299         * </p>
300         *
301         * @return (int) ( result >>> 32 ) ^ (int) result;
302         *
303         * @see java.lang.Object#hashCode()
304         */
305        @Override
306        public int hashCode() {
307            // the 2nd millionth prime, :-)
308            long code = 32452843;
309            code = code * 37 + symbol.hashCode();
310            long ll = Double.doubleToLongBits( scale );
311            code = code * 37 + (int) ( ll ^ ( ll >>> 32 ) );
312            return (int) ( code >>> 32 ) ^ (int) code;
313        }
314    
315        /**
316         * @return the scale to convert to the base unit.
317         */
318        public final double getScale() {
319            return scale;
320        }
321    
322        /**
323         * @return true if this is a base type
324         */
325        public final boolean isBaseType() {
326            return this.equals( this.baseType );
327        }
328    
329    }