/*
* 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.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.vecmath.MismatchedSizeException;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.geometry.MismatchedReferenceSystemException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
/**
* Builds a RubberSheet transformation from a set of control points, defined as
* a List of
* {@linkplain org.geotools.referencing.operation.builder.MappedPosition MappedPosition}
* objects, and a quadrilateral delimiting the outer area of interest, defined
* as a List of four
* {@linkplain org.opengis.geometry.DirectPosition DirectPosition} objects.
*
* An explanation of the RubberSheet transformation algorithm 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
* @author Adrian Custer
*/
public class RubberSheetBuilder extends MathTransformBuilder {
/* Map of the original and destination triangles. */
private HashMap trianglesMap;
/* Map of a original triangles and associated AffineTransformation.*/
private HashMap trianglesToKeysMap;
/**
* Creates the Builder from a List of control points and a List of four
* DirectPositions defining the vertexes of the area for interpolation.
*
* @param vectors A List of {@linkplain org.geotools.referencing.operation.builder.MappedPosition MappedPosition}
* @param vertices A List with four points defining the quadrilateral in the region of interest.
*
* @throws MismatchedSizeException
* @throws MismatchedDimensionException
* @throws MismatchedReferenceSystemException
* @throws TriangulationException
*/
public RubberSheetBuilder(List <MappedPosition> vectors, List <DirectPosition> vertices)
throws MismatchedSizeException, MismatchedDimensionException, MismatchedReferenceSystemException, TriangulationException {
//Validates the vectors parameter while setting it
super.setMappedPositions(vectors);
//Validate the vertices parameter
if ( vertices.size() != 4){
throw new IllegalArgumentException("The region of interest must have four vertices.");
}
//Get the DirectPositions (In Java 1.4 we fail hard on this cast.)
DirectPosition [] ddpp = new DirectPosition[4];
for (int i = 0; i< vertices.size(); i++){
ddpp[i] = (DirectPosition) vertices.get(i);
}
//Check they have a common crs;
CoordinateReferenceSystem crs;
try {
crs = getSourceCRS();
} catch (FactoryException e) {
// Can't fetch the CRS. Use the one from the first region of interest point instead.
crs = ddpp[0].getCoordinateReferenceSystem();
}
if ( !( CRS.equalsIgnoreMetadata( crs, ddpp[0].getCoordinateReferenceSystem() )
|| CRS.equalsIgnoreMetadata( crs, ddpp[1].getCoordinateReferenceSystem() )
|| CRS.equalsIgnoreMetadata( crs, ddpp[2].getCoordinateReferenceSystem() )
|| CRS.equalsIgnoreMetadata( crs, ddpp[3].getCoordinateReferenceSystem() )
)
) {
throw new MismatchedReferenceSystemException(
"Region of interest defined by mismatched DirectPositions.");
}
//Check the vectors are inside the vertices.
// This is a quick check by envelope, can be more rigorous when we move
// to n dimensional operations.
DirectPosition[] dpa = this.getSourcePoints();
GeneralEnvelope srcextnt = new GeneralEnvelope(2);
for (int i = 0; i<dpa.length; i++){
srcextnt.add(dpa[i]);
}
GeneralEnvelope vtxextnt = new GeneralEnvelope(2);
vtxextnt.add(ddpp[0]);
vtxextnt.add(ddpp[1]);
vtxextnt.add(ddpp[2]);
vtxextnt.add(ddpp[3]);
if (! vtxextnt.contains(srcextnt,true))
throw new IllegalArgumentException("The region of interest must contain the control points");
Quadrilateral quad = new Quadrilateral(ddpp[0],ddpp[1],ddpp[2],ddpp[3]);
MapTriangulationFactory trianglemap = new MapTriangulationFactory(quad,vectors);
this.trianglesMap = (HashMap) trianglemap.getTriangleMap();
this.trianglesToKeysMap = mapTrianglesToKey();
}
/**
* Returns the minimum number of points required by this builder.
*
* @return 1
*/
public int getMinimumPointCount() {
return 1;
}
/**
* Returns the map of source and destination triangles.
*
* @return The Map of source and destination triangles.
*/
public HashMap getMapTriangulation() {
return trianglesMap;
}
/**
* Returns MathTransform transformation setup as RubberSheet.
*
* @return calculated MathTransform
*
* @throws FactoryException when the size of source and destination point
* is not the same.
*/
protected MathTransform computeMathTransform() throws FactoryException {
return new RubberSheetTransform(trianglesToKeysMap);
}
/**
* Calculates affine transformation parameters from the pair of triangles.
*
* @return The HashMap where the keys are the original triangles and values
* are AffineTransformation Objects.
*/
private HashMap mapTrianglesToKey() {
AffineTransformBuilder calculator;
HashMap trianglesToKeysMap = (HashMap) trianglesMap.clone();
Iterator it = trianglesToKeysMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry a = (Map.Entry) it.next();
List pts = new ArrayList();
for (int i = 1; i <= 3; i++) {
pts.add(new MappedPosition(
((TINTriangle) a.getKey()).getPoints()[i],
((TINTriangle) a.getValue()).getPoints()[i]));
}
try {
calculator = new AffineTransformBuilder(pts);
a.setValue(calculator.getMathTransform());
} catch (Exception e) {
// should never reach here because AffineTransformBuilder(pts)
// should not throw any Exception.
e.printStackTrace();
}
}
return trianglesToKeysMap;
}
}