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 }