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 }