    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }