    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/crs/coordinatesystems/CoordinateSystem.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
    037    package org.deegree.crs.coordinatesystems;
    038
    039    import java.io.Serializable;
    040    import java.util.LinkedList;
    041    import java.util.List;
    042
    043    import javax.vecmath.Point3d;
    044
    045    import org.deegree.crs.Identifiable;
    046    import org.deegree.crs.components.Axis;
    047    import org.deegree.crs.components.Datum;
    048    import org.deegree.crs.components.GeodeticDatum;
    049    import org.deegree.crs.components.Unit;
    050    import org.deegree.crs.transformations.polynomial.PolynomialTransformation;
    051
    052    /**
    053     * Three kinds of <code>CoordinateSystem</code>s (in this class abbreviated with CRS) are supported in this lib.
    054     * <ul>
    055     * <li>Geographic CRS: A position (on the ellipsoid) is given in Lattitude / Longitude (Polar Cooridnates) given in rad°
    056     * min''sec. The order of the position's coordinates are to be contrued to the axis order of the CRS. These lat/lon
    057     * coordinates are to be tranformed to x,y,z values to define their location on the underlying datum.</li>
    058     * <li>GeoCentric CRS: A position (on the ellipsoid) is given in x, y, z (cartesian) coordinates with the same units
    059     * defined as the ones in the underlying datum. The order of the position's coordinates are to be contrued to the axis
    060     * order of the datum.</li>
    061     * <li>Projected CRS: The position (on the map) is given in a 2D-tuple in pre-defined units. The Axis of the CRS are
    062     * defined (through a transformation) for an underlying Datum, which can have it's own axis with their own units. The
    063     * order of the position's coordinates are to be contrued to the axis order of the CRS</li>
    064     * </ul>
    065     *
    066     * Summarizing it can be said, that each CRS has following features
    067     * <ul>
    068     * <li>A reference code (an casesensitive String identifying this CRS, for example 'EPGS:4326' or
    069     * 'urn:ogc:def:crs:OGC:2:84' or 'luref')</li>
    070     * <li>An optional version.</li>
    071     * <li>A humanly readable name.</li>
    072     * <li>An optional description.</li>
    073     * <li>An optional area of use, describing where this CRS is used.</li>
    074     * <li>The order in which the axis of ther crs are defined.</li>
    075     * <li>The underlying Datum</li>
    076     *
    077     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    078     *
    079     * @author last edited by: $Author: lbuesching $
    080     *
    081     * @version $Revision: 23399 $, $Date: 2010-04-07 10:05:58 +0200 (Mi, 07. Apr 2010) $
    082     *
    083     */
    084
    085    public abstract class CoordinateSystem extends Identifiable implements Serializable {
    086
    087        private static final long serialVersionUID = -1817981955729114332L;
    088
    089        private Axis[] axisOrder;
    090
    091        private Datum usedDatum;
    092
    093        private final List<PolynomialTransformation> transformations;
    094
    095        /**
    096         * Defines this CRS as a GeoCentric one.
    097         */
    098        public static final int GEOCENTRIC_CRS = 0;
    099
    100        /**
    101         * Defines this CRS as a Geographic one.
    102         */
    103        public static final int GEOGRAPHIC_CRS = 1;
    104
    105        /**
    106         * Defines this CRS as a Projected one.
    107         */
    108        public static final int PROJECTED_CRS = 2;
    109
    110        /**
    111         * Defines this CRS as a Compound one.
    112         */
    113        public static final int COMPOUND_CRS = 3;
    114
    115        /**
    116         * Defines this CRS as a Vertical one.
    117         */
    118        public static final int VERTICAL_CRS = 4;
    119
    120        /**
    121         * @param datum
    122         *            of this coordinate system.
    123         * @param axisOrder
    124         *            the axisorder of this coordinate system.
    125         * @param identity
    126         */
    127        public CoordinateSystem( Datum datum, Axis[] axisOrder, Identifiable identity ) {
    128            this( null, datum, axisOrder, identity );
    129        }
    130
    131        /**
    132         * @param datum
    133         *            of this coordinate system.
    134         * @param axisOrder
    135         *            the axisorder of this coordinate system.
    136         * @param identifiers
    137         *            of this coordinate system.
    138         * @param names
    139         * @param versions
    140         * @param descriptions
    141         * @param areasOfUse
    142         */
    143        public CoordinateSystem( Datum datum, Axis[] axisOrder, String[] identifiers, String[] names, String[] versions,
    144                                 String[] descriptions, String[] areasOfUse ) {
    145            super( identifiers, names, versions, descriptions, areasOfUse );
    146            this.axisOrder = axisOrder;
    147            this.usedDatum = datum;
    148            this.transformations = new LinkedList<PolynomialTransformation>();
    149        }
    150
    151        /**
    152         * @param transformations
    153         *            to use instead of the helmert transformation(s).
    154         * @param geodeticDatum
    155         *            of this crs
    156         * @param axisOrder
    157         * @param identity
    158         */
    159        public CoordinateSystem( List<PolynomialTransformation> transformations, Datum geodeticDatum, Axis[] axisOrder,
    160                                 Identifiable identity ) {
    161            super( identity );
    162            this.axisOrder = axisOrder;
    163            this.usedDatum = geodeticDatum;
    164            if ( transformations == null ) {
    165                transformations = new LinkedList<PolynomialTransformation>();
    166            }
    167            this.transformations = transformations;
    168        }
    169
    170        /**
    171         * @return (all) axis' in their defined order.
    172         */
    173        public Axis[] getAxis() {
    174            return axisOrder;
    175        }
    176
    177        /**
    178         * @return the usedDatum or <code>null</code> if the datum was not a Geodetic one.
    179         */
    180        public final GeodeticDatum getGeodeticDatum() {
    181            return ( usedDatum instanceof GeodeticDatum ) ? (GeodeticDatum) usedDatum : null;
    182        }
    183
    184        /**
    185         * @return the datum of this coordinate system.
    186         */
    187        public final Datum getDatum() {
    188            return usedDatum;
    189        }
    190
    191        /**
    192         * @return the units of all axis of the coordinatesystem.
    193         */
    194        public Unit[] getUnits() {
    195            Axis[] allAxis = getAxis();
    196            Unit[] result = new Unit[allAxis.length];
    197            for ( int i = 0; i < allAxis.length; ++i ) {
    198                result[i] = allAxis[i].getUnits();
    199            }
    200            return result;
    201        }
    202
    203        /**
    204         * @return the dimension of this CRS.
    205         */
    206        public abstract int getDimension();
    207
    208        /**
    209         * @return one of the *_CRS types defined in this class.
    210         */
    211        public abstract int getType();
    212
    213        /**
    214         * @param targetCRS
    215         *            to get the alternative Transformation for.
    216         * @return true if this crs has an alternative transformation for the given coordinatesystem, false otherwise.
    217         */
    218        public boolean hasDirectTransformation( CoordinateSystem targetCRS ) {
    219            if ( targetCRS == null ) {
    220                return false;
    221            }
    222            for ( PolynomialTransformation transformation : transformations ) {
    223                if ( transformation != null && targetCRS.equals( transformation.getTargetCRS() ) ) {
    224                    return true;
    225                }
    226            }
    227            return false;
    228        }
    229
    230        /**
    231         * @param targetCRS
    232         *            to get the alternative transformation for.
    233         * @return the transformation associated with the given crs, <code>null</code> otherwise.
    234         */
    235        public PolynomialTransformation getDirectTransformation( CoordinateSystem targetCRS ) {
    236            if ( targetCRS == null ) {
    237                return null;
    238            }
    239            for ( PolynomialTransformation transformation : transformations ) {
    240                if ( transformation.getTargetCRS().equals( targetCRS ) ) {
    241                    return transformation;
    242                }
    243            }
    244            return null;
    245        }
    246
    247        /**
    248         * Converts the given coordinates in given to the unit of the respective axis.
    249         *
    250         * @param coordinates
    251         *            to convert to.
    252         * @param units
    253         *            in which the coordinates were given.
    254         * @param invert
    255         *            if the operation should be inverted, e.g. the coordinates are given in the axis units and should be
    256         *            converted to the given units.
    257         * @return the converted coordinates.
    258         */
    259        public Point3d convertToAxis( Point3d coordinates, Unit[] units, boolean invert ) {
    260            if ( units != null && units.length < getDimension() && units.length > 0 ) {
    261                Unit[] axisUnits = getUnits();
    262                for ( int i = 0; i < axisUnits.length; i++ ) {
    263                    Unit axisUnit = axisUnits[i];
    264                    double value = ( i == 0 ) ? coordinates.x : ( i == 1 ) ? coordinates.y : coordinates.z;
    265                    if ( i < units.length ) {
    266                        Unit coordinateUnit = units[i];
    267                        if ( invert ) {
    268                            value = axisUnit.convert( value, coordinateUnit );
    269                        } else {
    270                            value = coordinateUnit.convert( value, axisUnit );
    271                        }
    272                    }
    273                    if ( i == 0 ) {
    274                        coordinates.x = value;
    275                    } else if ( i == 1 ) {
    276                        coordinates.y = value;
    277                    } else {
    278                        coordinates.z = value;
    279                    }
    280                }
    281            }
    282            return coordinates;
    283        }
    284
    285        /**
    286         * Helper function to get the typename as a String.
    287         *
    288         * @return either the type as a name or 'Unknown' if the type is not known.
    289         */
    290        protected String getTypeName() {
    291            switch ( getType() ) {
    292            case GEOCENTRIC_CRS:
    293                return "Geocentric CRS";
    294            case PROJECTED_CRS:
    295                return "Projected CRS";
    296            case GEOGRAPHIC_CRS:
    297                return "Geographic CRS";
    298            case COMPOUND_CRS:
    299                return "Compound CRS";
    300            default:
    301                return "Unknown CRS";
    302            }
    303        }
    304
    305        /**
    306         * Checks if the given axis match this.axisOrder[] in length and order.
    307         *
    308         * @param otherAxis
    309         *            the axis to check
    310         * @return true if the given axis match this.axisOrder[] false otherwise.
    311         */
    312        private boolean matchAxis( Axis[] otherAxis ) {
    313            Axis[] allAxis = getAxis();
    314            if ( otherAxis.length != allAxis.length ) {
    315                return false;
    316            }
    317            for ( int i = 0; i < allAxis.length; ++i ) {
    318                Axis a = allAxis[i];
    319                Axis b = otherAxis[i];
    320                if ( !a.equals( b ) ) {
    321                    return false;
    322                }
    323            }
    324            return true;
    325        }
    326
    327        @Override
    328        public boolean equals( Object other ) {
    329            if ( other != null && other instanceof CoordinateSystem ) {
    330                final CoordinateSystem that = (CoordinateSystem) other;
    331                return that.getType() == this.getType() && that.getDimension() == this.getDimension()
    332                       && matchAxis( that.getAxis() ) && super.equals( that )
    333                       && that.getGeodeticDatum().equals( this.getGeodeticDatum() );
    334            }
    335            return false;
    336        }
    337
    338        /**
    339         * Implementation as proposed by Joshua Block in Effective Java (Addison-Wesley 2001), which supplies an even
    340         * distribution and is relatively fast. It is created from field <b>f</b> as follows:
    341         * <ul>
    342         * <li>boolean -- code = (f ? 0 : 1)</li>
    343         * <li>byte, char, short, int -- code = (int)f</li>
    344         * <li>long -- code = (int)(f ^ (f &gt;&gt;&gt;32))</li>
    345         * <li>float -- code = Float.floatToIntBits(f);</li>
    346         * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l &gt;&gt;&gt; 32))</li>
    347         * <li>all Objects, (where equals(&nbsp;) calls equals(&nbsp;) for this field) -- code = f.hashCode(&nbsp;)</li>
    348         * <li>Array -- Apply above rules to each element</li>
    349         * </ul>
    350         * <p>
    351         * Combining the hash code(s) computed above: result = 37 * result + code;
    352         * </p>
    353         *
    354         * @return (int) ( result >>> 32 ) ^ (int) result;
    355         *
    356         * @see java.lang.Object#hashCode()
    357         */
    358        @Override
    359        public int hashCode() {
    360            // the 2.nd million th. prime, :-)
    361            long code = 32452843;
    362            if ( getAxis() != null ) {
    363                for ( Axis ax : getAxis() ) {
    364                    code = code * 37 + ax.hashCode();
    365                }
    366            }
    367            if ( usedDatum != null ) {
    368                code = code * 37 + usedDatum.hashCode();
    369            }
    370            code = code * 37 + getType();
    371            code = code * 37 + getDimension();
    372            return (int) ( code >>> 32 ) ^ (int) code;
    373        }
    374
    375        @Override
    376        public String toString() {
    377            StringBuilder sb = new StringBuilder( super.toString() );
    378            sb.append( "\n - type: " ).append( getTypeName() );
    379            sb.append( "\n - datum: " ).append( usedDatum );
    380            sb.append( "\n - dimension: " ).append( getDimension() );
    381            for ( Axis a : getAxis() ) {
    382                sb.append( "\n - axis: " ).append( a.toString() );
    383            }
    384            return sb.toString();
    385
    386        }
    387
    388        /**
    389         * @return the polynomial transformations.
    390         */
    391        public final List<PolynomialTransformation> getTransformations() {
    392            return transformations;
    393        }
    394
    395
    396        /**
    397         * Return the axis index associated with an easting value, if the axis could not be determined {@link Axis#AO_OTHER}
    398         * 0 will be returned.
    399         *
    400         * @return the index of the axis which represents the easting/westing component of a coordinate tuple.
    401         */
    402        public int getEasting() {
    403            Axis[] axis = getAxis();
    404            for ( int i = 0; i < axis.length; ++i ) {
    405                Axis a = axis[i];
    406                if ( a != null ) {
    407                    if ( a.getOrientation() == Axis.AO_EAST || a.getOrientation() == Axis.AO_WEST ) {
    408                        return i;
    409                    }
    410                }
    411            }
    412            return 0;
    413        }
    414
    415        /**
    416         * Return the axis index associated with a northing value, if the axis could not be determined (e.g not is
    417         * {@link Axis#AO_NORTH} {@link Axis#AO_SOUTH} or {@link Axis#AO_UP} or {@link Axis#AO_DOWN}) 1 will be returned.
    418         *
    419         * @return the index of the axis which represents the easting/westing component of a coordinate tuple.
    420         */
    421        public int getNorthing() {
    422            Axis[] axis = getAxis();
    423            for ( int i = 0; i < axis.length; ++i ) {
    424                Axis a = axis[i];
    425                if ( a != null ) {
    426                    if ( a.getOrientation() == Axis.AO_NORTH || a.getOrientation() == Axis.AO_SOUTH
    427                         || a.getOrientation() == Axis.AO_DOWN || a.getOrientation() == Axis.AO_UP ) {
    428                        return i;
    429                    }
    430                }
    431            }
    432            return 1;
    433        }
    434    }