Search code examples
javajfreechartrenderer

How to combine two polar charts?


I founded a very interesting code that allows to combine two differents charts.

I would like to do the same with two polar charts to permit multiple scale, but there is no renderer as line and shape or bar. Is there nevertheless a way to do that ?

The aim is to plot to series with big dynamic difference like distance & site of laser shoots for each azimuth, something like that:

enter image description here

CombineBarAndLineChartExample:

package com.boraji.tutorial.jfreechart;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.data.category.DefaultCategoryDataset;

/**
 * @author imssbora
 *
 */
public class CombineBarAndLineChartExample extends JFrame {

   private static final long serialVersionUID = 1L;

   public CombineBarAndLineChartExample(String title) {
      super(title);

      // Create Category plot
      CategoryPlot plot = new CategoryPlot();

      // Add the first dataset and render as bar
      CategoryItemRenderer lineRenderer = new LineAndShapeRenderer();
      plot.setDataset(0, createDataset());
      plot.setRenderer(0, lineRenderer);

      // Add the second dataset and render as lines
      CategoryItemRenderer baRenderer = new BarRenderer();
      plot.setDataset(1, createDataset());
      plot.setRenderer(1, baRenderer);

      // Set Axis
      plot.setDomainAxis(new CategoryAxis("Time"));
      plot.setRangeAxis(new NumberAxis("Value"));

      JFreeChart chart = new JFreeChart(plot);
      chart.setTitle("Combined Bar And Line Chart | BORAJI.COM");

      ChartPanel panel = new ChartPanel(chart);
      setContentPane(panel);
   }

   private DefaultCategoryDataset createDataset() {

      // First Series
      String series1 = "Vistor";
      DefaultCategoryDataset dataset = new DefaultCategoryDataset();
      dataset.addValue(200, series1, "2016-12-19");
      dataset.addValue(150, series1, "2016-12-20");
      dataset.addValue(100, series1, "2016-12-21");
      dataset.addValue(210, series1, "2016-12-22");
      dataset.addValue(240, series1, "2016-12-23");
      dataset.addValue(195, series1, "2016-12-24");
      dataset.addValue(245, series1, "2016-12-25");

      // Second Series
      String series2 = "Unique Visitor";
      dataset.addValue(150, series2, "2016-12-19");
      dataset.addValue(130, series2, "2016-12-20");
      dataset.addValue(95, series2, "2016-12-21");
      dataset.addValue(195, series2, "2016-12-22");
      dataset.addValue(200, series2, "2016-12-23");
      dataset.addValue(180, series2, "2016-12-24");
      dataset.addValue(230, series2, "2016-12-25");

      return dataset;
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> {
         CombineBarAndLineChartExample example = new CombineBarAndLineChartExample(
               "Line Chart and Bar chart Example");
         example.setSize(800, 400);
         example.setLocationRelativeTo(null);
         example.setVisible(true);
      });
   }
}

Solution

  • While I'm not aware of any support for nesting multiple polar plots with differing scales, does support zooming, as described here. Absent a domain axis, zooming is limited to the range axis. Because the zooming controls may be counterintuitive in this context, the variation below adds plot-specific controls: The getZoomOutAction() simply invokes the chart panel's restoreAutoBounds() method, like this, while the getZoomInAction() extends the range by 50%, like this.

    public Action getZoomInAction() {
        return new AbstractAction("Zoom In") {
            @Override
            public void actionPerformed(ActionEvent e) {
                rangeAxis.setLowerBound(-rangeAxis.getUpperBound() / 2);
            }
        };
    }
    
    public Action getZoomOutAction() {
        return new AbstractAction("Zoom Out") {
            @Override
            public void actionPerformed(ActionEvent e) {
                chartPanel.restoreAutoBounds();
            }
        };
    }
    

    Zoomed in:

    Zoomed in

    Zoomed out:

    Zoomed out

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JToolBar;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.axis.NumberAxis;
    import org.jfree.chart.axis.NumberTickUnit;
    import org.jfree.chart.plot.PolarPlot;
    import org.jfree.chart.renderer.DefaultPolarItemRenderer;
    import org.jfree.chart.util.ShapeUtils;
    import org.jfree.data.xy.XYDataset;
    import org.jfree.data.xy.XYSeries;
    import org.jfree.data.xy.XYSeriesCollection;
    
    /**
     * @see https://stackoverflow.com/q/71628281/230513
     * @see https://stackoverflow.com/q/71632468/230513
     * @see https://stackoverflow.com/a/46931762/230513
     */
    public class Skyplot {
        
        public static void main(String[] args) {
            EventQueue.invokeLater(new Skyplot()::plot);
        }
        
        private void plot() {
            JFrame plotFrame = new JFrame("PolarPlot");
            plotFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            Plotter plotter = new Plotter();
            plotFrame.add(plotter);
            JToolBar toolBar = new JToolBar();
            toolBar.add(plotter.getZoomInAction());
            toolBar.add(plotter.getZoomOutAction());
            plotFrame.add(toolBar, BorderLayout.SOUTH);
            plotFrame.pack();
            plotFrame.setLocationRelativeTo(null);
            plotFrame.setVisible(true);
        }
        
        private static final class Plotter extends JPanel {
            
            private final ChartPanel chartPanel;
            private NumberAxis rangeAxis;
            
            public Plotter() {
                super(new GridLayout());
                chartPanel = createChartPanel(getXYDataset());
                this.add(chartPanel);
            }
            
            public Action getZoomInAction() {
                return new AbstractAction("Zoom In") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        rangeAxis.setLowerBound(-rangeAxis.getUpperBound() / 2);
                    }
                };
            }
            
            public Action getZoomOutAction() {
                return new AbstractAction("Zoom Out") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        chartPanel.restoreAutoBounds();
                    }
                };
            }
            
            private ChartPanel createChartPanel(XYDataset dataset) {
                JFreeChart chart = ChartFactory.createPolarChart(
                    "Random Data", dataset, true, true, false);
                
                PolarPlot polarPlot = (PolarPlot) chart.getPlot();
                polarPlot.setAngleGridlinePaint(Color.BLACK);
                polarPlot.setRadiusGridlinePaint(Color.BLACK);
                
                DefaultPolarItemRenderer renderer = (DefaultPolarItemRenderer) polarPlot.getRenderer();
                renderer.setAutoPopulateSeriesShape(false);
                renderer.setDefaultShape(ShapeUtils.createDiamond(3));
                renderer.setShapesVisible(true);
                
                rangeAxis = (NumberAxis) polarPlot.getAxis();
                rangeAxis.setTickUnit(new NumberTickUnit(100.0));
                
                return new ChartPanel(chart) {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(500, 500);
                    }
                };
            }
            
            private XYDataset getXYDataset() {
                XYSeriesCollection dataset = new XYSeriesCollection();
                dataset.addSeries(createRandomData("Series 1", 500));
                dataset.addSeries(createRandomData("Series 2", 60));
                return dataset;
            }
            
            private static XYSeries createRandomData(final String name, final double maxvalue) {
                final XYSeries series = new XYSeries(name);
                for (double az = 0.0; az < 360.0; az = az + 10 * Math.random()) {
                    final double value = maxvalue * (0.8 + 0.2 * Math.random());
                    series.add(az, value);
                }
                return series;
            }
        }
    }