/* RocDialog.java created 2007-12-18 * */ package org.signalml.app.view.signal.roc; import static org.signalml.app.util.i18n.SvarogI18n._; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Window; import java.text.DecimalFormat; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableColumnModel; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.ui.RectangleInsets; import org.signalml.app.action.components.ExportChartToClipboardAction; import org.signalml.app.action.components.ExportChartToFileAction; import org.signalml.app.model.components.PropertySheetModel; import org.signalml.app.model.components.TableToTextExporter; import org.signalml.app.util.IconUtils; import org.signalml.app.view.common.dialogs.AbstractDialog; import org.signalml.app.view.workspace.ViewerFileChooser; import org.signalml.app.view.workspace.ViewerPropertySheet; import org.signalml.domain.roc.RocData; import org.signalml.plugin.export.SignalMLException; /** RocDialog * * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class RocDialog extends AbstractDialog { private static final int AXIS_SPACE_SIZE = 40; public static final int ROC_PLOT_SIZE = 400; private static final long serialVersionUID = 1L; private TableToTextExporter tableToTextExporter; private ViewerFileChooser fileChooser; private XYShapeHighlightingRenderer plotRenderer; private XYPlot rocPlot; private JFreeChart rocChart; private ChartPanel chartPanel; private JPopupMenu chartPopupMenu; private RocTableModel rocTableModel; private RocTable rocTable; private PropertySheetModel rocDataPropertySheetModel; private PropertySheetModel rocDataPointPropertySheetModel; private ViewerPropertySheet rocDataPropertySheet; private ViewerPropertySheet rocDataPointPropertySheet; private RocData rocData; public RocDialog() { super(); } public RocDialog(Window w, boolean isModal) { super(w, isModal); } @Override protected void initialize() { setTitle(_("Roc result")); setIconImage(IconUtils.loadClassPathImage("org/signalml/app/icon/roc.png")); setResizable(false); super.initialize(); } @Override public JComponent createInterface() { JPanel interfacePanel = new JPanel(new BorderLayout()); JPanel chartContainPanel = new JPanel(new BorderLayout()); chartContainPanel.setBorder(new CompoundBorder( new TitledBorder(_("Roc curve chart")), new EmptyBorder(3,3,3,3) )); chartContainPanel.add(getChartPanel(), BorderLayout.CENTER); JPanel tablePanel = new JPanel(new BorderLayout()); tablePanel.setBorder(new CompoundBorder( new TitledBorder(_("Roc parameters")), new EmptyBorder(3,3,3,3) )); JScrollPane scrollPane = new JScrollPane(getRocTable()); tablePanel.add(scrollPane, BorderLayout.CENTER); scrollPane.setPreferredSize(new Dimension(400,300)); JPanel topPanel = new JPanel(new BorderLayout()); topPanel.add(chartContainPanel, BorderLayout.WEST); topPanel.add(tablePanel, BorderLayout.CENTER); JPanel propertySheetPanel = new JPanel(new GridLayout(1,2,0,0)); JPanel rocDataPropertyPanel = new JPanel(new BorderLayout()); rocDataPropertyPanel.setBorder(new CompoundBorder( new TitledBorder(_("Roc properties")), new EmptyBorder(3,3,3,3) )); JScrollPane rocDataPropertySheetScrollPane = new JScrollPane(getRocDataPropertySheet()); rocDataPropertySheetScrollPane.setPreferredSize(new Dimension(400,220)); rocDataPropertyPanel.add(rocDataPropertySheetScrollPane, BorderLayout.CENTER); JPanel rocDataPointPropertyPanel = new JPanel(new BorderLayout()); rocDataPointPropertyPanel.setBorder(new CompoundBorder( new TitledBorder(_("Roc point properties")), new EmptyBorder(3,3,3,3) )); JScrollPane rocDataPointPropertySheetScrollPane = new JScrollPane(getRocDataPointPropertySheet()); rocDataPointPropertySheetScrollPane.setPreferredSize(new Dimension(400,220)); rocDataPointPropertyPanel.add(rocDataPointPropertySheetScrollPane, BorderLayout.CENTER); propertySheetPanel.add(rocDataPropertyPanel); propertySheetPanel.add(rocDataPointPropertyPanel); interfacePanel.add(topPanel, BorderLayout.NORTH); interfacePanel.add(propertySheetPanel, BorderLayout.CENTER); return interfacePanel; } public XYShapeHighlightingRenderer getPlotRenderer() { if (plotRenderer == null) { plotRenderer = new XYShapeHighlightingRenderer(true, true, 1); plotRenderer.setSeriesPaint(0, Color.LIGHT_GRAY); plotRenderer.setSeriesStroke(0, new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10F, new float[] { 10, 10 }, 0F)); plotRenderer.setSeriesPaint(1, Color.RED); plotRenderer.setSeriesPaint(2, Color.RED); plotRenderer.setSeriesStroke(2, new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10F, new float[] { 1, 3 }, 0F)); plotRenderer.setSeriesPaint(3, Color.RED); plotRenderer.setSeriesStroke(3, new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10F, new float[] { 1, 3 }, 0F)); } return plotRenderer; } public XYPlot getRocPlot() { if (rocPlot == null) { NumberAxis xAxis = new NumberAxis(); xAxis.setAutoRange(false); xAxis.setRange(0,1); xAxis.setTickUnit(new NumberTickUnit(0.2)); xAxis.setLabel(_("False positive rate")); NumberAxis yAxis = new NumberAxis(); yAxis.setAutoRange(false); yAxis.setRange(0,1); yAxis.setTickUnit(new NumberTickUnit(0.2)); yAxis.setLabel(_("True positive rate")); rocPlot = new XYPlot(null, xAxis, yAxis, getPlotRenderer()); AxisSpace axisSpace = new AxisSpace(); axisSpace.setBottom(AXIS_SPACE_SIZE); axisSpace.setLeft(AXIS_SPACE_SIZE); rocPlot.setFixedDomainAxisSpace(axisSpace); rocPlot.setFixedRangeAxisSpace(axisSpace); } return rocPlot; } public JFreeChart getRocChart() { if (rocChart == null) { rocChart = new JFreeChart(null, null, getRocPlot(), false); rocChart.setBorderVisible(true); rocChart.setBackgroundPaint(Color.WHITE); rocChart.setPadding(new RectangleInsets(5,3,3,5)); } return rocChart; } public ChartPanel getChartPanel() { if (chartPanel == null) { chartPanel = new ChartPanel(getRocChart()); chartPanel.setDomainZoomable(false); chartPanel.setRangeZoomable(false); chartPanel.setMouseZoomable(false); chartPanel.setPopupMenu(getChartPopupMenu()); chartPanel.setBackground(Color.WHITE); chartPanel.setPreferredSize(new Dimension(ROC_PLOT_SIZE, ROC_PLOT_SIZE)); chartPanel.setMinimumSize(new Dimension(ROC_PLOT_SIZE, ROC_PLOT_SIZE)); chartPanel.setMaximumSize(new Dimension(ROC_PLOT_SIZE, ROC_PLOT_SIZE)); } return chartPanel; } public RocTableModel getRocTableModel() { if (rocTableModel == null) { rocTableModel = new RocTableModel(); } return rocTableModel; } public RocTable getRocTable() { if (rocTable == null) { rocTable = new RocTable(getRocTableModel()); rocTable.setTableToTextExporter(tableToTextExporter); rocTable.setFileChooser(fileChooser); rocTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { int index = rocTable.getSelectedRow(); if (index < 0) { getPlotRenderer().clearHighlight(); getRocDataPointPropertySheetModel().setSubject(null); } else { getPlotRenderer().setHighlight(1, index); getRocDataPointPropertySheetModel().setSubject(rocData.getRocDataPointAt(index)); } } }); } return rocTable; } public JPopupMenu getChartPopupMenu() { if (chartPopupMenu == null) { chartPopupMenu = new JPopupMenu(); chartPopupMenu.add(new JMenuItem(new ExportRocChartToClipboardAction())); chartPopupMenu.add(new JMenuItem(new ExportRocChartToFileAction())); } return chartPopupMenu; } public PropertySheetModel getRocDataPropertySheetModel() { if (rocDataPropertySheetModel == null) { rocDataPropertySheetModel = new PropertySheetModel(); rocDataPropertySheetModel.setNumberFormat(new DecimalFormat("0.###")); } return rocDataPropertySheetModel; } public PropertySheetModel getRocDataPointPropertySheetModel() { if (rocDataPointPropertySheetModel == null) { rocDataPointPropertySheetModel = new PropertySheetModel(); rocDataPointPropertySheetModel.setNumberFormat(new DecimalFormat("0.###")); } return rocDataPointPropertySheetModel; } public ViewerPropertySheet getRocDataPropertySheet() { if (rocDataPropertySheet == null) { rocDataPropertySheet = new ViewerPropertySheet(getRocDataPropertySheetModel()); TableColumnModel columnModel = rocDataPropertySheet.getColumnModel(); columnModel.getColumn(0).setPreferredWidth(200); columnModel.getColumn(1).setPreferredWidth(50); } return rocDataPropertySheet; } public ViewerPropertySheet getRocDataPointPropertySheet() { if (rocDataPointPropertySheet == null) { rocDataPointPropertySheet = new ViewerPropertySheet(getRocDataPointPropertySheetModel()); TableColumnModel columnModel = rocDataPointPropertySheet.getColumnModel(); columnModel.getColumn(0).setPreferredWidth(200); columnModel.getColumn(1).setPreferredWidth(50); } return rocDataPointPropertySheet; } @Override public void fillDialogFromModel(Object model) throws SignalMLException { rocData = (RocData) model; DefaultXYDataset dataset = new DefaultXYDataset(); double[] falseRates = rocData.getFalseRates(); double[] trueRates = rocData.getTrueRates(); double[][] reference = new double[][] { {0,1}, {0,1} }; double[][] data = new double[][] { falseRates, trueRates }; dataset.addSeries("reference", reference); dataset.addSeries("data", data); if (falseRates.length > 0) { double[][] leadIn = new double[][] { {0, falseRates[0]}, {0, trueRates[0]} }; double[][] leadOut = new double[][] { { falseRates[falseRates.length-1], 1 }, { trueRates[trueRates.length-1], 1 } }; dataset.addSeries("leadin", leadIn); dataset.addSeries("leadout", leadOut); } getRocPlot().setDataset(dataset); getRocTableModel().setRocData(rocData); getRocDataPropertySheetModel().setSubject(rocData); getRocDataPointPropertySheetModel().setSubject(null); } @Override public void fillModelFromDialog(Object model) throws SignalMLException { // nothing to do } @Override public boolean supportsModelClass(Class<?> clazz) { return RocData.class.isAssignableFrom(clazz); } public TableToTextExporter getTableToTextExporter() { return tableToTextExporter; } public void setTableToTextExporter(TableToTextExporter tableToTextExporter) { this.tableToTextExporter = tableToTextExporter; } public ViewerFileChooser getFileChooser() { return fileChooser; } public void setFileChooser(ViewerFileChooser viewerFileChooser) { this.fileChooser = viewerFileChooser; } protected class ExportRocChartToFileAction extends ExportChartToFileAction { private static final long serialVersionUID = 1L; private ExportRocChartToFileAction() { super(); setFileChooser(fileChooser); setOptionPaneParent(RocDialog.this); } @Override protected JFreeChart getChart() { return getRocChart(); } @Override protected Dimension getImageSize() { return new Dimension(RocDialog.ROC_PLOT_SIZE, RocDialog.ROC_PLOT_SIZE); } } protected class ExportRocChartToClipboardAction extends ExportChartToClipboardAction { private static final long serialVersionUID = 1L; private ExportRocChartToClipboardAction() { super(); } @Override protected JFreeChart getChart() { return getRocChart(); } @Override protected Dimension getImageSize() { return new Dimension(RocDialog.ROC_PLOT_SIZE, RocDialog.ROC_PLOT_SIZE); } } }