001    //$HeadURL:  $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    
039    package org.deegree.crs.coordinatesystems;
040    
041    import org.deegree.crs.Identifiable;
042    import org.deegree.crs.components.Axis;
043    import org.deegree.crs.components.GeodeticDatum;
044    import org.deegree.crs.components.Unit;
045    
046    /**
047     * Three kinds of <code>CoordinateSystem</code>s (in this class abbreviated with CRS) are supported in this lib.
048     * <ul>
049     * <li>Geographic CRS: A position (on the ellipsoid) is given in Lattitude / Longitude (Polar Cooridnates) given in
050     * rad° min''sec. The order of the position's coordinates are to be contrued to the axis order of the CRS. These lat/lon
051     * coordinates are to be tranformed to x,y,z values to define their location on the underlying datum.</li>
052     * <li>GeoCentric CRS: A position (on the ellipsoid) is given in x, y, z (cartesian) coordinates with the same units
053     * defined as the ones in the underlying datum. The order of the position's coordinates are to be contrued to the axis
054     * order of the datum.</li>
055     * <li>Projected CRS: The position (on the map) is given in a 2D-tuple in pre-defined units. The Axis of the CRS are
056     * defined (through a transformation) for an underlying Datum, which can have it's own axis with their own units. The
057     * order of the position's coordinates are to be contrued to the axis order of the CRS</li>
058     * </ul>
059     * 
060     * Summarizing it can be said, that each CRS has following features
061     * <ul>
062     * <li>A reference code (an casesensitive String identifying this CRS, for example 'EPGS:4326' or
063     * 'urn:ogc:def:crs:OGC:2:84' or 'luref')</li>
064     * <li>An optional version.</li>
065     * <li>A humanly readable name.</li>
066     * <li>An optional description.</li>
067     * <li>An optional area of use, describing where this CRS is used.</li>
068     * <li>The order in which the axis of ther crs are defined.</li>
069     * <li>The underlying Datum</li>
070     * 
071     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
072     * 
073     * @author last edited by: $Author:$
074     * 
075     * @version $Revision:$, $Date:$
076     * 
077     */
078    
079    public abstract class CoordinateSystem extends Identifiable {
080    
081        private Axis[] axisOrder;
082    
083        private GeodeticDatum usedDatum;
084    
085        /**
086         * Defines this CRS as a GeoCentric one.
087         */
088        public static final int GEOCENTRIC_CRS = 0;
089    
090        /**
091         * Defines this CRS as a Geographic one.
092         */
093        public static final int GEOGRAPHIC_CRS = 1;
094    
095        /**
096         * Defines this CRS as a Projected one.
097         */
098        public static final int PROJECTED_CRS = 2;
099    
100        /**
101         * @param datum
102         *            of this coordinate system.
103         * @param axisOrder
104         *            the axisorder of this coordinate system.
105         * @param identity
106         */
107        public CoordinateSystem( GeodeticDatum datum, Axis[] axisOrder, Identifiable identity ) {
108            super( identity );
109            this.axisOrder = axisOrder;
110            this.usedDatum = datum;
111        }
112    
113        /**
114         * @param datum
115         *            of this coordinate system.
116         * @param axisOrder
117         *            the axisorder of this coordinate system.
118         * @param identifiers
119         *            of this coordinate system.
120         * @param names
121         * @param versions
122         * @param descriptions
123         * @param areasOfUse
124         */
125        public CoordinateSystem( GeodeticDatum datum, Axis[] axisOrder, String[] identifiers, String[] names,
126                                 String[] versions, String[] descriptions, String[] areasOfUse ) {
127            super( identifiers, names, versions, descriptions, areasOfUse );
128            this.axisOrder = axisOrder;
129            this.usedDatum = datum;
130        }
131    
132        /**
133         * @return (all) axis' in their defined order.
134         */
135        public final Axis[] getAxis() {
136            return axisOrder;
137        }
138    
139        /**
140         * @return the usedDatum.
141         */
142        public final GeodeticDatum getGeodeticDatum() {
143            return usedDatum;
144        }
145    
146        /**
147         * @return the units of the coordinatesystem.
148         */
149        public Unit getUnits() {
150            return axisOrder[0].getUnits();
151        }
152    
153        /**
154         * @return the dimension of this CRS.
155         */
156        public abstract int getDimension();
157    
158        /**
159         * @return one of the *_CRS types defined in this class.
160         */
161        public abstract int getType();
162    
163        /**
164         * Helper function to get the typename as a String.
165         * 
166         * @return either the type as a name or 'Unknown' if the tpye is not known.
167         */
168        protected String getTypeName() {
169            switch ( getType() ) {
170            case GEOCENTRIC_CRS:
171                return "Geocentric CRS";
172            case PROJECTED_CRS:
173                return "Projected CRS";
174            case GEOGRAPHIC_CRS:
175                return "Geographic CRS";
176            default:
177                return "Unknown CRS";
178            }
179        }
180    
181        @Override
182        public boolean equals( Object other ) {
183            if ( other != null && other instanceof CoordinateSystem ) {
184                final CoordinateSystem that = (CoordinateSystem) other;
185                return that.getType() == this.getType() && that.getDimension() == this.getDimension()
186                       && matchAxis( that.getAxis() )
187                       && that.getGeodeticDatum().equals( this.getGeodeticDatum() );
188            }
189            return false;
190        }
191    
192        /**
193         * Checks if the given axis match this.axisOrder[] in length and order.
194         * 
195         * @param otherAxis
196         *            the axis to check
197         * @return true if the given axis match this.axisOrder[] false otherwise.
198         */
199        private boolean matchAxis( Axis[] otherAxis ) {
200            if ( otherAxis.length != axisOrder.length ) {
201                return false;
202            }
203            for ( int i = 0; i < axisOrder.length; ++i ) {
204                Axis a = axisOrder[i];
205                Axis b = otherAxis[i];
206                if ( !a.equals( b ) ) {
207                    return false;
208                }
209            }
210            return true;
211        }
212    
213        /**
214         * Implementation as proposed by Joshua Block in Effective Java (Addison-Wesley 2001), which supplies an even
215         * distribution and is relatively fast. It is created from field <b>f</b> as follows:
216         * <ul>
217         * <li>boolean -- code = (f ? 0 : 1)</li>
218         * <li>byte, char, short, int -- code = (int)f </li>
219         * <li>long -- code = (int)(f ^ (f &gt;&gt;&gt;32))</li>
220         * <li>float -- code = Float.floatToIntBits(f);</li>
221         * <li>double -- long l = Double.doubleToLongBits(f); code = (int)(l ^ (l &gt;&gt;&gt; 32))</li>
222         * <li>all Objects, (where equals(&nbsp;) calls equals(&nbsp;) for this field) -- code = f.hashCode(&nbsp;)</li>
223         * <li>Array -- Apply above rules to each element</li>
224         * </ul>
225         * <p>
226         * Combining the hash code(s) computed above: result = 37 * result + code;
227         * </p>
228         * 
229         * @return (int) ( result >>> 32 ) ^ (int) result;
230         * 
231         * @see java.lang.Object#hashCode()
232         */
233        @Override
234        public int hashCode() {
235            // the 2.nd million th. prime, :-)
236            long code = 32452843;
237            if ( axisOrder != null ) {
238                for ( Axis ax : axisOrder ) {
239                    code = code * 37 + ax.hashCode();
240                }
241            }
242            if ( usedDatum != null ) {
243                code = code * 37 + usedDatum.hashCode();
244            }
245            code = code * 37 + getType();
246            code = code * 37 + getDimension();
247            return (int) ( code >>> 32 ) ^ (int) code;
248        }
249    
250        @Override
251        public String toString() {
252            StringBuilder sb = new StringBuilder( super.toString() );
253            sb.append( "\n - type: " ).append( getTypeName() );
254            sb.append( "\n - datum: " ).append( usedDatum );
255            sb.append( "\n - dimension: " ).append( getDimension() );
256            for ( Axis a : axisOrder ) {
257                sb.append( "\n - axis: " ).append( a.toString() );
258            }
259            return sb.toString();
260    
261        }
262    
263    }