/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.new_plotter.data; import com.rapidminer.gui.new_plotter.PlotConfigurationError; import com.rapidminer.gui.new_plotter.StaticDebug; import com.rapidminer.gui.new_plotter.configuration.DataTableColumn.ValueType; import com.rapidminer.gui.new_plotter.configuration.PlotConfiguration; import com.rapidminer.gui.new_plotter.configuration.RangeAxisConfig; import com.rapidminer.gui.new_plotter.configuration.SeriesFormat.StackingMode; import com.rapidminer.gui.new_plotter.configuration.SeriesFormat.VisualizationType; import com.rapidminer.gui.new_plotter.configuration.ValueSource; import com.rapidminer.gui.new_plotter.listener.events.RangeAxisConfigChangeEvent; import com.rapidminer.gui.new_plotter.utility.DataStructureUtils; import com.rapidminer.gui.new_plotter.utility.NumericalValueRange; import com.rapidminer.tools.container.Pair; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * * @author Marius Helf, Nils Woehler * */ public class RangeAxisData { private RangeAxisConfig rangeAxisConfig; private final PlotInstance plotInstance; private RangeAxisConfigChangeEvent lastProcessedEvent = null; private List<Pair<ValueSource, Double>> cachedDistinctSourcesAndValues = null; private double cachedMaxYValue = Double.NaN; private double cachedMinYValue = Double.NaN; private NumericalValueRange cachedAutoViewRange = null; public RangeAxisData(RangeAxisConfig rangeAxisConfig, PlotInstance plotInstance) { super(); this.rangeAxisConfig = rangeAxisConfig; this.plotInstance = plotInstance; } private void updateValueCache() { cachedDistinctSourcesAndValues = new LinkedList<Pair<ValueSource, Double>>(); Set<Double> distinctValues = new HashSet<Double>(); cachedMinYValue = Double.POSITIVE_INFINITY; cachedMaxYValue = Double.NEGATIVE_INFINITY; List<ValueSource> valueSources = new LinkedList<ValueSource>(); valueSources.addAll(rangeAxisConfig.getValueSources()); for (ValueSource valueSource : valueSources) { ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource); Pair<Double, Double> minMax = valueSourceData.getMinAndMaxValue(); double minValue = minMax.getFirst(); double maxValue = minMax.getSecond(); if (valueSource.getSeriesFormat().getSeriesType() == VisualizationType.AREA || valueSource.getSeriesFormat().getSeriesType() == VisualizationType.BARS) { if (valueSource.getSeriesFormat().getStackingMode() == StackingMode.RELATIVE) { minValue = 0; maxValue = 1; } } if (minValue < cachedMinYValue) { cachedMinYValue = minValue; } if (maxValue > cachedMaxYValue) { cachedMaxYValue = maxValue; } for (Double value : valueSourceData.getDistinctValues()) { if (!distinctValues.contains(value)) { distinctValues.add(value); cachedDistinctSourcesAndValues.add(new Pair<ValueSource, Double>(valueSource, value)); } } } cachedAutoViewRange = new NumericalValueRange(cachedMinYValue, cachedMaxYValue, -1); } /** * Returns a list of all values on this RangeAxisConfig, together with the ValueSource which * generates the respective value. If a value appears in more than one ValueSource, only the * value from the first ValueSource is added to the list. */ public List<Pair<ValueSource, Double>> getDistinctValues() { if (cachedDistinctSourcesAndValues == null) { updateValueCache(); } return cachedDistinctSourcesAndValues; } /** * This function is used receiving the viewing range of this range axis only. * * @return lower viewing bound */ public double getLowerViewBound() { if (rangeAxisConfig.isUsingUserDefinedLowerViewBound()) { return rangeAxisConfig.getUserDefinedRange().getLowerBound(); } else { if (cachedAutoViewRange == null) { updateValueCache(); } return cachedAutoViewRange.getLowerBound(); } } /** * This function is used receiving the viewing range of this range axis only. * * @return upper viewing bound */ public double getUpperViewBound() { if (rangeAxisConfig.isUsingUserDefinedUpperViewBound()) { return rangeAxisConfig.getUserDefinedRange().getUpperBound(); } else { if (cachedAutoViewRange == null) { updateValueCache(); } return cachedAutoViewRange.getUpperBound(); } } private void invalidateCache() { cachedDistinctSourcesAndValues = null; cachedMaxYValue = Double.NaN; cachedMinYValue = Double.NaN; invalidateViewRange(); } private void invalidateViewRange() { cachedAutoViewRange = null; } public double getMaxYValue() { if (Double.isNaN(cachedMaxYValue)) { updateValueCache(); } return cachedMaxYValue; } public double getMinYValue() { if (Double.isNaN(cachedMinYValue)) { updateValueCache(); } return cachedMinYValue; } public void rangeAxisConfigChanged(RangeAxisConfigChangeEvent e) { if (e == null || e == lastProcessedEvent) { return; } lastProcessedEvent = e; // update rangeAxisConfig to the one of the current plot configuration clone with the // corresponding id PlotConfiguration currentPlotConfigurationClone = plotInstance.getCurrentPlotConfigurationClone(); int id = e.getSource().getId(); RangeAxisConfig rangeAxisConfigById = currentPlotConfigurationClone.getRangeAxisConfigById(id); if (rangeAxisConfigById == null) { StaticDebug.debug("RangeAxisData: ### ATTENTION #### RangeAxis with ID " + id + " is null! Meta change event?"); return; // do nothing if range axis is not present anymore } setRangeAxisConfig(rangeAxisConfigById); switch (e.getType()) { case CLEARED: case RANGE_CHANGED: case VALUE_SOURCE_ADDED: case VALUE_SOURCE_CHANGED: case VALUE_SOURCE_REMOVED: case VALUE_SOURCE_MOVED: invalidateCache(); break; default: } } public RangeAxisConfig getRangeAxisConfig() { return rangeAxisConfig; } /** * @param rangeAxisConfig * the rangeAxisConfig to set */ private void setRangeAxisConfig(RangeAxisConfig rangeAxisConfig) { if (rangeAxisConfig != null && rangeAxisConfig.getId() == this.rangeAxisConfig.getId()) { this.rangeAxisConfig = rangeAxisConfig; } } public List<PlotConfigurationError> getWarnings() { List<PlotConfigurationError> warnings = new LinkedList<PlotConfigurationError>(); if (rangeAxisConfig.getValueType() == ValueType.UNKNOWN) { return warnings; } double lowerViewBound = getLowerViewBound(); double upperViewBound = getUpperViewBound(); boolean equalUpperAndLowerBound = DataStructureUtils.almostEqual(lowerViewBound, upperViewBound, 1E-6); if (DataStructureUtils.greaterOrAlmostEqual(lowerViewBound, upperViewBound, 1E-6) && !equalUpperAndLowerBound) { warnings.add(new PlotConfigurationError("user_range_includes_no_data", rangeAxisConfig.getLabel())); } // else if (Double.isInfinite(lowerViewBound) || Double.isInfinite(upperViewBound)) { // warnings.add(new PlotConfigurationError("user_range_includes_no_data", // rangeAxisConfig.getLabel())); // } else { double maxYValue = getMaxYValue(); double minYValue = getMinYValue(); boolean equalMinAndMaxValue = DataStructureUtils.almostEqual(minYValue, maxYValue, 1E-6); if ((DataStructureUtils.greaterOrAlmostEqual(lowerViewBound, maxYValue, 1E-6) || DataStructureUtils .greaterOrAlmostEqual(minYValue, upperViewBound, 1E-6)) && !equalMinAndMaxValue) { warnings.add(new PlotConfigurationError("user_range_includes_no_data", rangeAxisConfig.getLabel())); } } return warnings; } /** * @return */ public List<PlotConfigurationError> getErrors() { List<PlotConfigurationError> errors = new LinkedList<PlotConfigurationError>(); double lowerViewBound = getLowerViewBound(); double upperViewBound = getUpperViewBound(); if (rangeAxisConfig.getValueType() == ValueType.DATE_TIME && (Double.isInfinite(lowerViewBound) || Double.isInfinite(upperViewBound))) { errors.add(new PlotConfigurationError("infite_range_for_date_axis", rangeAxisConfig.getLabel())); return errors; } if (rangeAxisConfig.isUsingUserDefinedUpperViewBound() && DataStructureUtils.greaterOrAlmostEqual(lowerViewBound, upperViewBound, 1E-6)) { errors.add(new PlotConfigurationError("axis_upper_range_below_lower_auto_range", rangeAxisConfig.getLabel(), lowerViewBound + "")); return errors; } return errors; } }