javajfreechart

JFreeChart PolarPlot: mathematical orientation


I'd like to create a polar plot where the data is plotted in mathematical orientation (thus, the series starts and the east and continues counter-clockwise). The default behavior of JFreeChart's PolarPlot is to start north and continue the series clockwise.

Is there any support for this built in the PolarPlot class? I know how to transform the data to reach the goal, but this approach is rather cumbersome, since I'd need to adapt the angle labeling too.


Solution

  • As an aside, org.jfree.chart.plot.PolarPlot appears to have been designed for navigational and geodetic applications.

    Using the transformation θ' = π/4 – θ and overriding refreshAngleTicks(), as suggested by @mort, produces reasonable results.

    Addendum: See also this variation using the new PolarPlot API. The revised example below incorporates those changes and updates to Java 21, JFreeChart 1.5.5.

    archimedes spiral

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import javax.swing.JFrame;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.axis.NumberAxis;
    import org.jfree.chart.plot.PolarAxisLocation;
    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;
    
    /**
     * @see https://en.wikipedia.org/wiki/Polar_coordinate_system
     * @see https://stackoverflow.com/q/3458824/230513
     * @see https://stackoverflow.com/a/6541516/230513
     * @see https://stackoverflow.com/a/6585876/230513
     * @see https://stackoverflow.com/a/10227275/230513
     */
    public class ArchimedesSpiral {
    
        private static final String TITLE = "Archimedes' Spiral";
    
        private static XYDataset createDataset() {
            var result = new XYSeriesCollection();
            var series = new XYSeries(TITLE);
            for (int t = 0; t <= 3 * 360; t++) {
                series.add(t, t);
            }
            result.addSeries(series);
            return result;
        }
    
        private static JFreeChart createChart(XYDataset dataset) {
            var radiusAxis = new NumberAxis();
            radiusAxis.setTickLabelsVisible(false);
            var renderer = new DefaultPolarItemRenderer();
            renderer.setShapesVisible(false);
            var plot = new PolarPlot(dataset, radiusAxis, renderer);
            plot.setCounterClockwise(true);
            plot.setAxisLocation(PolarAxisLocation.EAST_BELOW);
            plot.setAngleOffset(0);
            plot.setBackgroundPaint(new Color(0x00f0f0f0));
            plot.setRadiusGridlinePaint(Color.gray);
            plot.addCornerTextItem("r(θ) = θ; 0 < θ < 6π");
            var chart = new JFreeChart(
                TITLE, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
            chart.setBackgroundPaint(Color.white);
            return chart;
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> {
                var frame = new JFrame(TITLE);
                var chart = createChart(createDataset());
                var panel = new ChartPanel(chart) {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(500, 500);
                    }
                };
                panel.setMouseZoomable(false);
                frame.add(panel);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            });
        }
    }