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 }