/******************************************************************************* * Copyright 2006 - 2012 Vienna University of Technology, * Department of Software Technology and Interactive Systems, IFS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This work originates from the Planets project, co-funded by the European Union under the Sixth Framework Programme. ******************************************************************************/ package eu.scape_project.planning.model.transform; import java.util.List; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.Enumerated; import org.slf4j.LoggerFactory; import eu.scape_project.planning.model.values.INumericValue; import eu.scape_project.planning.model.values.IOrdinalValue; import eu.scape_project.planning.model.values.TargetValue; import eu.scape_project.planning.validation.ValidationError; /** * Transforms numeric values to the {@link TargetValue}. For Transformation * modes see {@link TransformationMode}. * * @author Christoph Becker & Stephan Strodl */ @Entity @DiscriminatorValue("N") public class NumericTransformer extends Transformer { private static final long serialVersionUID = 5425443938944214916L; private Double threshold1 = 0.0; private Double threshold2 = 0.0; private Double threshold3 = 0.0; private Double threshold4 = 0.0; private Double threshold5 = 0.0; @Enumerated private TransformationMode mode = TransformationMode.THRESHOLD_STEPPING; public void setMode(TransformationMode mode) { this.mode = mode; } public void defaults(double best, double worst) { threshold1 = worst; threshold5 = best; threshold2 = worst + ((best - worst) / 4); threshold3 = worst + ((best - worst) / 4) * 2; threshold4 = worst + ((best - worst) / 4) * 3; } /** * Transforms the provided value to a target value. According to the * {@link #mode transformation mode}, either * {@link #thresholdstepping(double)} or {@link #linear(double)} is used. * * * @param d * value to be transformed * @return transformed {@link TargetValue} */ private TargetValue doTransform(double d) { TargetValue v = new TargetValue(); switch (mode) { case THRESHOLD_STEPPING: v.setValue(thresholdstepping(d)); break; case LINEAR: v.setValue(linear(d)); break; default: LoggerFactory.getLogger(this.getClass()).error("TransformationMode is not set correctly."); } return v; } /** * Determines if the threshold-scale increases with measured values. (i.g: * the higher, the better) * * @return */ public boolean hasIncreasingOrder() { return (threshold1 < threshold5); } /** * Calculates a measured value which would result in the given TargetValue * FIXME: this should do some interpolation! * * @param t * @return */ public double transformBack(double targetValue) { if (hasIncreasingOrder()) { // increasing thresholdscale if (targetValue < 0.0) { // FIXME: this should not happen! meta: yes, thats gonna be a // classic return Double.NaN; } if (targetValue >= 4.0) { return threshold5; } else if (targetValue >= 3.0) { return threshold4; } else if (targetValue >= 2.0) { return threshold3; } else if (targetValue >= 1.0) { return (threshold2); } else if (targetValue >= 0.0) { return threshold1; } } else { if (targetValue < 0.0) { // FIXME: this should not happen! meta: yes, thats gonna be a // classic return Double.NaN; } if (targetValue <= 1.0) { return threshold1; } else if (targetValue <= 2.0) { return threshold2; } else if (targetValue <= 3.0) { return threshold3; } else if (targetValue <= 4.0) { return threshold4; } else if (targetValue <= 5.0) { return threshold5; } } return Double.NaN; // // // } else // decreasing thresholdscale // { // if (value > threshold1) { // return 0; // } else if (value > threshold2) { // return ((value - threshold1) // / (threshold2 - threshold1)) + 1; // } else if (value > threshold3) { // return ((value - threshold2) // / (threshold3 - threshold2)) + 2; // } else if (value > threshold4) { // return ((value - threshold3) // / (threshold4 - threshold3)) + 3; // } else if (value <= threshold5) { // return 5; // } else { // return ((value - threshold4) // / (threshold5 - threshold4)) + 4; // } // } } public TargetValue transform(INumericValue v) { return doTransform(v.value()); } /** * This operation is not supported. Always throws an * {@link UnsupportedOperationException} * * @throws UnsupportedOperationException * because this is the numeric transformer. */ public TargetValue transform(IOrdinalValue v) { throw new UnsupportedOperationException("Cannot transform ordinal values. only numeric ones!"); } /** * Depending on {@link #thresholds} a value between 0 and 5 is returned. * Transformation is performed without interpolation. * * @see #linear(double) * @param value * to be transformed * @return transformed value depending on the {@link #thresholds} */ private double thresholdstepping(double value) { if (hasIncreasingOrder()) // increasing thresholdscale { if (value < threshold1) { return 0; } else if (value < threshold2) { return 1; } else if (value < threshold3) { return 2; } else if (value < threshold4) { return 3; } else if (value < threshold5) { return 4; } else { return 5; } } else // decreasing thresholdscale { if (value > threshold1) { return 0; } else if (value > threshold2) { return 1; } else if (value > threshold3) { return 2; } else if (value > threshold4) { return 3; } else if (value > threshold5) { return 4; } else { return 5; } } } /** * In contrast to {@link #thresholdstepping(double) threshold stepping}, * linear transformation interpolates values between thresholds. * * @param value * to be transformed * @return value (linearly interopolated) depending on the * {@link #thresholds} */ private double linear(double value) { if (hasIncreasingOrder()) // increasing thresholdscale { if (value < threshold1) { return 0; } else if (value < threshold2) { return ((value - threshold1) // difference to previous threshold / (threshold2 - threshold1)) // difference between the surrounding thresholds + 1; // plus the fixed value that results from being over the // previous boundary } else if (value < threshold3) { return ((value - threshold2) / (threshold3 - threshold2)) + 2; } else if (value < threshold4) { return ((value - threshold3) / (threshold4 - threshold3)) + 3; } else if (value >= threshold5) { return 5; } else { return ((value - threshold4) / (threshold5 - threshold4)) + 4; } } else // decreasing thresholdscale { if (value > threshold1) { return 0; } else if (value > threshold2) { return ((value - threshold1) / (threshold2 - threshold1)) + 1; } else if (value > threshold3) { return ((value - threshold2) / (threshold3 - threshold2)) + 2; } else if (value > threshold4) { return ((value - threshold3) / (threshold4 - threshold3)) + 3; } else if (value <= threshold5) { return 5; } else { return ((value - threshold4) / (threshold5 - threshold4)) + 4; } } } public TransformationMode getMode() { return mode; } /** * checks if the order of thresholds is consistent * * @return true iff threshold are consistently ascending or consistently * descending or consistently identical (e.g. initialised to 0.0) */ public boolean checkOrder() { int signum = Integer.signum(threshold1.compareTo(threshold2)); return ((signum == Integer.signum(threshold2.compareTo(threshold3))) && (signum == Integer.signum(threshold3.compareTo(threshold4))) && (signum == Integer.signum(threshold4 .compareTo(threshold5)))); } /** * A NumericTransformer is correctly configured if the thresholds are * <b>strictly</b> either ascending or descending. * * @return true if thresholds are in proper order */ public boolean isTransformable(List<ValidationError> errors) { boolean toReturn = true; if (!checkOrder()) { errors.add(new ValidationError("The order of thresholds is not consistent.", this)); toReturn = false; } return toReturn; } @Override public Transformer clone() { NumericTransformer nt = new NumericTransformer(); nt.setId(0); nt.setMode(mode); nt.setThreshold1(threshold1); nt.setThreshold2(threshold2); nt.setThreshold3(threshold3); nt.setThreshold4(threshold4); nt.setThreshold5(threshold5); return nt; } public Double getThreshold1() { return threshold1; } public void setThreshold1(Double threshold1) { this.threshold1 = threshold1; } public Double getThreshold2() { return threshold2; } public void setThreshold2(Double threshold2) { this.threshold2 = threshold2; } public Double getThreshold3() { return threshold3; } public void setThreshold3(Double threshold3) { this.threshold3 = threshold3; } public Double getThreshold4() { return threshold4; } public void setThreshold4(Double threshold4) { this.threshold4 = threshold4; } public Double getThreshold5() { return threshold5; } public void setThreshold5(Double threshold5) { this.threshold5 = threshold5; } }