/*
* RapidMiner
*
* Copyright (C) 2001-2014 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.templates;
import java.awt.Color;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.rapidminer.datatable.DataTable;
import com.rapidminer.gui.new_plotter.configuration.AxisParallelLineConfiguration;
import com.rapidminer.gui.new_plotter.configuration.DataTableColumn;
import com.rapidminer.gui.new_plotter.configuration.DefaultDimensionConfig;
import com.rapidminer.gui.new_plotter.configuration.DimensionConfig.PlotDimension;
import com.rapidminer.gui.new_plotter.configuration.LegendConfiguration.LegendPosition;
import com.rapidminer.gui.new_plotter.configuration.LineFormat.LineStyle;
import com.rapidminer.gui.new_plotter.configuration.PlotConfiguration;
import com.rapidminer.gui.new_plotter.configuration.RangeAxisConfig;
import com.rapidminer.gui.new_plotter.configuration.SeriesFormat;
import com.rapidminer.gui.new_plotter.configuration.ValueSource;
import com.rapidminer.gui.new_plotter.templates.gui.ScatterTemplatePanel;
import com.rapidminer.gui.new_plotter.templates.style.ColorRGB;
import com.rapidminer.gui.new_plotter.templates.style.PlotterStyleProvider;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.math.function.aggregation.AbstractAggregationFunction.AggregationFunctionType;
/**
* The template for a scatter plot. For the GUI, see {@link ScatterTemplatePanel}.
*
* @author Marco Boeck
*
*/
public class ScatterTemplate extends PlotterTemplate {
private static final String JITTER_ELEMENT = "jitter";
private static final String COLOR_LOGARITHMIC_ELEMENT = "colorLogarithmic";
private static final String Y_AXIS_LOGARITHMIC_ELEMENT = "yAxisLogarithmic";
private static final String X_AXIS_LOGARITHMIC_ELEMENT = "xAxisLogarithmic";
private static final String COLOR_COLUMN_ELEMENT = "colorColumn";
private static final String Y_AXIS_COLUMN_ELEMENT = "yAxisColumn";
private static final String X_AXIS_COLUMN_ELEMENT = "xAxisColumn";
/** the current {@link RangeAxisConfig} */
private RangeAxisConfig currentRangeAxisConfig;
/** the name of the x-axis column */
private String xAxisColumn;
/** the name of the y-axis column */
private String yAxisColumn;
/** the name of the color column */
private String colorColumn;
/** if true, the x-axis will be logarithmic */
private boolean xAxisLogarithmic;
/** if true, the y-axis will be logarithmic */
private boolean yAxisLogarithmic;
/** if true, the color will be logarithmic */
private boolean colorLogarithmic;
/** the jitter value for the plot */
private int jitter;
/**
* Creates a new {@link ScatterTemplate}. This template allows easy configuration of the scatter
* chart for the plotter.
*/
public ScatterTemplate() {
// value when "None" is selected
String noSelection = I18N.getMessage(I18N.getGUIBundle(), "gui.plotter.column.empty_selection.label");
xAxisColumn = noSelection;
yAxisColumn = noSelection;
colorColumn = noSelection;
xAxisLogarithmic = false;
yAxisLogarithmic = false;
colorLogarithmic = false;
jitter = 0;
guiPanel = new ScatterTemplatePanel(this);
}
@Override
public String getChartType() {
return ScatterTemplate.getI18NName();
}
@Override
protected void dataUpdated(final DataTable dataTable) {
// clear possible existing data
currentRangeAxisConfig = null;
}
/**
* Set the name of the column which will be used as the domain (X) axis.
*
* @param columnName
*/
public void setXAxisColum(String columnName) {
if (columnName == null) {
throw new IllegalArgumentException("columnName must not be null!");
}
xAxisColumn = columnName;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns the name of the domain (X) axis column.
*
* @return
*/
public String getXAxisColumn() {
return xAxisColumn;
}
/**
* Set the name of the column which will be used as the range (Y) axis.
*
* @param columnName
*/
public void setYAxisColum(String columnName) {
if (columnName == null) {
throw new IllegalArgumentException("columnName must not be null!");
}
yAxisColumn = columnName;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns the name of the range (Y) axis column.
*
* @return
*/
public String getYAxisColumn() {
return yAxisColumn;
}
/**
* Set the name of the column which will be used as the color.
*
* @param columnName
*/
public void setColorColum(String columnName) {
if (columnName == null) {
throw new IllegalArgumentException("columnName must not be null!");
}
colorColumn = columnName;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns the name of the column used for the color.
*
* @return
*/
public String getColorColumn() {
return colorColumn;
}
/**
* Sets whether the domain (X) axis should be logarithmic or not.
*
* @param log
*/
public void setXAxisLogarithmic(boolean log) {
xAxisLogarithmic = log;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns whether the domain (X) axis is logarithmic or not.
*
* @return
*/
public boolean isXAxisLogarithmic() {
return xAxisLogarithmic;
}
/**
* Sets whether the range (Y) axis should be logarithmic or not.
*
* @param log
*/
public void setYAxisLogarithmic(boolean log) {
yAxisLogarithmic = log;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns whether the range (Y) axis is logarithmic or not.
*
* @return
*/
public boolean isYAxisLogarithmic() {
return yAxisLogarithmic;
}
/**
* Sets whether the color should be logarithmic or not.
*
* @param log
*/
public void setColorLogarithmic(boolean log) {
colorLogarithmic = log;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns whether the color is logarithmic or not.
*
* @return
*/
public boolean isColorLogarithmic() {
return colorLogarithmic;
}
/**
* Sets the jitter for the plot.
*
* @param jitter
* 0 <= {@code jitter} <= 100
*/
public void setJitter(int jitter) {
if (jitter < 0 || jitter > 100) {
throw new IllegalArgumentException("jitter must be between 0 and 100!");
}
this.jitter = jitter;
setChanged();
notifyObservers();
updatePlotConfiguration();
}
/**
* Returns the current jitter setting for the plot.
*
* @return
*/
public int getJitter() {
return jitter;
}
public static String getI18NName() {
return I18N.getMessage(I18N.getGUIBundle(), "gui.plotter.scatter.name");
}
@Override
protected void updatePlotConfiguration() {
// don't do anything if updates are suspended due to batch updating
if (suspendUpdates) {
return;
}
PlotConfiguration plotConfiguration = plotInstance.getMasterPlotConfiguration();
// value when "None" is selected
String noSelection = I18N.getMessage(I18N.getGUIBundle(), "gui.plotter.column.empty_selection.label");
// stop event processing
boolean plotConfigurationProcessedEvents = plotConfiguration.isProcessingEvents();
plotConfiguration.setProcessEvents(false);
// restore crosshairs
List<AxisParallelLineConfiguration> clonedListOfDomainLines = new LinkedList<AxisParallelLineConfiguration>(listOfDomainLines);
for (AxisParallelLineConfiguration lineConfig : clonedListOfDomainLines) {
plotConfiguration.getDomainConfigManager().getCrosshairLines().addLine(lineConfig);
}
// x axis column selection
if (!xAxisColumn.equals(noSelection)) {
plotConfiguration.getDimensionConfig(PlotDimension.DOMAIN).setDataTableColumn(new DataTableColumn(currentDataTable, currentDataTable.getColumnIndex(xAxisColumn)));
plotConfiguration.getDimensionConfig(PlotDimension.DOMAIN).setLogarithmic(xAxisLogarithmic);
} else {
// remove config
if (currentRangeAxisConfig != null) {
currentRangeAxisConfig.removeRangeAxisConfigListener(rangeAxisConfigListener);
plotConfiguration.removeRangeAxisConfig(currentRangeAxisConfig);
currentRangeAxisConfig = null;
}
plotConfiguration.setProcessEvents(plotConfigurationProcessedEvents);
return;
}
// y axis column selection
if (!yAxisColumn.equals(noSelection)) {
RangeAxisConfig newRangeAxisConfig = new RangeAxisConfig(yAxisColumn, plotConfiguration);
newRangeAxisConfig.addRangeAxisConfigListener(rangeAxisConfigListener);
ValueSource valueSource;
valueSource = new ValueSource(plotConfiguration, new DataTableColumn(currentDataTable, currentDataTable.getColumnIndex(yAxisColumn)), AggregationFunctionType.count,
false);
SeriesFormat sFormat = new SeriesFormat();
valueSource.setSeriesFormat(sFormat);
newRangeAxisConfig.addValueSource(valueSource, null);
newRangeAxisConfig.setLogarithmicAxis(yAxisLogarithmic);
// remove old config
if (currentRangeAxisConfig != null) {
currentRangeAxisConfig.removeRangeAxisConfigListener(rangeAxisConfigListener);
plotConfiguration.removeRangeAxisConfig(currentRangeAxisConfig);
}
currentRangeAxisConfig = newRangeAxisConfig;
// add new config and restore crosshairs
List<AxisParallelLineConfiguration> clonedRangeAxisLineList = rangeAxisCrosshairLinesMap.get(newRangeAxisConfig.getLabel());
if (clonedRangeAxisLineList != null) {
for (AxisParallelLineConfiguration lineConfig : clonedRangeAxisLineList) {
newRangeAxisConfig.getCrossHairLines().addLine(lineConfig);
}
}
plotConfiguration.addRangeAxisConfig(newRangeAxisConfig);
// remember the new config so we can remove it later again
} else {
// remove config
if (currentRangeAxisConfig != null) {
currentRangeAxisConfig.removeRangeAxisConfigListener(rangeAxisConfigListener);
plotConfiguration.removeRangeAxisConfig(currentRangeAxisConfig);
currentRangeAxisConfig = null;
}
}
// color column selection
if (!colorColumn.equals(noSelection)) {
plotConfiguration.setDimensionConfig(PlotDimension.COLOR, null);
DefaultDimensionConfig dimConfig = new DefaultDimensionConfig(plotConfiguration, new DataTableColumn(currentDataTable, currentDataTable.getColumnIndex(colorColumn)),
PlotDimension.COLOR);
dimConfig.setLogarithmic(colorLogarithmic);
plotConfiguration.setDimensionConfig(PlotDimension.COLOR, dimConfig);
} else {
plotConfiguration.setDimensionConfig(PlotDimension.COLOR, null);
}
// general settings
plotConfiguration.setAxesFont(styleProvider.getAxesFont());
plotConfiguration.setTitleFont(styleProvider.getTitleFont());
plotConfiguration.getLegendConfiguration().setLegendFont(styleProvider.getLegendFont());
plotConfiguration.addColorSchemeAndSetActive(styleProvider.getColorScheme());
if (styleProvider.isShowLegend()) {
plotConfiguration.getLegendConfiguration().setLegendPosition(LegendPosition.BOTTOM);
} else {
plotConfiguration.getLegendConfiguration().setLegendPosition(LegendPosition.NONE);
}
plotConfiguration.setFrameBackgroundColor(ColorRGB.convertToColor(styleProvider.getFrameBackgroundColor()));
plotConfiguration.setPlotBackgroundColor(ColorRGB.convertToColor(styleProvider.getPlotBackgroundColor()));
plotConfiguration.setTitleText(styleProvider.getTitleText());
// continue event processing
plotConfiguration.setProcessEvents(plotConfigurationProcessedEvents);
}
@Override
public Element writeToXML(Document document) {
Element template = document.createElement(TEMPLATE_ELEMENT);
template.setAttribute(NAME_ELEMENT, getChartType());
Element setting;
setting = document.createElement(X_AXIS_COLUMN_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, xAxisColumn);
template.appendChild(setting);
setting = document.createElement(Y_AXIS_COLUMN_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, yAxisColumn);
template.appendChild(setting);
setting = document.createElement(COLOR_COLUMN_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, colorColumn);
template.appendChild(setting);
setting = document.createElement(X_AXIS_LOGARITHMIC_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, String.valueOf(xAxisLogarithmic));
template.appendChild(setting);
setting = document.createElement(Y_AXIS_LOGARITHMIC_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, String.valueOf(yAxisLogarithmic));
template.appendChild(setting);
setting = document.createElement(COLOR_LOGARITHMIC_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, String.valueOf(colorLogarithmic));
template.appendChild(setting);
setting = document.createElement(JITTER_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, String.valueOf(jitter));
template.appendChild(setting);
// store crosshairs (RangeAxis)
setting = document.createElement(CROSSHAIR_RANGE_AXIS_TOP_ELEMENT);
for (String rangeAxisLabel : rangeAxisCrosshairLinesMap.keySet()) {
// add crosshairs of currently not displayed range axes
List<AxisParallelLineConfiguration> lines = rangeAxisCrosshairLinesMap.get(rangeAxisLabel);
if (lines != null) {
for (AxisParallelLineConfiguration line : lines) {
Element rangeCrosshairElement = document.createElement(CROSSHAIR_RANGE_AXIS_ELEMENT);
rangeCrosshairElement.setAttribute(CROSSHAIR_RANGE_AXIS_LABEL_ATTRIBUTE, rangeAxisLabel);
rangeCrosshairElement.setAttribute(CROSSHAIR_VALUE_ATTRIBUTE, String.valueOf(line.getValue()));
rangeCrosshairElement.setAttribute(CROSSHAIR_WIDTH_ATTRIBUTE, String.valueOf(line.getFormat().getWidth()));
rangeCrosshairElement.setAttribute(CROSSHAIR_STYLE_ATTRIBUTE, String.valueOf(line.getFormat().getStyle()));
Element colorElement = document.createElement(CROSSHAIR_COLOR_ELEMENT);
colorElement.setAttribute(CROSSHAIR_COLOR_R_ATTRIBUTE, String.valueOf(line.getFormat().getColor().getRed()));
colorElement.setAttribute(CROSSHAIR_COLOR_G_ATTRIBUTE, String.valueOf(line.getFormat().getColor().getGreen()));
colorElement.setAttribute(CROSSHAIR_COLOR_B_ATTRIBUTE, String.valueOf(line.getFormat().getColor().getBlue()));
rangeCrosshairElement.appendChild(colorElement);
setting.appendChild(rangeCrosshairElement);
}
}
}
template.appendChild(setting);
// store crosshairs (domainAxis)
setting = document.createElement(CROSSHAIR_DOMAIN_TOP_ELEMENT);
Element domainCrosshairElement = document.createElement(CROSSHAIR_DOMAIN_ELEMENT);
for (AxisParallelLineConfiguration line : listOfDomainLines) {
domainCrosshairElement.setAttribute(CROSSHAIR_VALUE_ATTRIBUTE, String.valueOf(line.getValue()));
domainCrosshairElement.setAttribute(CROSSHAIR_WIDTH_ATTRIBUTE, String.valueOf(line.getFormat().getWidth()));
domainCrosshairElement.setAttribute(CROSSHAIR_STYLE_ATTRIBUTE, String.valueOf(line.getFormat().getStyle()));
Element colorElement = document.createElement(CROSSHAIR_COLOR_ELEMENT);
colorElement.setAttribute(CROSSHAIR_COLOR_R_ATTRIBUTE, String.valueOf(line.getFormat().getColor().getRed()));
colorElement.setAttribute(CROSSHAIR_COLOR_G_ATTRIBUTE, String.valueOf(line.getFormat().getColor().getGreen()));
colorElement.setAttribute(CROSSHAIR_COLOR_B_ATTRIBUTE, String.valueOf(line.getFormat().getColor().getBlue()));
domainCrosshairElement.appendChild(colorElement);
setting.appendChild(domainCrosshairElement);
}
template.appendChild(setting);
template.appendChild(styleProvider.createXML(document));
return template;
}
@Override
public void loadFromXML(Element templateElement) {
suspendUpdates = true;
for (int i=0; i<templateElement.getChildNodes().getLength(); i++) {
Node node = templateElement.getChildNodes().item(i);
if (node instanceof Element) {
Element setting = (Element) node;
if (setting.getNodeName().equals(X_AXIS_COLUMN_ELEMENT)) {
setXAxisColum(setting.getAttribute(VALUE_ATTRIBUTE));
} else if (setting.getNodeName().equals(Y_AXIS_COLUMN_ELEMENT)) {
setYAxisColum(setting.getAttribute(VALUE_ATTRIBUTE));
} else if (setting.getNodeName().equals(COLOR_COLUMN_ELEMENT)) {
setColorColum(setting.getAttribute(VALUE_ATTRIBUTE));
} else if (setting.getNodeName().equals(X_AXIS_LOGARITHMIC_ELEMENT)) {
setXAxisLogarithmic(Boolean.parseBoolean(setting.getAttribute(VALUE_ATTRIBUTE)));
} else if (setting.getNodeName().equals(Y_AXIS_LOGARITHMIC_ELEMENT)) {
setYAxisLogarithmic(Boolean.parseBoolean(setting.getAttribute(VALUE_ATTRIBUTE)));
} else if (setting.getNodeName().equals(COLOR_LOGARITHMIC_ELEMENT)) {
setColorLogarithmic(Boolean.parseBoolean(setting.getAttribute(VALUE_ATTRIBUTE)));
} else if (setting.getNodeName().equals(JITTER_ELEMENT)) {
try {
setJitter(Integer.parseInt(setting.getAttribute(VALUE_ATTRIBUTE)));
} catch (NumberFormatException e) {
//LogService.getRoot().warning("Could not restore jitter setting for scatter template!");
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.new_plotter.ScatterTemplate.restoring_jitter_setting_error");
}
} else if (setting.getNodeName().equals(CROSSHAIR_RANGE_AXIS_TOP_ELEMENT)) {
try {
// load range axes crosshairs
for (int j=0; j<setting.getChildNodes().getLength(); j++) {
Node rangeCrosshairNode = setting.getChildNodes().item(j);
if (rangeCrosshairNode instanceof Element) {
Element rangeCrosshairElement = (Element) rangeCrosshairNode;
if (rangeCrosshairElement.getNodeName().equals(CROSSHAIR_RANGE_AXIS_ELEMENT)) {
// load range axis crosshair
AxisParallelLineConfiguration line = new AxisParallelLineConfiguration(1.0, false);
String rangeAxisLabel = rangeCrosshairElement.getAttribute(CROSSHAIR_RANGE_AXIS_LABEL_ATTRIBUTE);
Double value = Double.parseDouble(rangeCrosshairElement.getAttribute(CROSSHAIR_VALUE_ATTRIBUTE));
Float width = Float.parseFloat(rangeCrosshairElement.getAttribute(CROSSHAIR_WIDTH_ATTRIBUTE));
LineStyle style = LineStyle.valueOf(rangeCrosshairElement.getAttribute(CROSSHAIR_STYLE_ATTRIBUTE));
for (int k=0; k<rangeCrosshairElement.getChildNodes().getLength(); k++) {
Node colorNode = rangeCrosshairElement.getChildNodes().item(k);
if (colorNode.getNodeName().equals(CROSSHAIR_COLOR_ELEMENT)) {
Element colorElement = (Element) colorNode;
int r = Integer.parseInt(colorElement.getAttribute(CROSSHAIR_COLOR_R_ATTRIBUTE));
int g = Integer.parseInt(colorElement.getAttribute(CROSSHAIR_COLOR_G_ATTRIBUTE));
int b = Integer.parseInt(colorElement.getAttribute(CROSSHAIR_COLOR_B_ATTRIBUTE));
Color color = new Color(r, g, b);
line.getFormat().setColor(color);
}
}
line.setValue(value);
line.getFormat().setWidth(width);
line.getFormat().setStyle(style);
// decide if crosshair is of the currently selected range axis or not
List<AxisParallelLineConfiguration> listOfLines = rangeAxisCrosshairLinesMap.get(rangeAxisLabel);
if (listOfLines == null) {
listOfLines = new LinkedList<AxisParallelLineConfiguration>();
}
listOfLines.add(line);
rangeAxisCrosshairLinesMap.put(rangeAxisLabel, listOfLines);
}
}
}
} catch (NumberFormatException e) {
//LogService.getRoot().warning("Could not restore range axis crosshairs!");
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.new_plotter.ScatterTemplate.restoring_range_axis_error");
}
} else if (setting.getNodeName().equals(CROSSHAIR_DOMAIN_TOP_ELEMENT)) {
try {
// load domain axis crosshairs
for (int j=0; j<setting.getChildNodes().getLength(); j++) {
Node domainCrosshairNode = setting.getChildNodes().item(j);
if (domainCrosshairNode instanceof Element) {
Element domainCrosshairElement = (Element) domainCrosshairNode;
if (domainCrosshairElement.getNodeName().equals(CROSSHAIR_DOMAIN_ELEMENT)) {
// load domain axis crosshair
AxisParallelLineConfiguration line = new AxisParallelLineConfiguration(1.0, false);
Double value = Double.parseDouble(domainCrosshairElement.getAttribute(CROSSHAIR_VALUE_ATTRIBUTE));
Float width = Float.parseFloat(domainCrosshairElement.getAttribute(CROSSHAIR_WIDTH_ATTRIBUTE));
LineStyle style = LineStyle.valueOf(domainCrosshairElement.getAttribute(CROSSHAIR_STYLE_ATTRIBUTE));
for (int k=0; k<domainCrosshairElement.getChildNodes().getLength(); k++) {
Node colorNode = domainCrosshairElement.getChildNodes().item(k);
if (colorNode.getNodeName().equals(CROSSHAIR_COLOR_ELEMENT)) {
Element colorElement = (Element) colorNode;
int r = Integer.parseInt(colorElement.getAttribute(CROSSHAIR_COLOR_R_ATTRIBUTE));
int g = Integer.parseInt(colorElement.getAttribute(CROSSHAIR_COLOR_G_ATTRIBUTE));
int b = Integer.parseInt(colorElement.getAttribute(CROSSHAIR_COLOR_B_ATTRIBUTE));
Color color = new Color(r, g, b);
line.getFormat().setColor(color);
}
}
line.setValue(value);
line.getFormat().setWidth(width);
line.getFormat().setStyle(style);
// add to DomainConfigManager
plotInstance.getMasterPlotConfiguration().getDomainConfigManager().getCrosshairLines().addLine(line);
}
}
}
} catch (NumberFormatException e) {
//LogService.getRoot().warning("Could not restore domain axis crosshairs!");
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.new_plotter.ScatterTemplate.restoring_domain_axis_error");
}
} else if (setting.getNodeName().equals(PlotterStyleProvider.STYLE_ELEMENT)) {
styleProvider.loadFromXML(setting);
}
}
}
suspendUpdates = false;
updatePlotConfiguration();
}
}