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> × <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> × <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> × <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×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 * 0, 1, 0<br/>
146 * -1, 0, 0<br/>
147 * 0, 0, 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 }