001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/ct/ConcatenedTransform.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 dependencies (SEAGIS)
054    import java.io.Serializable;
055    
056    import org.deegree.model.csct.pt.CoordinatePoint;
057    import org.deegree.model.csct.pt.Matrix;
058    import org.deegree.model.csct.resources.Utilities;
059    import org.deegree.model.csct.resources.css.ResourceKeys;
060    import org.deegree.model.csct.resources.css.Resources;
061    
062    
063    /**
064     * Base class for concatened transform. Concatened transforms are
065     * serializable if all their step transforms are serializables.
066     *
067     * @version 1.0
068     * @author Martin Desruisseaux
069     */
070    class ConcatenedTransform extends AbstractMathTransform implements Serializable
071    {
072        /**
073         * Serial number for interoperability with different versions.
074         */
075        private static final long serialVersionUID = 5772066656987558634L;
076    
077        /**
078         * The math transform factory that created this concatened transform.
079         * Will be used for creating the inverse transform when needed.
080         */
081        private MathTransformFactory provider;
082    
083        /**
084         * The first math transform.
085         */
086        protected final MathTransform transform1;
087    
088        /**
089         * The second math transform.
090         */
091        protected final MathTransform transform2;
092    
093        /**
094         * The inverse transform. This field
095         * will be computed only when needed.
096         */
097        private transient MathTransform inverse;
098    
099        /**
100         * Construct a concatenated transform.
101         */
102        public ConcatenedTransform(final MathTransformFactory provider, final MathTransform transform1, final MathTransform transform2)
103        {
104            this.provider   = provider;
105            this.transform1 = transform1;
106            this.transform2 = transform2;
107            if (!isValid())
108            {
109                throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_CANT_CONCATENATE_CS_$2,
110                                                   getName(transform1), getName(transform2)));
111            }
112        }
113    
114        /**
115         * Returns a name for the specified coordinate system.
116         */
117        private static final String getName(final MathTransform transform)
118        {
119            if (transform instanceof AbstractMathTransform)
120            {
121                String name = ((AbstractMathTransform) transform).getName();
122                if (name!=null && (name=name.trim()).length()!=0) return name;
123            }
124            return Utilities.getShortClassName(transform);
125        }
126    
127        /**
128         * Check if transforms are compatibles. The default
129         * implementation check if transfert dimension match.
130         */
131        protected boolean isValid()
132        {return transform1.getDimTarget() == transform2.getDimSource();}
133    
134        /**
135         * Gets the dimension of input points.
136         */
137        public final int getDimSource()
138        {return transform1.getDimSource();}
139    
140        /**
141         * Gets the dimension of output points.
142         */
143        public final int getDimTarget()
144        {return transform2.getDimTarget();}
145    
146        /**
147         * Transforms the specified <code>ptSrc</code> and stores the result in <code>ptDst</code>.
148         */
149        public CoordinatePoint transform(final CoordinatePoint ptSrc, CoordinatePoint ptDst) throws TransformException
150        {
151            //  Note: If we know that the transfert dimension is the same than source
152            //        and target dimension, then we don't need to use an intermediate
153            //        point. This optimization is done in ConcatenedTransformDirect.
154            return transform2.transform(transform1.transform(ptSrc, null), ptDst);
155        }
156    
157        /**
158         * Transforms a list of coordinate point ordinal values.
159         */
160        public void transform(final double[] srcPts, final int srcOff, final double[] dstPts, 
161                              final int dstOff, final int numPts) throws TransformException
162        {
163            //  Note: If we know that the transfert dimension is the same than source
164            //        and target dimension, then we don't need to use an intermediate
165            //        buffer. This optimization is done in ConcatenedTransformDirect.
166            final double[] tmp = new double[numPts*transform1.getDimTarget()];
167            transform1.transform(srcPts, srcOff, tmp, 0, numPts);
168            transform2.transform(tmp, 0, dstPts, dstOff, numPts);
169        }
170    
171        /**
172         * Transforms a list of coordinate point ordinal values.
173         */
174        public void transform(final float[] srcPts, final int srcOff, final float[] dstPts, 
175                              final int dstOff, final int numPts) throws TransformException
176        {
177            //  Note: If we know that the transfert dimension is the same than source
178            //        and target dimension, then we don't need to use an intermediate
179            //        buffer. This optimization is done in ConcatenedTransformDirect.
180            final float[] tmp = new float[numPts*transform1.getDimTarget()];
181            transform1.transform(srcPts, srcOff, tmp, 0, numPts);
182            transform2.transform(tmp, 0, dstPts, dstOff, numPts);
183        }
184    
185        /**
186         * Creates the inverse transform of this object.
187         */
188        public synchronized final MathTransform inverse() throws NoninvertibleTransformException
189        {
190            if (inverse==null)
191            {
192                if (provider == null) {
193                    provider = MathTransformFactory.getDefault();
194                }
195                inverse = provider.createConcatenatedTransform(transform2.inverse(), transform1.inverse());
196                if (inverse instanceof ConcatenedTransform)
197                {
198                    ((ConcatenedTransform) inverse).inverse = this;
199                }
200            }
201            return inverse;
202        }
203    
204        /**
205         * Gets the derivative of this transform at a point.
206         *
207         * @param  point The coordinate point where to evaluate the derivative.
208         * @return The derivative at the specified point (never <code>null</code>).
209         * @throws TransformException if the derivative can't be evaluated at the specified point.
210         */
211        public Matrix derivative(final CoordinatePoint point) throws TransformException
212        {
213            final Matrix matrix1 = transform1.derivative(point);
214            final Matrix matrix2 = transform2.derivative(transform1.transform(point, null));
215            // Compute "matrix = matrix2 * matrix1". Reuse an existing matrix object
216            // if possible, which is always the case when both matrix are square.
217            final int numRow = matrix2.getNumRow();
218            final int numCol = matrix1.getNumCol();
219            final Matrix matrix;
220            if (numCol == matrix2.getNumCol()) {
221                matrix = matrix2;
222                matrix2.mul(matrix1);
223            } else {
224                matrix = new Matrix(numRow, numCol);
225                matrix.mul(matrix2, matrix1);
226            }
227            return matrix;
228        }
229    
230        /**
231         * Tests whether this transform does not move any points.
232         * Default implementation check if the two transforms are
233         * identity. This a way too conservative aproach, but it
234         * it doesn't hurt since ConcatenedTransform should not
235         * have been created if it were to result in an identity
236         * transform (this case should have been detected earlier).
237         */
238        public final boolean isIdentity()
239        {return transform1.isIdentity() && transform2.isIdentity();}
240    
241        /**
242         * Returns a hash value for this transform.
243         */
244        public final int hashCode()
245        {return transform1.hashCode() + 37*transform2.hashCode();}
246    
247        /**
248         * Compares the specified object with
249         * this math transform for equality.
250         */
251        public final boolean equals(final Object object)
252        {
253            if (object==this) return true; // Slight optimization
254            if (super.equals(object))
255            {
256                final ConcatenedTransform that = (ConcatenedTransform) object;
257                return Utilities.equals(this.transform1, that.transform1) &&
258                       Utilities.equals(this.transform2, that.transform2);
259            }
260            return false;
261        }
262    
263        /**
264         * Returns the WKT for this math transform.
265         */
266        public final String toString()
267        {
268            final StringBuffer buffer = new StringBuffer("CONCAT_MT[");
269            addWKT(buffer, this, true);
270            buffer.append(']');
271            return buffer.toString();
272        }
273    
274        /**
275         * Append to a string buffer the WKT
276         * for the specified math transform.
277         */
278        private static void addWKT(final StringBuffer buffer, final MathTransform transform, final boolean first)
279        {
280            if (transform instanceof ConcatenedTransform)
281            {
282                final ConcatenedTransform concat = (ConcatenedTransform) transform;
283                addWKT(buffer, concat.transform1, first);
284                addWKT(buffer, concat.transform2, false);
285            }
286            else
287            {
288                if (!first)
289                    buffer.append(", ");
290                buffer.append(transform);
291            }
292        }
293    }