Multiple axes on the same data

夙愿已清 提交于 2019-11-27 05:38:28

Eventually i settled on this solution, it might not be the most elegant but it worked. I have a second axis feetAxis, and added a AxisChangeListener on the first axis called meterAxis. When the meterAxis changes set the range on feetAxis.

I used SwingUtilities.invokeLater, otherwise the range would be incorrect when zooming out of the chart, then the feetAxis would only go from 0 to 1. Didn't check why though.

feetAxis = new NumberAxis("Height [ft]");
metersAxis = new NumberAxis("Height [m]");
pathPlot.setRangeAxis(0, metersAxis);
pathPlot.setRangeAxis(1, feetAxis);

metersAxis.addChangeListener(new AxisChangeListener() {

   @Override
   public void axisChanged(AxisChangeEvent event) {

       SwingUtilities.invokeLater(new Runnable() {

           @Override
           public void run() {
               feetAxis.setRange(metersAxis.getLowerBound() * MetersToFeet, metersAxis.getUpperBound() * MetersToFeet);
                }
           });
       }
   });
trashgod

To avoid duplicating data, you can use the XYPlot method mapDatasetToRangeAxes() to map a dataset index to a list of axis indices. In the example below, meters is the principle axis, and the range of the corresponding feet axis is scaled accordingly, as shown here. Note that invokeLater() is required to ensure that the feet axis is scaled after any change in the meters axis.

import java.awt.EventQueue;
import java.util.Arrays;
import java.util.List;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.event.AxisChangeEvent;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * @see https://stackoverflow.com/q/13358758/230513
 */
public class AxisTest {

    private static final int N = 5;
    private static final double FEET_PER_METER = 3.28084;

    private static XYDataset createDataset() {
        XYSeriesCollection data = new XYSeriesCollection();
        final XYSeries series = new XYSeries("Data");
        for (int i = -N; i < N * N; i++) {
            series.add(i, i);
        }
        data.addSeries(series);
        return data;
    }

    private JFreeChart createChart(XYDataset dataset) {
        NumberAxis meters = new NumberAxis("Meters");
        NumberAxis feet = new NumberAxis("Feet");
        ValueAxis domain = new NumberAxis();
        XYItemRenderer renderer = new XYLineAndShapeRenderer();
        XYPlot plot = new XYPlot(dataset, domain, meters, renderer);
        plot.setRangeAxis(1, feet);
        plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT);
        List<Integer> axes = Arrays.asList(0, 1);
        plot.mapDatasetToRangeAxes(0, axes);
        scaleRange(feet, meters);
        meters.addChangeListener((AxisChangeEvent event) -> {
            EventQueue.invokeLater(() -> {
                scaleRange(feet, meters);
            });
        });
        JFreeChart chart = new JFreeChart("Axis Test",
            JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        return chart;
    }

    private void scaleRange(NumberAxis feet, NumberAxis meters) {
        feet.setRange(meters.getLowerBound() * FEET_PER_METER,
            meters.getUpperBound() * FEET_PER_METER);
    }

    private void display() {
        JFrame f = new JFrame("AxisTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new ChartPanel(createChart(createDataset())));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new AxisTest().display();
        });
    }
}

Alternatively, you can use a JCheckBox to flip between the two series, meters & feet, as shown in this related example. Using the methods available to XYLineAndShapeRenderer, you can hide a second series' lines, shapes and legend. The series itself must be visible to establish the axis range.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!