CombinedDomainXYPlot not rescaling domain axis
Asked Answered
B

2

3

When I hide number of series from a chart that is using a CombinedDomainXYPlot, all range axes are auto rescaled nicely. Howver, the domain axis does not get rescaled. Is there any way to manually refresh scaling, or perhaps there is a setting I am missing to enable auto scaling of a domain axis in this setting?

Brio answered 8/8, 2012 at 18:0 Comment(4)
It is not the model. Other plots, generated using factory methods in simple examples rescale axes on their own. it is not my model that failsBrio
I edited my answer, using 1.0.13 library btwBrio
@trashgod did you manage to reproduce my use case now?Brio
Cross posted here.Weaver
B
2

ANSWERING MY OWN QUESTION:

I managed to refresh the axis using a little hack:

 mainPlot.getDomainAxis().setAutoRange(false);
 mainPlot.getDomainAxis().setAutoRange(true);

It is not nice but it does the trick. Nevertheless, I wish someone could post a nicer solution...

Here is the code using non-custom data set that does not work. Please run it and then click on a big button called click multiple times to hide a few series. The domain axis is not rescaled.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleEdge;

public class Runner {

    private static Random rand = new Random();

    public static void main(String[] args) {
        XYSeriesCollection data = new XYSeriesCollection();
        int max = rand.nextInt(2) + 2;
        for (int i = 0; i < max; i++) {
            data.addSeries(generateSeries("Series" + (i + 1)));
        }
        final XYItemRenderer renderer1 = new StandardXYItemRenderer();
        final XYPlot plot1 = new XYPlot(data, null, new DateAxis("Dates"), renderer1);

        data = new XYSeriesCollection();
        for (int i = 0; i < max; i++) {
            data.addSeries(generateSeries("Series" + (i + 1)));
        }
        final XYPlot plot2 = new XYPlot(data, null, new NumberAxis("Numbers"), renderer1);

        final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Domain"));
        plot.setGap(10.0);

        // add the subplots...
        plot.add(plot1, 1);
        plot.add(plot2, 1);
        plot.setOrientation(PlotOrientation.VERTICAL);

        // return a new chart containing the overlaid plot...
        final JFreeChart chart = new JFreeChart("CombinedDomainXYPlot Demo",
            JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        chart.getLegend().setPosition(RectangleEdge.RIGHT);

        chart.addChangeListener(new ChartChangeListener() {

            boolean changed = false;

            @Override
            public void chartChanged(ChartChangeEvent event) {
                if (!changed) {
                } else {
                    changed = false;
                }
            }
        });

        ChartPanel panel = new ChartPanel(chart);
        JPanel panel2 = new JPanel(new BorderLayout(0, 10));
        panel2.add(panel, BorderLayout.CENTER);
        JButton b = new JButton("Click");
        b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                CombinedDomainXYPlot plot = (CombinedDomainXYPlot) chart.getXYPlot();
                List l = plot.getSubplots();
                int index = rand.nextInt(plot1.getSeriesCount() + plot2.getSeriesCount());
                boolean b = renderer1.isSeriesVisible(index);
                renderer1.setSeriesVisible(index, false);
            }
        });
        panel2.add(b, BorderLayout.NORTH);
        panel2.setVisible(true);

        JFrame frame = new JFrame("dsadsa");
        frame.add(panel2);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    private static XYSeries generateSeries(String key) {
        XYSeries series = new XYSeries(key);
        int points = 15;
        double val = 0.0;
        double x = 0.0;
        for (int i = 0; i < points; i++) {
            val += rand.nextDouble() * 6 - 3;
            x += rand.nextDouble() * 4;
            series.add(x, val);
        }
        return series;
    }
}
Brio answered 8/8, 2012 at 18:10 Comment(0)
W
4

CombinedDomainXYPlot establishes the combined maximal Range for its shared domain axis in getDataRange(). This is required to allow the axis to be shared. Changing the visibility of a series has no effect on the shared domain axis; changing the Dataset updates the shared domain axis via its configure() method. In either case, the subplots' range axes can be updated indepedently.

The example below allows one to update a subplot or change a series' visibility independently. Break on configure() to see the effect. Your suggestion to toggle setAutoRange () can be replaced with a single call to configure(); but the effect should be nil, as the data and its combined maximal Range are unchanged.

mainPlot.getDomainAxis().configure();

To automatically update the shared domain axis, use addSeries() or removeSeries() instead of setSeriesVisible().

As an aside, don't neglect Initial Threads and pack().

combined plot image

import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import static org.jfree.chart.renderer.xy.StandardXYItemRenderer.*;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * @see https://mcmap.net/q/429994/-how-to-create-multiple-scatterplot-chart-using-createcombinedchart-on-jfreechart
 * @see https://mcmap.net/q/429255/-combineddomainxyplot-not-rescaling-domain-axis/230513
 */
public class CombinedPlot {

    private static final int MAX = 3;
    private static final Random RND = new Random();

    public static void main(String[] args) {
        EventQueue.invokeLater(CombinedPlot::init);
    }

    private static void init() {
        XYItemRenderer renderer = new StandardXYItemRenderer(SHAPES_AND_LINES);
        XYPlot plot1 = new XYPlot(
            generateData(), null, new NumberAxis("Range 1"), renderer);
        XYPlot plot2 = new XYPlot(
            generateData(), null, new NumberAxis("Range 2"), renderer);
        final CombinedDomainXYPlot plot =
            new CombinedDomainXYPlot(new NumberAxis("Domain"));
        plot.setDomainPannable(true);
        plot.setRangePannable(true);
        plot.add(plot1);
        plot.add(plot2);
        plot.setOrientation(PlotOrientation.VERTICAL);
        JFreeChart chart = new JFreeChart(
            "Combined Plots", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new Dimension(800, 500));

        JPanel controlPanel = new JPanel();
        controlPanel.add(new JButton(new UpdateAction(plot, 0)));
        controlPanel.add(new JButton(new UpdateAction(plot, 1)));
        for (int i = 0; i < MAX; i++) {
            JCheckBox jcb = new JCheckBox(new VisibleAction(renderer, i));
            jcb.setSelected(true);
            renderer.setSeriesVisible(i, true);
            controlPanel.add(jcb);
        }

        JFrame frame = new JFrame("Combined Plot Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(chartPanel, BorderLayout.CENTER);
        frame.add(controlPanel, BorderLayout.SOUTH);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static class UpdateAction extends AbstractAction {

        private final XYPlot plot;

        public UpdateAction(CombinedDomainXYPlot plot, int i) {
            super("Update plot " + (i + 1));
            this.plot = (XYPlot) plot.getSubplots().get(i);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            plot.setDataset(CombinedPlot.generateData());
        }
    }

    private static class VisibleAction extends AbstractAction {

        private XYItemRenderer renderer;
        private int i;

        public VisibleAction(XYItemRenderer renderer, int i) {
            super("Series " + (i + 1));
            this.renderer = renderer;
            this.i = i;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            renderer.setSeriesVisible(i, !renderer.getSeriesVisible(i));
        }
    }

    private static XYSeriesCollection generateData() {
        XYSeriesCollection data = new XYSeriesCollection();
        for (int i = 0; i < MAX; i++) {
            data.addSeries(generateSeries("Series " + (i + 1)));
        }
        return data;
    }

    private static XYSeries generateSeries(String key) {
        XYSeries series = new XYSeries(key);
        for (int i = 0; i < 16; i++) {
            series.add(RND.nextGaussian(), RND.nextGaussian());
        }
        return series;
    }
}
Weaver answered 10/8, 2012 at 4:58 Comment(2)
Firstly thank you very much for your comments and effort you took to establish what you posted here. Many thanks. I am still puzzled about how f***ing bad the implementation of certain components is. Surely, the domain axis should rescale if I hide a series on ANY plot i.e, if I have two data sets, one range from 1 - 10, the other from 1 - 15, and then I hide the plot on the second data set, making its range 1-12, Then the COMBINED range should be 1-12... Why is that not a default behaviour? Considering auto rescaling of axis has not been in place unitl 1.0.13 I am not surprised...Brio
I can see the default behavior as less visually disruptive on a combined plot. Excluding invisible series would also increase the overhead of getDataRange(). It's a trade-off.Weaver
B
2

ANSWERING MY OWN QUESTION:

I managed to refresh the axis using a little hack:

 mainPlot.getDomainAxis().setAutoRange(false);
 mainPlot.getDomainAxis().setAutoRange(true);

It is not nice but it does the trick. Nevertheless, I wish someone could post a nicer solution...

Here is the code using non-custom data set that does not work. Please run it and then click on a big button called click multiple times to hide a few series. The domain axis is not rescaled.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleEdge;

public class Runner {

    private static Random rand = new Random();

    public static void main(String[] args) {
        XYSeriesCollection data = new XYSeriesCollection();
        int max = rand.nextInt(2) + 2;
        for (int i = 0; i < max; i++) {
            data.addSeries(generateSeries("Series" + (i + 1)));
        }
        final XYItemRenderer renderer1 = new StandardXYItemRenderer();
        final XYPlot plot1 = new XYPlot(data, null, new DateAxis("Dates"), renderer1);

        data = new XYSeriesCollection();
        for (int i = 0; i < max; i++) {
            data.addSeries(generateSeries("Series" + (i + 1)));
        }
        final XYPlot plot2 = new XYPlot(data, null, new NumberAxis("Numbers"), renderer1);

        final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Domain"));
        plot.setGap(10.0);

        // add the subplots...
        plot.add(plot1, 1);
        plot.add(plot2, 1);
        plot.setOrientation(PlotOrientation.VERTICAL);

        // return a new chart containing the overlaid plot...
        final JFreeChart chart = new JFreeChart("CombinedDomainXYPlot Demo",
            JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        chart.getLegend().setPosition(RectangleEdge.RIGHT);

        chart.addChangeListener(new ChartChangeListener() {

            boolean changed = false;

            @Override
            public void chartChanged(ChartChangeEvent event) {
                if (!changed) {
                } else {
                    changed = false;
                }
            }
        });

        ChartPanel panel = new ChartPanel(chart);
        JPanel panel2 = new JPanel(new BorderLayout(0, 10));
        panel2.add(panel, BorderLayout.CENTER);
        JButton b = new JButton("Click");
        b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                CombinedDomainXYPlot plot = (CombinedDomainXYPlot) chart.getXYPlot();
                List l = plot.getSubplots();
                int index = rand.nextInt(plot1.getSeriesCount() + plot2.getSeriesCount());
                boolean b = renderer1.isSeriesVisible(index);
                renderer1.setSeriesVisible(index, false);
            }
        });
        panel2.add(b, BorderLayout.NORTH);
        panel2.setVisible(true);

        JFrame frame = new JFrame("dsadsa");
        frame.add(panel2);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    private static XYSeries generateSeries(String key) {
        XYSeries series = new XYSeries(key);
        int points = 15;
        double val = 0.0;
        double x = 0.0;
        for (int i = 0; i < points; i++) {
            val += rand.nextDouble() * 6 - 3;
            x += rand.nextDouble() * 4;
            series.add(x, val);
        }
        return series;
    }
}
Brio answered 8/8, 2012 at 18:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.