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    }