/*
* 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 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.DimensionConfig;
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.SeriesFormat.ItemShape;
import com.rapidminer.gui.new_plotter.configuration.SeriesFormat.VisualizationType;
import com.rapidminer.gui.new_plotter.configuration.ValueSource;
import com.rapidminer.gui.new_plotter.data.PlotInstance;
import com.rapidminer.gui.new_plotter.templates.SeriesTemplate.DataTableWithIndexDelegate;
import com.rapidminer.gui.new_plotter.templates.gui.SeriesMultipleTemplatePanel;
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 series multiple plot.
*
* @author Marco Boeck
*
*/
public class SeriesMultipleTemplate extends PlotterTemplate {
private static final String PLOT_NAME_ELEMENT = "plotName";
private static final String PLOT_NAMES_ELEMENT = "plotNames";
private static final String INDEX_NAME_ELEMENT = "indexName";
/** the current {@link DataTable} */
private DataTable currentDataTable;
/** the current {@link RangeAxisConfig}s */
private List<RangeAxisConfig> currentRangeAxisConfigsList;
/** the name of the index column */
private String indexName;
/** the names of the plots to show */
private Object[] plotNames;
/**
* Creates a new {@link SeriesMultipleTemplate}. This template allows easy configuration of the
* histogram chart for the plotter.
*/
public SeriesMultipleTemplate() {
currentRangeAxisConfigsList = new LinkedList<RangeAxisConfig>();
// value when "None" is selected
String noSelection = I18N.getMessage(I18N.getGUIBundle(), "gui.plotter.column.empty_selection.label");
indexName = noSelection;
plotNames = new Object[0];
guiPanel = new SeriesMultipleTemplatePanel(this);
}
@Override
public String getChartType() {
return SeriesMultipleTemplate.getI18NName();
}
/**
* Sets the name for the index dimension column.
*
* @param columnName
*/
public void setIndexDimensionName(String columnName) {
indexName = columnName;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns the name of the index dimension column.
*
* @return
*/
public String getIndexDimensionName() {
return indexName;
}
/**
* Sets the currently selected plots by their name.
*
* @param plotNames
*/
public void setPlotSelection(Object[] plotNames) {
this.plotNames = plotNames;
updatePlotConfiguration();
setChanged();
notifyObservers();
}
/**
* Returns the currently selected plots.
*
* @return
*/
public Object[] getPlotSelection() {
return plotNames;
}
@Override
protected void dataUpdated(final DataTable dataTable) {
// add artifical index column if needed
if (dataTable.getColumnIndex(SeriesTemplate.ARTIFICAL_INDEX_COLUMN) == -1) {
currentDataTable = new DataTableWithIndexDelegate(dataTable);
PlotConfiguration plotConfiguration = new PlotConfiguration(new DataTableColumn(currentDataTable, 0));
PlotInstance plotInstance = new PlotInstance(plotConfiguration, currentDataTable);
setPlotInstance(plotInstance);
}
// clear possible existing data
currentRangeAxisConfigsList.clear();
}
public static String getI18NName() {
return I18N.getMessage(I18N.getGUIBundle(), "gui.plotter.series_multiple.name");
}
@Override
protected void updatePlotConfiguration() {
// don't do anything if updates are suspended due to batch updating
if (suspendUpdates) {
return;
}
PlotConfiguration plotConfiguration = getPlotConfiguration();
// stop event processing
boolean plotConfigurationProcessedEvents = plotConfiguration.isProcessingEvents();
plotConfiguration.setProcessEvents(false);
// remove old config(s)
for (RangeAxisConfig rAConfig : currentRangeAxisConfigsList) {
rAConfig.removeRangeAxisConfigListener(rangeAxisConfigListener);
plotConfiguration.removeRangeAxisConfig(rAConfig);
}
currentRangeAxisConfigsList.clear();
// no selection?
if (plotNames.length == 0) {
plotConfiguration.setProcessEvents(plotConfigurationProcessedEvents);
return;
}
// value when "None" is selected
String noSelection = I18N.getMessage(I18N.getGUIBundle(), "gui.plotter.column.empty_selection.label");
DataTableColumn indexColumn;
if (indexName.equals(noSelection)) {
indexColumn = new DataTableColumn(currentDataTable, currentDataTable.getColumnIndex(SeriesTemplate.ARTIFICAL_INDEX_COLUMN));
} else {
indexColumn = new DataTableColumn(currentDataTable, currentDataTable.getColumnIndex(indexName));
}
DimensionConfig domainDimensionConfig = plotConfiguration.getDimensionConfig(PlotDimension.DOMAIN);
domainDimensionConfig.setDataTableColumn(indexColumn);
// restore crosshairs
List<AxisParallelLineConfiguration> clonedListOfDomainLines = new LinkedList<AxisParallelLineConfiguration>(listOfDomainLines);
for (AxisParallelLineConfiguration lineConfig : clonedListOfDomainLines) {
plotConfiguration.getDomainConfigManager().getCrosshairLines().addLine(lineConfig);
}
int indexOfPlots = 0;
for (Object plot : plotNames) {
String plotName = String.valueOf(plot);
RangeAxisConfig newRangeAxisConfig = new RangeAxisConfig(plotName, plotConfiguration);
ValueSource valueSource;
DataTableColumn aDataTableColumn = new DataTableColumn(currentDataTable, currentDataTable.getColumnIndex(plotName));
valueSource = new ValueSource(plotConfiguration, aDataTableColumn, AggregationFunctionType.count, false);
SeriesFormat sFormat = new SeriesFormat();
sFormat.setSeriesType(VisualizationType.LINES_AND_SHAPES);
sFormat.setLineStyle(LineStyle.SOLID);
sFormat.setItemShape(ItemShape.NONE);
sFormat.setLineWidth(1.5f);
ColorRGB yAxisColor = styleProvider.getColorScheme().getColors().get(indexOfPlots++ % styleProvider.getColorScheme().getColors().size());
sFormat.setItemColor(ColorRGB.convertToColor(yAxisColor));
valueSource.setSeriesFormat(sFormat);
newRangeAxisConfig.addRangeAxisConfigListener(rangeAxisConfigListener);
newRangeAxisConfig.addValueSource(valueSource, null);
// add new config(s) 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
currentRangeAxisConfigsList.add(newRangeAxisConfig);
}
// 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(PlotterTemplate.TEMPLATE_ELEMENT);
template.setAttribute(PlotterTemplate.NAME_ELEMENT, getChartType());
Element setting;
setting = document.createElement(INDEX_NAME_ELEMENT);
setting.setAttribute(VALUE_ATTRIBUTE, String.valueOf(indexName));
template.appendChild(setting);
setting = document.createElement(PLOT_NAMES_ELEMENT);
for (Object key : plotNames) {
Element plotNameElement = document.createElement(PLOT_NAME_ELEMENT);
plotNameElement.setAttribute(VALUE_ATTRIBUTE, String.valueOf(key));
setting.appendChild(plotNameElement);
}
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(PLOT_NAMES_ELEMENT)) {
List<Object> plotNamesList = new LinkedList<Object>();
for (int j=0; j<setting.getChildNodes().getLength(); j++) {
Node plotNode = setting.getChildNodes().item(j);
if (plotNode instanceof Element) {
Element plotNameElement = (Element) plotNode;
if (plotNameElement.getNodeName().equals(PLOT_NAME_ELEMENT)) {
plotNamesList.add(plotNameElement.getAttribute(VALUE_ATTRIBUTE));
}
}
}
setPlotSelection(plotNamesList.toArray());
} else if (setting.getNodeName().equals(INDEX_NAME_ELEMENT)) {
setIndexDimensionName(setting.getAttribute(VALUE_ATTRIBUTE));
} 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!");
}
} 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!");
}
} else if (setting.getNodeName().equals(PlotterStyleProvider.STYLE_ELEMENT)) {
styleProvider.loadFromXML(setting);
}
}
}
suspendUpdates = false;
updatePlotConfiguration();
}
}