/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2001-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* 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.
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*/
package org.geotoolkit.referencing.operation.transform;
import java.io.Serializable;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import org.opengis.metadata.Identifier;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.parameter.InvalidParameterValueException;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterValueGroup;
import org.apache.sis.io.wkt.Formatter;
import org.apache.sis.io.wkt.FormattableObject;
import org.apache.sis.util.Classes;
import org.apache.sis.util.ComparisonMode;
import org.geotoolkit.resources.Errors;
import org.geotoolkit.resources.Vocabulary;
import org.apache.sis.internal.referencing.WKTUtilities;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.transform.Accessor;
import static org.geotoolkit.util.Utilities.hash;
/**
* @deprecated Moved to Apache SIS as {@link org.apache.sis.referencing.operation.transform.AbstractMathTransform}.
*
* {@section Two-dimensional transforms}
* {@code AbstractMathTransform} implements also the methods required by the {@link MathTransform2D} interface,
* but <strong>does not</strong> implements that interface directly. Subclasses must add the
* "{@code implements MathTransform2D}" clause themselves, or extend the {@link AbstractMathTransform2D} base class,
* if they know to map two-dimensional coordinate systems.
*/
@Deprecated
public abstract class AbstractMathTransform extends org.apache.sis.referencing.operation.transform.AbstractMathTransform
implements org.geotoolkit.io.wkt.Formattable
{
/**
* Constructs a math transform.
*/
protected AbstractMathTransform() {
}
/**
* Returns a name for this math transform (never {@code null}). This convenience methods
* returns the name of the {@linkplain #getParameterDescriptors parameter descriptors} if
* any, or the short class name otherwise.
*
* @return A name for this math transform (never {@code null}).
*
* @since 2.5
*/
public String getName() {
final ParameterDescriptorGroup descriptor = getParameterDescriptors();
if (descriptor != null) {
final Identifier identifier = descriptor.getName();
if (identifier != null) {
final String code = identifier.getCode();
if (code != null) {
return code;
}
}
}
return Classes.getShortClassName(this);
}
/**
* Constructs an error message for the {@link MismatchedDimensionException}.
*
* CAUTION: Argument order is different than in Apache SIS.
*
* @param argument The argument name with the wrong number of dimensions.
* @param dimension The wrong dimension.
* @param expected The expected dimension.
*/
static String mismatchedDimension(final String argument, final int dimension, final int expected) {
return Errors.format(Errors.Keys.MismatchedDimension_3, argument, dimension, expected);
}
/**
* Transforms the specified {@code ptSrc} and stores the result in {@code ptDst}.
* The default implementation performs the following steps:
* <p>
* <ul>
* <li>Ensures that the {@linkplain #getSourceDimensions() source} and
* {@linkplain #getTargetDimensions() target dimensions} of this math
* transform are equal to 2.</li>
* <li>Delegates to the {@link #transform(double[],int,double[],int,boolean)}
* method using a temporary array of doubles.</li>
* </ul>
*
* @param ptSrc The coordinate point to be transformed.
* @param ptDst The coordinate point that stores the result of transforming {@code ptSrc},
* or {@code null} if a new point should be created.
* @return The coordinate point after transforming {@code ptSrc} and storing the result in
* {@code ptDst}, or in a new point if {@code ptDst} was null.
* @throws MismatchedDimensionException If this transform doesn't map two-dimensional
* coordinate systems.
* @throws TransformException If the point can't be transformed.
*
* @see MathTransform2D#transform(Point2D,Point2D)
*/
public Point2D transform(final Point2D ptSrc, final Point2D ptDst) throws TransformException {
int dim;
if ((dim = getSourceDimensions()) != 2) {
throw new MismatchedDimensionException(mismatchedDimension("ptSrc", 2, dim));
}
if ((dim = getTargetDimensions()) != 2) {
throw new MismatchedDimensionException(mismatchedDimension("ptDst", 2, dim));
}
final double[] ord = new double[] {ptSrc.getX(), ptSrc.getY()};
transform(ord, 0, ord, 0, false);
if (ptDst != null) {
ptDst.setLocation(ord[0], ord[1]);
return ptDst;
} else {
return new Point2D.Double(ord[0], ord[1]);
}
}
/**
* Transform the specified shape. The default implementation computes quadratic curves
* using three points for each line segment in the shape. The returned object is often
* a {@link Path2D}, but may also be a {@link Line2D} or a {@link QuadCurve2D} if such
* simplification is possible.
*
* @param shape Shape to transform.
* @return Transformed shape, or {@code shape} if this transform is the identity transform.
* @throws IllegalStateException if this transform doesn't map 2D coordinate systems.
* @throws TransformException if a transform failed.
*
* @see MathTransform2D#createTransformedShape(Shape)
*/
public Shape createTransformedShape(final Shape shape) throws TransformException {
int dim;
if ((dim = getSourceDimensions()) != 2 || (dim = getTargetDimensions()) != 2) {
throw new MismatchedDimensionException(mismatchedDimension("shape", 2, dim));
}
return isIdentity() ? shape : Accessor.createTransformedShape((MathTransform2D) this, shape, null, null, false);
}
/**
* Gets the derivative of this transform at a point.
* The default implementation performs the following steps:
* <p>
* <ul>
* <li>Ensure that this math transform {@linkplain #getSourceDimensions() source dimensions}
* is equals to 2. Note that the {@linkplain #getTargetDimensions() target dimensions}
* can be anything, not necessarily 2 (so this transform doesn't need to implement the
* {@link MathTransform2D} interface).</li>
* <li>Copy the coordinate in a temporary array and pass that array to the
* {@link #transform(double[], int, double[], int, boolean)} method,
* with the {@code derivate} boolean argument set to {@code true}.</li>
* <li>If the later method returned a non-null matrix, returns that matrix.
* Otherwise throws {@link TransformException}.</li>
* </ul>
*
* @param point The coordinate point where to evaluate the derivative.
* @return The derivative at the specified point as a 2×2 matrix.
* @throws MismatchedDimensionException if the input dimension is not 2.
* @throws TransformException if the derivative can't be evaluated at the specified point.
*
* @see MathTransform2D#derivative(Point2D)
*/
public Matrix derivative(final Point2D point) throws TransformException {
final int dimSource = getSourceDimensions();
if (dimSource != 2) {
throw new MismatchedDimensionException(mismatchedDimension("point", 2, dimSource));
}
final double[] coordinate = new double[] {point.getX(), point.getY()};
final Matrix derivative = transform(coordinate, 0, null, 0, true);
if (derivative == null) {
throw new TransformException(Errors.format(Errors.Keys.CantComputeDerivative));
}
return derivative;
}
/**
* Makes sure that an argument is non-null. This is a
* convenience method for subclass constructors.
*
* @param name Argument name.
* @param object User argument.
* @throws InvalidParameterValueException if {@code object} is null.
*/
protected static void ensureNonNull(final String name, final Object object)
throws InvalidParameterValueException
{
if (object == null) {
throw new InvalidParameterValueException(Errors.format(
Errors.Keys.NullArgument_1, name), name, object);
}
}
/**
* Default implementation for inverse math transform. This inner class is the inverse of
* the enclosing {@link AbstractMathTransform}. It is serializable only if the enclosing
* math transform is also serializable.
*
* @author Martin Desruisseaux (IRD)
* @version 3.00
*
* @since 2.0
* @module
*/
protected abstract class Inverse extends AbstractMathTransform implements Serializable {
/**
* Serial number for inter-operability 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(Vocabulary.Keys.InverseTransform) + ')';
}
/**
* Gets the dimension of input points. The default
* implementation returns the dimension of output
* points of the enclosing math transform.
*/
@Override
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.
*/
@Override
public int getTargetDimensions() {
return AbstractMathTransform.this.getSourceDimensions();
}
/**
* Gets the derivative of this transform at a point. The default
* implementation computes the inverse of the matrix returned by
* the enclosing math transform.
*/
@Override
public Matrix derivative(final Point2D point) throws TransformException {
return MatrixSIS.castOrCopy(AbstractMathTransform.this.derivative(this.transform(point, null))).inverse();
}
/**
* Gets the derivative of this transform at a point. The default
* implementation computes the inverse of the matrix returned by
* the enclosing math transform.
*/
@Override
public Matrix derivative(final DirectPosition point) throws TransformException {
return MatrixSIS.castOrCopy(AbstractMathTransform.this.derivative(this.transform(point, null))).inverse();
}
/**
* Returns the enclosing class as a private method for protecting from user override.
*/
private AbstractMathTransform enclosing() {
return AbstractMathTransform.this;
}
/**
* 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 delegates this tests to the
* enclosing math transform.
*/
@Override
public boolean isIdentity() {
return AbstractMathTransform.this.isIdentity();
}
/**
* {@inheritDoc}
*/
@Override
protected int computeHashCode() {
return hash(AbstractMathTransform.this.hashCode(), super.computeHashCode());
}
/**
* 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 if so compares their enclosing {@code AbstractMathTransform}.
*/
@Override
public boolean equals(final Object object, final ComparisonMode mode) {
if (object == this) {
// Slight optimization
return true;
}
if (object != null && object.getClass() == getClass()) {
final Inverse that = (Inverse) object;
return AbstractMathTransform.this.equals(that.enclosing(), mode);
} else {
return false;
}
}
/**
* Formats the inner part of a
* <A HREF="http://www.geoapi.org/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html#INVERSE_MT"><cite>Well
* Known Text</cite> (WKT)</A> 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 <code>"INVERSE_MT"</code> entity.
*
* @param formatter The formatter to use.
* @return The WKT element name, which is {@code "PARAM_MT"} or
* {@code "INVERSE_MT"} in the default implementation.
*/
@Override
public String formatTo(final Formatter formatter) {
final ParameterValueGroup parameters = getParameterValues();
if (parameters != null) {
WKTUtilities.appendParamMT(parameters, formatter);
return "PARAM_MT";
} else {
formatter.append((FormattableObject) AbstractMathTransform.this);
return "INVERSE_MT";
}
}
}
@Override
public String formatTo(Formatter formatter) {
return super.formatTo(formatter);
}
}