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 }