001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/crs/utilities/Matrix.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.crs.utilities;
037    
038    import static org.deegree.crs.projections.ProjectionUtils.EPS11;
039    
040    import java.awt.geom.AffineTransform;
041    
042    import javax.vecmath.GMatrix;
043    import javax.vecmath.Matrix3d;
044    
045    import org.deegree.crs.components.Axis;
046    
047    /**
048     * The <code>Matrix</code> class TODO add documentation here
049     *
050     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
051     *
052     * @author last edited by: $Author: mschneider $
053     *
054     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
055     *
056     */
057    
058    public class Matrix extends GMatrix {
059        /**
060         * Serial number for interoperability with different versions.
061         */
062        private static final long serialVersionUID = 3778102551617232269L;
063    
064        /**
065         * Construct a square identity matrix of size <code>size</code>&nbsp;&times;&nbsp;<code>size</code>.
066         *
067         * @param size
068         */
069        public Matrix( final int size ) {
070            super( size, size );
071        }
072    
073        /**
074         * Construct a matrix of size <code>numRow</code>&nbsp;&times;&nbsp;<code>numCol</code>. Elements on the
075         * diagonal <var>j==i</var> are set to 1.
076         *
077         * @param numRow
078         * @param numCol
079         */
080        public Matrix( final int numRow, final int numCol ) {
081            super( numRow, numCol );
082        }
083    
084        /**
085         * Constructs a <code>numRow</code>&nbsp;&times;&nbsp;<code>numCol</code> matrix initialized to the values in
086         * the <code>matrix</code> array. The array values are copied in one row at a time in row major fashion. The array
087         * should be exactly <code>numRow*numCol</code> in length. Note that because row and column numbering begins with
088         * zero, <code>row</code> and <code>numCol</code> will be one larger than the maximum possible matrix index
089         * values.
090         *
091         * @param numRow
092         * @param numCol
093         * @param matrix
094         */
095        public Matrix( final int numRow, final int numCol, final double[] matrix ) {
096            super( numRow, numCol, matrix );
097            if ( numRow * numCol != matrix.length ) {
098                throw new IllegalArgumentException( String.valueOf( matrix.length ) );
099            }
100        }
101    
102        /**
103         * Constructs a new matrix from a two-dimensional array of doubles.
104         *
105         * @param matrix
106         *            Array of rows. Each row must have the same length.
107         * @throws IllegalArgumentException
108         *             if the specified matrix is not regular (i.e. if all rows doesn't have the same length).
109         */
110        public Matrix( final double[][] matrix ) throws IllegalArgumentException {
111            super( matrix.length, ( matrix.length != 0 ) ? matrix[0].length : 0 );
112            final int numRow = getNumRow();
113            final int numCol = getNumCol();
114            for ( int j = 0; j < numRow; j++ ) {
115                if ( matrix[j].length != numCol ) {
116                    throw new IllegalArgumentException( "Not a regular Matrix (given rows have different lengths)" );
117                }
118                setRow( j, matrix[j] );
119            }
120        }
121    
122        /**
123         * Constructs a new matrix and copies the initial values from the parameter matrix.
124         *
125         * @param matrix
126         */
127        public Matrix( final GMatrix matrix ) {
128            super( matrix );
129        }
130    
131        /**
132         * Construct a 3&times;3 matrix from the specified affine transform.
133         *
134         * @param transform
135         */
136        public Matrix( final AffineTransform transform ) {
137            super( 3, 3, new double[] { transform.getScaleX(), transform.getShearX(), transform.getTranslateX(),
138                                       transform.getShearY(), transform.getScaleY(), transform.getTranslateY(), 0, 0, 1 } );
139        }
140    
141        /**
142         * Construct an affine transform changing axis order. The resulting affine transform will convert incoming
143         * coordinates into the given destination Axis. For example if source axis are given with (NORTH,WEST) and
144         * destination axis as (EAST,NORTH) assuming the axis use the same units, the resulted matrix will look like:<br/><code>
145         *  &nbsp;0,&nbsp;1,&nbsp;0<br/>
146         * -1,&nbsp;0,&nbsp;0<br/>
147         *  &nbsp;0,&nbsp;0,&nbsp;1<br/>
148         *  </code>
149         * Axis orientation can be inverted only. Rotating axis (e.g. from NORTH,WEST, to NORTH,DOWN, ) is not supported.
150         *
151         * @param srcAxis
152         *            The set of axis orientation for source coordinate system.
153         * @param dstAxis
154         *            The set of axis orientation for destination coordinate system.
155         * @throws IllegalArgumentException
156         *             if the affine transform can't be created for some other reason.
157         */
158        public Matrix( final Axis[] srcAxis, final Axis[] dstAxis ) {
159            this( srcAxis.length + 1 );
160            final int dimension = srcAxis.length;
161            if ( dstAxis.length != dimension ) {
162                throw new IllegalArgumentException( "Given dimensions are of differnt length." );
163            }
164            /*
165             * Map source axis to destination axis. If no axis is moved (for example if the user want to transform
166             * (NORTH,EAST) to (SOUTH,EAST)), then source and destination index will be equal. If some axis are moved (for
167             * example if the user want to transform (NORTH,EAST) to (EAST,NORTH)), then ordinates at index <code>srcIndex</code>
168             * will have to be moved at index <code>dstIndex</code>.
169             */
170            setZero();
171            for ( int srcIndex = 0; srcIndex < dimension; srcIndex++ ) {
172                boolean hasFound = false;
173                final int srcAxe = srcAxis[srcIndex].getOrientation();
174                final int sourceAxisDirection = Math.abs( srcAxe );
175                for ( int dstIndex = 0; dstIndex < dimension; dstIndex++ ) {
176                    final int dstAxeDirection = dstAxis[dstIndex].getOrientation();
177                    if ( sourceAxisDirection == Math.abs( dstAxeDirection ) ) {
178                        if ( hasFound ) {
179                            throw new IllegalArgumentException( "Following axis are colinear: "
180                                                                + srcAxis[srcIndex].getName() + " dstAxe: "
181                                                                + dstAxis[dstIndex].getName() );
182                        }
183                        hasFound = true;
184                        // row, column, value
185                        setElement( dstIndex, srcIndex, ( srcAxe == dstAxeDirection ) ? 1 : -1 );
186                    }
187                }
188                if ( !hasFound ) {
189                    throw new IllegalArgumentException( "No appropriate transformation axis found for srcAxis: "
190                                                        + srcAxis[srcIndex].getName() );
191                }
192            }
193            setElement( dimension, dimension, 1 );
194    
195        }
196    
197        /**
198         * Returns <code>true</code> if this matrix is an affine transform. A transform is affine if the matrix is square
199         * and last row contains only zeros, except in the last column which contains 1.
200         *
201         * @return <code>true</code> if this matrix is an affine transform.
202         */
203        public final boolean isAffine() {
204            int dimension = getNumRow();
205            if ( dimension != getNumCol() ) {
206                return false;
207            }
208    
209            dimension--;
210            for ( int i = 0; i <= dimension; i++ ) {
211                if ( Math.abs( getElement( dimension, i ) - ( i == dimension ? 1 : 0 ) ) > EPS11 ) {
212                    return false;
213                }
214            }
215            return true;
216        }
217    
218        /**
219         * Copies the first 2x3 values into an affine transform object. If not enough values are available, an identity
220         * transform is returned.
221         *
222         * @return an affine transform for this matrix. or an identity if this matrix has not sufficient values.
223         *
224         */
225        public final Matrix3d toAffineTransform() {
226            if ( getNumCol() < 3 || getNumRow() < 2 ) {
227                return new Matrix3d();
228            }
229            return new Matrix3d( getElement( 0, 0 ), getElement( 0, 1 ), getElement( 0, 2 ), getElement( 1, 0 ),
230                                 getElement( 1, 1 ), getElement( 1, 2 ), 0, 0, 1 );
231        }
232    
233        /**
234         * Returns <code>true</code> if this matrix is an identity matrix.
235         *
236         * @return <code>true</code> if this matrix is an identity matrix.
237         */
238        public final boolean isIdentity() {
239            final int numRow = getNumRow();
240            final int numCol = getNumCol();
241            if ( numRow != numCol ) {
242                return false;
243            }
244            for ( int j = 0; j < numRow; j++ )
245                for ( int i = 0; i < numCol; i++ ) {
246                    if ( Math.abs( getElement( j, i ) - ( i == j ? 1 : 0 ) ) > EPS11 ) {
247                        return false;
248                    }
249                }
250            return true;
251        }
252    }