Search code examples
javaswingjfreechart

Abnormal Behaviour of the Zoom In and Zoom Out Functionality of the JFreeChart?


I have observed that the functionality of the "Zoom-In" and "Zoom-out" was implemented quite different than expected.Like for example when i do one step "Zoom-out" and then again after one step back of "Zoom-In". I couldn't see the Original graph again.

I don't see "Zoom-In" and "Zoom-out" functionality of JFreechart to be in the sync.

public class ChartPanelDemo {

    private static final String title = "Historical Data Graph";
    private ChartPanel chartPanel = createChart();
    private JButton ZoomX;
    private JButton ZoomY;
    private JButton Zoom;
    private JButton ZoomXOut;
    private JButton ZoomYOut;
    private JButton ZoomOut;
    private JButton originalGraph;
    private JButton xscaleInc;
    private JButton xscaleDec;
    private NumberAxis domainAxis;
    private NumberAxis rangeAxis;
    private int UNITS = 1;
    private static final int COUNT = 100;

    private JPanel panel;

    private double[][] sizeFr = { { TableLayout.FILL }, { TableLayout.FILL } };

    public ChartPanelDemo() {
        JFrame f = new JFrame(title);

        panel = new JPanel();
        f.setTitle(title);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new BorderLayout(0, 5));
        f.add(chartPanel, BorderLayout.CENTER);
        chartPanel.setMouseWheelEnabled(true); 
        // chartPanel.setHorizontalAxisTrace(true);
        // chartPanel.setVerticalAxisTrace(true);
        chartPanel.setZoomInFactor(0.9);
        chartPanel.setZoomOutFactor(0.9);

        JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        panel.add(createTrace());
        panel.add(createDate());
        panel.add(createZoom());
        panel.add(createZoomOutX());
        panel.add(createZoomOutY());
        panel.add(createZoomOut());
        panel.add(toOriginalGraph());
        panel.add(AutoScaleDecXaxis());
        //panel.add(AutoScaleDecXaxis());
        f.add(panel, BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }



    private JSpinner AutoScaleDecXaxis() {
        final JSpinner spinner = new JSpinner(
                new SpinnerNumberModel(UNITS, 1, COUNT, 1));
            spinner.addChangeListener(new ChangeListener() {

                @Override
                public void stateChanged(ChangeEvent e) {
                    JSpinner s = (JSpinner) e.getSource();
                    Number n = (Number) s.getValue();
                    domainAxis.setTickUnit(new NumberTickUnit(n.intValue()));
                }
            });
            return spinner;
    }

    private JButton toOriginalGraph() {
        originalGraph = new JButton(new AbstractAction("RestoreGraph") {

            @Override
            public void actionPerformed(ActionEvent e) {
                chartPanel.restoreAutoBounds();
            }

        });
        return originalGraph;
    }

    private JButton createZoomOut() {
        ZoomOut = new JButton(new AbstractAction("ZoomOut") {

            @Override
            public void actionPerformed(ActionEvent e) {
                ZoomOut.setActionCommand(chartPanel.ZOOM_OUT_BOTH_COMMAND);
                ZoomOut.addActionListener(chartPanel);

            }

        });
        return ZoomOut;
    }

    private JButton createZoomOutY() {
        ZoomYOut = new JButton(new AbstractAction("ZoomOutY") {

            @Override
            public void actionPerformed(ActionEvent e) {
                ZoomYOut.setActionCommand(chartPanel.ZOOM_OUT_RANGE_COMMAND);
                ZoomYOut.addActionListener(chartPanel);
            }

        });
        return ZoomYOut;
    }

    private JButton createZoomOutX() {
        ZoomXOut = new JButton(new AbstractAction("ZoomOutX") {
            @Override
            public void actionPerformed(ActionEvent e) {
                ZoomXOut.setActionCommand(chartPanel.ZOOM_OUT_DOMAIN_COMMAND);
                ZoomXOut.addActionListener(chartPanel);
            }
        });
        return ZoomXOut;
    }

    private JButton createTrace() {
        ZoomX = new JButton(new AbstractAction("ZoomInX") {

            @Override
            public void actionPerformed(ActionEvent e) {
                ZoomX.setActionCommand(chartPanel.ZOOM_IN_DOMAIN_COMMAND);
                ZoomX.addActionListener(chartPanel);
                // chartPanel.restoreAutoBounds();
            }
        });
        return ZoomX;
    }

    private JButton createDate() {
        ZoomY = new JButton(new AbstractAction("ZoomInY") {

            @Override
            public void actionPerformed(ActionEvent e) {
                ZoomY.setActionCommand(chartPanel.ZOOM_IN_RANGE_COMMAND);
                ZoomY.addActionListener(chartPanel);
            }
        });
        return ZoomY;
    }

    private JButton createZoom() {
        Zoom = new JButton(new AbstractAction("Zoom") {

            @Override
            public void actionPerformed(ActionEvent e) {
                Zoom.setActionCommand(chartPanel.ZOOM_IN_BOTH_COMMAND);
                Zoom.addActionListener(chartPanel);
            }
        });
        return Zoom;
    }

    private ChartPanel createChart() {
        XYDataset roiData = createDataset();
        JFreeChart chart = createChart(roiData);
        chart.setBackgroundPaint(new Color(0xBBBBDD));
        XYPlot plot = chart.getXYPlot();
        XYSplineRenderer r = new XYSplineRenderer(20);
        plot.setRenderer(r);
        r.setBaseShapesVisible(true);
        rangeAxis = (NumberAxis) plot.getRangeAxis();
        domainAxis = (NumberAxis) plot.getDomainAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

        return new ChartPanel(chart);
    }

    private JFreeChart createChart(XYDataset dataset) {
        JFreeChart chart = ChartFactory.createXYLineChart("Historical Data", // chart
                                                                                // title
                "X", // x axis label
                "Y", // y axis label
                dataset, // data
                PlotOrientation.VERTICAL, true, // include legend
                true, // tooltips
                false // urls
                );
        return chart;

    }

    private XYDataset createDataset() {
        XYSeries series1 = new XYSeries("First");
        series1.add(1.0, 1.0);
        series1.add(2.0, 4.0);
        series1.add(3.0, 3.0);
        series1.add(4.0, 5.0);
        series1.add(5.0, 5.0);
        series1.add(6.0, 7.0);
        series1.add(7.0, 7.0);
        series1.add(8.0, 8.0);
        XYSeries series2 = new XYSeries("Second");
        series2.add(1.0, 5.0);
        series2.add(2.0, 7.0);
        series2.add(3.0, 6.0);
        series2.add(4.0, 8.0);
        series2.add(5.0, 4.0);
        series2.add(6.0, 4.0);
        series2.add(7.0, 2.0);
        series2.add(8.0, 1.0);

        XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(series1);
        dataset.addSeries(series2);

        return dataset;
    }


    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ChartPanelDemo cpd = new ChartPanelDemo();
            }
        });
    }
}

Problem2: After two clicks on ZoomInX enter image description here

And again after two clicks on ZoomOutX(Which is the original graph) enter image description here

But Again after two clicks on ZoomInX I see the graph a different one, not as the first image in this question which should be. enter image description here


Solution

  • Your example appears to be mixing the two approaches suggested here, invoking the chart panel's handler from within your own actionPerformed() implementation. While the chart panel handlers are reusable, they are not re-entrant. Instead, use the chart panel's implementation directly. The example below focuses on two buttons to zoom the domain in and out:

    toolBar.add(createButton("Zoom In X", ZOOM_IN_DOMAIN_COMMAND));
    toolBar.add(createButton("Zoom Out X", ZOOM_OUT_DOMAIN_COMMAND));
    …
    private JButton createButton(String name, String command) {
        final JButton b = new JButton(name);
        b.setActionCommand(command);
        b.addActionListener(chartPanel);
        return b;
    }
    

    After clicking Zoom In X:

    after zoom in x

    After clicking Zoom Out X:

    after zoom out x

    This complete example re-factors the createButton() method to exercise the available zoom handlers and illustrates using a JToolBar.

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.text.NumberFormat;
    import javax.swing.JButton;
    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.plot.XYPlot;
    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
    import org.jfree.data.time.TimeSeries;
    import org.jfree.data.time.TimeSeriesCollection;
    import org.jfree.data.time.Year;
    import org.jfree.data.xy.XYDataset;
    import static org.jfree.chart.ChartPanel.*;
    
    /** @see https://stackoverflow.com/a/41337259/230513 */
    public class ZoomDemo {
    
        private static final String TITLE = "Return On Investment";
        private final ChartPanel chartPanel = createChart();
    
        public ZoomDemo() {
            JFrame f = new JFrame(TITLE);
            f.setTitle(TITLE);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLayout(new BorderLayout(0, 5));
            f.add(chartPanel, BorderLayout.CENTER);
            chartPanel.setMouseWheelEnabled(true);
    
            JPanel panel = new JPanel();
            JToolBar toolBar = new JToolBar();
            toolBar.add(createButton("Zoom In", ZOOM_IN_BOTH_COMMAND));
            toolBar.add(createButton("Zoom In X", ZOOM_IN_DOMAIN_COMMAND));
            toolBar.add(createButton("Zoom In Y", ZOOM_IN_RANGE_COMMAND));
            toolBar.add(createButton("Zoom Out", ZOOM_OUT_BOTH_COMMAND));
            toolBar.add(createButton("Zoom Out X", ZOOM_OUT_DOMAIN_COMMAND));
            toolBar.add(createButton("Zoom Out Y", ZOOM_OUT_RANGE_COMMAND));
            toolBar.add(createButton("Reset", ZOOM_RESET_BOTH_COMMAND));
            toolBar.add(createButton("Reset X", ZOOM_RESET_DOMAIN_COMMAND));
            toolBar.add(createButton("Reset Y", ZOOM_RESET_RANGE_COMMAND));
            panel.add(toolBar);
            f.add(panel, BorderLayout.SOUTH);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    
        private JButton createButton(String name, String command) {
            final JButton b = new JButton(name);
            b.setActionCommand(command);
            b.addActionListener(chartPanel);
            return b;
        }
    
        private ChartPanel createChart() {
            XYDataset roiData = createDataset();
            JFreeChart chart = ChartFactory.createTimeSeriesChart(
                TITLE, "Date", "Value", roiData, true, true, false);
            XYPlot plot = chart.getXYPlot();
            XYLineAndShapeRenderer renderer =
                (XYLineAndShapeRenderer) plot.getRenderer();
            renderer.setBaseShapesVisible(true);
            NumberFormat currency = NumberFormat.getCurrencyInstance();
            currency.setMaximumFractionDigits(0);
            NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
            rangeAxis.setNumberFormatOverride(currency);
            return new ChartPanel(chart);
        }
    
        private XYDataset createDataset() {
            TimeSeriesCollection tsc = new TimeSeriesCollection();
            tsc.addSeries(createSeries("Projected", 200));
            tsc.addSeries(createSeries("Actual", 100));
            return tsc;
        }
    
        private TimeSeries createSeries(String name, double scale) {
            TimeSeries series = new TimeSeries(name);
            for (int i = 0; i < 6; i++) {
                series.add(new Year(2016 + i), Math.pow(2, i) * scale);
            }
            return series;
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    ZoomDemo cpd = new ZoomDemo();
                }
            });
        }
    }