*
* However, this method does not assumes that source and target dimension are the same (in the * special case where source and target dimension are always the same, a simplier and more * efficient check is possible). The following example prepares a transformation from 2 * dimensional points to three dimensional points: *
*
* public void transform(double[] srcPts, int srcOff, * double[] dstPts, int dstOff, int numPts) * { * if (srcPts==dstPts && needCopy(srcOff, 2, dstOff, 3, numPts) { * final double[] old = srcPts; * srcPts = new double[numPts*2]; * System.arraycopy(old, srcOff, srcPts, 0, srcPts.length); * srcOff = 0; * } * }* * This method is for internal usage by the referencing module only. Do not use! * It will be replaced by a different mechanism in a future GeoTools version. * * @param srcOff The offset in the source coordinate array. * @param dimSource The dimension of input points. * @param dstOff The offset in the destination coordinate array. * @param dimTarget The dimension of output points. * @param numPts The number of points to transform. * @return {@code true} if the source coordinates should be copied before to apply the * transformation in order to avoid an overlap with the destination array. */ protected static boolean needCopy(final int srcOff, final int dimSource, final int dstOff, final int dimTarget, final int numPts) { if (numPts <= 1 || (srcOff >= dstOff && dimSource >= dimTarget)) { /* * Source coordinates are stored after target coordinates. If implementation * read coordinates from lower index to upper index, then the destination will * not overwrite the source coordinates, even if there is an overlaps. */ return false; } return srcOff < dstOff + numPts*dimTarget && dstOff < srcOff + numPts*dimSource; } /** * Ensures that the specified longitude stay within ±π radians. This method * is typically invoked after geographic coordinates are transformed. This method may add * or substract some amount of 2π radians to x. * * @param x The longitude in radians. * @return The longitude in the range ±π radians. */ protected static double rollLongitude(final double x) { return x - (2*Math.PI) * Math.floor(x / (2*Math.PI) + 0.5); } /** * Wraps the specified matrix in a Geotools implementation of {@link Matrix}. If {@code matrix} * is already an instance of {@code XMatrix}, then it is returned unchanged. Otherwise, all * elements are copied in a new {@code XMatrix} object. */ static XMatrix toXMatrix(final Matrix matrix) { if (matrix instanceof XMatrix) { return (XMatrix) matrix; } return MatrixFactory.create(matrix); } /** * Wraps the specified matrix in a Geotools implementation of {@link Matrix}. If {@code matrix} * is already an instance of {@code GeneralMatrix}, then it is returned unchanged. Otherwise, * all elements are copied in a new {@code GeneralMatrix} object. ** Before to use this method, check if a {@link XMatrix} (to be obtained with {@link #toXMatrix}) * would be suffisient. Use this method only if a {@code GeneralMatrix} is really necessary. */ static GeneralMatrix toGMatrix(final Matrix matrix) { if (matrix instanceof GeneralMatrix) { return (GeneralMatrix) matrix; } else { return new GeneralMatrix(matrix); } } /** * Inverts the specified matrix in place. If the matrix can't be inverted (for example * because of a {@link SingularMatrixException}), then the exception is wrapped into a * {@link NoninvertibleTransformException}. */ static Matrix invert(final Matrix matrix) throws NoninvertibleTransformException { try { final XMatrix m = toXMatrix(matrix); m.invert(); return m; } catch (SingularMatrixException exception) { NoninvertibleTransformException e = new NoninvertibleTransformException( Errors.format(ErrorKeys.NONINVERTIBLE_TRANSFORM)); e.initCause(exception); throw e; } } /** * Default implementation for inverse math transform. This inner class is the inverse * of the enclosing {@link MathTransform}. It is serializable only if the enclosing * math transform is also serializable. * * @since 2.0 * @version $Id$ * @author Martin Desruisseaux (IRD) */ protected abstract class Inverse extends AbstractMathTransform implements Serializable { /** * Serial number for interoperability with different versions. This serial number is * especilly important for inner classes, since the default {@code serialVersionUID} * computation will not produce consistent results across implementations of different * Java compiler. This is because different compilers may generate different names for * synthetic members used in the implementation of inner classes. See: * * http://developer.java.sun.com/developer/bugParade/bugs/4211550.html */ private static final long serialVersionUID = 3528274816628012283L; /** * Constructs an inverse math transform. */ protected Inverse() { } /** * Returns a name for this math transform (never {@code null}). The default implementation * returns the direct transform name concatenated with localized flavor (when available) * of "(Inverse transform)". * * @return A name for this math transform (never {@code null}). * * @since 2.5 */ @Override public String getName() { return AbstractMathTransform.this.getName() + " (" + Vocabulary.format(VocabularyKeys.INVERSE_TRANSFORM) + ')'; } /** * Gets the dimension of input points. The default * implementation returns the dimension of output * points of the enclosing math transform. */ public int getSourceDimensions() { return AbstractMathTransform.this.getTargetDimensions(); } /** * Gets the dimension of output points. The default * implementation returns the dimension of input * points of the enclosing math transform. */ public int getTargetDimensions() { return AbstractMathTransform.this.getSourceDimensions(); } /** * Gets the derivative of this transform at a point. The default * implementation compute the inverse of the matrix returned by * the enclosing math transform. */ @Override public Matrix derivative(final Point2D point) throws TransformException { return invert(AbstractMathTransform.this.derivative(this.transform(point, null))); } /** * Gets the derivative of this transform at a point. The default * implementation compute the inverse of the matrix returned by * the enclosing math transform. */ @Override public Matrix derivative(final DirectPosition point) throws TransformException { return invert(AbstractMathTransform.this.derivative(this.transform(point, null))); } /** * Returns the inverse of this math transform, which is the enclosing math transform. * This behavior should not be changed since some implementation assume that the inverse * of {@code this} is always {@code AbstractMathTransform.this}. */ @Override public MathTransform inverse() { return AbstractMathTransform.this; } /** * Tests whether this transform does not move any points. * The default implementation delegate this tests to the * enclosing math transform. */ @Override public boolean isIdentity() { return AbstractMathTransform.this.isIdentity(); } /** * Returns a hash code value for this math transform. */ @Override public int hashCode() { return ~AbstractMathTransform.this.hashCode(); } /** * Compares the specified object with this inverse math transform for equality. * The default implementation tests if {@code object} in an instance of the same * class than {@code this}, and then test their enclosing math transforms. */ @Override public boolean equals(final Object object) { if (object == this) { // Slight optimization return true; } if (object instanceof Inverse) { final Inverse that = (Inverse) object; return Utilities.equals(this.inverse(), that.inverse()); } else { return false; } } /** * Format the inner part of a * Well * Known Text (WKT) element. If this inverse math transform * has any parameter values, then this method format the WKT as in the * {@linkplain AbstractMathTransform#formatWKT super-class method}. Otherwise * this method format the math transform as an
"INVERSE_MT"
entity. * * @param formatter The formatter to use. * @return The WKT element name, which is"PARAM_MT"
or *"INVERSE_MT"
in the default implementation. */ @Override protected String formatWKT(final Formatter formatter) { final ParameterValueGroup parameters = getParameterValues(); if (parameters != null) { formatter.append(formatter.getName(parameters.getDescriptor())); formatter.append(parameters); return "PARAM_MT"; } else { formatter.append((Formattable) AbstractMathTransform.this); return "INVERSE_MT"; } } } }