/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2002-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.
*/
package org.geotoolkit.referencing.operation.transform;
import java.awt.Dimension;
import java.awt.geom.Rectangle2D;
import java.awt.image.DataBuffer;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.geotoolkit.resources.Errors;
import org.apache.sis.internal.referencing.DirectPositionView;
/**
* A specialization of {@link GridTransform} in the two-dimensional case. The default implementation
* is invertible for the {@link GridType#OFFSET OFFSET}, {@link GridType#NADCON NADCON} and
* {@link GridType#NTv2 NTv2} grid types (assuming that the offsets are small), but not for the
* {@linkplain GridType#LOCALIZATION LOCALIZATION} type. For an invertible localization grid,
* see the {@link org.geotoolkit.referencing.operation.builder.LocalizationGrid} builder.
*
* @author Rueben Schulz (UBC)
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.20
*
* @since 3.00
* @module
*
* @deprecated Replaced by {@link org.apache.sis.referencing.operation.transform.InterpolatedTransform}
*/
@Deprecated
public class GridTransform2D extends GridTransform implements MathTransform2D {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -5797129125459758798L;
/**
* The inverse of this math transform. Will be created only when first needed.
*/
private transient MathTransform2D inverse;
/**
* Constructs a grid using the specified data.
*
* @param type Whatever the grid values are directly the target coordinates or offsets
* to apply on source coordinates.
* @param grid The grid of values. It must complies with the conditions documented in the
* {@link #grid} field.
* @param size Number of columns ({@linkplain Dimension#width width}) and rows
* ({@linkplain Dimension#height height}) in the grid.
* @param area Grid envelope in "real world" coordinates, or {@code null} if none. The
* minimal (<var>x</var>,<var>y</var>) coordinate will maps the (0,0) grid
* coordinate, and the maximal (<var>x</var>,<var>y</var>) coordinate will
* maps the ({@linkplain #width width}, {@linkplain #height height}) grid
* coordinate.
*/
protected GridTransform2D(final GridType type, final DataBuffer grid,
final Dimension size, final Rectangle2D area)
{
super(type, grid, size, area);
final int n = grid.getNumBanks();
if (n != 2) {
throw new MismatchedDimensionException(Errors.format(
Errors.Keys.MismatchedDimension_3, "grid", n, 2));
}
}
/**
* Returns the inverse of this transform.
*
* @throws NoninvertibleTransformException If this transform is not invertible.
*/
@Override
public synchronized MathTransform2D inverse() throws NoninvertibleTransformException {
if (inverse == null) {
switch (type) {
case LOCALIZATION: {
// Actually throws an exception since this transform is not invertible.
inverse = (MathTransform2D) super.inverse();
break;
}
default: {
inverse = new Inverse();
break;
}
}
}
return inverse;
}
/**
* Transforms target coordinates to source coordinates. This is done by iteratively
* finding a target coordinate that shifts to the input coordinate. The input coordinate
* is used as the first approximation.
* <p>
* This method is not applicable in the {@link GridType#LOCALIZATION} case.
*
* @author Rueben Schulz (UBC)
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.00
*
* @since 3.00
* @module
*/
private final class Inverse extends AbstractMathTransform.Inverse implements MathTransform2D {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -6779719408779847014L;
/**
* Difference allowed in iterative computations. This is half the value
* used in the NGS Fortran code (so all tests pass).
*/
private static final double ITERATION_TOLERANCE = 5E-10;
/**
* Maximum number of iterations for iterative computations.
*/
private static final int MAXIMUM_ITERATIONS = 10;
/**
* Creates an inverse transform.
*/
Inverse() {
GridTransform2D.this.super();
}
/**
* Transforms a single coordinate in a list of ordinal values, and optionally returns
* the derivative at that location.
*
* @throws TransformException If there is no convergence.
*
* @since 3.20 (derived from 3.00)
*/
@Override
public Matrix transform(final double[] srcPts, final int srcOff,
final double[] dstPts, final int dstOff,
final boolean derivate) throws TransformException
{
Matrix derivative = null;
if (derivate) {
derivative = derivative(new DirectPositionView(srcPts, srcOff, getSourceDimensions()));
}
if (dstPts == null) {
return derivative;
}
double xi, yi;
final double x, y;
dstPts[dstOff ] = xi = x = srcPts[srcOff ];
dstPts[dstOff+1] = yi = y = srcPts[srcOff+1];
int i = MAXIMUM_ITERATIONS;
do {
GridTransform2D.this.transform(dstPts, dstOff, dstPts, dstOff, false);
final double dx = dstPts[dstOff ] - x;
final double dy = dstPts[dstOff+1] - y;
dstPts[dstOff ] = (xi -= dx);
dstPts[dstOff+1] = (yi -= dy);
if (Math.abs(dx) <= ITERATION_TOLERANCE && Math.abs(dy) <= ITERATION_TOLERANCE) {
return derivative;
}
} while (--i >= 0);
throw new TransformException(Errors.format(Errors.Keys.NoConvergence));
}
/**
* Returns the inverse of this transform.
*/
@Override
public MathTransform2D inverse() {
return GridTransform2D.this;
}
/**
* Restores reference to this object after deserialization.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
GridTransform2D.this.inverse = this;
}
}
}