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 }