/*
* ContinuousScale.java
*
* Copyright (C) 2006-2014 Andrew Rambaut
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package figtree.treeviewer.decorators;
import jebl.util.Attributable;
import java.util.Set;
import java.util.TreeSet;
/**
* @author Andrew Rambaut
* @version $Id$
*
* $HeadURL$
*
* $LastChangedBy$
* $LastChangedDate$
* $LastChangedRevision$
*/
public class ContinuousScale {
/**
* constructor that sets options from a string
* @param settings
*/
public ContinuousScale(String settings) {
if (settings.startsWith("{")) {
settings = settings.substring(1, settings.length());
}
if (settings.endsWith("}")) {
settings = settings.substring(0, settings.length() - 1);
}
String[] parts = settings.split("[, ]+");
if (parts.length != 4) {
throw new IllegalArgumentException("ContinousScale settings string not in correct format");
}
try {
normalize = Boolean.parseBoolean(parts[0]);
logarithm = Boolean.parseBoolean(parts[1]);
lowerRange = Double.parseDouble(parts[2]);
upperRange = Double.parseDouble(parts[3]);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("ContinousScale settings string not in correct format");
}
}
public ContinuousScale() {
this(false, 0.0, 0.0, false);
}
public ContinuousScale(boolean normalize,
double lowerRange,
double upperRange,
boolean logarithm) {
this.normalize = normalize;
this.lowerRange = lowerRange;
this.upperRange = upperRange;
this.logarithm = logarithm;
}
public ContinuousScale(String attributeName, Set<? extends Attributable> items) {
this();
setAttributes(attributeName, items);
}
public void setAttributes(String attributeName, Set<? extends Attributable> items) {
this.attributeName = attributeName;
// First collect the set of all attribute values
Set<Object> values = new TreeSet<Object>();
boolean isNumber = true;
// Find the range of numbers
for (Attributable item : items) {
Object value = item.getAttribute(attributeName);
if (value != null) {
double realValue = -1.0;
if (value instanceof Boolean) {
realValue = ((Boolean)value ? 1 : 0);
} else if (value instanceof Number) {
realValue = ((Number)value).doubleValue();
} else if (value instanceof String) {
// it is a string but it could still code for
// a boolean, integer or real
if (value.toString().equalsIgnoreCase("true")) {
realValue = 1;
} else if (value.toString().equalsIgnoreCase("false")) {
realValue = 0;
} else {
try {
realValue = Double.parseDouble(value.toString());
} catch(NumberFormatException nfe) {
isNumber = false;
}
}
}
if (isNumber) {
if (realValue < minValue) {
minValue = realValue;
}
if (realValue > maxValue) {
maxValue = realValue;
}
}
values.add(realValue);
}
}
if (!isNumber) {
throw new NumberFormatException("One or more values for this attribute are not numbers");
}
if (normalize && minValue < 0 && maxValue > 0) {
// if normalizing, and some are negative, assume we are normalizing with 0 at 0.5
minValue = - maxValue;
}
if (logarithm) {
if (minValue <= 0.0) {
throw new NumberFormatException("One or more values for this attribute are negative or zero under a log scale");
}
minValue = Math.log(minValue);
maxValue = Math.log(maxValue);
}
}
public double getValue(Attributable item) {
return getValue(item.getAttribute(attributeName));
}
public double getValue(Object value) {
if (value != null) {
double number = 0.0;
if (value instanceof Number) {
number = ((Number)value).doubleValue();
} else {
number = Double.parseDouble(value.toString());
}
return scaleValue(number);
}
return Double.NaN;
}
/**
* Scales the value to a range 0,1 based on the current settings
* @param value
* @return
*/
public double scaleValue(double value) {
if (logarithm) {
value = Math.log(value);
}
double min = getMinValue();
double max = getMaxValue();
return ((value - min)/(max - min));
}
public String getAttributeName() {
return attributeName;
}
public boolean isNormalize() {
return normalize;
}
public void setNormalize(boolean normalize) {
this.normalize = normalize;
}
public boolean isLogarithm() {
return logarithm;
}
public void setLogarithm(boolean logarithm) {
this.logarithm = logarithm;
}
public double getLowerRange() {
return lowerRange;
}
public void setLowerRange(double lowerRange) {
this.lowerRange = lowerRange;
}
public double getUpperRange() {
return upperRange;
}
public void setUpperRange(double upperRange) {
this.upperRange = upperRange;
}
public double getMinValue() {
double min = minValue;
if (normalize && lowerRange < minValue) {
min = lowerRange;
}
return min;
}
public double getMaxValue() {
double max = maxValue;
if (normalize && upperRange > maxValue) {
max = upperRange;
}
return max;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(normalize);
sb.append(",");
sb.append(logarithm);
sb.append(",");
sb.append(lowerRange);
sb.append(",");
sb.append(upperRange);
sb.append("}");
return sb.toString();
}
private boolean normalize;
private boolean logarithm;
private double lowerRange;
private double upperRange;
private String attributeName = null;
private double minValue = Double.MAX_VALUE;
private double maxValue = Double.MIN_VALUE;
}