/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-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.builder;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.Iterator;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.referencing.operation.transform.AbstractMathTransform;
/**
* This provides the transformation method based on RubberSheeting (also
* known as Billinear interpolated transformation) The class is accessed
* {@linkplain org.geotools.referencing.operation.builder.RubberSheetBuilder
* RubberSheetBuilder}. More about Rubber Sheet transformation can be seen <a
* href =
* "http://planner.t.u-tokyo.ac.jp/member/fuse/rubber_sheeting.pdf">here</a>.
*
* @since 2.4
* @source $URL$
* @version $Id$
* @author Jan Jezek
*
* @todo Consider moving this class to the {@linkplain
* org.geotools.referencing.operation.transform} package.
*/
class RubberSheetTransform extends AbstractMathTransform implements MathTransform2D {
/**
* Helper variable to hold triangle. It is use for optimalization of searching in TIN for
* triangle containing points that are transformed.
*/
private TINTriangle previousTriangle = null;
/**
* The HashMap where the keys are the original {@link
* Polygon} and values are {@link
* #org.opengis.referencing.operation.MathTransform}.
*/
private HashMap trianglesToKeysMap;
/**
* Constructs the RubberSheetTransform.
*
* @param trianglesToAffineTransform The HashMap where the keys are the original
* {@linkplain org.geotools.referencing.operation.builder.algorithm.TINTriangle}
* and values are {@linkplain org.opengis.referencing.operation.MathTransform}.
*/
public RubberSheetTransform(HashMap trianglesToAffineTransform) {
this.trianglesToKeysMap = trianglesToAffineTransform;
}
/**
* Gets the dimension of input points, which is 2.
*
* @return dimension of input points
*/
public final int getSourceDimensions() {
return 2;
}
/**
* Gets the dimension of output points, which is 2.
*
* @return dimension of output points
*/
public final int getTargetDimensions() {
return 2;
}
/**
* String representation.
*
* @return String expression of the triangle and its affine transform
* parameters
*
* @todo This method doesn't meet the {@link MathTransform#toString}
* constract, which should uses Well Known Text (WKT) format as much
* as possible.
*/
@Override
public String toString() {
final String lineSeparator = System.getProperty("line.separator", "\n");
final StringBuilder buffer = new StringBuilder();
for (final Iterator i = trianglesToKeysMap.keySet().iterator(); i.hasNext();) {
TINTriangle trian = (TINTriangle) i.next();
MathTransform mt = (MathTransform) trianglesToKeysMap.get(trian);
buffer.append(trian.toString());
buffer.append(lineSeparator);
buffer.append(mt.toString());
buffer.append(lineSeparator);
}
return buffer.toString();
}
/* (non-Javadoc)
* @see org.opengis.referencing.operation.MathTransform#transform(double[], int, double[], int, int)
*/
public void transform(double[] srcPts, int srcOff, final double[] dstPt, int dstOff, int numPts)
throws TransformException {
for (int i = srcOff; i < numPts; i++) {
Point2D pos = (Point2D) (new DirectPosition2D(srcPts[2 * i], srcPts[(2 * i) + 1]));
TINTriangle triangle = searchTriangle((DirectPosition) pos);
AffineTransform AT = (AffineTransform) trianglesToKeysMap.get(triangle);
Point2D dst = AT.transform(pos, null);
dstPt[2 * i] = dst.getX();
dstPt[(2 * i) + 1] = dst.getY();
}
}
/**
* Search the TIN for the triangle that contains p
* @param p Point of interest
* @return Triangle containing p
* @throws TransformException if points are outside the area of TIN.
*/
private TINTriangle searchTriangle(DirectPosition p)
throws TransformException {
/* optimalization for finding triangles.
* Assuming the point are close to each other -
* so why not to check if next point is in the same triangle as previous one.
*/
if ((previousTriangle != null) && previousTriangle.containsOrIsVertex(p)) {
return previousTriangle;
}
for (Iterator i = trianglesToKeysMap.keySet().iterator(); i.hasNext();) {
TINTriangle triangle = (TINTriangle) i.next();
if (triangle.containsOrIsVertex(p)) {
previousTriangle = triangle;
return triangle;
}
}
throw (new TransformException("Points are outside the scope"));
}
/**
* Returns the inverse transform.
* @return
*/
@Override
public MathTransform2D inverse() throws NoninvertibleTransformException {
return (MathTransform2D) super.inverse();
}
}