001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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: apoth $
052 *
053 * @version $Revision: 22735 $, $Date: 2010-02-24 15:46:50 +0100 (Mi, 24 Feb 2010) $
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 >>>32))</li>
292 * <li>float -- code = Float.floatToIntBits(f);</li>
293 * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l >>> 32))</li>
294 * <li>all Objects, (where equals( ) calls equals( ) for this field) -- code = f.hashCode( )</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 /**
330 *
331 * @return symbol (short string) for a Unit
332 */
333 public String getSymbol() {
334 return this.symbol;
335 }
336 }