001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/model/csct/ct/MatrixTransform.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.transformations; 039 040 // Geometry 041 import java.util.ArrayList; 042 import java.util.List; 043 044 import javax.vecmath.GMatrix; 045 import javax.vecmath.Matrix3d; 046 import javax.vecmath.Matrix4d; 047 import javax.vecmath.Point3d; 048 049 import org.deegree.crs.coordinatesystems.CoordinateSystem; 050 import org.deegree.crs.projections.ProjectionUtils; 051 import org.deegree.crs.utilities.Matrix; 052 053 /** 054 * 055 * The <code>MatrixTransform</code> class allows transformations using matrices. Although technically n × m 056 * matrices are possible, at the moment only 2 × 2, 3 × 3 and 4 × 4 matrices are supported. 057 * 058 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 059 * 060 * @author last edited by: $Author:$ 061 * 062 * @version $Revision:$, $Date:$ 063 * 064 */ 065 public class MatrixTransform extends CRSTransformation { 066 /** 067 * Serial number for interoperability with different versions. 068 */ 069 private static final long serialVersionUID = -2104496465933824935L; 070 071 /** 072 * the number of rows. 073 */ 074 private final int numRow; 075 076 /** 077 * the number of columns. 078 */ 079 private final int numCol; 080 081 private GMatrix matrix = null; 082 083 private GMatrix invertMatrix = null; 084 085 private Matrix3d matrix3D = null; 086 087 private Matrix3d invertMatrix3D = null; 088 089 private Matrix4d matrix4D = null; 090 091 private Matrix4d invertMatrix4D = null; 092 093 /** 094 * Set super values and numRow,numCol. 095 * 096 * @param source 097 * @param target 098 * @param identifier 099 * @param name 100 * @param numRow 101 * @param numCol 102 */ 103 private MatrixTransform( CoordinateSystem source, CoordinateSystem target, String identifier, String name, 104 int numRow, int numCol ) { 105 super( source, target, identifier, name, null, null, null ); 106 this.numCol = numCol; 107 this.numRow = numRow; 108 } 109 110 /** 111 * Construct a transform. 112 * 113 * @param source 114 * the source coordinate system 115 * @param target 116 * the target coordinate system. 117 * 118 * @param matrix 119 * @param identifier 120 * @param name 121 * of the transformation 122 */ 123 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final GMatrix matrix, String identifier, 124 String name ) { 125 this( source, target, identifier, name, matrix.getNumRow(), matrix.getNumCol() ); 126 this.matrix = new GMatrix( matrix ); 127 invertMatrix = new GMatrix( matrix ); 128 invertMatrix.invert(); 129 } 130 131 /** 132 * Construct a transform with name set to 'Matrix-Transform'. 133 * 134 * @param source 135 * the source coordinate system 136 * @param target 137 * the target coordinate system. 138 * 139 * @param matrix 140 * @param identifier 141 */ 142 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final GMatrix matrix, String identifier ) { 143 this( source, target, matrix, identifier, "Matrix-Transform" ); 144 } 145 146 /** 147 * Construct a transform with id set to {@link CRSTransformation#createFromTo(String, String)} and name to 148 * 'Matrix-Transform'. 149 * 150 * @param source 151 * the source coordinate system 152 * @param target 153 * the target coordinate system. 154 * 155 * @param matrix 156 */ 157 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final GMatrix matrix ) { 158 this( source, target, matrix, createFromTo( source.getIdentifier(), target.getIdentifier() ) ); 159 } 160 161 /** 162 * Construct a 3d transform. 163 * 164 * @param source 165 * the source coordinate system 166 * @param target 167 * the target coordinate system. 168 * 169 * @param matrix 170 * @param identifier 171 * @param name 172 * of the transformation 173 */ 174 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final Matrix3d matrix, String identifier, 175 String name ) { 176 this( source, target, identifier, name, 3, 3 ); 177 this.matrix3D = new Matrix3d( matrix ); 178 invertMatrix3D = new Matrix3d(); 179 invertMatrix3D.invert( matrix3D ); 180 } 181 182 /** 183 * Construct a 3d transform with name set to 'Matrix-Transform'. 184 * 185 * @param source 186 * the source coordinate system 187 * @param target 188 * the target coordinate system. 189 * 190 * @param matrix 191 * @param identifier 192 */ 193 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, final Matrix3d matrix, String identifier ) { 194 this( source, target, matrix, identifier, "Matrix-Transform" ); 195 } 196 197 /** 198 * Construct a 3d transform with id set to {@link CRSTransformation#createFromTo(String, String)} and name to 199 * 'Matrix-Transform'. 200 * 201 * @param source 202 * the source coordinate system 203 * @param target 204 * the target coordinate system. 205 * 206 * @param matrix 207 */ 208 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix3d matrix ) { 209 this( source, target, matrix, createFromTo( source.getIdentifier(), target.getIdentifier() ) ); 210 } 211 212 /** 213 * Construct a 4d transform. 214 * 215 * @param source 216 * the source coordinate system 217 * @param target 218 * the target coordinate system. 219 * 220 * @param matrix 221 * @param identifier 222 * @param name 223 * of this transform 224 */ 225 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix4d matrix, String identifier, 226 String name ) { 227 this( source, target, identifier, name, 4, 4 ); 228 matrix4D = new Matrix4d( matrix ); 229 invertMatrix4D = new Matrix4d(); 230 invertMatrix4D.invert( matrix4D ); 231 } 232 233 /** 234 * Construct a 4d transform with name set to 'Matrix-Transform'. 235 * 236 * @param source 237 * the source coordinate system 238 * @param target 239 * the target coordinate system. 240 * 241 * @param matrix 242 * @param identifier 243 */ 244 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix4d matrix, String identifier ) { 245 this( source, target, matrix, identifier, "Matrix-Transform" ); 246 } 247 248 /** 249 * Construct a 4d transform with id set to {@link CRSTransformation#createFromTo(String, String)} and name to 250 * 'Matrix-Transform'. 251 * 252 * @param source 253 * the source coordinate system 254 * @param target 255 * the target coordinate system. 256 * 257 * @param matrix 258 */ 259 public MatrixTransform( CoordinateSystem source, CoordinateSystem target, Matrix4d matrix ) { 260 this( source, target, matrix, createFromTo( source.getIdentifier(), target.getIdentifier() ) ); 261 } 262 263 @Override 264 public List<Point3d> doTransform( List<Point3d> srcPts ) { 265 if ( isIdentity() ) { 266 return srcPts; 267 } 268 // System.out.println( "The matrix is a: " + ( ( matrix3D != null ) ? "Matrix3D" 269 // : ( ( matrix4D != null ) ? "Matrix4D" 270 // : "customMatrix" ) ) 271 // + " with following values:\n" 272 // + ( ( matrix3D != null ) ? matrix3D : ( ( matrix4D != null ) ? matrix4D : matrix ) ) ); 273 List<Point3d> results = new ArrayList<Point3d>( srcPts ); 274 if ( isInverseTransform() ) { 275 // System.out.println( "A inverse matrix transform with incoming points: " + srcPts ); 276 if ( matrix3D != null ) { 277 transform( invertMatrix3D, results ); 278 } else if ( matrix4D != null ) { 279 transform( invertMatrix4D, results ); 280 } else { 281 transform( invertMatrix, results ); 282 } 283 } else { 284 // System.out.println( "A matrix transform with incoming points: " + srcPts ); 285 if ( matrix3D != null ) { 286 transform( matrix3D, results ); 287 } else if ( matrix4D != null ) { 288 transform( matrix4D, results ); 289 } else { 290 transform( matrix, results ); 291 } 292 } 293 return results; 294 } 295 296 /** 297 * @return the dimension of input points. 298 */ 299 public int getDimSource() { 300 return numCol - 1; 301 } 302 303 /** 304 * @return the dimension of output points. 305 */ 306 public int getDimTarget() { 307 return numRow - 1; 308 } 309 310 /** 311 * @return true if this transformation holds an identiy matrix (e.g. doesn't transform at all). 312 */ 313 @Override 314 public boolean isIdentity() { 315 if ( numRow != numCol ) { 316 return false; 317 } 318 for ( int row = 0; row < numRow; row++ ) 319 for ( int col = 0; col < numCol; col++ ) { 320 double value = ( matrix3D != null ) ? matrix3D.getElement( row, col ) 321 : ( ( matrix4D != null ) ? matrix4D.getElement( row, col ) 322 : matrix.getElement( row, col ) ); 323 if ( Math.abs( value - ( col == row ? 1 : 0 ) ) > ProjectionUtils.EPS11 ) { 324 return false; 325 } 326 } 327 return true; 328 // return true; 329 } 330 331 @Override 332 public boolean equals( final Object object ) { 333 if ( object == this ) { 334 return true; // Slight optimization 335 } 336 if ( object != null && super.equals( object ) ) { 337 return matrix.equals( ( (MatrixTransform) object ).matrix ); 338 } 339 return false; 340 } 341 342 /** 343 * Transforms an array of floating point coordinates by this matrix. Point coordinates must have a dimension equals 344 * to <code>{@link Matrix#getNumCol()}-1</code>. For example, for square matrix of size 4×4, coordinate 345 * points are three-dimensional and stored in the arrays starting at the specified offset (<code>srcOff</code>) 346 * in the order <code>[x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>, 347 * x<sub>1</sub>, y<sub>1</sub>, z<sub>1</sub>..., 348 * x<sub>n</sub>, y<sub>n</sub>, z<sub>n</sub>]</code>. 349 * 350 * The transformed points <code>(x',y',z')</code> are computed as below (note that this computation is similar to 351 * {@link PerspectiveTransform}): 352 * 353 * <blockquote> <code> 354 * <pre> 355 * [ u ] [ m<sub>00</sub> m<sub>01</sub> m<sub>02</sub> m<sub>03</sub> ] [ x ] 356 * [ v ] = [ m<sub>10</sub> m<sub>11</sub> m<sub>12</sub> m<sub>13</sub> ] [ y ] 357 * [ w ] [ m<sub>20</sub> m<sub>21</sub> m<sub>22</sub> m<sub>23</sub> ] [ z ] 358 * [ t ] [ m<sub>30</sub> m<sub>31</sub> m<sub>32</sub> m<sub>33</sub> ] [ 1 ] 359 * 360 * x' = u/t 361 * y' = v/t 362 * w' = w/t 363 * </pre> 364 * </code> </blockquote> 365 * 366 * @param srcPts 367 * The array containing the source point coordinates. 368 */ 369 private void transform( GMatrix gm, List<Point3d> srcPts ) { 370 throw new UnsupportedOperationException( "not yet implemented" ); 371 // final int inputDimension = numCol - 1; // The last value will be assumed equals to 1. 372 // final int outputDimension = numRow - 1; 373 // final double[] buffer = new double[numRow]; 374 // while ( --numPts >= 0 ) { 375 // int mix = 0; 376 // for ( int j = 0; j < numRow; j++ ) { 377 // // double sum = elt[mix + inputDimension]; 378 // // for ( int i = 0; i < inputDimension; i++ ) { 379 // // sum += srcPts[srcOff + i] * elt[mix++]; 380 // // } 381 // // buffer[j] = sum; 382 // mix++; 383 // } 384 // final double w = buffer[outputDimension]; 385 // for ( int j = 0; j < outputDimension; j++ ) { 386 // // 'w' is equals to 1 if the transform is affine. 387 // dstPts[dstOff++] = buffer[j] / w; 388 // } 389 // srcOff += inputDimension; 390 // } 391 392 } 393 394 /** 395 * Use the given GMatrix to transform the given points inplace. 396 * 397 * @param m4d 398 * the matrix to use (e.g. the inverse matrix or the forward matrix. 399 * @param srcPts 400 * The array containing the source point coordinates. 401 */ 402 private void transform( Matrix4d m4d, List<Point3d> srcPts ) { 403 for ( Point3d p : srcPts ) { 404 m4d.transform( p ); 405 } 406 } 407 408 /** 409 * Use the given GMatrix to transform the given points inplace. 410 * 411 * @param m4d 412 * the matrix to use (e.g. the inverse matrix or the forward matrix. 413 * @param srcPts 414 * The array containing the source point coordinates. 415 */ 416 private void transform( Matrix3d m3d, List<Point3d> srcPts ) { 417 for ( Point3d p : srcPts ) { 418 m3d.transform( p ); 419 } 420 } 421 422 /** 423 * @return the matrix. 424 */ 425 public final GMatrix getMatrix() { 426 if ( matrix != null ) { 427 return isInverse ? invertMatrix : matrix; 428 } 429 GMatrix result = new GMatrix( numRow, numCol ); 430 if ( matrix3D != null ) { 431 if ( isInverse ) { 432 result.set( invertMatrix3D ); 433 } else { 434 result.set( matrix3D ); 435 } 436 } 437 if ( matrix4D != null ) { 438 if ( isInverse ) { 439 result.set( invertMatrix4D ); 440 } else { 441 result.set( matrix4D ); 442 } 443 } 444 return result; 445 } 446 447 }