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