001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2002, 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: import javax.units.Unit;
023:
024: // OpenGIS dependencies
025: import org.opengis.parameter.ParameterDescriptor;
026: import org.opengis.parameter.ParameterDescriptorGroup;
027: import org.opengis.parameter.ParameterNotFoundException;
028: import org.opengis.parameter.ParameterValue;
029: import org.opengis.parameter.ParameterValueGroup;
030: import org.opengis.referencing.operation.Conversion;
031: import org.opengis.referencing.operation.MathTransform;
032: import org.opengis.referencing.operation.MathTransform1D;
033:
034: // Geotools dependencies
035: import org.geotools.metadata.iso.citation.Citations;
036: import org.geotools.parameter.DefaultParameterDescriptor;
037: import org.geotools.parameter.FloatParameter;
038: import org.geotools.referencing.NamedIdentifier;
039: import org.geotools.referencing.operation.LinearTransform;
040: import org.geotools.referencing.operation.MathTransformProvider;
041: import org.geotools.resources.i18n.VocabularyKeys;
042: import org.geotools.resources.i18n.Vocabulary;
043:
044: /**
045: * A one dimensional exponentional transform.
046: * Input values <var>x</var> are converted into
047: * output values <var>y</var> using the following equation:
048: *
049: * <p align="center"><var>y</var> =
050: * {@linkplain #scale}×{@linkplain #base}<sup><var>x</var></sup></p>
051: *
052: * This equation may be written in other form:
053: *
054: * <p align="center">{@linkplain #base}<sup><var>a</var> + <var>b</var>×<var>x</var></sup> =
055: * {@linkplain #base}<sup><var>a</var></sup>×({@linkplain #base}<sup><var>b</var></sup>)<sup><var>x</var></sup></p>
056: *
057: * @since 2.0
058: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/ExponentialTransform1D.java $
059: * @version $Id: ExponentialTransform1D.java 24384 2007-02-14 00:23:05Z desruisseaux $
060: * @author Martin Desruisseaux
061: *
062: * @see LogarithmicTransform1D
063: * @see LinearTransform1D
064: */
065: public class ExponentialTransform1D extends AbstractMathTransform
066: implements MathTransform1D, Serializable {
067: /**
068: * Serial number for interoperability with different versions.
069: */
070: private static final long serialVersionUID = 5331178990358868947L;
071:
072: /**
073: * The base to be raised to a power.
074: */
075: public final double base;
076:
077: /**
078: * Natural logarithm of {@link #base}.
079: */
080: final double lnBase;
081:
082: /**
083: * The scale value to be multiplied.
084: */
085: public final double scale;
086:
087: /**
088: * The inverse of this transform. Created only when first needed.
089: * Serialized in order to avoid rounding error if this transform
090: * is actually the one which was created from the inverse.
091: */
092: private MathTransform inverse;
093:
094: /**
095: * Constructs a new exponentional transform which is the
096: * inverse of the supplied logarithmic transform.
097: */
098: ExponentialTransform1D(final LogarithmicTransform1D inverse) {
099: this .base = inverse.base;
100: this .lnBase = inverse.lnBase;
101: this .scale = Math.pow(base, -inverse.offset);
102: this .inverse = inverse;
103: }
104:
105: /**
106: * Constructs a new exponentional transform. This constructor is provided for subclasses only.
107: * Instances should be created using the {@linkplain #create factory method}, which
108: * may returns optimized implementations for some particular argument values.
109: *
110: * @param base The base to be raised to a power.
111: * @param scale The scale value to be multiplied.
112: */
113: protected ExponentialTransform1D(final double base,
114: final double scale) {
115: this .base = base;
116: this .scale = scale;
117: this .lnBase = Math.log(base);
118: }
119:
120: /**
121: * Constructs a new exponentional transform.
122: *
123: * @param base The base to be raised to a power.
124: * @param scale The scale value to be multiplied.
125: */
126: public static MathTransform1D create(final double base,
127: final double scale) {
128: if (base == 0 || scale == 0) {
129: return LinearTransform1D.create(0, 0);
130: }
131: if (base == 1) {
132: return LinearTransform1D.create(0, scale);
133: }
134: return new ExponentialTransform1D(base, scale);
135: }
136:
137: /**
138: * Returns the parameter descriptors for this math transform.
139: */
140: public ParameterDescriptorGroup getParameterDescriptors() {
141: return Provider.PARAMETERS;
142: }
143:
144: /**
145: * Returns the parameter values for this math transform.
146: *
147: * @return A copy of the parameter values for this math transform.
148: */
149: public ParameterValueGroup getParameterValues() {
150: return new org.geotools.parameter.ParameterGroup(
151: getParameterDescriptors(), new ParameterValue[] {
152: new FloatParameter(Provider.BASE, base),
153: new FloatParameter(Provider.SCALE, scale) });
154: }
155:
156: /**
157: * Gets the dimension of input points, which is 1.
158: */
159: public int getSourceDimensions() {
160: return 1;
161: }
162:
163: /**
164: * Gets the dimension of output points, which is 1.
165: */
166: public int getTargetDimensions() {
167: return 1;
168: }
169:
170: /**
171: * Creates the inverse transform of this object.
172: */
173: public MathTransform inverse() {
174: if (inverse == null) {
175: inverse = new LogarithmicTransform1D(this );
176: }
177: return inverse;
178: }
179:
180: /**
181: * Gets the derivative of this function at a value.
182: */
183: public double derivative(final double value) {
184: return lnBase * transform(value);
185: }
186:
187: /**
188: * Transforms the specified value.
189: */
190: public double transform(final double value) {
191: return scale * Math.pow(base, value);
192: }
193:
194: /**
195: * Transforms a list of coordinate point ordinal values.
196: */
197: public void transform(float[] srcPts, int srcOff, float[] dstPts,
198: int dstOff, int numPts) {
199: if (srcPts != dstPts || srcOff >= dstOff) {
200: while (--numPts >= 0) {
201: dstPts[dstOff++] = (float) (scale * Math.pow(base,
202: srcPts[srcOff++]));
203: }
204: } else {
205: srcOff += numPts;
206: dstOff += numPts;
207: while (--numPts >= 0) {
208: dstPts[--dstOff] = (float) (scale * Math.pow(base,
209: srcPts[--srcOff]));
210: }
211: }
212: }
213:
214: /**
215: * Transforms a list of coordinate point ordinal values.
216: */
217: public void transform(final double[] srcPts, int srcOff,
218: final double[] dstPts, int dstOff, int numPts) {
219: if (srcPts != dstPts || srcOff >= dstOff) {
220: while (--numPts >= 0) {
221: dstPts[dstOff++] = scale
222: * Math.pow(base, srcPts[srcOff++]);
223: }
224: } else {
225: srcOff += numPts;
226: dstOff += numPts;
227: while (--numPts >= 0) {
228: dstPts[--dstOff] = scale
229: * Math.pow(base, srcPts[--srcOff]);
230: }
231: }
232: }
233:
234: /**
235: * Concatenates in an optimized way a {@link MathTransform} {@code other} to this
236: * {@code MathTransform}. This implementation can optimize some concatenation with
237: * {@link LinearTransform1D} and {@link LogarithmicTransform1D}.
238: *
239: * @param other The math transform to apply.
240: * @param applyOtherFirst {@code true} if the transformation order is {@code other}
241: * followed by {@code this}, or {@code false} if the transformation order is
242: * {@code this} followed by {@code other}.
243: * @return The combined math transform, or {@code null} if no optimized combined
244: * transform is available.
245: */
246: MathTransform concatenate(final MathTransform other,
247: final boolean applyOtherFirst) {
248: if (other instanceof LinearTransform) {
249: final LinearTransform1D linear = (LinearTransform1D) other;
250: if (applyOtherFirst) {
251: final double newBase = Math.pow(base, linear.scale);
252: final double newScale = Math.pow(base, linear.offset)
253: * scale;
254: if (!Double.isNaN(newBase) && !Double.isNaN(newScale)) {
255: return create(newBase, newScale);
256: }
257: } else {
258: if (linear.offset == 0) {
259: return create(base, scale * linear.scale);
260: }
261: }
262: } else if (other instanceof LogarithmicTransform1D) {
263: return concatenateLog((LogarithmicTransform1D) other,
264: applyOtherFirst);
265: }
266: return super .concatenate(other, applyOtherFirst);
267: }
268:
269: /**
270: * Concatenates in an optimized way a {@link LogarithmicTransform1D} {@code other} to this
271: * {@code ExponentialTransform1D}.
272: *
273: * @param other The math transform to apply.
274: * @param applyOtherFirst {@code true} if the transformation order is {@code other}
275: * followed by {@code this}, or {@code false} if the transformation order is
276: * {@code this} followed by {@code other}.
277: * @return The combined math transform, or {@code null} if no optimized combined
278: * transform is available.
279: */
280: MathTransform concatenateLog(final LogarithmicTransform1D other,
281: final boolean applyOtherFirst) {
282: if (applyOtherFirst) {
283: final double newScale = scale
284: * Math.pow(base, other.offset);
285: final double newPower = lnBase / other.lnBase;
286: if (!Double.isNaN(newScale)) {
287: if (newPower == 1) {
288: return LinearTransform1D.create(newScale, 0);
289: }
290: // TODO: Needs a transform here with the following equation:
291: //
292: // y(x) = newScale * Math.pow(x, newPower);
293: }
294: } else if (scale > 0) {
295: return LinearTransform1D.create(lnBase / other.lnBase, Math
296: .log(scale)
297: / other.lnBase + other.offset);
298: }
299: return null;
300: }
301:
302: /**
303: * Returns a hash value for this transform.
304: * This value need not remain consistent between
305: * different implementations of the same class.
306: */
307: public int hashCode() {
308: long code;
309: code = serialVersionUID + Double.doubleToLongBits(base);
310: code = code * 37 + Double.doubleToLongBits(scale);
311: return (int) (code >>> 32) ^ (int) code;
312: }
313:
314: /**
315: * Compares the specified object with this math transform for equality.
316: */
317: public boolean equals(final Object object) {
318: if (object == this ) {
319: // Slight optimization
320: return true;
321: }
322: if (super .equals(object)) {
323: final ExponentialTransform1D that = (ExponentialTransform1D) object;
324: return Double.doubleToLongBits(this .base) == Double
325: .doubleToLongBits(that.base)
326: && Double.doubleToLongBits(this .scale) == Double
327: .doubleToLongBits(that.scale);
328: }
329: return false;
330: }
331:
332: /**
333: * The provider for the {@link ExponentialTransform1D}.
334: *
335: * @version $Id: ExponentialTransform1D.java 24384 2007-02-14 00:23:05Z desruisseaux $
336: * @author Martin Desruisseaux
337: */
338: public static class Provider extends MathTransformProvider {
339: /**
340: * Serial number for interoperability with different versions.
341: */
342: private static final long serialVersionUID = -5838840021166379987L;
343:
344: /**
345: * The operation parameter descriptor for the {@link #base base} parameter value.
346: * Valid values range from 0 to infinity. The default value is 10.
347: */
348: public static final ParameterDescriptor BASE = LogarithmicTransform1D.Provider.BASE;
349:
350: /**
351: * The operation parameter descriptor for the {@link #scale scale} parameter value.
352: * Valid values range is unrestricted. The default value is 1.
353: */
354: public static final ParameterDescriptor SCALE = new DefaultParameterDescriptor(
355: "scale", 1, Double.NEGATIVE_INFINITY,
356: Double.POSITIVE_INFINITY, Unit.ONE);
357:
358: /**
359: * The parameters group.
360: */
361: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
362: new NamedIdentifier[] { new NamedIdentifier(
363: Citations.GEOTOOLS,
364: Vocabulary
365: .formatInternational(VocabularyKeys.EXPONENTIAL)) },
366: new ParameterDescriptor[] { BASE, SCALE });
367:
368: /**
369: * Create a provider for logarithmic transforms.
370: */
371: public Provider() {
372: super (1, 1, PARAMETERS);
373: }
374:
375: /**
376: * Returns the operation type.
377: */
378: public Class getOperationType() {
379: return Conversion.class;
380: }
381:
382: /**
383: * Creates a logarithmic transform from the specified group of parameter values.
384: *
385: * @param values The group of parameter values.
386: * @return The created math transform.
387: * @throws ParameterNotFoundException if a required parameter was not found.
388: */
389: protected MathTransform createMathTransform(
390: final ParameterValueGroup values)
391: throws ParameterNotFoundException {
392: return create(doubleValue(BASE, values), doubleValue(SCALE,
393: values));
394: }
395: }
396: }
|