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