/* * Geotools 2 - OpenSource mapping toolkit * (C) 2003, Geotools Project Managment Committee (PMC) * (C) 2002, Institut de Recherche pour le Développement * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.geotools.ct; // J2SE dependencies import java.io.Serializable; import javax.media.jai.ParameterList; import javax.media.jai.util.Range; import org.geotools.resources.i18n.VocabularyKeys; /** * A one dimensional power transform. Input values are converted into output values * using the following equation: * *

y = {@link #scale}*{@link #base}x

* * Reminder: This equation may be written in other form: * *

{@link #base}a + b*x = * {@link #base}a*({@link #base}b)x

* * @source $URL$ * @version $Id$ * @author Martin Desruisseaux * * @see LogarithmicTransform1D * @see LinearTransform1D * * @deprecated Replaced by {@link org.geotools.referencing.operation.transform.ExponentialTransform1D}. */ final class ExponentialTransform1D extends AbstractMathTransform implements MathTransform1D, Serializable { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -6793025722241238043L; /** * The base to be raised to power. */ public final double base; /** * Natural logarithm of {@link #base}. */ final double lnBase; /** * The scale value to be multiplied. */ public final double scale; /** * The inverse of this transform. Created only when first needed. */ private transient MathTransform inverse; /** * Construct a new logarithmic transform which is the * inverse of the supplied power transform. */ ExponentialTransform1D(final LogarithmicTransform1D inverse) { this.base = inverse.base; this.lnBase = inverse.lnBase; this.scale = Math.pow(base, -inverse.offset); this.inverse = inverse; } /** * Construct a new power transform. The transformation equation is * * y = scale * base^x * * @param base The base to be raised to a power. * @param scale The scale value to be multiplied. */ protected ExponentialTransform1D(final double base, final double scale) { this.base = base; this.scale = scale; this.lnBase = Math.log(base); } /** * Construct a new power transform. The transformation equation is * * y = scale * base^x * * @param base The base to be raised to a power. * @param scale The scale value to be multiplied. */ public static MathTransform1D create(final double base, final double scale) { if (base==0 || scale==0) { return LinearTransform1D.create(0, 0); } if (base==1) { return LinearTransform1D.create(0, scale); } return new ExponentialTransform1D(base, scale); } /** * Gets the dimension of input points, which is 1. */ public int getDimSource() { return 1; } /** * Gets the dimension of output points, which is 1. */ public int getDimTarget() { return 1; } /** * Creates the inverse transform of this object. */ public MathTransform inverse() { if (inverse == null) { inverse = new LogarithmicTransform1D(this); } return inverse; } /** * Gets the derivative of this function at a value. */ public double derivative(final double value) { return lnBase * transform(value); } /** * Transforms the specified value. */ public double transform(final double value) { return scale * Math.pow(base, value); } /** * Transforms a list of coordinate point ordinal values. */ public void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) { if (srcPts!=dstPts || srcOff>=dstOff) { while (--numPts >= 0) { dstPts[dstOff++] = (float) (scale*Math.pow(base, srcPts[srcOff++])); } } else { srcOff += numPts; dstOff += numPts; while (--numPts >= 0) { dstPts[--dstOff] = (float) (scale*Math.pow(base, srcPts[--srcOff])); } } } /** * Transforms a list of coordinate point ordinal values. */ public void transform(final double[] srcPts, int srcOff, final double[] dstPts, int dstOff, int numPts) { if (srcPts!=dstPts || srcOff>=dstOff) { while (--numPts >= 0) { dstPts[dstOff++] = scale*Math.pow(base, srcPts[srcOff++]); } } else { srcOff += numPts; dstOff += numPts; while (--numPts >= 0) { dstPts[--dstOff] = scale*Math.pow(base, srcPts[--srcOff]); } } } /** * Concatenates in an optimized way a {@link MathTransform} other to this * MathTransform. This implementation can optimize some concatenation with * {@link LinearTransform1D} and {@link LogarithmicTransform1D}. * * @param other The math transform to apply. * @param applyOtherFirst true if the transformation order is other * followed by this, or false if the transformation order is * this followed by other. * @return The combined math transform, or null if no optimized combined * transform is available. */ MathTransform concatenate(final MathTransform other, final boolean applyOtherFirst) { if (other instanceof LinearTransform) { final LinearTransform1D linear = (LinearTransform1D) other; if (applyOtherFirst) { final double newBase = Math.pow(base, linear.scale); final double newScale = Math.pow(base, linear.offset)*scale; if (!Double.isNaN(newBase) && !Double.isNaN(newScale)) { return create(newBase, newScale); } } else { if (linear.offset == 0) { return create(base, scale*linear.scale); } } } else if (other instanceof LogarithmicTransform1D) { return concatenateLog((LogarithmicTransform1D) other, applyOtherFirst); } return super.concatenate(other, applyOtherFirst); } /** * Concatenates in an optimized way a {@link LogarithmicTransform1D} other to this * ExponentialTransform1D. * * @param other The math transform to apply. * @param applyOtherFirst true if the transformation order is other * followed by this, or false if the transformation order is * this followed by other. * @return The combined math transform, or null if no optimized combined * transform is available. */ MathTransform concatenateLog(final LogarithmicTransform1D other, final boolean applyOtherFirst) { if (applyOtherFirst) { final double newScale = scale*Math.pow(base, other.offset); final double newPower = lnBase/other.lnBase; if (!Double.isNaN(newScale)) { if (newPower == 1) { return LinearTransform1D.create(newScale, 0); } // TODO: Needs a transform here with the following equation: // // y(x) = newScale * Math.pow(x, newPower); } } else if (scale > 0) { return LinearTransform1D.create(lnBase/other.lnBase, Math.log(scale)/other.lnBase + other.offset); } return null; } /** * Returns a hash value for this transform. * This value need not remain consistent between * different implementations of the same class. */ public int hashCode() { long code; code = 16427632 + Double.doubleToLongBits(base); code = code*37 + Double.doubleToLongBits(scale); return (int)(code >>> 32) ^ (int)code; } /** * Compares the specified object with * this math transform for equality. */ public boolean equals(final Object object) { if (object==this) { // Slight optimization return true; } if (super.equals(object)) { final ExponentialTransform1D that = (ExponentialTransform1D) object; return Double.doubleToLongBits(this.base) == Double.doubleToLongBits(that.base) && Double.doubleToLongBits(this.scale) == Double.doubleToLongBits(that.scale); } return false; } /** * Returns the WKT for this math transform. */ public String toString() { final StringBuffer buffer = paramMT("Exponential"); addParameter(buffer, "base", base); if (scale != 1) { // TODO: The following is NOT a parameter. For WKT formatting, we should decompose this // LogarithmicTransform1D into a ConcatenatedTransform using a AffineTransform instead. addParameter(buffer, "scale", scale); } buffer.append(']'); return buffer.toString(); } /** * The provider for {@link ExponentialTransform1D} and {@link LogarithmicTransform1D}. * * @version $Id$ * @author Martin Desruisseaux */ static final class Provider extends MathTransformProvider { /** * The range of allowed value for the "Dimension" parameter. * Current implementation support only one-dimensional transform. */ private static final Range DIMENSION_RANGE; static { final Integer ONE = new Integer(1); DIMENSION_RANGE = new Range(Integer.class, ONE, true, ONE, true); } /** * false to create a provider for {@link ExponentialTransform1D}, or * true to create a provider for {@link LogarithmicTransform1D}. */ private final boolean logarithm; /** * Create a provider for power or logarithmic transforms. * * @param logarithm false to create a provider for * {@link ExponentialTransform1D}, or true to create * a provider for {@link LogarithmicTransform1D}. */ public Provider(final boolean logarithm) { super(logarithm ? "Logarithmic" : "Exponential", logarithm ? VocabularyKeys.LOGARITHMIC : VocabularyKeys.EXPONENTIAL, null); this.logarithm = logarithm; put("base", 10, POSITIVE_RANGE); putInt("dimension", 1, DIMENSION_RANGE); } /** * Returns a transform for the specified parameters. * * @param parameters The parameter values. * @return A {@link MathTransform} object of this classification. */ public MathTransform create(final ParameterList parameters) { final double base = parameters.getDoubleParameter("base"); final int dimension = parameters.getIntParameter("dimension"); if (dimension == 1) { if (logarithm) { return new LogarithmicTransform1D(base, 0); } else { return ExponentialTransform1D.create(base, 1); } } // TODO: make it more general. throw new UnsupportedOperationException("Only 1D transforms are currently supported."); } } }