/* * 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; } }