001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/model/csct/ct/MatrixTransform.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    package org.deegree.crs.transformations;
039    
040    // Geometry
041    import java.util.ArrayList;
042    import java.util.List;
043    
044    import javax.vecmath.GMatrix;
045    import javax.vecmath.Matrix3d;
046    import javax.vecmath.Matrix4d;
047    import javax.vecmath.Point3d;
048    
049    import org.deegree.crs.coordinatesystems.CoordinateSystem;
050    import org.deegree.crs.projections.ProjectionUtils;
051    import org.deegree.crs.utilities.Matrix;
052    
053    /**
054     * 
055     * The <code>MatrixTransform</code> class allows transformations using matrices. Although technically n &times; m
056     * matrices are possible, at the moment only 2 &times; 2, 3 &times; 3 and 4 &times; 4 matrices are supported.
057     * 
058     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
059     * 
060     * @author last edited by: $Author:$
061     * 
062     * @version $Revision:$, $Date:$
063     * 
064     */
065    public class MatrixTransform extends CRSTransformation {
066        /**
067         * Serial number for interoperability with different versions.
068         */
069        private static final long serialVersionUID = -2104496465933824935L;
070    
071        /**
072         * the number of rows.
073         */
074        private final int numRow;
075    
076        /**
077         * the number of columns.
078         */
079        private final int numCol;
080    
081        private GMatrix matrix = null;
082    
083        private GMatrix invertMatrix = null;
084    
085        private Matrix3d matrix3D = null;
086    
087        private Matrix3d invertMatrix3D = null;
088    
089        private Matrix4d matrix4D = null;
090    
091        private Matrix4d invertMatrix4D = null;
092    
093        /**
094         * Set super values and numRow,numCol.
095         * 
096         * @param source
097         * @param target
098         * @param identifier
099         * @param name
100         * @param numRow
101         * @param numCol
102         */
103        private MatrixTransform( CoordinateSystem source, CoordinateSystem target, String identifier, String name,
104                                 int numRow, int numCol ) {
105            super( source, target, identifier, name, null, null, null );
106            this.numCol = numCol;
107            this.numRow = numRow;
108        }
109    
110        /**
111         * Construct a transform.
112         * 
113         * @param source
114         *            the source coordinate system
115         * @param target
116         *            the target coordinate system.
117         * 
118         * @param matrix
119         * @param identifier
120         * @param name
121         *            of the transformation
122         */
123        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final GMatrix matrix, String identifier,
124                                String name ) {
125            this( source, target, identifier, name, matrix.getNumRow(), matrix.getNumCol() );
126            this.matrix = new GMatrix( matrix );
127            invertMatrix = new GMatrix( matrix );
128            invertMatrix.invert();
129        }
130    
131        /**
132         * Construct a transform with name set to 'Matrix-Transform'.
133         * 
134         * @param source
135         *            the source coordinate system
136         * @param target
137         *            the target coordinate system.
138         * 
139         * @param matrix
140         * @param identifier
141         */
142        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final GMatrix matrix, String identifier ) {
143            this( source, target, matrix, identifier, "Matrix-Transform" );
144        }
145    
146        /**
147         * Construct a transform with id set to {@link CRSTransformation#createFromTo(String, String)} and name to
148         * 'Matrix-Transform'.
149         * 
150         * @param source
151         *            the source coordinate system
152         * @param target
153         *            the target coordinate system.
154         * 
155         * @param matrix
156         */
157        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final GMatrix matrix ) {
158            this( source, target, matrix, createFromTo( source.getIdentifier(), target.getIdentifier() ) );
159        }
160    
161        /**
162         * Construct a 3d transform.
163         * 
164         * @param source
165         *            the source coordinate system
166         * @param target
167         *            the target coordinate system.
168         * 
169         * @param matrix
170         * @param identifier
171         * @param name
172         *            of the transformation
173         */
174        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final Matrix3d matrix, String identifier,
175                                String name ) {
176            this( source, target, identifier, name, 3, 3 );
177            this.matrix3D = new Matrix3d( matrix );
178            invertMatrix3D = new Matrix3d();
179            invertMatrix3D.invert( matrix3D );
180        }
181    
182        /**
183         * Construct a 3d transform with name set to 'Matrix-Transform'.
184         * 
185         * @param source
186         *            the source coordinate system
187         * @param target
188         *            the target coordinate system.
189         * 
190         * @param matrix
191         * @param identifier
192         */
193        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final Matrix3d matrix, String identifier ) {
194            this( source, target, matrix, identifier, "Matrix-Transform" );
195        }
196    
197        /**
198         * Construct a 3d transform with id set to {@link CRSTransformation#createFromTo(String, String)} and name to
199         * 'Matrix-Transform'.
200         * 
201         * @param source
202         *            the source coordinate system
203         * @param target
204         *            the target coordinate system.
205         * 
206         * @param matrix
207         */
208        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix3d matrix ) {
209            this( source, target, matrix, createFromTo( source.getIdentifier(), target.getIdentifier() ) );
210        }
211    
212        /**
213         * Construct a 4d transform.
214         * 
215         * @param source
216         *            the source coordinate system
217         * @param target
218         *            the target coordinate system.
219         * 
220         * @param matrix
221         * @param identifier
222         * @param name
223         *            of this transform
224         */
225        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix4d matrix, String identifier,
226                                String name ) {
227            this( source, target, identifier, name, 4, 4 );
228            matrix4D = new Matrix4d( matrix );
229            invertMatrix4D = new Matrix4d();
230            invertMatrix4D.invert( matrix4D );
231        }
232    
233        /**
234         * Construct a 4d transform with name set to 'Matrix-Transform'.
235         * 
236         * @param source
237         *            the source coordinate system
238         * @param target
239         *            the target coordinate system.
240         * 
241         * @param matrix
242         * @param identifier
243         */
244        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix4d matrix, String identifier ) {
245            this( source, target, matrix, identifier, "Matrix-Transform" );
246        }
247    
248        /**
249         * Construct a 4d transform with id set to {@link CRSTransformation#createFromTo(String, String)} and name to
250         * 'Matrix-Transform'.
251         * 
252         * @param source
253         *            the source coordinate system
254         * @param target
255         *            the target coordinate system.
256         * 
257         * @param matrix
258         */
259        public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix4d matrix ) {
260            this( source, target, matrix, createFromTo( source.getIdentifier(), target.getIdentifier() ) );
261        }
262    
263        @Override
264        public List<Point3d> doTransform( List<Point3d> srcPts ) {
265            if ( isIdentity() ) {
266                return srcPts;
267            }
268            // System.out.println( "The matrix is a: " + ( ( matrix3D != null ) ? "Matrix3D"
269            // : ( ( matrix4D != null ) ? "Matrix4D"
270            // : "customMatrix" ) )
271            // + " with following values:\n"
272            // + ( ( matrix3D != null ) ? matrix3D : ( ( matrix4D != null ) ? matrix4D : matrix ) ) );
273            List<Point3d> results = new ArrayList<Point3d>( srcPts );
274            if ( isInverseTransform() ) {
275                // System.out.println( "A inverse matrix transform with incoming points: " + srcPts );
276                if ( matrix3D != null ) {
277                    transform( invertMatrix3D, results );
278                } else if ( matrix4D != null ) {
279                    transform( invertMatrix4D, results );
280                } else {
281                    transform( invertMatrix, results );
282                }
283            } else {
284                // System.out.println( "A matrix transform with incoming points: " + srcPts );
285                if ( matrix3D != null ) {
286                    transform( matrix3D, results );
287                } else if ( matrix4D != null ) {
288                    transform( matrix4D, results );
289                } else {
290                    transform( matrix, results );
291                }
292            }
293            return results;
294        }
295    
296        /**
297         * @return the dimension of input points.
298         */
299        public int getDimSource() {
300            return numCol - 1;
301        }
302    
303        /**
304         * @return the dimension of output points.
305         */
306        public int getDimTarget() {
307            return numRow - 1;
308        }
309    
310        /**
311         * @return true if this transformation holds an identiy matrix (e.g. doesn't transform at all).
312         */
313        @Override
314        public boolean isIdentity() {
315            if ( numRow != numCol ) {
316                return false;
317            }
318            for ( int row = 0; row < numRow; row++ )
319                for ( int col = 0; col < numCol; col++ ) {
320                    double value = ( matrix3D != null ) ? matrix3D.getElement( row, col )
321                                                       : ( ( matrix4D != null ) ? matrix4D.getElement( row, col )
322                                                                               : matrix.getElement( row, col ) );
323                    if ( Math.abs( value - ( col == row ? 1 : 0 ) ) > ProjectionUtils.EPS11 ) {
324                        return false;
325                    }
326                }
327            return true;
328            // return true;
329        }
330    
331        @Override
332        public boolean equals( final Object object ) {
333            if ( object == this ) {
334                return true; // Slight optimization
335            }
336            if ( object != null && super.equals( object ) ) {
337                return matrix.equals( ( (MatrixTransform) object ).matrix );
338            }
339            return false;
340        }
341    
342        /**
343         * Transforms an array of floating point coordinates by this matrix. Point coordinates must have a dimension equals
344         * to <code>{@link Matrix#getNumCol()}-1</code>. For example, for square matrix of size 4&times;4, coordinate
345         * points are three-dimensional and stored in the arrays starting at the specified offset (<code>srcOff</code>)
346         * in the order <code>[x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>,
347         *        x<sub>1</sub>, y<sub>1</sub>, z<sub>1</sub>...,
348         *        x<sub>n</sub>, y<sub>n</sub>, z<sub>n</sub>]</code>.
349         * 
350         * The transformed points <code>(x',y',z')</code> are computed as below (note that this computation is similar to
351         * {@link PerspectiveTransform}):
352         * 
353         * <blockquote> <code>
354         * <pre>
355         *    [ u ]     [ m&lt;sub&gt;00&lt;/sub&gt;  m&lt;sub&gt;01&lt;/sub&gt;  m&lt;sub&gt;02&lt;/sub&gt;  m&lt;sub&gt;03&lt;/sub&gt; ] [ x ]
356         *    [ v ]  =  [ m&lt;sub&gt;10&lt;/sub&gt;  m&lt;sub&gt;11&lt;/sub&gt;  m&lt;sub&gt;12&lt;/sub&gt;  m&lt;sub&gt;13&lt;/sub&gt; ] [ y ]
357         *    [ w ]     [ m&lt;sub&gt;20&lt;/sub&gt;  m&lt;sub&gt;21&lt;/sub&gt;  m&lt;sub&gt;22&lt;/sub&gt;  m&lt;sub&gt;23&lt;/sub&gt; ] [ z ]
358         *    [ t ]     [ m&lt;sub&gt;30&lt;/sub&gt;  m&lt;sub&gt;31&lt;/sub&gt;  m&lt;sub&gt;32&lt;/sub&gt;  m&lt;sub&gt;33&lt;/sub&gt; ] [ 1 ]
359         *                         
360         *     x' = u/t
361         *     y' = v/t
362         *     w' = w/t
363         * </pre>
364         * </code> </blockquote>
365         * 
366         * @param srcPts
367         *            The array containing the source point coordinates.
368         */
369        private void transform( GMatrix gm, List<Point3d> srcPts ) {
370            throw new UnsupportedOperationException( "not yet implemented" );
371            // final int inputDimension = numCol - 1; // The last value will be assumed equals to 1.
372            // final int outputDimension = numRow - 1;
373            // final double[] buffer = new double[numRow];
374            // while ( --numPts >= 0 ) {
375            // int mix = 0;
376            // for ( int j = 0; j < numRow; j++ ) {
377            // // double sum = elt[mix + inputDimension];
378            // // for ( int i = 0; i < inputDimension; i++ ) {
379            // // sum += srcPts[srcOff + i] * elt[mix++];
380            // // }
381            // // buffer[j] = sum;
382            // mix++;
383            // }
384            // final double w = buffer[outputDimension];
385            // for ( int j = 0; j < outputDimension; j++ ) {
386            // // 'w' is equals to 1 if the transform is affine.
387            // dstPts[dstOff++] = buffer[j] / w;
388            // }
389            // srcOff += inputDimension;
390            // }
391    
392        }
393    
394        /**
395         * Use the given GMatrix to transform the given points inplace.
396         * 
397         * @param m4d
398         *            the matrix to use (e.g. the inverse matrix or the forward matrix.
399         * @param srcPts
400         *            The array containing the source point coordinates.
401         */
402        private void transform( Matrix4d m4d, List<Point3d> srcPts ) {
403            for ( Point3d p : srcPts ) {
404                m4d.transform( p );
405            }
406        }
407    
408        /**
409         * Use the given GMatrix to transform the given points inplace.
410         * 
411         * @param m4d
412         *            the matrix to use (e.g. the inverse matrix or the forward matrix.
413         * @param srcPts
414         *            The array containing the source point coordinates.
415         */
416        private void transform( Matrix3d m3d, List<Point3d> srcPts ) {
417            for ( Point3d p : srcPts ) {
418                m3d.transform( p );
419            }
420        }
421    
422        /**
423         * @return the matrix.
424         */
425        public final GMatrix getMatrix() {
426            if ( matrix != null ) {
427                return isInverse ? invertMatrix : matrix;
428            }
429            GMatrix result = new GMatrix( numRow, numCol );
430            if ( matrix3D != null ) {
431                if ( isInverse ) {
432                    result.set( invertMatrix3D );
433                } else {
434                    result.set( matrix3D );
435                }
436            }
437            if ( matrix4D != null ) {
438                if ( isInverse ) {
439                    result.set( invertMatrix4D );
440                } else {
441                    result.set( matrix4D );
442                }
443            }
444            return result;
445        }
446    
447    }