同一数据上有多个轴
我试图在同一数据上有两个轴。
数据是几个DefaultTableXYDatasets
。 该图是一个XYPlot
,我有两个XYLineAndShapeRenderers
和一个StackedXYAreaRenderer2
。
所有数据都以米为单位表示y值,我希望有一个轴以米为单位显示,一个轴以英尺显示。 现在这感觉很常见,但我无法决定最明显的方法。 一种方法是复制数据并使y值以英尺为单位,然后添加另一个NumberAxis
并完成它。
但我认为NumberAxis
会更明智,或者在NumberAxis
注入一些function来扩展值。 或者我应该采用第一种方法?
你怎么看?
最终我决定使用这个解决方案,它可能不是最优雅但它有效。 我有第二个轴feetAxis
,并在名为meterAxis
的第一个轴上添加了一个meterAxis
。 当meterAxis
更改时,设置meterAxis
的范围。
我使用了SwingUtilities.invokeLater
,否则当缩小图表时范围会不正确,那么feetAxis
只会从0变为1.没有检查原因。
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); } }); } });
为避免重复数据,可以使用XYPlot
方法mapDatasetToRangeAxes()
将数据集索引映射到轴索引列表。 在下面的示例中, meters
是主轴,相应的feet
轴的范围相应地缩放,如此处所示。 请注意,需要invokeLater()
以确保在meters
轴的任何更改后缩放feet
轴。
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 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(); }); } }
或者,您可以使用JCheckBox
在两个系列,米和英尺之间进行切换,如相关示例所示。 使用XYLineAndShapeRenderer
可用的方法,您可以隐藏第二个系列的线条,形状和图例。 系列本身必须可见才能建立轴范围。