/* * 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.io.Serializable; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.NoninvertibleTransformException; import org.opengis.referencing.operation.TransformException; 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.GeneralMatrix; import org.geotools.referencing.wkt.Formatter; import org.geotools.util.Utilities; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; /** * Transform which passes through a subset of ordinates to another transform. * This allows transforms to operate on a subset of ordinates. For example, * if you have (latitude,longitude,height) * coordinates, then you may wish to convert the height values from feet to * meters without affecting the latitude and longitude values. * * @since 2.0 * * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * * @see DimensionFilter */ public class PassThroughTransform extends AbstractMathTransform implements Serializable { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -1673997634240223449L; /** * Index of the first affected ordinate. */ protected final int firstAffectedOrdinate; /** * Number of unaffected ordinates after the affected ones. * Always 0 when used through the strict OpenGIS API. */ protected final int numTrailingOrdinates; /** * The sub transform. * * @see #getSubTransform */ protected final MathTransform subTransform; /** * The inverse transform. This field will be computed only when needed. * But it is serialized in order to avoid rounding error. */ private PassThroughTransform inverse; /** * Create a pass through transform. * * @param firstAffectedOrdinate Index of the first affected ordinate. * @param subTransform The sub transform. * @param numTrailingOrdinates Number of trailing ordinates to pass through. * Affected ordinates will range from {@code firstAffectedOrdinate} * inclusive to {@code dimTarget-numTrailingOrdinates} exclusive. */ protected PassThroughTransform(final int firstAffectedOrdinate, final MathTransform subTransform, final int numTrailingOrdinates) { if (firstAffectedOrdinate < 0) { throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "firstAffectedOrdinate", firstAffectedOrdinate)); } if (numTrailingOrdinates < 0) { throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "numTrailingOrdinates", numTrailingOrdinates)); } if (subTransform instanceof PassThroughTransform) { final PassThroughTransform passThrough = (PassThroughTransform) subTransform; this.firstAffectedOrdinate = passThrough.firstAffectedOrdinate + firstAffectedOrdinate; this.numTrailingOrdinates = passThrough.numTrailingOrdinates + numTrailingOrdinates; this.subTransform = passThrough.subTransform; } else { this.firstAffectedOrdinate = firstAffectedOrdinate; this.numTrailingOrdinates = numTrailingOrdinates; this.subTransform = subTransform; } } /** * Creates a transform which passes through a subset of ordinates to another transform. * This allows transforms to operate on a subset of ordinates. For example, if you have * (latitidue,longitude,height) coordinates, then you * may wish to convert the height values from feet to meters without affecting the * latitude and longitude values. * * @param firstAffectedOrdinate Index of the first affected ordinate. * @param subTransform The sub transform. * @param numTrailingOrdinates Number of trailing ordinates to pass through. * Affected ordinates will range from {@code firstAffectedOrdinate} * inclusive to {@code dimTarget-numTrailingOrdinates} exclusive. * @return A pass through transform with the following dimensions:
*
     * Source: firstAffectedOrdinate + subTransform.getSourceDimensions() + numTrailingOrdinates
     * Target: firstAffectedOrdinate + subTransform.getTargetDimensions() + numTrailingOrdinates
*/ public static MathTransform create(final int firstAffectedOrdinate, final MathTransform subTransform, final int numTrailingOrdinates) { if (firstAffectedOrdinate < 0) { throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "firstAffectedOrdinate", firstAffectedOrdinate)); } if (numTrailingOrdinates < 0) { throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, "numTrailingOrdinates", numTrailingOrdinates)); } if (firstAffectedOrdinate==0 && numTrailingOrdinates==0) { return subTransform; } /* * Optimize the "Identity transform" case. */ if (subTransform.isIdentity()) { final int dimension = subTransform.getSourceDimensions(); if (dimension == subTransform.getTargetDimensions()) { return IdentityTransform.create(firstAffectedOrdinate + dimension + numTrailingOrdinates); } } /* * Special case for transformation backed by a matrix. Is is possible to use a * new matrix for such transform, instead of wrapping the sub-transform into a * PassThroughTransform object. It is faster and easier to concatenate. */ if (subTransform instanceof LinearTransform) { GeneralMatrix matrix = toGMatrix(((LinearTransform)subTransform).getMatrix()); matrix = PassThroughTransform.expand(matrix, firstAffectedOrdinate, numTrailingOrdinates, 1); return ProjectiveTransform.create(matrix); } /* * Constructs the general PassThroughTransform object. An optimisation * for the "Pass through case" is done right in the constructor. */ return new PassThroughTransform(firstAffectedOrdinate, subTransform, numTrailingOrdinates); } /** * Returns the sub transform. * * @return The sub transform. * * @since 2.2 */ public MathTransform getSubTransform() { return subTransform; } /** * Ordered sequence of positive integers defining the positions in a coordinate * tuple of the coordinates affected by this pass-through transform. The returned * index are for source coordinates. * * @return The modified coordinates. */ public int[] getModifiedCoordinates() { final int[] index = new int[subTransform.getSourceDimensions()]; for (int i=0; i= 0) { System.arraycopy (srcPts, srcOff, dstPts, dstOff, firstAffectedOrdinate); subTransform.transform(srcPts, srcOff+=firstAffectedOrdinate, dstPts, dstOff+=firstAffectedOrdinate, 1); System.arraycopy (srcPts, srcOff+=subDimSource, dstPts, dstOff+=subDimTarget, numTrailingOrdinates); srcOff += srcStep; dstOff += dstStep; } } /** * Transforms a list of coordinate point ordinal values. */ public void transform(final double[] srcPts, int srcOff, final double[] dstPts, int dstOff, int numPts) throws TransformException { final int subDimSource = subTransform.getSourceDimensions(); final int subDimTarget = subTransform.getTargetDimensions(); int srcStep = numTrailingOrdinates; int dstStep = numTrailingOrdinates; if (srcPts==dstPts && srcOff= 0) { System.arraycopy (srcPts, srcOff, dstPts, dstOff, firstAffectedOrdinate); subTransform.transform(srcPts, srcOff+=firstAffectedOrdinate, dstPts, dstOff+=firstAffectedOrdinate, 1); System.arraycopy (srcPts, srcOff+=subDimSource, dstPts, dstOff+=subDimTarget, numTrailingOrdinates); srcOff += srcStep; dstOff += dstStep; } } /** * Gets the derivative of this transform at a point. */ @Override public Matrix derivative(final DirectPosition point) throws TransformException { final int nSkipped = firstAffectedOrdinate + numTrailingOrdinates; final int transDim = subTransform.getSourceDimensions(); final int pointDim = point.getDimension(); if (pointDim != transDim+nSkipped) { throw new MismatchedDimensionException(Errors.format( ErrorKeys.MISMATCHED_DIMENSION_$3, "point", pointDim, transDim + nSkipped)); } final GeneralDirectPosition subPoint = new GeneralDirectPosition(transDim); for (int i=0; iWell * Known Text (WKT) element. * * @param formatter The formatter to use. * @return The WKT element name. * * @todo The {@link #numTrailingOrdinates} parameter is not part of OpenGIS specification. * We should returns a more complex WKT when {@code numTrailingOrdinates != 0}, * using an affine transform to change the coordinates order. */ @Override protected String formatWKT(final Formatter formatter) { formatter.append(firstAffectedOrdinate); if (numTrailingOrdinates != 0) { formatter.append(numTrailingOrdinates); formatter.setInvalidWKT(PassThroughTransform.class); } formatter.append(subTransform); return "PASSTHROUGH_MT"; } }