Java tutorial
/******************************************************************************* * Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany * * 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 3 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, see <http://www.gnu.org/licenses/>. * * Contributors: * Department Biological Safety - BfR *******************************************************************************/ package de.bund.bfr.knime.pmm.common.chart; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.math3.distribution.TDistribution; import org.lsmp.djep.djep.DJep; import org.nfunk.jep.Node; import org.nfunk.jep.ParseException; import de.bund.bfr.knime.pmm.common.math.MathUtilities; import de.bund.bfr.knime.pmm.common.pmmtablemodel.AttributeUtilities; import de.bund.bfr.knime.pmm.common.units.Categories; import de.bund.bfr.knime.pmm.common.units.Category; import de.bund.bfr.knime.pmm.common.units.ConvertException; public class Plotable { private static final int FUNCTION_STEPS = 1000; private static final double EPSILON = 1e-5; public static final int DATASET = 0; public static final int DATASET_STRICT = 1; public static final int FUNCTION = 2; public static final int BOTH = 3; public static final int BOTH_STRICT = 4; public static final int FUNCTION_SAMPLE = 5; private int type; private Map<String, List<Double>> valueLists; private String function; private String functionValue; private Double minValue; private Double maxValue; private Map<String, List<Double>> functionArguments; private Map<String, Double> functionParameters; private Map<String, Map<String, Double>> covariances; private Map<String, Double> minArguments; private Map<String, Double> maxArguments; private Map<String, List<String>> categories; private Map<String, String> units; private List<Double> samples; private Integer degreesOfFreedom; public Plotable(int type) { this.type = type; valueLists = new LinkedHashMap<>(); functionArguments = new LinkedHashMap<>(); minArguments = new LinkedHashMap<>(); maxArguments = new LinkedHashMap<>(); categories = new LinkedHashMap<>(); units = new LinkedHashMap<>(); functionParameters = new LinkedHashMap<>(); covariances = new LinkedHashMap<>(); samples = new ArrayList<>(); degreesOfFreedom = null; } public int getType() { return type; } public List<Double> getValueList(String parameter) { return valueLists.get(parameter); } public void addValueList(String parameter, List<Double> valueList) { valueLists.put(parameter, valueList); } public String getFunction() { return function; } public void setFunction(String function) { this.function = function; } public String getFunctionValue() { return functionValue; } public void setFunctionValue(String functionValue) { this.functionValue = functionValue; } public Double getMinValue() { return minValue; } public void setMinValue(Double minValue) { this.minValue = minValue; } public Double getMaxValue() { return maxValue; } public void setMaxValue(Double maxValue) { this.maxValue = maxValue; } public Map<String, List<Double>> getFunctionArguments() { return functionArguments; } public Map<String, List<Double>> getPossibleArgumentValues(boolean useData, boolean useMinMax) { Map<String, List<Double>> args = new LinkedHashMap<>(); for (String var : functionArguments.keySet()) { Double min = minArguments.get(var); Double max = maxArguments.get(var); if (useData && valueLists.get(var) != null) { Set<Double> valuesSet = new LinkedHashSet<>(valueLists.get(var)); List<Double> valuesList = new ArrayList<>(valuesSet); valuesList.removeAll(Arrays.asList((Object) null)); Collections.sort(valuesList); args.put(var, valuesList); } else if (useMinMax && min != null) { args.put(var, new ArrayList<>(Arrays.asList(min))); } else if (useMinMax && max != null) { args.put(var, new ArrayList<>(Arrays.asList(max))); } else { args.put(var, new ArrayList<Double>()); } } return args; } public void setFunctionArguments(Map<String, List<Double>> functionArguments) { this.functionArguments = functionArguments; } public Map<String, Double> getFunctionParameters() { return functionParameters; } public void setFunctionParameters(Map<String, Double> functionParameters) { this.functionParameters = functionParameters; } public Map<String, Map<String, Double>> getCovariances() { return covariances; } public void setCovariances(Map<String, Map<String, Double>> covariances) { this.covariances = covariances; } public Map<String, Double> getMinArguments() { return minArguments; } public void setMinArguments(Map<String, Double> minArguments) { this.minArguments = minArguments; } public Map<String, Double> getMaxArguments() { return maxArguments; } public void setMaxArguments(Map<String, Double> maxArguments) { this.maxArguments = maxArguments; } public Map<String, List<String>> getCategories() { return categories; } public void setCategories(Map<String, List<String>> categories) { this.categories = categories; } public Map<String, String> getUnits() { return units; } public void setUnits(Map<String, String> units) { this.units = units; } public List<Double> getSamples() { return samples; } public void setSamples(List<Double> samples) { this.samples = samples; } public Integer getDegreesOfFreedom() { return degreesOfFreedom; } public void setDegreesOfFreedom(Integer degreesOfFreedom) { this.degreesOfFreedom = degreesOfFreedom; } public double[][] getPoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY) throws ConvertException { return getPoints(paramX, paramY, unitX, unitY, transformX, transformY, getStandardChoice()); } public double[][] getPoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, Map<String, Integer> choice) throws ConvertException { List<Double> xList = valueLists.get(paramX); List<Double> yList = valueLists.get(paramY); if (xList == null || yList == null) { return null; } List<Boolean> usedPoints = new ArrayList<>(Collections.nCopies(xList.size(), true)); if (type == BOTH_STRICT || type == DATASET_STRICT) { for (String arg : functionArguments.keySet()) { if (!arg.equals(paramX) && valueLists.containsKey(arg)) { Double fixedValue = functionArguments.get(arg).get(choice.get(arg)); List<Double> values = valueLists.get(arg); for (int i = 0; i < values.size(); i++) { if (!fixedValue.equals(values.get(i))) { usedPoints.set(i, false); } } } } if (!usedPoints.contains(true)) { return null; } } List<Point2D.Double> points = new ArrayList<>(xList.size()); for (int i = 0; i < xList.size(); i++) { Double x = xList.get(i); Double y = yList.get(i); if (x != null) { x = convertToUnit(paramX, x, unitX); x = transform(x, transformX); } if (y != null) { y = convertToUnit(paramY, y, unitY); y = transform(y, transformY); } if (usedPoints.get(i) && isValidValue(x) && isValidValue(y)) { points.add(new Point2D.Double(x, y)); } } Collections.sort(points, new Comparator<Point2D.Double>() { @Override public int compare(Point2D.Double p1, Point2D.Double p2) { return Double.compare(p1.x, p2.x); } }); double[][] pointsArray = new double[2][points.size()]; for (int i = 0; i < points.size(); i++) { pointsArray[0][i] = points.get(i).x; pointsArray[1][i] = points.get(i).y; } return pointsArray; } public double[][] getFunctionPoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY) throws ConvertException { return getFunctionPoints(paramX, paramY, unitX, unitY, transformX, transformY, minX, maxX, minY, maxY, getStandardChoice()); } public double[][] getFunctionPoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, Map<String, Integer> choice) throws ConvertException { if (function == null) { return null; } if (!function.startsWith(paramY + "=") || !functionArguments.containsKey(paramX)) { return null; } double[][] points = new double[2][FUNCTION_STEPS]; DJep parser = MathUtilities.createParser(); Node f = null; for (String param : functionParameters.keySet()) { if (functionParameters.get(param) == null) { return null; } parser.addConstant(param, functionParameters.get(param)); } for (String param : functionArguments.keySet()) { if (!param.equals(paramX)) { parser.addConstant(param, functionArguments.get(param).get(choice.get(param))); } } parser.addVariable(paramX, 0.0); try { f = parser.parse(function.replace(paramY + "=", "")); } catch (ParseException e) { e.printStackTrace(); } for (int j = 0; j < FUNCTION_STEPS; j++) { double x = minX + (double) j / (double) (FUNCTION_STEPS - 1) * (maxX - minX); parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(x, transformX), unitX)); try { Object number = parser.evaluate(f); Double y; if (isInRange(number)) { y = (Double) number; y = convertToUnit(paramY, y, unitY); y = transform(y, transformY); if (y == null || y < minY || y > maxY || y.isInfinite()) { y = Double.NaN; } } else { y = Double.NaN; } points[0][j] = x; points[1][j] = y; } catch (ParseException e) { points[0][j] = x; points[1][j] = Double.NaN; } catch (ClassCastException e) { e.printStackTrace(); } } return points; } public double[][] getFunctionErrors(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY) throws ConvertException { return getFunctionErrors(paramX, paramY, unitX, unitY, transformX, transformY, minX, maxX, minY, maxY, getStandardChoice()); } public double[][] getFunctionErrors(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, Map<String, Integer> choice) throws ConvertException { if (function == null) { return null; } if (!function.startsWith(paramY + "=") || !functionArguments.containsKey(paramX)) { return null; } double[][] points = new double[2][FUNCTION_STEPS]; DJep parser = MathUtilities.createParser(); Node f = null; List<String> paramList = new ArrayList<>(covariances.keySet()); for (String param : functionParameters.keySet()) { if (functionParameters.get(param) == null) { return null; } parser.addConstant(param, functionParameters.get(param)); } if (paramList.isEmpty()) { return null; } for (String param : paramList) { if (covariances.get(param) == null) { return null; } for (String param2 : paramList) { if (covariances.get(param).get(param2) == null) { return null; } } } for (String param : functionArguments.keySet()) { if (!param.equals(paramX)) { parser.addConstant(param, functionArguments.get(param).get(choice.get(param))); } } Map<String, Node> derivatives = new LinkedHashMap<>(); parser.addVariable(paramX, 0.0); try { f = parser.parse(function.replace(paramY + "=", "")); for (String param : paramList) { derivatives.put(param, parser.differentiate(f, param)); } } catch (ParseException e) { e.printStackTrace(); } for (int n = 0; n < FUNCTION_STEPS; n++) { double x = minX + (double) n / (double) (FUNCTION_STEPS - 1) * (maxX - minX); parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(x, transformX), unitX)); try { Double y = 0.0; boolean failed = false; for (String param : paramList) { Object obj = parser.evaluate(derivatives.get(param)); if (!(obj instanceof Double)) { failed = true; break; } y += (Double) obj * (Double) obj * covariances.get(param).get(param); } for (int i = 0; i < paramList.size() - 1; i++) { for (int j = i + 1; j < paramList.size(); j++) { Object obj1 = parser.evaluate(derivatives.get(paramList.get(i))); Object obj2 = parser.evaluate(derivatives.get(paramList.get(j))); if (!(obj1 instanceof Double) || !(obj2 instanceof Double)) { failed = true; break; } double cov = covariances.get(paramList.get(i)).get(paramList.get(j)); y += 2.0 * (Double) obj1 * (Double) obj2 * cov; } } points[0][n] = x; if (!failed) { // 95% interval TDistribution dist = new TDistribution(degreesOfFreedom); y = Math.sqrt(y) * dist.inverseCumulativeProbability(1.0 - 0.05 / 2.0); y = convertToUnit(paramY, y, unitY); y = transform(y, transformY); if (y != null) { points[1][n] = y; } else { points[1][n] = Double.NaN; } } else { points[1][n] = Double.NaN; } } catch (ParseException e) { e.printStackTrace(); } catch (ClassCastException e) { e.printStackTrace(); } } return points; } public double[][] getFunctionSamplePoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, List<String> warnings) throws ConvertException { return getFunctionSamplePoints(paramX, paramY, unitX, unitY, transformX, transformY, minX, maxX, minY, maxY, getStandardChoice(), warnings); } public double[][] getFunctionSamplePoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, Map<String, Integer> choice, List<String> warnings) throws ConvertException { if (function == null || samples.isEmpty()) { return null; } if (!function.startsWith(paramY + "=") || !functionArguments.containsKey(paramX)) { return null; } DJep parser = MathUtilities.createParser(); for (String param : functionParameters.keySet()) { if (functionParameters.get(param) == null) { return null; } parser.addConstant(param, functionParameters.get(param)); } for (String param : functionArguments.keySet()) { if (!param.equals(paramX)) { parser.addConstant(param, functionArguments.get(param).get(choice.get(param))); } } parser.addVariable(paramX, 0.0); Node f = null; try { f = parser.parse(function.replace(paramY + "=", "")); } catch (ParseException e) { e.printStackTrace(); } double[][] points = new double[2][samples.size()]; boolean containsValidPoint = false; for (int i = 0; i < samples.size(); i++) { Double x = samples.get(i); if (x == null || x < minX || x > maxX) { points[0][i] = Double.NaN; points[1][i] = Double.NaN; continue; } parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(x, transformX), unitX)); try { Object number = parser.evaluate(f); Double y; if (isInRange(number)) { y = (Double) number; y = convertToUnit(paramY, y, unitY); y = transform(y, transformY); if (y == null || y < minY || y > maxY || y.isInfinite()) { y = Double.NaN; } else { containsValidPoint = true; } } else { y = Double.NaN; } points[0][i] = x; points[1][i] = y; } catch (ParseException e) { e.printStackTrace(); } catch (ClassCastException e) { e.printStackTrace(); } } if (!containsValidPoint) { return null; } if (warnings != null) { for (Double d : samples) { if (d != null) { Double min = minArguments.get(AttributeUtilities.TIME); Double max = maxArguments.get(AttributeUtilities.TIME); String unit = units.get(AttributeUtilities.TIME); min = Categories.getTimeCategory().convert(min, unit, unitX); max = Categories.getTimeCategory().convert(max, unit, unitX); if (min != null && d < min) { warnings.add("Time exceeds the validity range of the model: " + d + " " + unitX + " < min value " + min + " " + unitX); } else if (max != null && d > max) { warnings.add("Time exceeds the validity range of the model: " + d + " " + unitX + " > max value " + max + " " + unitX); } } } } return points; } public double[][] getFunctionSamplePointsErrors(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY) throws ConvertException { return getFunctionSamplePointsErrors(paramX, paramY, unitX, unitY, transformX, transformY, minX, maxX, minY, maxY, getStandardChoice()); } public double[][] getFunctionSamplePointsErrors(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, Map<String, Integer> choice) throws ConvertException { if (function == null) { return null; } if (!function.startsWith(paramY + "=") || !functionArguments.containsKey(paramX)) { return null; } double[][] points = new double[2][samples.size()]; boolean containsValidPoint = false; DJep parser = MathUtilities.createParser(); Node f = null; for (String param : functionParameters.keySet()) { if (functionParameters.get(param) == null || covariances.get(param) == null) { return null; } for (String param2 : functionParameters.keySet()) { if (covariances.get(param).get(param2) == null) { return null; } } parser.addConstant(param, functionParameters.get(param)); } for (String param : functionArguments.keySet()) { if (!param.equals(paramX)) { parser.addConstant(param, functionArguments.get(param).get(choice.get(param))); } } Map<String, Node> derivatives = new LinkedHashMap<>(); parser.addVariable(paramX, 0.0); try { f = parser.parse(function.replace(paramY + "=", "")); for (String param : functionParameters.keySet()) { derivatives.put(param, parser.differentiate(f, param)); } } catch (ParseException e) { e.printStackTrace(); } for (int n = 0; n < samples.size(); n++) { Double x = samples.get(n); if (x == null || x < minX || x > maxX) { points[0][n] = Double.NaN; points[1][n] = Double.NaN; continue; } parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(x, transformX), unitX)); try { Double y = 0.0; boolean failed = false; List<String> paramList = new ArrayList<>(functionParameters.keySet()); for (String param : paramList) { Object obj = parser.evaluate(derivatives.get(param)); if (!(obj instanceof Double)) { failed = true; break; } y += (Double) obj * (Double) obj * covariances.get(param).get(param); } for (int i = 0; i < paramList.size() - 1; i++) { for (int j = i + 1; j < paramList.size(); j++) { Object obj1 = parser.evaluate(derivatives.get(paramList.get(i))); Object obj2 = parser.evaluate(derivatives.get(paramList.get(j))); if (!(obj1 instanceof Double) || !(obj2 instanceof Double)) { failed = true; break; } double cov = covariances.get(paramList.get(i)).get(paramList.get(j)); y += 2.0 * (Double) obj1 * (Double) obj2 * cov; } } points[0][n] = x; if (!failed) { // 95% interval TDistribution dist = new TDistribution(degreesOfFreedom); y = Math.sqrt(y) * dist.inverseCumulativeProbability(1.0 - 0.05 / 2.0); y = convertToUnit(paramY, y, unitY); y = transform(y, transformY); if (y != null) { points[1][n] = y; containsValidPoint = true; } else { points[1][n] = Double.NaN; } } else { points[1][n] = Double.NaN; } } catch (ParseException e) { e.printStackTrace(); } catch (ClassCastException e) { e.printStackTrace(); } } if (!containsValidPoint) { return null; } return points; } public double[][] getInverseFunctionSamplePoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, List<String> warnings) throws ConvertException { return getInverseFunctionSamplePoints(paramX, paramY, unitX, unitY, transformX, transformY, minX, maxX, minY, maxY, getStandardChoice(), warnings); } public double[][] getInverseFunctionSamplePoints(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, double minX, double maxX, double minY, double maxY, Map<String, Integer> choice, List<String> warnings) throws ConvertException { if (function == null || samples.isEmpty()) { return null; } if (!function.startsWith(paramY + "=") || !functionArguments.containsKey(paramX)) { return null; } DJep parser = MathUtilities.createParser(); for (String param : functionParameters.keySet()) { if (functionParameters.get(param) == null) { return null; } parser.addConstant(param, functionParameters.get(param)); } for (String param : functionArguments.keySet()) { if (!param.equals(paramX)) { parser.addConstant(param, functionArguments.get(param).get(choice.get(param))); } } parser.addVariable(paramX, 0.0); Node f = null; try { f = parser.parse(function.replace(paramY + "=", "")); } catch (ParseException e) { e.printStackTrace(); } double[][] points = new double[2][samples.size()]; boolean containsValidPoint = false; for (int i = 0; i < samples.size(); i++) { Double y = samples.get(i); Double x = getValueX(paramX, paramY, unitX, unitY, transformX, transformY, y, minX, maxX, null, null, parser, f); if (MathUtilities.isValid(x) && MathUtilities.isValid(y)) { containsValidPoint = true; } points[0][i] = MathUtilities.isValid(x) ? x : Double.NaN; points[1][i] = MathUtilities.isValid(y) ? y : Double.NaN; } if (!containsValidPoint) { return null; } return points; } public boolean isPlotable() { if (type == FUNCTION || type == FUNCTION_SAMPLE) { for (String param : functionParameters.keySet()) { if (functionParameters.get(param) == null) { return false; } } return true; } else { List<String> paramsX = new ArrayList<>(valueLists.keySet()); List<String> paramsY = new ArrayList<>(); if (type == DATASET || type == DATASET_STRICT) { paramsY = paramsX; } else if (type == BOTH || type == BOTH_STRICT) { if (functionValue != null) { paramsY = Arrays.asList(functionValue); } } for (String paramX : paramsX) { for (String paramY : paramsY) { if (isPlotable(paramX, paramY)) { return true; } } } return false; } } public List<Map<String, Integer>> getAllChoices() { List<Map<String, Integer>> choices = new ArrayList<>(); List<String> argList = new ArrayList<>(functionArguments.keySet()); List<Integer> choice = new ArrayList<>(Collections.nCopies(argList.size(), 0)); boolean done = false; while (!done) { Map<String, Integer> map = new LinkedHashMap<>(); for (int i = 0; i < argList.size(); i++) { map.put(argList.get(i), choice.get(i)); } choices.add(map); for (int i = 0;; i++) { if (i >= argList.size()) { done = true; break; } choice.set(i, choice.get(i) + 1); if (choice.get(i) >= functionArguments.get(argList.get(i)).size()) { choice.set(i, 0); } else { break; } } } return choices; } public int getNumberOfCombinations() { int nMax = 0; if (functionArguments.size() == 1) { String arg = new ArrayList<>(functionArguments.keySet()).get(0); if (valueLists.containsKey(arg) && valueLists.get(arg).size() != 0) { return 1; } } for (String arg0 : functionArguments.keySet()) { int n = 1; boolean valueFound = false; for (String arg : functionArguments.keySet()) { if (!arg.equals(arg0) && valueLists.containsKey(arg)) { Set<Double> set = new LinkedHashSet<>(valueLists.get(arg)); n *= set.size(); valueFound = true; } } if (valueFound) { nMax = Math.max(nMax, n); } } return nMax; } public Double convertToUnit(String param, Double value, String unit) throws ConvertException { String currentUnit = units.get(param); Category category = Categories.getCategoryByUnit(currentUnit); return category.convert(value, currentUnit, unit); } public Double convertFromUnit(String param, Double value, String unit) throws ConvertException { String newUnit = units.get(param); Category category = Categories.getCategoryByUnit(newUnit); return category.convert(value, unit, newUnit); } private Map<String, Integer> getStandardChoice() { if (functionArguments == null) { return null; } Map<String, Integer> choice = new LinkedHashMap<>(); for (String arg : functionArguments.keySet()) { choice.put(arg, 0); } return choice; } private boolean isPlotable(String paramX, String paramY) { boolean dataSetPlotable = false; boolean functionPlotable = false; List<Double> xs = valueLists.get(paramX); List<Double> ys = valueLists.get(paramY); if (xs != null && ys != null) { for (int i = 0; i < xs.size(); i++) { if (isValidValue(xs.get(i)) && isValidValue(ys.get(i))) { dataSetPlotable = true; break; } } } if (function != null && functionValue.equals(paramY) && functionArguments.containsKey(paramX)) { boolean notValid = false; for (Double value : functionParameters.values()) { if (!isValidValue(value)) { notValid = true; break; } } if (!notValid) { functionPlotable = true; } } if (type == DATASET) { return dataSetPlotable; } else if (type == FUNCTION || type == FUNCTION_SAMPLE) { return functionPlotable; } else if (type == BOTH || type == BOTH_STRICT) { return dataSetPlotable && functionPlotable; } return false; } private boolean isValidValue(Double value) { return value != null && !value.isNaN() && !value.isInfinite(); } private Double getValueX(String paramX, String paramY, String unitX, String unitY, String transformX, String transformY, Double y, double minX, double maxX, Double minY, Double maxY, DJep parser, Node f) { if (y == null) { return null; } if (minY == null) { parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(minX, transformX), unitX)); try { minY = transform(convertToUnit(paramY, (Double) parser.evaluate(f), unitY), transformY); } catch (ParseException e) { } catch (ClassCastException e) { } } if (maxY == null) { parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(maxX, transformX), unitX)); try { maxY = transform(convertToUnit(paramY, (Double) parser.evaluate(f), unitY), transformY); } catch (ParseException e) { } catch (ClassCastException e) { } } if (!MathUtilities.isValid(minY) || !MathUtilities.isValid(maxY)) { return null; } if (Math.abs(minX - maxX) < EPSILON) { return minX; } double midX = (minX + maxX) / 2; Double midY = null; parser.setVarValue(paramX, convertFromUnit(paramX, inverseTransform(midX, transformX), unitX)); try { midY = transform(convertToUnit(paramY, (Double) parser.evaluate(f), unitY), transformY); } catch (ParseException e) { } catch (ClassCastException e) { } if ((minY <= y && midY >= y) || (minY >= y && midY <= y)) { return getValueX(paramX, paramY, unitX, unitY, transformX, transformY, y, minX, midX, minY, midY, parser, f); } if ((midY <= y && maxY >= y) || (midY >= y && maxY <= y)) { return getValueX(paramX, paramY, unitX, unitY, transformX, transformY, y, midX, maxX, midY, maxY, parser, f); } return null; } private boolean isInRange(Object number) { if (!MathUtilities.isValid(number)) { return false; } double value = (Double) number; if (minValue != null && value < minValue) { return false; } if (maxValue != null && value > maxValue) { return false; } return true; } public static Double transform(Double value, String transform) { if (value == null) { return null; } else if (transform.equals(ChartConstants.NO_TRANSFORM)) { return value; } else if (transform.equals(ChartConstants.SQRT_TRANSFORM)) { return Math.sqrt(value); } else if (transform.equals(ChartConstants.LOG_TRANSFORM)) { return Math.log(value); } else if (transform.equals(ChartConstants.LOG10_TRANSFORM)) { return Math.log10(value); } else if (transform.equals(ChartConstants.EXP_TRANSFORM)) { return Math.exp(value); } else if (transform.equals(ChartConstants.EXP10_TRANSFORM)) { return Math.pow(10.0, value); } else if (transform.equals(ChartConstants.DIVX_TRANSFORM)) { return 1 / value; } else if (transform.equals(ChartConstants.DIVX2_TRANSFORM)) { return 1 / value / value; } return null; } public static Double inverseTransform(Double value, String transform) { if (value == null) { return null; } else if (transform.equals(ChartConstants.NO_TRANSFORM)) { return value; } else if (transform.equals(ChartConstants.SQRT_TRANSFORM)) { return value * value; } else if (transform.equals(ChartConstants.LOG_TRANSFORM)) { return Math.exp(value); } else if (transform.equals(ChartConstants.LOG10_TRANSFORM)) { return Math.pow(10.0, value); } else if (transform.equals(ChartConstants.EXP_TRANSFORM)) { return Math.log(value); } else if (transform.equals(ChartConstants.EXP10_TRANSFORM)) { return Math.log10(value); } else if (transform.equals(ChartConstants.DIVX_TRANSFORM)) { return 1 / value; } else if (transform.equals(ChartConstants.DIVX2_TRANSFORM)) { return 1 / Math.sqrt(value); } return null; } }