001 //$HeadURL: https://sushibar/svn/deegree/base/trunk/src/org/deegree/model/csct/pt/Matrix.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.utilities;
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 import org.deegree.crs.projections.ProjectionUtils;
047
048 /**
049 * The <code>Matrix</code> class TODO add documentation here
050 *
051 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
052 *
053 * @author last edited by: $Author:$
054 *
055 * @version $Revision:$, $Date:$
056 *
057 */
058
059 public class Matrix extends GMatrix {
060 /**
061 * Serial number for interoperability with different versions.
062 */
063 private static final long serialVersionUID = 3778102551617232269L;
064
065 /**
066 * Construct a square identity matrix of size <code>size</code> × <code>size</code>.
067 *
068 * @param size
069 */
070 public Matrix( final int size ) {
071 super( size, size );
072 }
073
074 /**
075 * Construct a matrix of size <code>numRow</code> × <code>numCol</code>. Elements on the
076 * diagonal <var>j==i</var> are set to 1.
077 *
078 * @param numRow
079 * @param numCol
080 */
081 public Matrix( final int numRow, final int numCol ) {
082 super( numRow, numCol );
083 }
084
085 /**
086 * Constructs a <code>numRow</code> × <code>numCol</code> matrix initialized to the values in
087 * the <code>matrix</code> array. The array values are copied in one row at a time in row major fashion. The array
088 * should be exactly <code>numRow*numCol</code> in length. Note that because row and column numbering begins with
089 * zero, <code>row</code> and <code>numCol</code> will be one larger than the maximum possible matrix index
090 * values.
091 *
092 * @param numRow
093 * @param numCol
094 * @param matrix
095 */
096 public Matrix( final int numRow, final int numCol, final double[] matrix ) {
097 super( numRow, numCol, matrix );
098 if ( numRow * numCol != matrix.length ) {
099 throw new IllegalArgumentException( String.valueOf( matrix.length ) );
100 }
101 }
102
103 /**
104 * Constructs a new matrix from a two-dimensional array of doubles.
105 *
106 * @param matrix
107 * Array of rows. Each row must have the same length.
108 * @throws IllegalArgumentException
109 * if the specified matrix is not regular (i.e. if all rows doesn't have the same length).
110 */
111 public Matrix( final double[][] matrix ) throws IllegalArgumentException {
112 super( matrix.length, ( matrix.length != 0 ) ? matrix[0].length : 0 );
113 final int numRow = getNumRow();
114 final int numCol = getNumCol();
115 for ( int j = 0; j < numRow; j++ ) {
116 if ( matrix[j].length != numCol ) {
117 throw new IllegalArgumentException( "Not a regular Matrix (given rows have different lengths)" );
118 }
119 setRow( j, matrix[j] );
120 }
121 }
122
123 /**
124 * Constructs a new matrix and copies the initial values from the parameter matrix.
125 *
126 * @param matrix
127 */
128 public Matrix( final GMatrix matrix ) {
129 super( matrix );
130 }
131
132 /**
133 * Construct a 3×3 matrix from the specified affine transform.
134 *
135 * @param transform
136 */
137 public Matrix( final AffineTransform transform ) {
138 super( 3, 3, new double[] { transform.getScaleX(),
139 transform.getShearX(),
140 transform.getTranslateX(),
141 transform.getShearY(),
142 transform.getScaleY(),
143 transform.getTranslateY(),
144 0,
145 0,
146 1 } );
147 }
148
149 /**
150 * Construct an affine transform mapping a source region to a destination region. The regions must have the same
151 * number of dimensions, but their axis order and axis orientation may be different.
152 *
153 * @param srcRegion
154 * The source region.
155 * @param srcAxis
156 * Axis orientation for each dimension of the source region.
157 * @param dstRegion
158 * The destination region.
159 * @param dstAxis
160 * Axis orientation for each dimension of the destination region.
161 * @param validRegions
162 * <code>true</code> if source and destination regions must be taken in account. If <code>false</code>,
163 * then source and destination regions will be ignored and may be null.
164 */
165 private Matrix( final BBox srcRegion, final Axis[] srcAxis, final BBox dstRegion, final Axis[] dstAxis,
166 final boolean validRegions ) {
167 this( srcAxis.length + 1 );
168 final int dimension = srcAxis.length;
169 if ( dstAxis.length != dimension ) {
170 throw new IllegalArgumentException( "Given dimensions are of differnt length." );
171 }
172 if ( validRegions ) {
173 if ( dstRegion.getDimension() != dimension || srcRegion.getDimension() != dimension ) {
174 throw new IllegalArgumentException( "The dimensions of the given regions do not match the axis dimension" );
175 }
176 }
177 /*
178 * Map source axis to destination axis. If no axis is moved (for example if the user want to transform
179 * (NORTH,EAST) to (SOUTH,EAST)), then source and destination index will be equal. If some axis are moved (for
180 * example if the user want to transform (NORTH,EAST) to (EAST,NORTH)), then ordinates at index <code>srcIndex</code>
181 * will have to be moved at index <code>dstIndex</code>.
182 */
183 setZero();
184 for ( int srcIndex = 0; srcIndex < dimension; srcIndex++ ) {
185 boolean hasFound = false;
186 final int srcAxe = srcAxis[srcIndex].getOrientation();
187 final int sourceAxisDirection = Math.abs( srcAxe );
188 //System.out.println( "Source axis: " + srcAxis[srcIndex] );
189 for ( int dstIndex = 0; dstIndex < dimension; dstIndex++ ) {
190 final int dstAxeDirection = dstAxis[dstIndex].getOrientation();
191 //System.out.println( "Dest axis: " + dstAxis[dstIndex] );
192 if ( sourceAxisDirection == Math.abs( dstAxeDirection ) ) {
193 if ( hasFound ) {
194 throw new IllegalArgumentException( "Following axis are colinear: " + srcAxis[srcIndex].getName()
195 + " dstAxe: "
196 + dstAxis[dstIndex].getName() );
197 }
198 hasFound = true;
199 /*
200 * Set the matrix elements. Some matrix elements will never be set. They will be left to zero, which
201 * is their wanted value.
202 */
203 final boolean normal = ( srcAxe == dstAxeDirection );
204 double scale = ( normal ) ? +1 : -1;
205 double translate = 0;
206 if ( validRegions ) {
207 translate = ( normal ) ? dstRegion.getMinimum( dstIndex ) : dstRegion.getMaximum( dstIndex );
208 scale *= dstRegion.getLength( dstIndex ) / srcRegion.getLength( srcIndex );
209 translate -= srcRegion.getMinimum( srcIndex ) * scale;
210 }
211 setElement( dstIndex, srcIndex, scale );
212 setElement( dstIndex, dimension, translate );
213 }
214 }
215 if ( !hasFound ) {
216 throw new IllegalArgumentException( "No appropriate transformation axis found for srcAxis: " + srcAxis[srcIndex].getName() );
217 }
218 }
219 setElement( dimension, dimension, 1 );
220
221 }
222
223 /**
224 * Construct an affine transform changing axis order and/or orientation. For example, the affine transform may
225 * convert (NORTH,WEST) coordinates into (EAST,NORTH). Axis orientation can be inversed only. For example, it is
226 * illegal to transform (NORTH,WEST) coordinates into (NORTH,DOWN).
227 *
228 * @param srcAxis
229 * The set of axis orientation for source coordinate system.
230 * @param dstAxis
231 * The set of axis orientation for destination coordinate system.
232 * @return a Matrix as an affine transform.
233 * @throws IllegalArgumentException
234 * if the affine transform can't be created for some other raison.
235 */
236 public static Matrix createAffineTransform( final Axis[] srcAxis, final Axis[] dstAxis ) {
237 return new Matrix( null, srcAxis, null, dstAxis, false );
238 }
239
240 /**
241 * Construct an affine transform that maps a source region to a destination region. Axis order and orientation are
242 * left unchanged.
243 *
244 * @param srcRegion
245 * The source region.
246 * @param dstRegion
247 * The destination region.
248 * @return a Matrix as an affine transform.
249 */
250 public static Matrix createAffineTransform( final BBox srcRegion, final BBox dstRegion ) {
251 final int dimension = srcRegion.getDimension();
252 if ( dstRegion.getDimension() != dimension ) {
253 throw new IllegalArgumentException( "Dimensions do not fit" );
254 }
255
256 final Matrix matrix = new Matrix( dimension + 1 );
257 for ( int i = 0; i < dimension; i++ ) {
258 final double scale = dstRegion.getLength( i ) / srcRegion.getLength( i );
259 final double translate = dstRegion.getMinimum( i ) - srcRegion.getMinimum( i ) * scale;
260 matrix.setElement( i, i, scale );
261 matrix.setElement( i, dimension, translate );
262 }
263 matrix.setElement( dimension, dimension, 1 );
264 return matrix;
265 }
266
267 /**
268 * Returns <code>true</code> if this matrix is an affine transform. A transform is affine if the matrix is square
269 * and last row contains only zeros, except in the last column which contains 1.
270 *
271 * @return <code>true</code> if this matrix is an affine transform.
272 */
273 public final boolean isAffine() {
274 int dimension = getNumRow();
275 if ( dimension != getNumCol() ) {
276 return false;
277 }
278
279 dimension--;
280 for ( int i = 0; i <= dimension; i++ ) {
281 if ( Math.abs( getElement( dimension, i ) - ( i == dimension ? 1 : 0 ) ) > ProjectionUtils.EPS11 ) {
282 return false;
283 }
284 }
285 return true;
286 }
287
288 /**
289 * Copies the first 2x3 values into an affine transform object. If not enough values are available, an identity
290 * transform is returned.
291 *
292 * @return an affine transform for this matrix. or an identity if this matrix has not sufficient values.
293 *
294 */
295 public final Matrix3d toAffineTransform() {
296 if ( getNumCol() < 3 || getNumRow() < 2 ) {
297 return new Matrix3d();
298 }
299 return new Matrix3d( getElement( 0, 0 ), getElement( 0, 1 ), getElement( 0,2 ),
300 getElement( 1, 0 ), getElement( 1, 1 ), getElement( 1, 2 ),
301 0, 0 , 1);
302 }
303
304 /**
305 * Returns <code>true</code> if this matrix is an identity matrix.
306 *
307 * @return <code>true</code> if this matrix is an identity matrix.
308 */
309 public final boolean isIdentity() {
310 final int numRow = getNumRow();
311 final int numCol = getNumCol();
312 if ( numRow != numCol ) {
313 return false;
314 }
315 for ( int j = 0; j < numRow; j++ )
316 for ( int i = 0; i < numCol; i++ ) {
317 if ( Math.abs( getElement( j, i ) - ( i == j ? 1 : 0 ) ) > ProjectionUtils.EPS11 ) {
318 return false;
319 }
320 }
321 return true;
322 }
323 }