/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.referencing.piecewise;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.referencing.operation.transform.LinearTransform1D;
import org.geotools.renderer.i18n.ErrorKeys;
import org.geotools.renderer.i18n.Errors;
import org.geotools.util.NumberRange;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
import org.opengis.referencing.operation.MathTransform1D;
import org.opengis.referencing.operation.NoninvertibleTransformException;
/**
* Convenience class for linear transformations that maps an interval to another interval.
* @author Simone Giannecchini, GeoSolutions
*
*
* @source $URL$
*/
public class DefaultLinearPiecewiseTransform1DElement extends DefaultPiecewiseTransform1DElement implements
PiecewiseTransform1DElement {
/**
*
*/
private static final long serialVersionUID = 4026834241134908025L;
private final static Logger LOGGER = Logging
.getLogger("org.geotools.referencing.piecewise.DefaultLinearPiecewiseTransform1DElement");
/**
* @uml.property name="outputMaximum"
*/
private double outputMaximum;
/**
* @uml.property name="outputMinimum"
*/
private double outputMinimum;
/**
* @uml.property name="outputRange"
*/
private NumberRange<? extends Number> outputRange;
/**
* @uml.property name="outputMinimumNaN"
*/
private boolean outputMinimumNaN;
/**
* @uml.property name="outputMaximumNaN"
*/
private boolean outputMaximumNaN;
/**
* @uml.property name="outputMinimumInfinite"
*/
private boolean outputMinimumInfinite;
/**
* @uml.property name="outputMaximumInfinite"
*/
private boolean outputMaximumInfinite;
/**
* Constructor.
*
* @param name
* for this {@link DefaultLinearPiecewiseTransform1DElement}.
* @param inRange
* for this {@link DefaultLinearPiecewiseTransform1DElement}.
* @param outRange
* for this {@link DefaultLinearPiecewiseTransform1DElement}.
*/
public DefaultLinearPiecewiseTransform1DElement(CharSequence name, NumberRange<? extends Number> inRange,
NumberRange<? extends Number> outRange) {
super(name, inRange);
this.outputRange = outRange;
// /////////////////////////////////////////////////////////////////////
//
// Checks
//
// /////////////////////////////////////////////////////////////////////
// //
//
// the output class can only be integer
//
// //
final Class<? extends Number> type = outRange.getElementClass();
boolean minInc = outRange.isMinIncluded();
boolean maxInc = outRange.isMaxIncluded();
outputMinimum = PiecewiseUtilities.doubleValue(type, outRange
.getMinValue(), minInc ? 0 : +1);
outputMaximum = PiecewiseUtilities.doubleValue(type, outRange
.getMaxValue(), maxInc ? 0 : -1);
outputMinimumNaN = Double.isNaN(outputMinimum);
outputMaximumNaN = Double.isNaN(outputMaximum);
outputMinimumInfinite = Double.isInfinite(outputMinimum);
outputMaximumInfinite = Double.isInfinite(outputMaximum);
// //
//
// No open intervals for the output range
//
// //
if (outputMinimumInfinite || outputMinimumInfinite) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.BAD_RANGE_$2, outputRange.getMinValue(),
outputRange.getMaxValue()));
}
final int compareOutBounds = PiecewiseUtilities.compare(outputMinimum,
outputMaximum);
// //
//
// the output values are correctly ordered
//
// //
if (compareOutBounds > 0) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.BAD_RANGE_$2, outputRange.getMinValue(),
outputRange.getMaxValue()));
}
// //
//
// mapping NaN to a single value
//
// //
if (isInputMaximumNaN() && isInputMinimumNaN())
if (compareOutBounds == 0) {
setTransform(LinearTransform1D.create(0, outputMinimum));
setInverse(LinearTransform1D.create(outputMinimum, 0));
return;
} else
throw new IllegalArgumentException(Errors.format(
ErrorKeys.BAD_RANGE_$2, outputRange.getMinValue(),
outputRange.getMaxValue()));
// //
//
// Mapping an open interval to a single value, there is no way to map an
// open interval to another interval!
//
// //
if (isInputMaximumInfinite() || isInputMinimumInfinite())
if (compareOutBounds == 0) {
setTransform(PiecewiseUtilities.createLinearTransform1D(0,
outputMinimum));
setInverse(null);
return;
} else
throw new IllegalArgumentException(Errors.format(
ErrorKeys.BAD_RANGE_$2, outputRange.getMinValue(),
outputRange.getMaxValue()));
final MathTransform1D transform = PiecewiseUtilities.createLinearTransform1D(inRange,
NumberRange.create(outputMinimum, outputMaximum));
setTransform(transform);
// //
//
// Checking the created transformation
//
// //
assert transform instanceof LinearTransform1D;
assert !Double.isNaN(((LinearTransform1D) transform).scale)
&& !Double
.isInfinite(((LinearTransform1D) transform).scale);
// //
//
// Inverse
//
// //
LinearTransform1D tempTransform = (LinearTransform1D) transform;
final double scale = tempTransform.scale;
if (Math.abs(scale) < 1E-6)
if (PiecewiseUtilities.compare(getInputMaximum(), getInputMinimum()) == 0)
setInverse(LinearTransform1D.create(0, getInputMinimum()));
else
setInverse(null);
else
try {
setInverse((MathTransform1D) transform.inverse());
} catch (NoninvertibleTransformException e) {
if (LOGGER.isLoggable(Level.WARNING))
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
}
}
/**
* Returns the maximum output values for this {@link DefaultLinearPiecewiseTransform1DElement} ;
* @return the maximum output values for this {@link DefaultLinearPiecewiseTransform1DElement} ;
* @uml.property name="outputMaximum"
*/
public double getOutputMaximum() {
return outputMaximum;
}
/**
* Returns the minimum output values for this {@link DefaultLinearPiecewiseTransform1DElement} ;
* @return the minimum output values for this {@link DefaultLinearPiecewiseTransform1DElement} ;
* @uml.property name="outputMinimum"
*/
public double getOutputMinimum() {
return outputMinimum;
}
/**
* Returns the range for the output values for this {@link DefaultLinearPiecewiseTransform1DElement} ;
* @return the range for the output values for this {@link DefaultLinearPiecewiseTransform1DElement} ;
* @uml.property name="outputRange"
*/
public NumberRange<? extends Number> getOutputRange() {
return outputRange;
}
/**
* Tells me if the lower boundary of the output range is NaN
* @return <code>true</code> if the lower boundary of the output range is NaN, <code>false</code> otherwise.
* @uml.property name="outputMinimumNaN"
*/
public boolean isOutputMinimumNaN() {
return outputMinimumNaN;
}
/**
* Tells me if the upper boundary of the output range is NaN
* @return <code>true</code> if the upper boundary of the output range is NaN, <code>false</code> otherwise.
* @uml.property name="outputMaximumNaN"
*/
public boolean isOutputMaximumNaN() {
return outputMaximumNaN;
}
/**
* Tells me if the lower boundary of the output range is infinite
* @return <code>true</code> if the lower boundary of the output range is infinite, <code>false</code> otherwise.
* @uml.property name="outputMinimumInfinite"
*/
public boolean isOutputMinimumInfinite() {
return outputMinimumInfinite;
}
/**
* Tells me if the upper boundary of the output range is infinite
* @return <code>true</code> if the upper boundary of the output range is infinite, <code>false</code> otherwise.
* @uml.property name="outputMaximumInfinite"
*/
public boolean isOutputMaximumInfinite() {
return outputMaximumInfinite;
}
/**
* Retrieves the scale factor for this linear {@link PiecewiseTransform1DElement}.
*
* @return the scale factor for this linear {@link PiecewiseTransform1DElement}.
*/
public double getScale(){
//get the transform at this point it is linear for sure
final LinearTransform1D transform= (LinearTransform1D) getTransform();
return transform.scale;
}
/**
* Retrieves the offset factor for this linear {@link PiecewiseTransform1DElement}.
*
* @return the offset factor for this linear {@link PiecewiseTransform1DElement}.
*/
public double getOffset(){
//get the transform at this point it is linear for sure
final LinearTransform1D transform= (LinearTransform1D) getTransform();
return transform.offset;
}
/*
* (non-Javadoc)
* @see org.geotools.referencing.piecewise.DefaultPiecewiseTransform1DElement#toString()
*/
public String toString() {
final StringBuilder buffer= new StringBuilder(super.toString());
buffer.append("\n").append("output range=").append(this.outputRange);
return buffer.toString();
}
protected Class<?> getEquivalenceClass(){
return DefaultLinearPiecewiseTransform1DElement.class;
}
@Override
public boolean equals(Object obj) {
if(this==obj)
return true;
if(!(obj instanceof DefaultLinearPiecewiseTransform1DElement))
return false;
final DefaultLinearPiecewiseTransform1DElement that= (DefaultLinearPiecewiseTransform1DElement) obj;
if(that.getEquivalenceClass()!=this.getEquivalenceClass())
return false;
if(!Utilities.equals(outputRange, that.outputRange))
return false;
if(!Utilities.equals(outputMaximum, that.outputMaximum))
return false;
if(!Utilities.equals(outputMinimum, that.outputMinimum))
return false;
return super.equals(obj);
}
@Override
public int hashCode() {
int hashCode=37;
hashCode=Utilities.hash(outputRange,hashCode);
hashCode=Utilities.hash( outputMaximum,hashCode);
hashCode=Utilities.hash( outputMinimum,hashCode);
hashCode=Utilities.hash( super.hashCode(),hashCode);
return hashCode;
}
}