    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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 &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        /**
    330         *
    331         * @return symbol (short string) for a Unit
    332         */
    333        public String getSymbol() {
    334            return this.symbol;
    335        }
    336    }