001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2005, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.referencing.operation.matrix;
018:
019: // J2SE dependencies and extensions
020: import java.awt.geom.AffineTransform;
021: import java.awt.geom.NoninvertibleTransformException;
022: import javax.vecmath.SingularMatrixException;
023:
024: // OpenGIS dependencies
025: import org.opengis.referencing.operation.Matrix;
026:
027: // Geotools dependencies
028: import org.geotools.resources.i18n.Errors;
029: import org.geotools.resources.i18n.ErrorKeys;
030:
031: /**
032: * An affine matrix of fixed {@value #SIZE}×{@value #SIZE} size. Here, the term "affine"
033: * means a matrix with the last row fixed to {@code [0,0,1]} values. Such matrices are used for
034: * affine transformations in a 2D space.
035: * <p>
036: * This class both extends the <cite>Java2D</cite> {@link AffineTransform} class and implements
037: * the {@link Matrix} interface. It allows interoperbility for code that need to pass the same
038: * matrix to both <cite>Java2D</cite> API and more generic API working with coordinates of
039: * arbitrary dimension.
040: * <p>
041: * This class do not implements the {@link XMatrix} interface because the inherited {@code invert()}
042: * method (new in J2SE 1.6) declares a checked exception, {@code setZero()} would be an unsupported
043: * operation (because it is not possible to change the value at {@code (2,2)}), {@code transpose()}
044: * would fails in most cases, and {@code isAffine()} would be useless.
045: *
046: * @since 2.3
047: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/matrix/AffineTransform2D.java $
048: * @version $Id: AffineTransform2D.java 24493 2007-02-17 17:28:12Z desruisseaux $
049: * @author Martin Desruisseaux
050: */
051: public class AffineTransform2D extends AffineTransform implements
052: Matrix {
053: /**
054: * Serial number for interoperability with different versions.
055: */
056: private static final long serialVersionUID = -9104194268576601386L;
057:
058: /**
059: * The matrix size, which is {@value}.
060: */
061: public static final int SIZE = 3;
062:
063: /**
064: * Creates a new identity matrix.
065: */
066: public AffineTransform2D() {
067: }
068:
069: /**
070: * Constructs a 3×3 matrix from the specified affine transform.
071: */
072: public AffineTransform2D(final AffineTransform transform) {
073: super (transform);
074: }
075:
076: /**
077: * Creates a new matrix initialized to the same value than the specified one.
078: * The specified matrix size must be {@value #SIZE}×{@value #SIZE}.
079: */
080: public AffineTransform2D(final Matrix matrix) {
081: if (matrix.getNumRow() != SIZE || matrix.getNumCol() != SIZE) {
082: throw new IllegalArgumentException(Errors
083: .format(ErrorKeys.ILLEGAL_MATRIX_SIZE));
084: }
085: for (int i = 0; i < SIZE; i++) {
086: checkLastRow(i, matrix.getElement(SIZE - 1, i));
087: }
088: int c = 0;
089: final double[] values = new double[6];
090: for (int j = 0; j < SIZE - 1; j++) {
091: for (int i = 0; i < SIZE; i++) {
092: values[c++] = matrix.getElement(j, i);
093: }
094: }
095: assert c == values.length : c;
096: // TODO: invokes the super constructor instead if Sun fixes RFE #4093999
097: setTransform(values);
098: }
099:
100: /**
101: * Sets this affine transform to the specified flat matrix.
102: */
103: private void setTransform(final double[] matrix) {
104: setTransform(matrix[0], matrix[1], matrix[2], matrix[3],
105: matrix[4], matrix[5]);
106: }
107:
108: /**
109: * Returns the number of rows in this matrix, which is always {@value #SIZE}
110: * in this implementation.
111: */
112: public final int getNumRow() {
113: return SIZE;
114: }
115:
116: /**
117: * Returns the number of colmuns in this matrix, which is always {@value #SIZE}
118: * in this implementation.
119: */
120: public final int getNumCol() {
121: return SIZE;
122: }
123:
124: /**
125: * Retrieves the value at the specified row and column of this matrix.
126: *
127: * @param row The row number to be retrieved (zero indexed).
128: * @param column The column number to be retrieved (zero indexed).
129: * @return The value at the indexed element.
130: */
131: public double getElement(final int row, final int column) {
132: switch (row) {
133: case 0: {
134: switch (column) {
135: case 0:
136: return getScaleX();
137: case 1:
138: return getShearX();
139: case 2:
140: return getTranslateX();
141: }
142: break;
143: }
144: case 1: {
145: switch (column) {
146: case 0:
147: return getShearY();
148: case 1:
149: return getScaleY();
150: case 2:
151: return getTranslateY();
152: }
153: break;
154: }
155: case 2: {
156: switch (column) {
157: case 0: // fall through
158: case 1:
159: return 0;
160: case 2:
161: return 1;
162: }
163: break;
164: }
165: default: {
166: throw new IndexOutOfBoundsException(Errors.format(
167: ErrorKeys.ILLEGAL_ARGUMENT_$2, "column",
168: new Integer(column)));
169: }
170: }
171: throw new IndexOutOfBoundsException(Errors.format(
172: ErrorKeys.ILLEGAL_ARGUMENT_$2, "row", new Integer(row)));
173: }
174:
175: /**
176: * Modifies the value at the specified row and column of this matrix.
177: *
178: * @param row The row number to be retrieved (zero indexed).
179: * @param column The column number to be retrieved (zero indexed).
180: * @param value The new matrix element value.
181: */
182: public void setElement(final int row, final int column,
183: final double value) {
184: if (row < 0 || row >= SIZE) {
185: throw new IndexOutOfBoundsException(Errors.format(
186: ErrorKeys.ILLEGAL_ARGUMENT_$2, "row", new Integer(
187: row)));
188: }
189: if (column < 0 || column >= SIZE) {
190: throw new IndexOutOfBoundsException(Errors.format(
191: ErrorKeys.ILLEGAL_ARGUMENT_$2, "column",
192: new Integer(column)));
193: }
194: if (row == SIZE - 1) {
195: checkLastRow(column, value);
196: return; // Nothing to set.
197: }
198: final double[] matrix = new double[6];
199: getMatrix(matrix);
200: matrix[row * SIZE + column] = value;
201: setTransform(matrix);
202: assert Double.compare(getElement(row, column), value) == 0 : value;
203: }
204:
205: /**
206: * Check if the specified value is valid for the last row if this matrix.
207: * The last row contains only 0 values except the last column which is set to 1.
208: * This method throws an exception if the specified value is not the expected one.
209: */
210: private static void checkLastRow(final int column,
211: final double value) throws IllegalArgumentException {
212: if (value != (column == SIZE - 1 ? 1 : 0)) {
213: throw new IllegalArgumentException(Errors.format(
214: ErrorKeys.ILLEGAL_ARGUMENT_$2, "matrix["
215: + (SIZE - 1) + ',' + column + ']',
216: new Double(value)));
217: }
218: }
219:
220: /**
221: * Returns a string representation of this matrix. The returned string is implementation
222: * dependent. It is usually provided for debugging purposes only.
223: */
224: public String toString() {
225: return GeneralMatrix.toString(this);
226: }
227: }
|