/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-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.transform; import java.awt.Shape; import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; import java.util.prefs.Preferences; import org.opengis.util.Cloneable; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.operation.MathTransform2D; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.NoninvertibleTransformException; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.MismatchedDimensionException; import org.geotools.geometry.GeneralDirectPosition; import org.geotools.referencing.operation.LinearTransform; import org.geotools.referencing.operation.matrix.Matrix2; import org.geotools.referencing.operation.matrix.Matrix3; import org.geotools.referencing.operation.matrix.XAffineTransform; import org.geotools.referencing.wkt.Formatter; import org.geotools.referencing.wkt.Symbols; import org.geotools.resources.Formattable; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.util.Utilities; /** * Transforms two-dimensional coordinate points using an affine transform. This class both * extends {@link AffineTransform} and implements {@link MathTransform2D}, so it can be * used as a bridge between Java2D and the referencing module. * * @since 2.5 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ public class AffineTransform2D extends XAffineTransform implements MathTransform2D, LinearTransform, Formattable, Cloneable { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -5299837898367149069L; /** * The inverse transform. This field will be computed only when needed. */ private transient AffineTransform2D inverse; /** * Constructs a new affine transform with the same coefficient than the specified transform. */ public AffineTransform2D(final AffineTransform transform) { super(transform); } /** * Constructs a new {@code AffineTransform2D} from 6 values representing the 6 specifiable * entries of the 3×3 transformation matrix. Those values are given unchanged to the * {@link AffineTransform#AffineTransform(double,double,double,double,double,double) super * class constructor}. * * @since 2.5 */ public AffineTransform2D(double m00, double m10, double m01, double m11, double m02, double m12) { super(m00, m10, m01, m11, m02, m12); } /** * Throws an {@link UnsupportedOperationException} when a mutable method * is invoked, since {@code AffineTransform2D} must be immutable. */ @Override protected final void checkPermission() throws UnsupportedOperationException { super.checkPermission(); } /** * Returns the matrix elements as a group of parameters values. The number of parameters * depends on the matrix size. Only matrix elements different from their default value * will be included in this group. * * @return A copy of the parameter values for this math transform. */ public ParameterValueGroup getParameterValues() { return ProjectiveTransform.getParameterValues(getMatrix()); } /** * Gets the dimension of input points, which is fixed to 2. */ public final int getSourceDimensions() { return 2; } /** * Gets the dimension of output points, which is fixed to 2. */ public final int getTargetDimensions() { return 2; } /** * Transforms the specified {@code ptSrc} and stores the result in {@code ptDst}. */ public DirectPosition transform(final DirectPosition ptSrc, DirectPosition ptDst) { if (ptDst == null) { ptDst = new GeneralDirectPosition(2); } else { final int dimension = ptDst.getDimension(); if (dimension != 2) { throw new MismatchedDimensionException(Errors.format( ErrorKeys.MISMATCHED_DIMENSION_$3, "ptDst", dimension, 2)); } } final double[] array = ptSrc.getCoordinate(); transform(array, 0, array, 0, 1); ptDst.setOrdinate(0, array[0]); ptDst.setOrdinate(1, array[1]); return ptDst; } /** * Transforms the specified shape. * * @param shape Shape to transform. * @return Transformed shape, or {@code shape} if this transform is the identity transform. */ @Override public Shape createTransformedShape(final Shape shape) { return transform(this, shape, false); } /** * Returns this transform as an affine transform matrix. */ public Matrix getMatrix() { return new Matrix3(this); } /** * Gets the derivative of this transform at a point. For an affine transform, * the derivative is the same everywhere. */ public Matrix derivative(final Point2D point) { return new Matrix2(getScaleX(), getShearX(), getShearY(), getScaleY()); } /** * Gets the derivative of this transform at a point. For an affine transform, * the derivative is the same everywhere. */ public Matrix derivative(final DirectPosition point) { return derivative((Point2D) null); } /** * Creates the inverse transform of this object. * * @throws NoninvertibleTransformException if this transform can't be inverted. */ public MathTransform2D inverse() throws NoninvertibleTransformException { if (inverse == null) { if (isIdentity()) { inverse = this; } else try { synchronized (this) { inverse = new AffineTransform2D(createInverse()); inverse.inverse = this; } } catch (java.awt.geom.NoninvertibleTransformException exception) { throw new NoninvertibleTransformException(exception.getLocalizedMessage(), exception); } } return inverse; } /** * Returns a new affine transform which is a modifiable copy of this transform. We override * this method because it is {@linkplain AffineTransform#clone defined in the super-class}. * However this implementation do not returns a new {@code AffineTransform2D} instance because * the later is unmodifiable, which make exact cloning useless. */ @Override public AffineTransform clone() { return new AffineTransform(this); } /** * Format the inner part of a * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A> element. * * @param formatter The formatter to use. * @return The WKT element name. */ public String formatWKT(final Formatter formatter) { final ParameterValueGroup parameters = getParameterValues(); formatter.append(formatter.getName(parameters.getDescriptor())); formatter.append(parameters); return "PARAM_MT"; } /** * Returns the WKT for this transform. */ public String toWKT() { int indentation = 2; try { indentation = Preferences.userNodeForPackage(org.geotools.referencing.wkt.Formattable.class) .getInt("Indentation", indentation); } catch (SecurityException ignore) { // Ignore. Will fallback on the default indentation. } final Formatter formatter = new Formatter(Symbols.DEFAULT, indentation); formatter.append(this); return formatter.toString(); } /** * Returns the WKT representation of this transform. */ @Override public String toString() { return toWKT(); } @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.getShearX()) && Utilities.equals(getShearY(), a.getShearY()) && Utilities.equals(getTranslateX(), a.getTranslateX()) && Utilities.equals(getTranslateY(), a.getTranslateY()); } }