/*
* 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 org.geotools.geometry.DirectPosition2D;
import org.geotools.referencing.operation.builder.MappedPosition;
import org.opengis.geometry.DirectPosition;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Implements methods for triangulation for {@linkplain org.geotools.referencing.operation.builder.RubberSheetBuilder
* RubberSheeting} transformation.
*
* @since 2.4
* @source $URL$
* @version $Id$
* @author Jan Jezek
*/
class MapTriangulationFactory {
private final Quadrilateral quad;
private final List <MappedPosition> vectors;
/**
*
* @param quad defines the area for transformation.
* @param vectors represents pairs of identical points.
* @throws TriangulationException thrown when the source points are outside the quad.
*/
public MapTriangulationFactory(Quadrilateral quad,
List <MappedPosition> vectors) throws TriangulationException {
this.quad = quad;
this.vectors = vectors;
}
/**
* Generates map of source and destination triangles.
*
* @return Map of a source and destination triangles.
*
* @throws TriangulationException thrown when the source points are outside
* the quad.
*/
public Map getTriangleMap() throws TriangulationException {
Quadrilateral mQuad = mappedQuad(quad, vectors);
ExtendedPosition[] vertices = new ExtendedPosition[vectors.size()];
// converts MappedPosition to ExtendedPosition
for (int i = 0; i < vectors.size(); i++) {
vertices[i] = new ExtendedPosition(((MappedPosition) vectors.get(i))
.getSource(), ((MappedPosition) vectors.get(i)).getTarget());
}
TriangulationFactory triangulator = new TriangulationFactory(mQuad,
vertices);
List taggedSourceTriangles = triangulator.getTriangulation();
final HashMap triangleMap = new HashMap();
for (Iterator i = taggedSourceTriangles.iterator(); i.hasNext();) {
final TINTriangle sourceTriangle = (TINTriangle) i.next();
triangleMap.put(sourceTriangle,
new TINTriangle(((ExtendedPosition) sourceTriangle.p0)
.getMappedposition(),
((ExtendedPosition) sourceTriangle.p1).getMappedposition(),
((ExtendedPosition) sourceTriangle.p2).getMappedposition()));
}
return triangleMap;
}
/**
* Generates mapped quad from destination quad and source quad. The
* vertices of destination quad are calculated from source quad and
* difference of nearest pair of identical points.
*
* @param sourceQuad the quad that defines the area for triangulating.
* @param vectors of identical points (MappedCoordinates).
*
* @return destination quad
*/
private Quadrilateral mappedQuad(Quadrilateral sourceQuad, List<MappedPosition> vectors) {
if (vectors.isEmpty()) {
return (Quadrilateral) sourceQuad.clone();
}
//super.setMappedPositions(vectors);
MappedPosition[] mappedVertices = new MappedPosition[4];
for (int i = 0; i < mappedVertices.length; i++) {
mappedVertices[i] = generateCoordFromNearestOne(sourceQuad.getPoints()[i],
vectors);
}
return new Quadrilateral(new ExtendedPosition(
mappedVertices[0].getSource(), mappedVertices[0].getTarget()),
new ExtendedPosition(mappedVertices[1].getSource(),
mappedVertices[1].getTarget()),
new ExtendedPosition(mappedVertices[2].getSource(),
mappedVertices[2].getTarget()),
new ExtendedPosition(mappedVertices[3].getSource(),
mappedVertices[3].getTarget()));
}
/**
* Calculate the destination position for the quad vertex as source
* position using the difference between nearest source and destination
* point pair.
*
* @param x the original coordinate.
* @param vertices List of the MappedPosition.
*
* @return MappedPosition from the original and new coordinate, so the
* difference between them is the same as for the nearest one
* MappedPosition. It is used for calculating destination quad.
*/
protected MappedPosition generateCoordFromNearestOne(DirectPosition x,
List <MappedPosition> vertices) {
MappedPosition nearestOne = nearestMappedCoordinate(x, vertices);
double dstX = x.getCoordinate()[0]
+ (nearestOne.getTarget().getCoordinate()[0]
- nearestOne.getSource().getCoordinate()[0]);
double dstY = x.getCoordinate()[1]
+ (nearestOne.getTarget().getCoordinate()[1]
- nearestOne.getSource().getCoordinate()[1]);
DirectPosition dst = new DirectPosition2D(nearestOne.getTarget()
.getCoordinateReferenceSystem(),
dstX, dstY);
return new MappedPosition(x, dst);
}
/**
* Returns the nearest MappedPosition to specified point P.
*
* @param dp P point.
* @param vertices the List of MappedCoordinates.
*
* @return the MappedPosition to the x Coordinate.
*/
protected MappedPosition nearestMappedCoordinate(DirectPosition dp,
List <MappedPosition> vertices) {
DirectPosition2D x = new DirectPosition2D(dp);
// Assert.isTrue(vectors.size() > 0);
MappedPosition nearestOne = (MappedPosition) vertices.get(0);
for (Iterator <MappedPosition> i = vertices.iterator(); i.hasNext();) {
MappedPosition candidate = (MappedPosition) i.next();
if (((DirectPosition2D) candidate.getSource()).distance(
x.toPoint2D()) < ((DirectPosition2D) nearestOne
.getSource()).distance(x.toPoint2D())) {
nearestOne = candidate;
}
}
return nearestOne;
}
}