001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2008, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.referencing.operation.builder;
017:
018: import java.util.ArrayList;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023:
024: import javax.vecmath.MismatchedSizeException;
025:
026: import org.geotools.geometry.GeneralEnvelope;
027: import org.geotools.referencing.CRS;
028: import org.opengis.geometry.DirectPosition;
029: import org.opengis.geometry.MismatchedDimensionException;
030: import org.opengis.geometry.MismatchedReferenceSystemException;
031: import org.opengis.referencing.FactoryException;
032: import org.opengis.referencing.crs.CoordinateReferenceSystem;
033: import org.opengis.referencing.operation.MathTransform;
034:
035: /**
036: * Builds a RubberSheet transformation from a set of control points, defined as
037: * a List of
038: * {@linkplain org.geotools.referencing.operation.builder.MappedPosition MappedPosition}
039: * objects, and a quadrilateral delimiting the outer area of interest, defined
040: * as a List of four
041: * {@linkplain org.opengis.geometry.DirectPosition DirectPosition} objects.
042: *
043: * An explanation of the RubberSheet transformation algorithm can be seen
044: * <a href ="http://planner.t.u-tokyo.ac.jp/member/fuse/rubber_sheeting.pdf">here</a>.
045: *
046: * @since 2.4
047: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/builder/RubberSheetBuilder.java $
048: * @version $Id: RubberSheetBuilder.java 28982 2008-01-28 16:27:33Z acuster $
049: * @author Jan Jezek
050: * @author Adrian Custer
051: */
052: public class RubberSheetBuilder extends MathTransformBuilder {
053:
054: /* Map of the original and destination triangles. */
055: private HashMap trianglesMap;
056:
057: /* Map of a original triangles and associated AffineTransformation.*/
058: private HashMap trianglesToKeysMap;
059:
060: /**
061: * Creates the Builder from a List of control points and a List of four
062: * DirectPositions defining the vertexes of the area for interpolation.
063: *
064: * @param vectors A List of {@linkplain org.geotools.referencing.operation.builder.MappedPosition MappedPosition}
065: * @param vertices A List with four points defining the quadrilateral in the region of interest.
066: *
067: * @throws MismatchedSizeException
068: * @throws MismatchedDimensionException
069: * @throws MismatchedReferenceSystemException
070: * @throws TriangulationException
071: */
072: public RubberSheetBuilder(List /*<MappedPosition>*/vectors,
073: List /*<DirectPosition>*/vertices)
074: throws MismatchedSizeException,
075: MismatchedDimensionException,
076: MismatchedReferenceSystemException, TriangulationException {
077:
078: //Validates the vectors parameter while setting it
079: super .setMappedPositions(vectors);
080:
081: //Validate the vertices parameter
082: if (vertices.size() != 4) {
083: throw new IllegalArgumentException(
084: "The region of interest must have four vertices.");
085: }
086:
087: //Get the DirectPositions (In Java 1.4 we fail hard on this cast.)
088: DirectPosition[] ddpp = new DirectPosition[4];
089: for (int i = 0; i < vertices.size(); i++) {
090: ddpp[i] = (DirectPosition) vertices.get(i);
091: }
092:
093: //Check they have a common crs;
094: CoordinateReferenceSystem crs;
095: try {
096: crs = getSourceCRS();
097: } catch (FactoryException e) {
098: // Can't fetch the CRS. Use the one from the first region of interest point instead.
099: crs = ddpp[0].getCoordinateReferenceSystem();
100: }
101: if (!(CRS.equalsIgnoreMetadata(crs, ddpp[0]
102: .getCoordinateReferenceSystem())
103: || CRS.equalsIgnoreMetadata(crs, ddpp[1]
104: .getCoordinateReferenceSystem())
105: || CRS.equalsIgnoreMetadata(crs, ddpp[2]
106: .getCoordinateReferenceSystem()) || CRS
107: .equalsIgnoreMetadata(crs, ddpp[3]
108: .getCoordinateReferenceSystem()))) {
109: throw new MismatchedReferenceSystemException(
110: "Region of interest defined by mismatched DirectPositions.");
111: }
112:
113: //Check the vectors are inside the vertices.
114: // This is a quick check by envelope, can be more rigorous when we move
115: // to n dimensional operations.
116: DirectPosition[] dpa = this .getSourcePoints();
117: GeneralEnvelope srcextnt = new GeneralEnvelope(2);
118: for (int i = 0; i < dpa.length; i++) {
119: srcextnt.add(dpa[i]);
120: }
121: GeneralEnvelope vtxextnt = new GeneralEnvelope(2);
122: vtxextnt.add(ddpp[0]);
123: vtxextnt.add(ddpp[1]);
124: vtxextnt.add(ddpp[2]);
125: vtxextnt.add(ddpp[3]);
126: if (!vtxextnt.contains(srcextnt, true))
127: throw new IllegalArgumentException(
128: "The region of interest must contain the control points");
129:
130: Quadrilateral quad = new Quadrilateral(ddpp[0], ddpp[1],
131: ddpp[2], ddpp[3]);
132:
133: MapTriangulationFactory trianglemap = new MapTriangulationFactory(
134: quad, vectors);
135:
136: this .trianglesMap = (HashMap) trianglemap.getTriangleMap();
137: this .trianglesToKeysMap = mapTrianglesToKey();
138: }
139:
140: /**
141: * Returns the minimum number of points required by this builder.
142: *
143: * @return 1
144: */
145: public int getMinimumPointCount() {
146: return 1;
147: }
148:
149: /**
150: * Returns the map of source and destination triangles.
151: *
152: * @return The Map of source and destination triangles.
153: */
154: public HashMap getMapTriangulation() {
155: return trianglesMap;
156: }
157:
158: /**
159: * Returns MathTransform transformation setup as RubberSheet.
160: *
161: * @return calculated MathTransform
162: *
163: * @throws FactoryException when the size of source and destination point
164: * is not the same.
165: */
166: protected MathTransform computeMathTransform()
167: throws FactoryException {
168: return new RubberSheetTransform(trianglesToKeysMap);
169: }
170:
171: /**
172: * Calculates affine transformation parameters from the pair of triangles.
173: *
174: * @return The HashMap where the keys are the original triangles and values
175: * are AffineTransformation Objects.
176: */
177: private HashMap mapTrianglesToKey() {
178: AffineTransformBuilder calculator;
179:
180: HashMap trianglesToKeysMap = (HashMap) trianglesMap.clone();
181:
182: Iterator it = trianglesToKeysMap.entrySet().iterator();
183:
184: while (it.hasNext()) {
185:
186: Map.Entry a = (Map.Entry) it.next();
187: List pts = new ArrayList();
188:
189: for (int i = 1; i <= 3; i++) {
190: pts.add(new MappedPosition(((TINTriangle) a.getKey())
191: .getPoints()[i], ((TINTriangle) a.getValue())
192: .getPoints()[i]));
193:
194: }
195:
196: try {
197: calculator = new AffineTransformBuilder(pts);
198: a.setValue(calculator.getMathTransform());
199: } catch (Exception e) {
200: // should never reach here because AffineTransformBuilder(pts)
201: // should not throw any Exception.
202: e.printStackTrace();
203: }
204: }
205:
206: return trianglesToKeysMap;
207: }
208:
209: }
|