// @LogService logsvc /* Create_Boxplot.bsh * IJ BAR: https://github.com/tferr/Scripts#scripts * * Generates a polar plot from data in an ImageJ table using BAR[1] and JFreeChart[2]. * The plot can be exported as vector graphics. * * [1] http://tferr.github.io/Scripts/apidocs/ * [2] http://www.jfree.org/jfreechart/api/javadoc/ * * Tiago Ferreira, v1.0.2 2016.09 */ import bar.Utils; import bar.PlotUtils; import ij.IJ; import ij.WindowManager; import ij.gui.GUI; import ij.gui.GenericDialog; import ij.gui.NonBlockingGenericDialog; import ij.measure.ResultsTable; import ij.plugin.frame.PlugInFrame; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.PolarChartPanel; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.renderer.DefaultPolarItemRenderer; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; /** * Retrieves a list of column choices for the specified ResultsTable. * The "Label" column (containing non-numeric data) is excluded). */ String[] getColumnChoices(ResultsTable rt) { n = rt.getLastColumn(); cChoices = new String[n+2]; for (int i=0; i<=n; i++) cChoices[i] = rt.getColumnHeading(i); cChoices[n+1] = "*None*"; return cChoices; } /** * Retrieves data to be ploted from user. * * @param rt the ResulstTable containing input data * @param renderer the PolarPlot rendererer used to customize plotted series * @return the Plotting data or null if rt is invalid or user dismisses prompt */ XYSeriesCollection getDataFromUser(ResultsTable rt, DefaultPolarItemRenderer renderer) { // Define column choices for drop-down menus colChoices = getColumnChoices(rt); if (colChoices.length<3) return null; // Prompt user for choices. Screen height is really the only restriction // to the n. of series to be defined in the prompt gd = new NonBlockingGenericDialog("Polar Plot Builder"); noneChoice = colChoices.length-1; nSeries = Math.min(4, noneChoice-1); for (i=1; i<=nSeries; i++) { gd.addChoice("Series_"+ i +"_Angles", colChoices, colChoices[(i==1)?0:noneChoice]); gd.addChoice("Series_"+ i +"_Radii", colChoices, colChoices[(i==1)?1:noneChoice]); gd.addStringField("Series_"+ i +"_Label", "Series "+ i, 12); gd.setInsets(0, 0, 15); gd.addCheckbox("Fill_series_"+ i + " curve", false); } gd.setInsets(0,0,0); gd.addMessage("Once data is plotted, right-click on the graph\n" + "canvas for customization and export options."); gd.showDialog(); if (gd.wasCanceled()) return null; // Populate XYSeriesCollection with table's data sCollection = new XYSeriesCollection(); for (i=1; i<=nSeries; i++) { thetaCol = gd.getNextChoiceIndex(); radiusCol = gd.getNextChoiceIndex(); label = gd.getNextString(); if (thetaCol!=noneChoice && radiusCol!=noneChoice) { XYSeries series = new XYSeries(label); invalidRows = 0; for (row=0; row0) logsvc.info("[Polar Plot] "+ label + ": Ignored " + invalidRows + " row(s) containing invalid data"); renderer.setSeriesFilled(i-1, gd.getNextBoolean()); } else { logsvc.info("[Polar Plot] Skipping "+ label + " as requested by user"); } } return sCollection; } /** * Registers the plotting frame in WindowManager, reusing any * pre-existing ones displayed from previous runs of the script */ PlugInFrame getFrame(String title) { PlugInFrame frame; if (WindowManager.getWindow(title)==null) { frame = new PlugInFrame(title); WindowManager.addWindow(frame); } else { frame = WindowManager.getFrame(title); frame.removeAll(); } return frame; } // Retrieve a valid dataset from user rt = Utils.getTable(); if (rt==null) return; renderer = new DefaultPolarItemRenderer(); dataset = getDataFromUser(rt, renderer); if (dataset == null) return; if (dataset.getSeriesCount()<1) { logsvc.info("[Polar Plot] No data to be plotted. Please revise input choices."); return; } // Create plot chart = ChartFactory.createPolarChart(null, // chart title dataset, // the dataset (dataset.getSeriesCount()>1), // legend required? true, // tooltips required? false); // URLs required? // Apply rendering customizations plot = (PolarPlot)chart.getPlot(); plot.setRenderer(renderer); // Tweak: Make plot look like an ij.gui.Plot plot.setBackgroundPaint(java.awt.Color.WHITE); plot.setOutlineVisible(false); if (chart.getLegend()!=null) chart.getLegend().setFrame(org.jfree.chart.block.BlockBorder.NONE); if (plot.isAngleGridlinesVisible()) plot.setAngleGridlinePaint(java.awt.Color.BLACK); if (plot.isRadiusGridlinesVisible()) plot.setRadiusGridlinePaint(java.awt.Color.LIGHT_GRAY); // Tweak: Ensure chart is always drawn and not scaled to avoid rendering artifacts cp = new PolarChartPanel(chart); cp.setMinimumDrawWidth(0); cp.setMaximumDrawWidth(Integer.MAX_VALUE); cp.setMinimumDrawHeight(0); cp.setMaximumDrawHeight(Integer.MAX_VALUE); // Tweak: Support mouse wheel cp.setMouseWheelEnabled(true); // Tweak: Add a right-click entry for rendering options menu = cp.getPopupMenu(); mi = new JMenuItem("Rendering Options...."); mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { gd = new GenericDialog("Rendering Options", frame); gd.addCheckbox("Connect first and last points", renderer.getConnectFirstAndLastPoint()); gd.addCheckbox("Display shapes", renderer.getShapesVisible()); gd.addCheckbox("Draw outlines when filling series", renderer.getDrawOutlineWhenFilled()); gd.addCheckbox("Angles in counterclockwise direction", plot.isCounterClockwise()); gd.addCheckbox("Toggle legend (multi-series data)", false); gd.setLocation(frame.getX()+frame.getWidth()/2, frame.getY()+frame.getHeight()/2); gd.showDialog(); if (gd.wasOKed()) { renderer.setConnectFirstAndLastPoint(gd.getNextBoolean()); renderer.setShapesVisible(gd.getNextBoolean()); renderer.setDrawOutlineWhenFilled(gd.getNextBoolean()); plot.setCounterClockwise(gd.getNextBoolean()); if (chart.getLegend()!=null && gd.getNextBoolean()) chart.getLegend().setVisible(!chart.getLegend().isVisible()); } } }); menu.add(mi,0); // Tweak: Add right-click options for SVG and PDF export menu.addSeparator(); mi = new JMenuItem("Export as PDF..."); mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PlotUtils.exportChartAsPDF(chart, cp.getBounds()); } }); menu.add(mi); mi = new JMenuItem("Export as SVG..."); mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PlotUtils.exportChartAsSVG(chart, cp.getBounds()); } }); menu.add(mi); // Display plot in IJ frame = getFrame("Polar Plot"); frame.add(cp); frame.pack(); GUI.center(frame); frame.setVisible(true);