/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.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.plotter.charts;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JComponent;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItem;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.LegendItemSource;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.block.BlockResult;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.RectangleEdge;
import com.rapidminer.datatable.DataTable;
import com.rapidminer.datatable.DataTableRow;
import com.rapidminer.gui.plotter.LocalNormalizationPlotterAdapter;
import com.rapidminer.gui.plotter.PlotterConfigurationModel;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.math.MathFunctions;
/**
* This is the new parallel plotter.
*
* @author Ingo Mierswa
*/
public class ParallelPlotter2 extends LocalNormalizationPlotterAdapter {
private static final long serialVersionUID = -8763693366081949249L;
/** The currently used data table object. */
private transient DataTable dataTable;
/** The data set used for the plotter. */
private XYSeriesCollection dataset = null;
/** The column which is used for the values. */
private int colorColumn = -1;
private String[] domainAxisMap = null;
private ChartPanel panel = new ChartPanel(null);
private double[] colorMap = null;
public ParallelPlotter2(final PlotterConfigurationModel settings) {
super(settings);
setBackground(Color.white);
}
public ParallelPlotter2(PlotterConfigurationModel settings, DataTable dataTable) {
this(settings);
setDataTable(dataTable);
}
@Override
public JComponent getPlotter() {
return panel;
}
private static JFreeChart createChart(XYDataset dataset) {
// create the chart...
JFreeChart chart = ChartFactory.createXYLineChart(
null, // chart title
null, // x axis label
null, // y axis label
dataset, // data
PlotOrientation.VERTICAL,
false, // include legend
true, // tooltips
false // urls
);
chart.setBackgroundPaint(Color.white);
// get a reference to the plot for further customization...
XYPlot plot = (XYPlot) chart.getPlot();
plot.setBackgroundPaint(Color.WHITE);
ValueAxis valueAxis = plot.getRangeAxis();
valueAxis.setLabelFont(LABEL_FONT_BOLD);
valueAxis.setTickLabelFont(LABEL_FONT);
return chart;
}
@Override
public void setDataTable(DataTable dataTable) {
super.setDataTable(dataTable);
this.dataTable = dataTable;
updatePlotter();
}
@Override
public void setPlotColumn(int index, boolean plot) {
if (plot)
this.colorColumn = index;
else
this.colorColumn = -1;
updatePlotter();
}
@Override
public boolean getPlotColumn(int index) {
return colorColumn == index;
}
@Override
public String getPlotName() { return "Color Column"; }
@Override
public int getNumberOfAxes() {
return 0;
}
private void prepareData() {
this.dataset = new XYSeriesCollection();
// calculate min and max
int columns = this.dataTable.getNumberOfColumns();
double[] min = new double[columns];
double[] max = new double[columns];
if (isLocalNormalized()) {
for (int c = 0; c < columns; c++) {
min[c] = Double.POSITIVE_INFINITY;
max[c] = Double.NEGATIVE_INFINITY;
}
synchronized (dataTable) {
Iterator<DataTableRow> i = dataTable.iterator();
while (i.hasNext()) {
DataTableRow row = i.next();
for (int c = 0; c < dataTable.getNumberOfColumns(); c++) {
double value = row.getValue(c);
min[c] = MathFunctions.robustMin(min[c], value);
max[c] = MathFunctions.robustMax(max[c], value);
}
}
}
}
this.domainAxisMap = null;
synchronized (dataTable) {
this.colorMap = new double[dataTable.getNumberOfRows()];
Iterator<DataTableRow> i = this.dataTable.iterator();
int idCounter = 0;
while (i.hasNext()) {
DataTableRow row = i.next();
String id = row.getId();
if (id == null) {
id = (idCounter + 1) + "";
}
XYSeries series = new XYSeries(id, false, false);
int counter = 0;
for (int column = 0; column < dataTable.getNumberOfColumns(); column++) {
if ((!dataTable.isSpecial(column)) && (column != colorColumn)) {
double value = row.getValue(column);
if (isLocalNormalized()) {
value = (value - min[column]) / (max[column] - min[column]);
}
series.add(counter, value);
counter++;
}
}
if (colorColumn >= 0) {
this.colorMap[idCounter] = row.getValue(colorColumn);
}
this.dataset.addSeries(series);
idCounter++;
}
}
if (domainAxisMap == null) {
List<String> domainValues = new LinkedList<String>();
int counter = 0;
for (int column = 0; column < dataTable.getNumberOfColumns(); column++) {
if ((!dataTable.isSpecial(column)) && (column != colorColumn)) {
domainValues.add(dataTable.getColumnName(column));
counter++;
}
}
this.domainAxisMap = new String[domainValues.size()];
domainValues.toArray(this.domainAxisMap);
}
}
@Override
public void updatePlotter() {
prepareData();
JFreeChart chart = createChart(this.dataset);
chart.setAntiAlias(false);
// set the background color for the chart...
chart.setBackgroundPaint(Color.white);
// general plot settings
XYPlot plot = chart.getXYPlot();
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
// domain axis
SymbolAxis axis = null;
if (this.dataTable.isSupportingColumnWeights()) {
List<Double> weightList = new LinkedList<Double>();
for (int column = 0; column < dataTable.getNumberOfColumns(); column++) {
if ((!dataTable.isSpecial(column)) && (column != colorColumn)) {
weightList.add(this.dataTable.getColumnWeight(column));
}
}
double[] weights = new double[weightList.size()];
int index = 0;
for (Double d : weightList) {
weights[index++] = d;
}
axis = new WeightBasedSymbolAxis(null, domainAxisMap, weights);
} else {
axis = new SymbolAxis(null, domainAxisMap);
}
axis.setTickLabelFont(LABEL_FONT);
axis.setLabelFont(LABEL_FONT_BOLD);
// rotate labels
if (isLabelRotating()) {
axis.setTickLabelsVisible(true);
axis.setVerticalTickLabels(true);
}
chart.getXYPlot().setDomainAxis(axis);
// renderer
final ColorizedLineAndShapeRenderer renderer = new ColorizedLineAndShapeRenderer(this.colorMap);
plot.setRenderer(renderer);
// legend settings
if ((colorColumn >= 0) && (this.dataTable.isNominal(colorColumn))) {
final LegendItemCollection legendItemCollection = new LegendItemCollection();
for (int i = 0; i < this.dataTable.getNumberOfValues(colorColumn); i++) {
legendItemCollection.add(new LegendItem(this.dataTable.mapIndex(colorColumn, i), null, null, null, new Rectangle2D.Double(0, 0, 7, 7), getColorProvider().getPointColor(i / (double)(this.dataTable.getNumberOfValues(colorColumn) - 1)), new BasicStroke(0.75f), Color.GRAY));
}
chart.addLegend(new LegendTitle(new LegendItemSource() {
public LegendItemCollection getLegendItems() {
return legendItemCollection;
}
}));
LegendTitle legend = chart.getLegend();
if (legend != null) {
legend.setPosition(RectangleEdge.TOP);
legend.setFrame(BlockBorder.NONE);
legend.setHorizontalAlignment(HorizontalAlignment.LEFT);
legend.setItemFont(LABEL_FONT);
}
} else if (colorColumn >= 0) {
chart.addLegend(new LegendTitle(new LegendItemSource() {
public LegendItemCollection getLegendItems() {
LegendItemCollection itemCollection = new LegendItemCollection();
itemCollection.add(new LegendItem("Dummy"));
return itemCollection;
}
}) {
private static final long serialVersionUID = 1288380309936848376L;
@Override
public Object draw(java.awt.Graphics2D g2, java.awt.geom.Rectangle2D area, java.lang.Object params) {
if (dataTable.isDate(colorColumn) || dataTable.isTime(colorColumn) || dataTable.isDateTime(colorColumn)) {
drawSimpleDateLegend(g2, (int)(area.getCenterX() - 170), (int)(area.getCenterY() + 7), dataTable, colorColumn, renderer.getMinColorValue(), renderer.getMaxColorValue());
return new BlockResult();
} else {
final String minColorString = Tools.formatNumber(renderer.getMinColorValue());
final String maxColorString = Tools.formatNumber(renderer.getMaxColorValue());
drawSimpleNumericalLegend(g2, (int)(area.getCenterX() - 90), (int)(area.getCenterY() + 7), dataTable.getColumnName(colorColumn), minColorString, maxColorString);
return new BlockResult();
}
}
@Override
public void draw(java.awt.Graphics2D g2, java.awt.geom.Rectangle2D area) {
draw(g2, area, null);
}
});
}
// chart panel
if (panel instanceof AbstractChartPanel) {
panel.setChart(chart);
} else {
panel = new AbstractChartPanel(chart, getWidth(), getHeight() - MARGIN);
final ChartPanelShiftController controller = new ChartPanelShiftController(panel);
panel.addMouseListener(controller);
panel.addMouseMotionListener(controller);
}
// tooltips
/*
class CustomXYToolTipGenerator implements XYToolTipGenerator {
public CustomXYToolTipGenerator() {}
public String generateToolTip(XYDataset dataset, int row, int column) {
String id = idMap.get(new SeriesAndItem(row, column));
if (id != null) {
return "<html><b>Id: " + id + "</b> (" + dataset.getSeriesKey(row) + ", " + Tools.formatIntegerIfPossible(dataset.getXValue(row, column)) + ", " + Tools.formatIntegerIfPossible(dataset.getYValue(row, column)) + ")</html>";
} else {
return "<html>(" + dataset.getSeriesKey(row) + ", " + Tools.formatIntegerIfPossible(dataset.getXValue(row, column)) + ", " + Tools.formatIntegerIfPossible(dataset.getYValue(row, column)) + ")</html>";
}
}
}
*/
// ATTENTION: WITHOUT THIS WE GET SEVERE MEMORY LEAKS!!!
panel.getChartRenderingInfo().setEntityCollection(null);
}
@Override
public JComponent getOptionsComponent(int index) {
if (index == 0) {
return getLocalNormalizationComponent();
} else if (index == 1) {
return getRotateLabelComponent();
} else {
return null;
}
}
@Override
public String getPlotterName() {
return PlotterConfigurationModel.PARALLEL_PLOT;
}
}