JFreechart:如何自定义单个数据集中的每个时间序列

JFreechart : how to customize each time series in a single dataset

我需要创建一个包含多个时间序列的图表,其中一些绘制在左侧 Y 轴上,一些绘制在右侧 Y 轴上,一些是线,另一些是条形图。

我正在通过以下方式创建带有十字准线的图表,只有一个数据系列:

    import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.text.SimpleDateFormat;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.panel.CrosshairOverlay;
import org.jfree.chart.plot.Crosshair;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.data.general.DatasetUtils;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;

public class Charts extends JFrame implements ChartMouseListener {

        private ChartPanel chartPanel;

        private Crosshair xCrosshair;

        private Crosshair yCrosshair;

        public Charts (List<CompanySummaryEuHistorical> sortedCos, String title) {
            super(title);
            setContentPane(createContent(sortedCos));
        }

        private JPanel createContent(List<CompanySummaryEuHistorical> sortedCos) {

            // create chart with price and nav data series
            JFreeChart chart = createChart(createDataset(sortedCos));

            // getdatasets to format it
            XYPlot plot = (XYPlot) chart.getPlot();
            plot.getDataset().getSeriesKey(0);
            plot.setRenderer(0,new XYBarRenderer());

            // create crosshair
            this.chartPanel = new ChartPanel(chart);
            this.chartPanel.addChartMouseListener(this);
            CrosshairOverlay crosshairOverlay = new CrosshairOverlay();
            this.xCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
            this.xCrosshair.setLabelVisible(true);
            this.xCrosshair.setLabelGenerator(crshr ->
                    new SimpleDateFormat("dd/MM/YYYY").format(crshr.getValue()));

            this.yCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
            this.yCrosshair.setLabelVisible(true);
            crosshairOverlay.addDomainCrosshair(xCrosshair);
            crosshairOverlay.addRangeCrosshair(yCrosshair);
            chartPanel.addOverlay(crosshairOverlay);
            return chartPanel;
        }

        private JFreeChart createChart(XYDataset dataset) {
            return ChartFactory.createTimeSeriesChart("Chart",
                    "Date", "Value", dataset);
        }

    private XYDataset createDataset(List<CompanySummaryEuHistorical> sortedCos) {
        
        TimeSeries priceSeries = new TimeSeries("Price");
        TimeSeries navSeries = new TimeSeries("Nav");
         for (CompanySummaryEuHistorical c : sortedCos) {
            Day d = new Day(c.getDate_bom().getDayOfMonth(), c.getDate_bom().getMonthValue(), c.getDate_bom().getYear());
            priceSeries.add(d, c.getPrice());
             System.out.println("adding nav " + c.getNav() + "; date: " + c.getDate_bom());
             navSeries.add(d, c.getNav());
        }

        final TimeSeriesCollection dataset = new TimeSeriesCollection(priceSeries);
        dataset.addSeries(navSeries);

        return dataset;
    }

        @Override
        public void chartMouseClicked(ChartMouseEvent event) {
            // ignore
        }

        @Override
        public void chartMouseMoved(ChartMouseEvent event) {
            Rectangle2D dataArea = this.chartPanel.getScreenDataArea();
            JFreeChart chart = event.getChart();
            XYPlot plot = (XYPlot) chart.getPlot();
            ValueAxis xAxis = plot.getDomainAxis();
            double x = xAxis.java2DToValue(event.getTrigger().getX(), dataArea,
                    RectangleEdge.BOTTOM);
            double y = DatasetUtils.findYValue(plot.getDataset(), 0, x);
            this.xCrosshair.setValue(x);
            this.yCrosshair.setValue(y);
        }
}

但是,我无法理解如何访问我独特的数据系列中的每个时间序列,以便将它们更改为条形图或更改它们的轴。我试过这个,但它改变了所有时间序列:

        XYPlot plot = (XYPlot) chart.getPlot();
        plot.getDataset().getSeriesKey(0);
        plot.setRenderer(0,new XYBarRenderer());

谁能帮我解决这个问题?谢谢!

我最终放弃了使用具有多个序列的单个数据集的想法,而是使用了多个时间序列,正如我的问题的评论中所建议的那样,以 this code 作为模型。

    public class Charts extends JFrame implements ChartMouseListener {
    
            private TimeSeries priceSeries = new TimeSeries("Price");
            private TimeSeries navSeries = new TimeSeries("Nav");

            private ChartPanel chartPanel;
    
            private Crosshair xCrosshair;
            private Crosshair yCrosshair;
            private Crosshair yNavCrosshair;
            private Crosshair yDivCrosshair;
    
            public Charts (List<CompanySummaryEuHistorical> sortedCos, String title) {
                super(title);
                setContentPane(createContent(sortedCos));
            }
    
            private JPanel createContent(List<CompanySummaryEuHistorical> sortedCos) {
    
                // create series
               createSeries(sortedCos);
               XYDataset priceData = new TimeSeriesCollection(priceSeries);

            XYLineAndShapeRenderer r0 = new XYLineAndShapeRenderer();
            r0.setSeriesPaint(0, new Color(0, 0, 0x00));
            r0.setSeriesShapesVisible(0,  false);

            XYLineAndShapeRenderer r2 = new XYLineAndShapeRenderer();
            r2.setSeriesPaint(0, new Color(255, 41, 194));
            r2.setSeriesShapesVisible(0,  false);

            XYLineAndShapeRenderer r3 = new XYLineAndShapeRenderer();
            r3.setSeriesPaint(0, new Color(255, 49, 40));
            r3.setSeriesShapesVisible(0,  false);

                // create chart with price and nav data series
            JFreeChart chart = ChartFactory.createTimeSeriesChart(
                    title, "Date", "Value", priceData, true, true, true);
            plot = (XYPlot) chart.getPlot();

            plot.setBackgroundPaint(new Color(192, 196, 196));
            NumberAxis rangeAxis1 = (NumberAxis) plot.getRangeAxis();
            rangeAxis1.setLowerMargin(0.40); // Leave room for volume bars
            rangeAxis1.setNumberFormatOverride(NumberFormat.getCurrencyInstance(getCountry(sortedCos)));
            plot.setRenderer(0, r0);
            renderer = (XYLineAndShapeRenderer) plot.getRenderer();
            renderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator(
                StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,
                    new SimpleDateFormat("dd/MM/YYYY"), NumberFormat.getCurrencyInstance()));

            // nav series in bars
            plot.setDataset(1, new TimeSeriesCollection(navSeries));
            Charts.NavRender myRenderer = new NavRender();
            myRenderer.setShadowVisible(false);
            myRenderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator(
                    StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,
                    new SimpleDateFormat("dd/MM/YYYY"), NumberFormat.getNumberInstance()));
            plot.setRenderer(1, myRenderer);

            // div series on second axis
            NumberAxis rangeAxis2 = new NumberAxis("Value");
            rangeAxis2.setUpperBound(rangeAxis1.getUpperBound()/10); // Leave room for price line
            plot.setRangeAxis(1, rangeAxis2);
            plot.setDataset(2, new TimeSeriesCollection(divSeries));
            plot.mapDatasetToRangeAxis(2, 1);
            plot.setRenderer(2, r2);

    
                // create crosshair
                this.chartPanel = new ChartPanel(chart);
                this.chartPanel.addChartMouseListener(this);
                CrosshairOverlay crosshairOverlay = new CrosshairOverlay();
                this.xCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
                this.xCrosshair.setLabelVisible(true);
                this.xCrosshair.setLabelGenerator(crshr ->
                        new SimpleDateFormat("dd/MM/YYYY").format(crshr.getValue()));
    
                this.yCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
                this.yCrosshair.setLabelVisible(true);
    
                this.yNavCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
                this.yNavCrosshair.setLabelVisible(true);
    
                this.yDivCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
                this.yDivCrosshair.setLabelVisible(true);
    
                crosshairOverlay.addDomainCrosshair(xCrosshair);                     
                crosshairOverlay.addRangeCrosshair(yCrosshair);
                
                chartPanel.addOverlay(crosshairOverlay);
                return chartPanel;
                }
        
                private JFreeChart createChart(XYDataset dataset) {
                    return ChartFactory.createTimeSeriesChart("Chart",
                            "Date", "Value", dataset);
                }
        
            private void createSeries(List<CompanySummaryEuHistorical> sortedCos) {
                for (CompanySummaryEuHistorical c : sortedCos) {
                Day d = new Day(c.getDate_bom().getDayOfMonth(), c.getDate_bom().getMonthValue(), c.getDate_bom().getYear());
                priceSeries.add(d, c.getPrice());
                navSeries.add(d, c.getNav());
                divSeries.add(d, c.getDiv());}
        }
    
            @Override
            public void chartMouseClicked(ChartMouseEvent event) {
                // ignore
            }
    
            @Override
            public void chartMouseMoved(ChartMouseEvent event) {
                Rectangle2D dataArea = this.chartPanel.getScreenDataArea();
                JFreeChart chart = event.getChart();
                XYPlot plot = (XYPlot) chart.getPlot();
                ValueAxis xAxis = plot.getDomainAxis();
                double x = xAxis.java2DToValue(event.getTrigger().getX(), dataArea,
                        RectangleEdge.BOTTOM);
                double y = DatasetUtils.findYValue(plot.getDataset(), 0, x);
                double yNav = DatasetUtils.findYValue(plot.getDataset(1), 0, x);
                double yDiv = DatasetUtils.findYValue(plot.getDataset(2), 0, x);

                this.xCrosshair.setValue(x);
                this.yCrosshair.setValue(y);
                this.yNavCrosshair.setValue(yNav);
                this.yDivCrosshair.setValue(yDiv);

            }
    }