/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.referencing.operation.matrix; import java.awt.geom.AffineTransform; import org.opengis.referencing.operation.Matrix; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.util.Utilities; /** * An affine matrix of fixed {@value #SIZE}×{@value #SIZE} size. Here, the term "affine" * means a matrix with the last row fixed to {@code [0,0,1]} values. Such matrices are used for * affine transformations in a 2D space. * <p> * This class both extends the <cite>Java2D</cite> {@link AffineTransform} class and implements * the {@link Matrix} interface. It allows interoperbility for code that need to pass the same * matrix to both <cite>Java2D</cite> API and more generic API working with coordinates of * arbitrary dimension. * <p> * This class do not implements the {@link XMatrix} interface because the inherited {@code invert()} * method (new in J2SE 1.6) declares a checked exception, {@code setZero()} would be an unsupported * operation (because it is not possible to change the value at {@code (2,2)}), {@code transpose()} * would fails in most cases, and {@code isAffine()} would be useless. * * @since 2.3 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ public class AffineTransform2D extends AffineTransform implements Matrix { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -9104194268576601386L; /** * The matrix size, which is {@value}. */ public static final int SIZE = 3; /** * Creates a new identity matrix. */ public AffineTransform2D() { } /** * Constructs a 3×3 matrix from the specified affine transform. */ public AffineTransform2D(final AffineTransform transform) { super(transform); } /** * Creates a new matrix initialized to the same value than the specified one. * The specified matrix size must be {@value #SIZE}×{@value #SIZE}. */ public AffineTransform2D(final Matrix matrix) { if (matrix.getNumRow()!=SIZE || matrix.getNumCol()!=SIZE) { throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_MATRIX_SIZE)); } for (int i=0; i<SIZE; i++) { checkLastRow(i, matrix.getElement(SIZE-1, i)); } int c=0; final double[] values = new double[6]; for (int j=0; j<SIZE-1; j++) { for (int i=0; i<SIZE; i++) { values[c++] = matrix.getElement(j,i); } } assert c == values.length : c; // TODO: invokes the super constructor instead if Sun fixes RFE #4093999 setTransform(values); } /** * Sets this affine transform to the specified flat matrix. */ private void setTransform(final double[] matrix) { setTransform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); } /** * Returns the number of rows in this matrix, which is always {@value #SIZE} * in this implementation. */ public final int getNumRow() { return SIZE; } /** * Returns the number of colmuns in this matrix, which is always {@value #SIZE} * in this implementation. */ public final int getNumCol() { return SIZE; } /** * Retrieves the value at the specified row and column of this matrix. * * @param row The row number to be retrieved (zero indexed). * @param column The column number to be retrieved (zero indexed). * @return The value at the indexed element. */ public double getElement(final int row, final int column) { switch (row) { case 0: { switch (column) { case 0: return getScaleX(); case 1: return getShearX(); case 2: return getTranslateX(); } break; } case 1: { switch (column) { case 0: return getShearY(); case 1: return getScaleY(); case 2: return getTranslateY(); } break; } case 2: { switch (column) { case 0: // fall through case 1: return 0; case 2: return 1; } break; } default: { throw new IndexOutOfBoundsException( Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "column", column)); } } throw new IndexOutOfBoundsException(Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, "row", row)); } /** * Modifies the value at the specified row and column of this matrix. * * @param row The row number to be retrieved (zero indexed). * @param column The column number to be retrieved (zero indexed). * @param value The new matrix element value. */ public void setElement(final int row, final int column, final double value) { if (row<0 || row>=SIZE) { throw new IndexOutOfBoundsException(Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, "row", row)); } if (column<0 || column>=SIZE) { throw new IndexOutOfBoundsException(Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, "column", column)); } if (row == SIZE-1) { checkLastRow(column, value); return; // Nothing to set. } final double[] matrix = new double[6]; getMatrix(matrix); matrix[row*SIZE + column] = value; setTransform(matrix); assert Double.compare(getElement(row, column), value) == 0 : value; } /** * Check if the specified value is valid for the last row if this matrix. * The last row contains only 0 values except the last column which is set to 1. * This method throws an exception if the specified value is not the expected one. */ private static void checkLastRow(final int column, final double value) throws IllegalArgumentException { if (value != (column == SIZE-1 ? 1 : 0)) { throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "matrix[" + (SIZE-1) + ',' + column + ']', value)); } } /** * Returns a string representation of this matrix. The returned string is implementation * dependent. It is usually provided for debugging purposes only. */ @Override public String toString() { return GeneralMatrix.toString(this); } /** * Returns a clone of this affine transform. */ @Override public AffineTransform2D clone() { return (AffineTransform2D) super.clone(); } @Override public boolean equals(Object obj) { if (!(obj instanceof AffineTransform)) { return false; } AffineTransform a = (AffineTransform) obj; return Utilities.equals(getScaleX(), a.getScaleX()) && Utilities.equals(getScaleY(), a.getScaleY()) && Utilities.equals(getShearX(), a.getShearY()) && Utilities.equals(getTranslateX(), a.getTranslateX()) && Utilities.equals(getTranslateY(), a.getTranslateY()); } }