001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/ct/ConcatenedTransform.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001 by:
006 EXSE, Department of Geography, University of Bonn
007 http://www.giub.uni-bonn.de/exse/
008 lat/lon GmbH
009 http://www.lat-lon.de
010
011 It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
012 (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
013 SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite
014 Institut de Recherche pour le D�veloppement / US-Espace
015 mailto:seasnet@teledetection.fr
016
017
018 This library is free software; you can redistribute it and/or
019 modify it under the terms of the GNU Lesser General Public
020 License as published by the Free Software Foundation; either
021 version 2.1 of the License, or (at your option) any later version.
022
023 This library is distributed in the hope that it will be useful,
024 but WITHOUT ANY WARRANTY; without even the implied warranty of
025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
026 Lesser General Public License for more details.
027
028 You should have received a copy of the GNU Lesser General Public
029 License along with this library; if not, write to the Free Software
030 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
031
032 Contact:
033
034 Andreas Poth
035 lat/lon GmbH
036 Aennchenstr. 19
037 53115 Bonn
038 Germany
039 E-Mail: poth@lat-lon.de
040
041 Klaus Greve
042 Department of Geography
043 University of Bonn
044 Meckenheimer Allee 166
045 53115 Bonn
046 Germany
047 E-Mail: klaus.greve@uni-bonn.de
048
049
050 ---------------------------------------------------------------------------*/
051 package org.deegree.model.csct.ct;
052
053 // OpenGIS dependencies (SEAGIS)
054 import java.io.Serializable;
055
056 import org.deegree.model.csct.pt.CoordinatePoint;
057 import org.deegree.model.csct.pt.Matrix;
058 import org.deegree.model.csct.resources.Utilities;
059 import org.deegree.model.csct.resources.css.ResourceKeys;
060 import org.deegree.model.csct.resources.css.Resources;
061
062
063 /**
064 * Base class for concatened transform. Concatened transforms are
065 * serializable if all their step transforms are serializables.
066 *
067 * @version 1.0
068 * @author Martin Desruisseaux
069 */
070 class ConcatenedTransform extends AbstractMathTransform implements Serializable
071 {
072 /**
073 * Serial number for interoperability with different versions.
074 */
075 private static final long serialVersionUID = 5772066656987558634L;
076
077 /**
078 * The math transform factory that created this concatened transform.
079 * Will be used for creating the inverse transform when needed.
080 */
081 private MathTransformFactory provider;
082
083 /**
084 * The first math transform.
085 */
086 protected final MathTransform transform1;
087
088 /**
089 * The second math transform.
090 */
091 protected final MathTransform transform2;
092
093 /**
094 * The inverse transform. This field
095 * will be computed only when needed.
096 */
097 private transient MathTransform inverse;
098
099 /**
100 * Construct a concatenated transform.
101 */
102 public ConcatenedTransform(final MathTransformFactory provider, final MathTransform transform1, final MathTransform transform2)
103 {
104 this.provider = provider;
105 this.transform1 = transform1;
106 this.transform2 = transform2;
107 if (!isValid())
108 {
109 throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_CANT_CONCATENATE_CS_$2,
110 getName(transform1), getName(transform2)));
111 }
112 }
113
114 /**
115 * Returns a name for the specified coordinate system.
116 */
117 private static final String getName(final MathTransform transform)
118 {
119 if (transform instanceof AbstractMathTransform)
120 {
121 String name = ((AbstractMathTransform) transform).getName();
122 if (name!=null && (name=name.trim()).length()!=0) return name;
123 }
124 return Utilities.getShortClassName(transform);
125 }
126
127 /**
128 * Check if transforms are compatibles. The default
129 * implementation check if transfert dimension match.
130 */
131 protected boolean isValid()
132 {return transform1.getDimTarget() == transform2.getDimSource();}
133
134 /**
135 * Gets the dimension of input points.
136 */
137 public final int getDimSource()
138 {return transform1.getDimSource();}
139
140 /**
141 * Gets the dimension of output points.
142 */
143 public final int getDimTarget()
144 {return transform2.getDimTarget();}
145
146 /**
147 * Transforms the specified <code>ptSrc</code> and stores the result in <code>ptDst</code>.
148 */
149 public CoordinatePoint transform(final CoordinatePoint ptSrc, CoordinatePoint ptDst) throws TransformException
150 {
151 // Note: If we know that the transfert dimension is the same than source
152 // and target dimension, then we don't need to use an intermediate
153 // point. This optimization is done in ConcatenedTransformDirect.
154 return transform2.transform(transform1.transform(ptSrc, null), ptDst);
155 }
156
157 /**
158 * Transforms a list of coordinate point ordinal values.
159 */
160 public void transform(final double[] srcPts, final int srcOff, final double[] dstPts,
161 final int dstOff, final int numPts) throws TransformException
162 {
163 // Note: If we know that the transfert dimension is the same than source
164 // and target dimension, then we don't need to use an intermediate
165 // buffer. This optimization is done in ConcatenedTransformDirect.
166 final double[] tmp = new double[numPts*transform1.getDimTarget()];
167 transform1.transform(srcPts, srcOff, tmp, 0, numPts);
168 transform2.transform(tmp, 0, dstPts, dstOff, numPts);
169 }
170
171 /**
172 * Transforms a list of coordinate point ordinal values.
173 */
174 public void transform(final float[] srcPts, final int srcOff, final float[] dstPts,
175 final int dstOff, final int numPts) throws TransformException
176 {
177 // Note: If we know that the transfert dimension is the same than source
178 // and target dimension, then we don't need to use an intermediate
179 // buffer. This optimization is done in ConcatenedTransformDirect.
180 final float[] tmp = new float[numPts*transform1.getDimTarget()];
181 transform1.transform(srcPts, srcOff, tmp, 0, numPts);
182 transform2.transform(tmp, 0, dstPts, dstOff, numPts);
183 }
184
185 /**
186 * Creates the inverse transform of this object.
187 */
188 public synchronized final MathTransform inverse() throws NoninvertibleTransformException
189 {
190 if (inverse==null)
191 {
192 if (provider == null) {
193 provider = MathTransformFactory.getDefault();
194 }
195 inverse = provider.createConcatenatedTransform(transform2.inverse(), transform1.inverse());
196 if (inverse instanceof ConcatenedTransform)
197 {
198 ((ConcatenedTransform) inverse).inverse = this;
199 }
200 }
201 return inverse;
202 }
203
204 /**
205 * Gets the derivative of this transform at a point.
206 *
207 * @param point The coordinate point where to evaluate the derivative.
208 * @return The derivative at the specified point (never <code>null</code>).
209 * @throws TransformException if the derivative can't be evaluated at the specified point.
210 */
211 public Matrix derivative(final CoordinatePoint point) throws TransformException
212 {
213 final Matrix matrix1 = transform1.derivative(point);
214 final Matrix matrix2 = transform2.derivative(transform1.transform(point, null));
215 // Compute "matrix = matrix2 * matrix1". Reuse an existing matrix object
216 // if possible, which is always the case when both matrix are square.
217 final int numRow = matrix2.getNumRow();
218 final int numCol = matrix1.getNumCol();
219 final Matrix matrix;
220 if (numCol == matrix2.getNumCol()) {
221 matrix = matrix2;
222 matrix2.mul(matrix1);
223 } else {
224 matrix = new Matrix(numRow, numCol);
225 matrix.mul(matrix2, matrix1);
226 }
227 return matrix;
228 }
229
230 /**
231 * Tests whether this transform does not move any points.
232 * Default implementation check if the two transforms are
233 * identity. This a way too conservative aproach, but it
234 * it doesn't hurt since ConcatenedTransform should not
235 * have been created if it were to result in an identity
236 * transform (this case should have been detected earlier).
237 */
238 public final boolean isIdentity()
239 {return transform1.isIdentity() && transform2.isIdentity();}
240
241 /**
242 * Returns a hash value for this transform.
243 */
244 public final int hashCode()
245 {return transform1.hashCode() + 37*transform2.hashCode();}
246
247 /**
248 * Compares the specified object with
249 * this math transform for equality.
250 */
251 public final boolean equals(final Object object)
252 {
253 if (object==this) return true; // Slight optimization
254 if (super.equals(object))
255 {
256 final ConcatenedTransform that = (ConcatenedTransform) object;
257 return Utilities.equals(this.transform1, that.transform1) &&
258 Utilities.equals(this.transform2, that.transform2);
259 }
260 return false;
261 }
262
263 /**
264 * Returns the WKT for this math transform.
265 */
266 public final String toString()
267 {
268 final StringBuffer buffer = new StringBuffer("CONCAT_MT[");
269 addWKT(buffer, this, true);
270 buffer.append(']');
271 return buffer.toString();
272 }
273
274 /**
275 * Append to a string buffer the WKT
276 * for the specified math transform.
277 */
278 private static void addWKT(final StringBuffer buffer, final MathTransform transform, final boolean first)
279 {
280 if (transform instanceof ConcatenedTransform)
281 {
282 final ConcatenedTransform concat = (ConcatenedTransform) transform;
283 addWKT(buffer, concat.transform1, first);
284 addWKT(buffer, concat.transform2, false);
285 }
286 else
287 {
288 if (!first)
289 buffer.append(", ");
290 buffer.append(transform);
291 }
292 }
293 }