001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/ct/MathTransformProvider.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.ct;
052
053 // OpenGIS (SEAS) dependencies
054 import java.util.Locale;
055
056 import javax.media.jai.ParameterList;
057 import javax.media.jai.ParameterListDescriptor;
058 import javax.media.jai.ParameterListDescriptorImpl;
059 import javax.media.jai.ParameterListImpl;
060 import javax.media.jai.util.Range;
061
062 import org.deegree.model.csct.pt.Latitude;
063 import org.deegree.model.csct.pt.Longitude;
064 import org.deegree.model.csct.resources.Utilities;
065 import org.deegree.model.csct.resources.XArray;
066 import org.deegree.model.csct.resources.css.Resources;
067
068
069
070 /**
071 * Base class for {@link MathTransform} providers.
072 * Instance of this class allow the creation of transform
073 * objects from a classification name.
074 * <br><br>
075 * <strong>Note: this class is not part of OpenGIS specification and
076 * may change in a future version. Do not rely strongly on it.</strong>
077 *
078 * @version 1.0
079 * @author Martin Desruisseaux
080 */
081 public abstract class MathTransformProvider
082 {
083 /**
084 * The zero value.
085 */
086 private static final Double ZERO = new Double(0);
087
088 /**
089 * Range of positives values. Range goes
090 * from 0 exclusive to positive infinity.
091 */
092 protected static final Range POSITIVE_RANGE = new Range(Double.class, ZERO, false, null, false);
093
094 /**
095 * Range of longitude values. Range goes
096 * from -180� to +180� inclusives.
097 */
098 protected static final Range LONGITUDE_RANGE = new Range(Double.class, new Double(Longitude.MIN_VALUE), true, new Double(Longitude.MAX_VALUE), true);
099
100 /**
101 * Range of latitude values. Range goes
102 * from -90� to +90� inclusives.
103 */
104 protected static final Range LATITUDE_RANGE = new Range(Double.class, new Double(Latitude.MIN_VALUE), true, new Double(Latitude.MAX_VALUE), true);
105
106 /**
107 * Number of colunms in table {@link #properties} below.
108 */
109 private static final int RECORD_LENGTH = 4;
110
111 /**
112 * A default parameter list descriptor for
113 * map projections. This descriptor declare
114 * <code>"semi_major"</code>,
115 * <code>"semi_minor"</code>,
116 * <code>"central_meridian"</code>,
117 * <code>"latitude_of_origin"</code>,
118 * <code>"false_easting"</code> and
119 * <code>"false_northing"</code> parameters.
120 */
121 public static final ParameterListDescriptor DEFAULT_PROJECTION_DESCRIPTOR = getDescriptor(new Object[]
122 {
123 "semi_major", Double.class, ParameterListDescriptor.NO_PARAMETER_DEFAULT, POSITIVE_RANGE,
124 "semi_minor", Double.class, ParameterListDescriptor.NO_PARAMETER_DEFAULT, POSITIVE_RANGE,
125 "central_meridian", Double.class, ZERO, LONGITUDE_RANGE,
126 "latitude_of_origin", Double.class, ZERO, LATITUDE_RANGE,
127 "false_easting", Double.class, ZERO, null,
128 "false_northing", Double.class, ZERO, null,
129 "scale_factor", Double.class, ZERO, null
130 });
131
132 /**
133 * The set parameters to use for {@link ParameterListDescriptor} construction,
134 * or <code>null</code> if the descriptor is already constructed.
135 */
136 private Object[] properties;
137
138 /**
139 * The parameter list descriptor. This object will
140 * be constructed only the first time it is needed.
141 */
142 private ParameterListDescriptor descriptor;
143
144 /**
145 * The classification name. This name do
146 * not contains leading or trailing blanks.
147 */
148 private final String classification;
149
150 /**
151 * Resources key for a human readable name. This
152 * is used for {@link #getName} implementation.
153 */
154 private final int nameKey;
155
156 /**
157 * Construct a new provider.
158 *
159 * @param classification The classification name.
160 * @param inherit The parameter list descriptor to inherit from, or <code>null</code>
161 * if there is none. All parameter descriptions from <code>inherit</code> will
162 * be copied into this newly created <code>MathTransformProvider</code>. For
163 * map projections, this argument may be {@link #DEFAULT_PROJECTION_DESCRIPTOR}.
164 * Subclasses may add or change parameters in their constructor by invoking
165 * {@link #put}.
166 */
167 protected MathTransformProvider(final String classification, final ParameterListDescriptor inherit)
168 {this(classification, -1, inherit);}
169
170 /**
171 * Construct a new provider.
172 *
173 * @param classification The classification name.
174 * @param nameKey Resources key for a human readable name.
175 * This is used for {@link #getName} implementation.
176 * @param inherit The parameter list descriptor to inherit from, or <code>null</code>
177 * if there is none. All parameter descriptions from <code>inherit</code> will
178 * be copied into this newly created <code>MathTransformProvider</code>. For
179 * map projections, this argument may be {@link #DEFAULT_PROJECTION_DESCRIPTOR}.
180 * Subclasses may add or change parameters in their constructor by invoking
181 * {@link #put}.
182 */
183 MathTransformProvider(final String classification, final int nameKey, final ParameterListDescriptor inherit)
184 {
185 this.classification = classification.trim();
186 this.nameKey = nameKey;
187 if (inherit!=null)
188 {
189 final String[] names = inherit.getParamNames();
190 final Class [] classes = inherit.getParamClasses();
191 final Object[] defaults = inherit.getParamDefaults();
192 properties = new Object[names.length*RECORD_LENGTH];
193 for (int i=0; i<names.length; i++)
194 {
195 final int j=i*RECORD_LENGTH;
196 properties[j+0] = names [i];
197 properties[j+1] = classes [i];
198 properties[j+2] = defaults[i];
199 properties[j+3] = inherit.getParamValueRange(names[i]);
200 }
201 }
202 else properties = new Object[0];
203 }
204
205 /**
206 * Adds or changes a parameter to this math transform provider. If this <code>MathTransformProvider</code>
207 * has been constructed with {@link #DEFAULT_PROJECTION_DESCRIPTOR} as argument, then default values
208 * are already provided for "semi_major", "semi_minor", "central_meridian" and "latitude_of_origin".
209 * Subclasses may call this method in their constructor for adding or changing parameters.
210 *
211 * @param parameter The parameter name.
212 * @param defaultValue The default value for this parameter, or {@link Double#NaN} if there is none.
213 * @param range The range of legal values. May be one of the predefined constants
214 * ({@link #POSITIVE_RANGE}, {@link #LONGITUDE_RANGE}, {@link #LATITUDE_RANGE})
215 * or any other {@link Range} object. May be <code>null</code> if all values
216 * are valid for this parameter.
217 * @throws IllegalStateException If {@link #getParameterList} has already been invoked prior to this call.
218 */
219 protected final void put(final String parameter, final double defaultValue, final Range range) throws IllegalStateException
220 {put(parameter, Double.class, wrap(defaultValue), range);}
221
222 /**
223 * Adds or changes an integer parameter to this math transform provider.
224 * Support of integer values help to make the API clearer, but the true
225 * OpenGIS's parameter class support only <code>double</code> values.
226 * This is why this method is not yet public. Current SEAGIS version use
227 * integer parameters only for matrix dimension and for a custom parameter
228 * in geocentric transform. We hope the user will barely notice it...
229 *
230 * @param parameter The parameter name.
231 * @param defaultValue The default value for this parameter.
232 * @param range The range of legal values. This is up to the caller to
233 * build is own range with integer values (predefined ranges
234 * like {@link #POSITIVE_RANGE} will not work).
235 *
236 * @throws IllegalStateException If {@link #getParameterList}
237 * has already been invoked prior to this call.
238 */
239 final void putInt(final String parameter, final int defaultValue, final Range range) throws IllegalStateException
240 {put(parameter, Integer.class, new Integer(defaultValue), range);}
241
242 /**
243 * Adds or changes a parameter to this math transform provider.
244 *
245 * @param parameter The parameter name.
246 * @param type The parameter type.
247 * @param defaultValue The default value for this parameter.
248 * @param range The range of legal values.
249 *
250 * @throws IllegalStateException If {@link #getParameterList}
251 * has already been invoked prior to this call.
252 */
253 private synchronized void put(String parameter, final Class type, Object defaultValue, final Range range) throws IllegalStateException
254 {
255 if (properties==null)
256 {
257 // Construction is finished.
258 throw new IllegalStateException();
259 }
260 if (defaultValue!=null && range!=null)
261 {
262 // Slight optimization for reducing the amount of objects in the heap.
263 Object check;
264 if (defaultValue.equals(check=range.getMinValue())) defaultValue=check;
265 if (defaultValue.equals(check=range.getMaxValue())) defaultValue=check;
266 }
267 parameter = parameter.trim();
268 final int end = properties.length;
269 for (int i=0; i<end; i+=RECORD_LENGTH)
270 {
271 if (parameter.equalsIgnoreCase(properties[i].toString()))
272 {
273 properties[i+0] = parameter;
274 properties[i+1] = type;
275 properties[i+2] = defaultValue;
276 properties[i+3] = range;
277 return;
278 }
279 }
280 properties = XArray.resize(properties, end+RECORD_LENGTH);
281 properties[end+0] = parameter;
282 properties[end+1] = type;
283 properties[end+2] = defaultValue;
284 properties[end+3] = range;
285 }
286
287 /**
288 * Wrap the specified double value in an object.
289 */
290 private static Object wrap(final double value)
291 {
292 if (Double.isNaN(value)) return ParameterListDescriptor.NO_PARAMETER_DEFAULT;
293 if (value == Latitude.MIN_VALUE) return LATITUDE_RANGE.getMinValue();
294 if (value == Latitude.MAX_VALUE) return LATITUDE_RANGE.getMaxValue();
295 if (value == Longitude.MIN_VALUE) return LONGITUDE_RANGE.getMinValue();
296 if (value == Longitude.MAX_VALUE) return LONGITUDE_RANGE.getMaxValue();
297 if (value == 0) return ZERO;
298 return new Double(value);
299 }
300
301 /**
302 * Returns the parameter list descriptor for the specified properties list.
303 */
304 private static ParameterListDescriptor getDescriptor(final Object[] properties)
305 {
306 final String[] names = new String[properties.length/RECORD_LENGTH];
307 final Class [] classes = new Class [names.length];
308 final Object[] defaults = new Object[names.length];
309 final Range [] ranges = new Range [names.length];
310 for (int i=0; i<names.length; i++)
311 {
312 final int j = i*RECORD_LENGTH;
313 names [i] = (String)properties[j+0];
314 classes [i] = (Class)properties[j+1];
315 defaults[i] = properties[j+2];
316 ranges [i] = (Range)properties[j+3];
317 }
318 return new ParameterListDescriptorImpl(null, names, classes, defaults, ranges);
319 }
320
321 /**
322 * Returns the classification name.
323 */
324 public String getClassName()
325 {return classification;}
326
327 /**
328 * Returns a human readable name localized for the specified locale.
329 * If no name is available for the specified locale, this method may
330 * returns a name in an arbitrary locale.
331 */
332 public String getName(final Locale locale)
333 {return (nameKey>=0) ? Resources.getResources(locale).getString(nameKey) : getClassName();}
334
335 /**
336 * Returns the parameter list descriptor.
337 */
338 final synchronized ParameterListDescriptor getParameterListDescriptor()
339 {
340 if (descriptor==null)
341 {
342 descriptor = getDescriptor(properties);
343 properties = null; // No longer needed.
344 }
345 return descriptor;
346 }
347
348 /**
349 * Returns a newly created parameter list. The set of parameter
350 * depend of the transform this provider is for. Parameters may
351 * have default values and a range of validity.
352 */
353 public ParameterList getParameterList()
354 {return new ParameterListImpl(getParameterListDescriptor());}
355
356 /**
357 * Returns a transform for the specified parameters.
358 *
359 * @param parameters The parameter values in standard units.
360 * @return A {@link MathTransform} object of this classification.
361 */
362 public abstract MathTransform create(final ParameterList parameters);
363
364 /**
365 * Returns a string representation for this provider.
366 */
367 public String toString()
368 {return Utilities.getShortClassName(this)+'['+getName(null)+']';}
369 }