/* * Geotools 2 - OpenSource mapping toolkit * (C) 2003, Geotools Project Managment Committee (PMC) * (C) 2001, Institut de Recherche pour le Développement * * 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; either * version 2.1 of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.ct; // J2SE dependencies import java.awt.geom.Point2D; import java.io.Serializable; import org.geotools.pt.CoordinatePoint; import org.geotools.pt.Matrix; import org.geotools.resources.Utilities; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.opengis.referencing.operation.NoninvertibleTransformException; import org.opengis.referencing.operation.TransformException; /** * Base class for concatenated transform. Concatenated transforms are * serializable if all their step transforms are serializables. * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux * * @deprecated Replaced by {@link org.geotools.referencing.operation.transform.ConcatenatedTransform} * in the org.geotools.referencing.operation.transform package. */ class ConcatenatedTransform extends AbstractMathTransform implements Serializable { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = 5772066656987558634L; /** * The math transform factory that created this concatenated transform. * Will be used for creating the inverse transform when needed. This * field is usually not null, except after deserialization. It can't * be serialized since {@link MathTransformFactory} are usually not * serializable. * * @task REVISIT: Not serializing this field may not be the right thing to do if * a user wants to work with a custom {@link MathTransformFactory}. */ private transient MathTransformFactory provider; /** * The first math transform. */ protected final MathTransform transform1; /** * The second math transform. */ protected final MathTransform transform2; /** * The inverse transform. This field * will be computed only when needed. */ private transient MathTransform inverse; /** * Construct a concatenated transform. This constructor is for subclasses only. To * create a concatenated transform, use the factory method {@link #create} instead. * * @param provider The math transform factory that created this concatenated transform. * @param transform1 The first math transform. * @param transform2 The second math transform. */ protected ConcatenatedTransform(final MathTransformFactory provider, final MathTransform transform1, final MathTransform transform2) { this.provider = provider; this.transform1 = transform1; this.transform2 = transform2; if (!isValid()) { throw new IllegalArgumentException(Errors.format( ErrorKeys.CANT_CONCATENATE_TRANSFORMS_$2, getName(transform1), getName(transform2))); } } /** * Construct a new concatenated transform. This factory method checks for step transforms * dimension. The returned transform will implements {@link MathTransform2D} if source and * target dimensions are equal to 2. Likewise, it will implements {@link MathTransform1D} * if source and target dimensions are equal to 1. {@link MathTransform} implementations * are available in two version: direct and non-direct. The "non-direct" version use an * intermediate buffer when performing transformations; they are slower and consume more * memory. They are used only as a fallback when a "direct" version can't be created. * * @param provider The math transform factory that created this concatenated transform. * @param tr1 The first math transform. * @param tr2 The second math transform. * @return The concatenated transform. */ public static ConcatenatedTransform create(final MathTransformFactory provider, final MathTransform tr1, final MathTransform tr2) { final int dimSource = tr1.getDimSource(); final int dimTarget = tr2.getDimTarget(); // // Check if the result need to be a MathTransform1D. // if (dimSource==1 && dimTarget==1) { if (tr1 instanceof MathTransform1D && tr2 instanceof MathTransform1D) { return new ConcatenatedTransformDirect1D(provider, (MathTransform1D)tr1, (MathTransform1D)tr2); } else { return new ConcatenatedTransform1D(provider, tr1, tr2); } } else // // Check if the result need to be a MathTransform2D. // if (dimSource==2 && dimTarget==2) { if (tr1 instanceof MathTransform2D && tr2 instanceof MathTransform2D) { return new ConcatenatedTransformDirect2D(provider, (MathTransform2D)tr1, (MathTransform2D)tr2); } else { return new ConcatenatedTransform2D(provider, tr1, tr2); } } else // // Check for the general case. // if (dimSource==tr1.getDimTarget() && tr2.getDimSource()==dimTarget) { return new ConcatenatedTransformDirect(provider, tr1, tr2); } else { return new ConcatenatedTransform(provider, tr1, tr2); } } /** * Returns a name for the specified coordinate system. */ private static final String getName(final MathTransform transform) { if (transform instanceof AbstractMathTransform) { String name = ((AbstractMathTransform) transform).getName(null); if (name!=null && (name=name.trim()).length()!=0) { return name; } } return Utilities.getShortClassName(transform); } /** * Check if transforms are compatibles. The default * implementation check if transfert dimension match. */ protected boolean isValid() { return transform1.getDimTarget() == transform2.getDimSource(); } /** * Gets the dimension of input points. */ public final int getDimSource() { return transform1.getDimSource(); } /** * Gets the dimension of output points. */ public final int getDimTarget() { return transform2.getDimTarget(); } /** * Transforms the specified ptSrc and stores the result in ptDst. */ public CoordinatePoint transform(final CoordinatePoint ptSrc, CoordinatePoint ptDst) throws TransformException { assert isValid(); // Note: If we know that the transfert dimension is the same than source // and target dimension, then we don't need to use an intermediate // point. This optimization is done in ConcatenatedTransformDirect. return transform2.transform(transform1.transform(ptSrc, null), ptDst); } /** * Transforms a list of coordinate point ordinal values. */ public void transform(final double[] srcPts, final int srcOff, final double[] dstPts, final int dstOff, final int numPts) throws TransformException { assert isValid(); // Note: If we know that the transfert dimension is the same than source // and target dimension, then we don't need to use an intermediate // buffer. This optimization is done in ConcatenatedTransformDirect. final double[] tmp = new double[numPts*transform1.getDimTarget()]; transform1.transform(srcPts, srcOff, tmp, 0, numPts); transform2.transform(tmp, 0, dstPts, dstOff, numPts); } /** * Transforms a list of coordinate point ordinal values. */ public void transform(final float[] srcPts, final int srcOff, final float[] dstPts, final int dstOff, final int numPts) throws TransformException { assert isValid(); // Note: If we know that the transfert dimension is the same than source // and target dimension, then we don't need to use an intermediate // buffer. This optimization is done in ConcatenatedTransformDirect. final float[] tmp = new float[numPts*transform1.getDimTarget()]; transform1.transform(srcPts, srcOff, tmp, 0, numPts); transform2.transform(tmp, 0, dstPts, dstOff, numPts); } /** * Creates the inverse transform of this object. */ public synchronized final MathTransform inverse() throws NoninvertibleTransformException { assert isValid(); if (inverse == null) { if (provider == null) { provider = MathTransformFactory.getDefault(); } inverse = provider.createConcatenatedTransform(transform2.inverse(), transform1.inverse()); if (inverse instanceof ConcatenatedTransform) { ((ConcatenatedTransform) inverse).inverse = this; } } return inverse; } /** * Gets the derivative of this transform at a point. We must delegate to the * {@link #derivative(CoordinatePoint)} version because the transformation * steps {@link #transform1} and {@link #transform2} may not be instances of * {@link MathTransform2D}. The only class which is allowed to delegate directly * to the {@link Point2D} version is {@link ConcatenatedTransformDirect2D}. * * @param point The coordinate point where to evaluate the derivative. * @return The derivative at the specified point as a 2×2 matrix. * @throws TransformException if the derivative can't be evaluated at the specified point. */ public Matrix derivative(final Point2D point) throws TransformException { return derivative(new CoordinatePoint(point)); } /** * Gets the derivative of this transform at a point. * * @param point The coordinate point where to evaluate the derivative. * @return The derivative at the specified point (never null). * @throws TransformException if the derivative can't be evaluated at the specified point. */ public Matrix derivative(final CoordinatePoint point) throws TransformException { final Matrix matrix1 = transform1.derivative(point); final Matrix matrix2 = transform2.derivative(transform1.transform(point, null)); // Compute "matrix = matrix2 * matrix1". Reuse an existing matrix object // if possible, which is always the case when both matrix are square. final int numRow = matrix2.getNumRow(); final int numCol = matrix1.getNumCol(); final Matrix matrix; if (numCol == matrix2.getNumCol()) { matrix = matrix2; matrix2.mul(matrix1); } else { matrix = new Matrix(numRow, numCol); matrix.mul(matrix2, matrix1); } return matrix; } /** * Tests whether this transform does not move any points. * Default implementation check if the two transforms are * identity. This a way too conservative aproach, but it * it doesn't hurt since ConcatenatedTransform should not * have been created if it were to result in an identity * transform (this case should have been detected earlier). */ public final boolean isIdentity() { return transform1.isIdentity() && transform2.isIdentity(); } /** * Returns a hash value for this transform. */ public final int hashCode() { return transform1.hashCode() + 37*transform2.hashCode(); } /** * Compares the specified object with * this math transform for equality. */ public final boolean equals(final Object object) { if (object==this) { // Slight optimization return true; } if (super.equals(object)) { final ConcatenatedTransform that = (ConcatenatedTransform) object; return Utilities.equals(this.transform1, that.transform1) && Utilities.equals(this.transform2, that.transform2); } return false; } /** * Returns the WKT for this math transform. */ public final String toString() { final StringBuffer buffer = new StringBuffer("CONCAT_MT["); addWKT(buffer, this, true); buffer.append(']'); return buffer.toString(); } /** * Append to a string buffer the WKT * for the specified math transform. */ private static void addWKT(final StringBuffer buffer, final MathTransform transform, final boolean first) { if (transform instanceof ConcatenatedTransform) { final ConcatenatedTransform concat = (ConcatenatedTransform) transform; addWKT(buffer, concat.transform1, first); addWKT(buffer, concat.transform2, false); } else { if (!first) { buffer.append(", "); } buffer.append(transform); } } }