001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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    }