001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2001, Institut de Recherche pour le Développement
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: */
018: package org.geotools.referencing.operation.transform;
019:
020: // J2SE dependencies
021: import java.io.Serializable;
022:
023: // OpenGIS dependencies
024: import org.opengis.parameter.ParameterDescriptorGroup;
025: import org.opengis.parameter.ParameterValueGroup;
026: import org.opengis.referencing.operation.MathTransform;
027: import org.opengis.referencing.operation.MathTransform1D;
028: import org.opengis.referencing.operation.Matrix;
029: import org.opengis.referencing.operation.NoninvertibleTransformException;
030: import org.opengis.referencing.operation.TransformException;
031: import org.opengis.geometry.DirectPosition;
032:
033: // Geotools dependencies
034: import org.geotools.referencing.operation.matrix.Matrix1;
035: import org.geotools.referencing.operation.matrix.Matrix2;
036: import org.geotools.referencing.operation.LinearTransform;
037:
038: /**
039: * A one dimensional, linear transform. Input values <var>x</var> are converted into
040: * output values <var>y</var> using the following equation:
041: *
042: * <p align="center"><var>y</var> =
043: * {@linkplain #offset} + {@linkplain #scale}×<var>x</var></p>
044: *
045: * This class is the same as a 2×2 affine transform. However, this specialized
046: * {@code LinearTransform1D} class is faster. It is defined there because extensively
047: * used by {@link org.geotools.coverage.grid.GridCoverage2D}.
048: *
049: * @since 2.0
050: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/LinearTransform1D.java $
051: * @version $Id: LinearTransform1D.java 25972 2007-06-21 13:38:35Z desruisseaux $
052: * @author Martin Desruisseaux
053: *
054: * @see LogarithmicTransform1D
055: * @see ExponentialTransform1D
056: */
057: public class LinearTransform1D extends AbstractMathTransform implements
058: MathTransform1D, LinearTransform, Serializable {
059: /**
060: * Serial number for interoperability with different versions.
061: */
062: private static final long serialVersionUID = -7595037195668813000L;
063:
064: /**
065: * The identity transform.
066: */
067: public static final LinearTransform1D IDENTITY = IdentityTransform1D.ONE;
068:
069: /**
070: * The value which is multiplied to input values.
071: */
072: public final double scale;
073:
074: /**
075: * The value to add to input values.
076: */
077: public final double offset;
078:
079: /**
080: * The inverse of this transform. Created only when first needed.
081: */
082: private transient MathTransform inverse;
083:
084: /**
085: * Constructs a new linear transform. This constructor is provided for subclasses only.
086: * Instances should be created using the {@linkplain #create factory method}, which
087: * may returns optimized implementations for some particular argument values.
088: *
089: * @param scale The {@code scale} term in the linear equation.
090: * @param offset The {@code offset} term in the linear equation.
091: */
092: protected LinearTransform1D(final double scale, final double offset) {
093: this .scale = scale;
094: this .offset = offset;
095: }
096:
097: /**
098: * Constructs a new linear transform.
099: *
100: * @param scale The {@code scale} term in the linear equation.
101: * @param offset The {@code offset} term in the linear equation.
102: */
103: public static LinearTransform1D create(final double scale,
104: final double offset) {
105: if (scale == 0) {
106: return new ConstantTransform1D(offset);
107: }
108: if (scale == 1 && offset == 0) {
109: return IDENTITY;
110: }
111: return new LinearTransform1D(scale, offset);
112: }
113:
114: /**
115: * Returns the parameter descriptors for this math transform.
116: */
117: public ParameterDescriptorGroup getParameterDescriptors() {
118: return ProjectiveTransform.ProviderAffine.PARAMETERS;
119: }
120:
121: /**
122: * Returns the matrix elements as a group of parameters values. The number of parameters
123: * depends on the matrix size. Only matrix elements different from their default value
124: * will be included in this group.
125: *
126: * @return A copy of the parameter values for this math transform.
127: */
128: public ParameterValueGroup getParameterValues() {
129: return ProjectiveTransform.getParameterValues(getMatrix());
130: }
131:
132: /**
133: * Gets the dimension of input points, which is 1.
134: */
135: public int getSourceDimensions() {
136: return 1;
137: }
138:
139: /**
140: * Gets the dimension of output points, which is 1.
141: */
142: public int getTargetDimensions() {
143: return 1;
144: }
145:
146: /**
147: * Returns this transform as an affine transform matrix.
148: */
149: public Matrix getMatrix() {
150: return new Matrix2(scale, offset, 0, 1);
151: }
152:
153: /**
154: * Creates the inverse transform of this object.
155: */
156: public MathTransform inverse()
157: throws NoninvertibleTransformException {
158: if (inverse == null) {
159: if (isIdentity()) {
160: inverse = this ;
161: } else if (scale != 0) {
162: final LinearTransform1D inverse;
163: inverse = create(1 / scale, -offset / scale);
164: inverse.inverse = this ;
165: this .inverse = inverse;
166: } else {
167: inverse = super .inverse();
168: }
169: }
170: return inverse;
171: }
172:
173: /**
174: * Tests whether this transform does not move any points.
175: */
176: public boolean isIdentity() {
177: return isIdentity(0);
178: }
179:
180: /**
181: * Tests whether this transform does not move any points by using the provided tolerance.
182: * This method work in the same way than
183: * {@link org.geotools.referencing.operation.matrix.XMatrix#isIdentity(double)}.
184: *
185: * @since 2.3.1
186: */
187: public boolean isIdentity(double tolerance) {
188: tolerance = Math.abs(tolerance);
189: return Math.abs(offset) <= tolerance
190: && Math.abs(scale - 1) <= tolerance;
191: }
192:
193: /**
194: * Gets the derivative of this transform at a point. This implementation is different
195: * from the default {@link AbstractMathTransform#derivative} implementation in that no
196: * coordinate point is required and {@link Double#NaN} may be a legal output value for
197: * some users.
198: */
199: public Matrix derivative(final DirectPosition point)
200: throws TransformException {
201: return new Matrix1(scale);
202: }
203:
204: /**
205: * Gets the derivative of this function at a value.
206: */
207: public double derivative(final double value) {
208: return scale;
209: }
210:
211: /**
212: * Transforms the specified value.
213: */
214: public double transform(double value) {
215: return offset + scale * value;
216: }
217:
218: /**
219: * Transforms a list of coordinate point ordinal values.
220: */
221: public void transform(final float[] srcPts, int srcOff,
222: final float[] dstPts, int dstOff, int numPts) {
223: if (srcPts != dstPts || srcOff >= dstOff) {
224: while (--numPts >= 0) {
225: dstPts[dstOff++] = (float) (offset + scale
226: * srcPts[srcOff++]);
227: }
228: } else {
229: srcOff += numPts;
230: dstOff += numPts;
231: while (--numPts >= 0) {
232: dstPts[--dstOff] = (float) (offset + scale
233: * srcPts[--srcOff]);
234: }
235: }
236: }
237:
238: /**
239: * Transforms a list of coordinate point ordinal values.
240: */
241: public void transform(final double[] srcPts, int srcOff,
242: final double[] dstPts, int dstOff, int numPts) {
243: if (srcPts != dstPts || srcOff >= dstOff) {
244: while (--numPts >= 0) {
245: dstPts[dstOff++] = offset + scale * srcPts[srcOff++];
246: }
247: } else {
248: srcOff += numPts;
249: dstOff += numPts;
250: while (--numPts >= 0) {
251: dstPts[--dstOff] = offset + scale * srcPts[--srcOff];
252: }
253: }
254: }
255:
256: /**
257: * Returns a hash value for this transform.
258: * This value need not remain consistent between
259: * different implementations of the same class.
260: */
261: public int hashCode() {
262: long code;
263: code = (int) serialVersionUID
264: + Double.doubleToRawLongBits(offset);
265: code = code * 37 + Double.doubleToRawLongBits(scale);
266: return (int) (code >>> 32) ^ (int) code;
267: }
268:
269: /**
270: * Compares the specified object with this math transform for equality.
271: */
272: public boolean equals(final Object object) {
273: if (object == this ) {
274: // Slight optimization
275: return true;
276: }
277: if (super .equals(object)) {
278: final LinearTransform1D that = (LinearTransform1D) object;
279: return Double.doubleToRawLongBits(this .scale) == Double
280: .doubleToRawLongBits(that.scale)
281: && Double.doubleToRawLongBits(this .offset) == Double
282: .doubleToRawLongBits(that.offset);
283: /*
284: * NOTE: 'LinearTransform1D' and 'ConstantTransform1D' are heavily used by 'Category'
285: * from 'org.geotools.cv' package. It is essential for Cateory to differenciate
286: * various NaN values. Because 'equals' is used by CanonicalSet.unique(Object)
287: * (which is used by 'DefaultMathTransformFactory'), test for equality can't use
288: * the doubleToLongBits method because it collapse all NaN into a single canonical
289: * value. The 'doubleToRawLongBits' instead provided the needed functionality.
290: */
291: }
292: return false;
293: }
294: }
|