001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/cs/Projection.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/exse/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
012     (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
013     SEAGIS Contacts:  Surveillance de l'Environnement Assist�e par Satellite
014     Institut de Recherche pour le D�veloppement / US-Espace
015     mailto:seasnet@teledetection.fr
016    
017    
018     This library is free software; you can redistribute it and/or
019     modify it under the terms of the GNU Lesser General Public
020     License as published by the Free Software Foundation; either
021     version 2.1 of the License, or (at your option) any later version.
022    
023     This library is distributed in the hope that it will be useful,
024     but WITHOUT ANY WARRANTY; without even the implied warranty of
025     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
026     Lesser General Public License for more details.
027    
028     You should have received a copy of the GNU Lesser General Public
029     License along with this library; if not, write to the Free Software
030     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
031    
032     Contact:
033    
034     Andreas Poth
035     lat/lon GmbH
036     Aennchenstr. 19
037     53115 Bonn
038     Germany
039     E-Mail: poth@lat-lon.de
040    
041     Klaus Greve
042     Department of Geography
043     University of Bonn
044     Meckenheimer Allee 166
045     53115 Bonn
046     Germany
047     E-Mail: klaus.greve@uni-bonn.de
048    
049     
050     ---------------------------------------------------------------------------*/
051    package org.deegree.model.csct.cs;
052    
053    // OpenGIS dependencies
054    import java.awt.geom.Point2D;
055    import java.util.Map;
056    
057    import javax.media.jai.ParameterList;
058    import javax.media.jai.ParameterListDescriptor;
059    import javax.media.jai.ParameterListImpl;
060    
061    import org.deegree.model.csct.ct.MathTransformProvider;
062    import org.deegree.model.csct.ct.MissingParameterException;
063    import org.deegree.model.csct.resources.Naming;
064    import org.deegree.model.csct.resources.Utilities;
065    import org.deegree.model.csct.units.Unit;
066    
067    /**
068     * A projection from geographic coordinates to projected coordinates.
069     * 
070     * @version 1.00
071     * @author OpenGIS (www.opengis.org)
072     * @author Martin Desruisseaux
073     * 
074     * @author last edited by: $Author: bezema $
075     * 
076     * @version $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $
077     * 
078     * @see "org.opengis.cs.CS_Projection"
079     */
080    public class Projection extends Info {
081        /**
082         * Serial number for interoperability with different versions.
083         */
084        private static final long serialVersionUID = 2153398430020498215L;
085    
086        /**
087         * Classification string for projection (e.g. "Transverse_Mercator").
088         */
089        private final String classification;
090    
091        /**
092         * Parameters to use for projection, in metres or degrees.
093         */
094        private final ParameterList parameters;
095    
096        /**
097         * Convenience constructor for a projection using the specified ellipsoid.
098         * 
099         * @param name
100         *            Name to give new object.
101         * @param classification
102         *            Classification string for projection (e.g. "Transverse_Mercator").
103         * @param ellipsoid
104         *            Ellipsoid parameter. If non-null, then <code>"semi_major"</code> and
105         *            <code>"semi_minor"</code> parameters will be set according.
106         * @param centre
107         *            Central meridian and latitude of origin, in degrees. If non-null, then
108         *            <code>"central_meridian"</code> and <code>"latitude_of_origin"</code> will be
109         *            set according.
110         * @param translation
111         *            False easting and northing, in metres. If non-null, then
112         *            <code>"false_easting"</code> and <code>"false_northing"</code> will be set
113         *            according.
114         * @param scaleFactor
115         */
116        public Projection( final String name, final String classification, final Ellipsoid ellipsoid,
117                           final Point2D centre, final Point2D translation, final double scaleFactor ) {
118            super( name );
119            ensureNonNull( "classification", classification );
120            this.classification = classification;
121            this.parameters = init( getParameterList( classification ), ellipsoid, centre, translation,
122                                    scaleFactor );
123        }
124    
125        /**
126         * Creates a projection. The set of parameters (<code>parameters</code>) may be queried with
127         * <code>{@link org.deegree.model.csct.ct.MathTransformFactory#getMathTransformProvider
128         *              MathTransformFactory.getMathTransformProvider}(classification).{@link
129         *              MathTransformProvider#getParameterList getParameterList()}</code>.
130         * 
131         * @param name
132         *            Name to give new object.
133         * @param classification
134         *            Classification string for projection (e.g. "Transverse_Mercator").
135         * @param parameters
136         *            Parameters to use for projection, in metres or degrees.
137         * 
138         */
139        public Projection( final String name, final String classification,
140                           final ParameterList parameters ) {
141            super( name );
142            ensureNonNull( "classification", classification );
143            ensureNonNull( "parameters", parameters );
144            this.classification = classification;
145            this.parameters = clone( parameters );
146        }
147    
148        /**
149         * Creates a projection.
150         * 
151         * @param properties
152         *            The set of properties (see {@link Info}).
153         * @param classification
154         *            Classification string for projection (e.g. "Transverse_Mercator").
155         * @param parameters
156         *            Parameters to use for projection, in metres or degrees.
157         */
158        Projection( final Map properties, final String classification, final ParameterList parameters ) {
159            super( properties );
160            this.classification = classification;
161            this.parameters = parameters;
162            // Accept null values.
163        }
164    
165        /**
166         * Returns a parameter list for the specified classification. If there is no special parameter
167         * descriptor for the specified classification, then a default descriptor is used.
168         * 
169         * @param classification
170         * @return a parameter list for the specified classification. If there is no special parameter
171         *         descriptor for the specified classification, then a default descriptor is used.
172         */
173        static ParameterList getParameterList( final String classification ) {
174            return Naming.PROJECTIONS.getParameterList(
175                                                        classification,
176                                                        MathTransformProvider.DEFAULT_PROJECTION_DESCRIPTOR );
177        }
178    
179        /**
180         * Initialize a list of parameter from the specified ellipsoid and points.
181         * 
182         * @param parameters
183         *            The parameters to initialize.
184         * @param ellipsoid
185         *            Ellipsoid parameter. If non-null, then <code>"semi_major"</code> and
186         *            <code>"semi_minor"</code> parameters will be set according.
187         * @param centre
188         *            Central meridian and latitude of origin, in degrees. If non-null, then
189         *            <code>"central_meridian"</code> and <code>"latitude_of_origin"</code> will be
190         *            set according.
191         * @param translation
192         *            False easting and northing, in metres. If non-null, then
193         *            <code>"false_easting"</code> and <code>"false_northing"</code> will be set
194         *            according.
195         * @param scaleFactor
196         * @return <code>parameters</code> for convenience.
197         */
198        static ParameterList init( final ParameterList parameters, final Ellipsoid ellipsoid,
199                                   final Point2D centre, final Point2D translation,
200                                   final double scaleFactor ) {
201            if ( ellipsoid != null ) {
202                final Unit axisUnit = ellipsoid.getAxisUnit();
203                parameters.setParameter( "semi_major",
204                                         Unit.METRE.convert( ellipsoid.getSemiMajorAxis(), axisUnit ) );
205                parameters.setParameter( "semi_minor",
206                                         Unit.METRE.convert( ellipsoid.getSemiMinorAxis(), axisUnit ) );
207            }
208            if ( centre != null ) {
209                parameters.setParameter( "central_meridian", centre.getX() );
210                parameters.setParameter( "latitude_of_origin", centre.getY() );
211            }
212            if ( translation != null ) {
213                parameters.setParameter( "false_easting", translation.getX() );
214                parameters.setParameter( "false_northing", translation.getY() );
215            }
216            if ( scaleFactor < 0 || scaleFactor > 3.5 ) {
217                parameters.setParameter( "scale_factor", 1.0 );
218            } else {
219                parameters.setParameter( "scale_factor", scaleFactor );
220            }
221            return parameters;
222        }
223    
224        /**
225         * Returns a clone of a parameter list.
226         */
227        private static ParameterList clone( final ParameterList list ) {
228            if ( list == null ) {
229                return null;
230            }
231            final ParameterListDescriptor descriptor = list.getParameterListDescriptor();
232            final ParameterList copy = new ParameterListImpl( descriptor );
233            final String[] names = descriptor.getParamNames();
234            if ( names != null )
235                for ( int i = 0; i < names.length; i++ ) {
236                    final String name = names[i];
237                    copy.setParameter( name, list.getObjectParameter( name ) );
238                }
239            return copy;
240        }
241    
242        /**
243         * Gets the projection classification name (e.g. "Transverse_Mercator").
244         * 
245         * @return the projection classification name (e.g. "Transverse_Mercator").
246         * 
247         * @see "org.opengis.cs.CS_Projection#getClassName()"
248         */
249        public String getClassName() {
250            return classification;
251        }
252    
253        /**
254         * Returns all parameters.
255         * 
256         * @return all parameters.
257         * 
258         * @see "org.opengis.cs.CS_Projection#getNumParameters()"
259         * @see "org.opengis.cs.CS_Projection#getParameter(int)"
260         */
261        public ParameterList getParameters() {
262            return clone( parameters );
263        }
264    
265        /**
266         * Convenience method for fetching a parameter value. Search is case-insensitive and ignore
267         * leading and trailing blanks.
268         * 
269         * @param name
270         *            Parameter to look for.
271         * @return The parameter value.
272         * @throws MissingParameterException
273         *             if parameter <code>name</code> is not found.
274         */
275        public double getValue( final String name )
276                                throws MissingParameterException {
277            return getValue( parameters, name, Double.NaN, true );
278        }
279    
280        /**
281         * Convenience method for fetching a parameter value. Search is case-insensitive and ignore
282         * leading and trailing blanks.
283         * 
284         * @param name
285         *            Parameter to look for.
286         * @param defaultValue
287         *            Default value to return if parameter <code>name</code> is not found.
288         * @return The parameter value, or <code>defaultValue</code> if the parameter
289         *         <code>name</code> is not found.
290         */
291        public double getValue( final String name, final double defaultValue ) {
292            return getValue( parameters, name, defaultValue, false );
293        }
294    
295        /**
296         * Convenience method for fetching a parameter value. Search is case-insensitive and ignore
297         * leading and trailing blanks.
298         * 
299         * @param parameters
300         *            User-suplied parameters.
301         * @param name
302         *            Parameter to look for.
303         * @param defaultValue
304         *            Default value to return if parameter <code>name</code> is not found.
305         * @param required
306         *            <code>true</code> if the parameter is required (in which case
307         *            <code>defaultValue</code> is ignored), or <code>false</code> otherwise.
308         * @return The parameter value, or <code>defaultValue</code> if the parameter is not found and
309         *         <code>required</code> is <code>false</code>.
310         * @throws MissingParameterException
311         *             if <code>required</code> is <code>true</code> and parameter <code>name</code>
312         *             is not found.
313         */
314        private static double getValue( final ParameterList parameters, String name,
315                                        final double defaultValue, final boolean required )
316                                throws MissingParameterException {
317            name = name.trim();
318            RuntimeException cause = null;
319            if ( parameters != null ) {
320                try {
321                    final Object value = parameters.getObjectParameter( name );
322                    if ( value instanceof Number ) {
323                        // Do not require an instance of Double.
324                        return ( (Number) value ).doubleValue();
325                    }
326                    // May require an instance of Double. Will
327                    // probably throw ClassCastException since
328                    // the last try didn't worked.
329                    return parameters.getDoubleParameter( name );
330    
331                } catch ( IllegalArgumentException exception ) {
332                    // There is no parameter with the specified name.
333                    cause = exception;
334                } catch ( IllegalStateException exception ) {
335                    // the parameter value is still NO_PARAMETER_DEFAULT
336                    cause = exception;
337                }
338            }
339            if ( !required ) {
340                return defaultValue;
341            }
342            if ( cause != null ) {
343                throw cause;
344            }
345            final MissingParameterException exception = new MissingParameterException( null, name );
346    
347            throw exception;
348        }
349    
350        /**
351         * Returns a hash value for this projection.
352         * 
353         * @return a hash value for this projection.
354         */
355        public int hashCode() {
356            int code = 45896321;
357            if ( classification != null )
358                code = code * 37 + classification.hashCode();
359            if ( parameters != null )
360                code = code * 37 + parameters.hashCode();
361            return code;
362        }
363    
364        /**
365         * Compares the specified object with this projection for equality.
366         * 
367         * @param object
368         * @return
369         */
370        public boolean equals( final Object object ) {
371            if ( super.equals( object ) ) {
372                final Projection that = (Projection) object;
373                return Utilities.equals( this.classification, that.classification )
374                       && Utilities.equals( this.parameters, that.parameters );
375            }
376            return false;
377        }
378    
379        /**
380         * Fill the part inside "[...]". Used for formatting Well Know Text (WKT).
381         * 
382         * @return the String "PROJECTION"
383         */
384        String addString() {
385            return "PROJECTION";
386        }
387    
388    }