001: //$HeadURL: $
002: /*---------------- FILE HEADER ------------------------------------------
003: This file is part of deegree.
004: Copyright (C) 2001-2008 by:
005: Department of Geography, University of Bonn
006: http://www.giub.uni-bonn.de/deegree/
007: lat/lon GmbH
008: http://www.lat-lon.de
009:
010: This library is free software; you can redistribute it and/or
011: modify it under the terms of the GNU Lesser General Public
012: License as published by the Free Software Foundation; either
013: version 2.1 of the License, or (at your option) any later version.
014: This library is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018: You should have received a copy of the GNU Lesser General Public
019: License along with this library; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: Contact:
022:
023: Andreas Poth
024: lat/lon GmbH
025: Aennchenstr. 19
026: 53177 Bonn
027: Germany
028: E-Mail: poth@lat-lon.de
029:
030: Prof. Dr. Klaus Greve
031: Department of Geography
032: University of Bonn
033: Meckenheimer Allee 166
034: 53115 Bonn
035: Germany
036: E-Mail: greve@giub.uni-bonn.de
037: ---------------------------------------------------------------------------*/
038:
039: package org.deegree.crs.transformations;
040:
041: import java.util.ArrayList;
042: import java.util.List;
043:
044: import javax.vecmath.Point3d;
045:
046: import org.deegree.crs.coordinatesystems.CoordinateSystem;
047: import org.deegree.crs.exceptions.TransformationException;
048: import org.deegree.i18n.Messages;
049:
050: /**
051: * The change of coordinates from one CRS to another CRS based on different datum is 'currently' only possible via a
052: * coordinate <code>Transformation</code>.
053: * <p>
054: * The transformation parameters could only be derived empirically by a set of points common to both coordinate
055: * reference systems it means by identical points. Choice, allocation, number and the quality of coordinates of the
056: * points affect extensive the results and the accuracy. Therefore different realizations for transformations from one
057: * datum to another exist.
058: * </p>
059: *
060: * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
061: *
062: * @author last edited by: $Author:$
063: *
064: * @version $Revision:$, $Date:$
065: *
066: */
067:
068: public abstract class CRSTransformation {
069:
070: private CoordinateSystem sourceCRS;
071:
072: private CoordinateSystem targetCRS;
073:
074: boolean isInverse;
075:
076: /**
077: * @param sourceCRS
078: * @param targetCRS
079: */
080: public CRSTransformation(CoordinateSystem sourceCRS,
081: CoordinateSystem targetCRS) {
082: this .sourceCRS = sourceCRS;
083: this .targetCRS = targetCRS;
084: isInverse = false;
085: }
086:
087: /**
088: * Little helper function to create a temporary id or name.
089: *
090: * @param from
091: * containing the value (id or name) of the 'from' coourdinateSystem
092: * @param to
093: * containing the value (id or name) of the 'to' coourdinateSystem
094: * @return a following string "FROM_fromValue_TO_toValue".
095: */
096: protected static String createFromTo(String from, String to) {
097: return new StringBuilder("FROM_").append(from).append("_TO_")
098: .append(to).toString();
099: }
100:
101: /**
102: * Do a transformation, e.g. the incoming data comes from the sourceCRS and must be transformed to the targetCRS.
103: *
104: * @param srcPts
105: * the points which must be transformed, expected are following values either, long_1, lat_1, height_1,
106: * long_2, lat_2, height_2. or long_1, lat_1, long_2, lat_2
107: * @return the transformed points
108: * @throws TransformationException if a transform could not be calculated.
109: */
110: public abstract List<Point3d> doTransform(final List<Point3d> srcPts)
111: throws TransformationException;
112:
113: /**
114: * Wraps the incoming coordinates into a List<Point3d> and calls the {@link #doTransform(List)}. The source array
115: * will be read according to the dimension of the source CRS {@link #getSourceDimension()} and the target
116: * coordinates will be put according to the dimension of the targetCRS {@link #getTargetDimension()}. If the
117: * sourceDim < 2 or > 3 a transformation exception will be thrown.
118: *
119: * @param srcCoords
120: * the array holding the source ('original') coordinates.
121: * @param startPositionSrc
122: * the position to start reading the coordinates from the source array (0 is the first).
123: * @param destCoords
124: * the array which will receive the transformed coordinates.
125: * @param startPositionDest
126: * the index of the destCoords array to put the results, if the result will exceed the array.length, the
127: * array will be enlarged to hold the transformed coordinates.
128: * @param lastCoord
129: * the index of the last coordinate (normally length-1)
130: * @throws TransformationException
131: * If the sourceDim < 2 or soureDim > 3;
132: * @throws IllegalArgumentException
133: * if
134: * <ul>
135: * <li> the srcCoords is null</li>
136: * <li>the startPositionSrc > srcCoords.length</li>
137: * <li> the lastCoord > startPositionSrc</li>
138: * <li>the number of source coordinates are not congruent with the source dimension</li>
139: * <li> the lastCoord < startCoordSrc</li>
140: * <li>the source or target dimension < 2 or > 3</li>
141: * </ul>
142: */
143: public void doTransform(double[] srcCoords, int startPositionSrc,
144: double[] destCoords, int startPositionDest, int lastCoord)
145: throws TransformationException {
146: if (startPositionSrc < 0) {
147: startPositionSrc = 0;
148: }
149: if (srcCoords == null) {
150: throw new IllegalArgumentException(Messages.getMessage(
151: "CRS_PARAMETER_NOT_NULL",
152: "doTransform(double[],int,double[],int,int)",
153: "srcCoords"));
154: }
155: if (startPositionSrc > srcCoords.length) {
156: throw new IllegalArgumentException(Messages
157: .getMessage("CRS_TRANSFORM_START_GT_LENGTH"));
158: }
159: if (lastCoord > srcCoords.length) {
160: throw new IllegalArgumentException(Messages
161: .getMessage("CRS_TRANSFORM_END_GT_LENGTH"));
162: }
163: if ((lastCoord - startPositionSrc) % getSourceDimension() != 0) {
164: throw new IllegalArgumentException(Messages
165: .getMessage("CRS_TRANSFORM_SRC_WRONG_DIM"));
166: }
167: int listSize = (lastCoord - startPositionSrc)
168: / getSourceDimension();
169: if (listSize < 0) {
170: throw new IllegalArgumentException(Messages
171: .getMessage("CRS_TRANSFORM_LAST_LT_START"));
172: }
173:
174: List<Point3d> sourceCoords = new ArrayList<Point3d>(listSize);
175: final int dim = getSourceDimension();
176: if (dim > 3 || dim < 2) {
177: throw new TransformationException(Messages.getMessage(
178: "CRS_TRANSFORM_WRONG_CRS_DIM", "source"));
179: }
180: for (int i = startPositionSrc; i < lastCoord
181: && (i + (dim - 1)) < lastCoord; i += dim) {
182: sourceCoords
183: .add(new Point3d(srcCoords[i], srcCoords[i + 1],
184: (dim == 3) ? srcCoords[i + 2] : 0));
185: }
186: List<Point3d> result = doTransform(sourceCoords);
187: if (startPositionDest < 0) {
188: startPositionDest = 0;
189: }
190: final int requiredSpace = result.size() * getTargetDimension();
191: if (destCoords == null) {
192: startPositionDest = 0;
193: destCoords = new double[requiredSpace];
194: }
195: final int requiredSize = startPositionDest + requiredSpace;
196: if (requiredSize > destCoords.length) {
197: double[] tmp = new double[requiredSize];
198: System.arraycopy(destCoords, 0, tmp, 0, startPositionDest);
199: destCoords = tmp;
200: }
201: final int dimDest = getTargetDimension();
202: if (dimDest > 3 || dimDest < 2) {
203: throw new TransformationException(Messages.getMessage(
204: "CRS_TRANSFORM_WRONG_CRS_DIM", "target"));
205: }
206: int arrayPos = startPositionDest;
207: for (Point3d coord : result) {
208: destCoords[arrayPos++] = coord.x;
209: destCoords[arrayPos++] = coord.y;
210: if (dimDest == 3) {
211: destCoords[arrayPos++] = coord.z;
212: }
213: }
214:
215: }
216:
217: /**
218: * Transforms a single point3d (by calling the doTransform( List<Point3d>).
219: *
220: * @param coordinate
221: * to transform, if <code>null</code> null will be returned.
222: * @return the transformed coordinate.
223: * @throws TransformationException if the coordinate could not be transformed from the sourceCRS to the targetCRS.
224: */
225: public Point3d doTransform(Point3d coordinate)
226: throws TransformationException {
227: if (coordinate == null) {
228: return null;
229: }
230: List<Point3d> coord = new ArrayList<Point3d>(1);
231: coord.add(coordinate);
232: return doTransform(coord).get(0);
233: }
234:
235: /**
236: * @return true if this transformation doesn't transform the incoming points. (e.g. is the id. matrix)
237: */
238: public abstract boolean isIdentity();
239:
240: /**
241: * @return the name of the transformation.
242: */
243: public abstract String getName();
244:
245: /**
246: * @return true if the doInverseTransform method should be called, false otherwise.
247: */
248: public boolean isInverseTransform() {
249: return isInverse;
250: }
251:
252: /**
253: * This method flags the transformation about it's state. If this transformation was inverse calling this method
254: * will result in a forward transformation and vice versa.
255: */
256: public void inverse() {
257: isInverse = !isInverse;
258: }
259:
260: /**
261: * @return a representation of this transformations name, including the 'Forward' or 'Inverse' modifier.
262: */
263: public String getTransformationName() {
264: StringBuilder result = new StringBuilder(isInverse ? "Inverse "
265: : "Forward ");
266: result.append(getName());
267: return result.toString();
268: }
269:
270: /**
271: * @return the sourceCRS.
272: */
273: public final CoordinateSystem getSourceCRS() {
274: return isInverse ? targetCRS : sourceCRS;
275: }
276:
277: /**
278: * @return the targetCRS.
279: */
280: public final CoordinateSystem getTargetCRS() {
281: return isInverse ? sourceCRS : targetCRS;
282: }
283:
284: /**
285: * @return the dimension of the source coordinateSystem.
286: */
287: public int getSourceDimension() {
288: return getSourceCRS().getDimension();
289: }
290:
291: /**
292: * @return the dimension of the target coordinateSystem.
293: */
294: public int getTargetDimension() {
295: return getTargetCRS().getDimension();
296: }
297:
298: /**
299: * Checks if this transformation is the inverse of the other transformation, which means, this.sourceCRS equals
300: * other.targetCRS && this.targetCRS == other.sourceCRS. If Both transformations are identity this method also
301: * returns true.
302: *
303: * @param other
304: * the transformation to check
305: * @return true if this and the other transformation are eachothers inverse.
306: */
307: public boolean areInverse(CRSTransformation other) {
308: return (other == null) ? false
309: : (this .isIdentity() && other.isIdentity())
310: || ((this .getSourceCRS().equals(
311: other.getTargetCRS()) && this
312: .getTargetCRS().equals(
313: other.getSourceCRS())));
314: }
315:
316: /**
317: * @param sb
318: * to add the transformation chain to, if <code>null</code> a new StringBuilder will be created.
319: * @return the given StringBuilder (or a new instance) with the appended transformation steps.
320: */
321: public final StringBuilder getTransformationPath(StringBuilder sb) {
322: if (sb == null) {
323: sb = new StringBuilder();
324: }
325: outputTransform(0, sb, this );
326: return sb;
327: }
328:
329: private int outputTransform(int level, StringBuilder sb,
330: CRSTransformation t) {
331: if (t instanceof ConcatenatedTransform) {
332: level = outputTransform(level, sb,
333: ((ConcatenatedTransform) t).getFirstTransform());
334: level = outputTransform(level, sb,
335: ((ConcatenatedTransform) t).getSecondTransform());
336: } else {
337: if (level != 0) {
338: sb.append("->");
339: }
340: sb.append("(").append(level).append(")").append(
341: t.getTransformationName());
342: return ++level;
343: }
344: return level;
345: }
346: }
|