Search code examples
javajfreechart

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


I need to create a chart with multiple timeseries, some of them plotted on the left hand Y axis, some of them being plotted on the right hand Y axis, some of them being lines, and other being bars.

I am creating the chart with a crosshair in the following way, with just one dataserie:

    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);
        }
}

However, I cannot understand how to access each timeseries in my unique dataserie, in order to change them into bars or in order to change their axis. I have tried this, but it changes all time series:

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

Could anyone help me with this? Thank you!


Solution

  • I finally ended up dropping the idea to use a single dataset with multiple series, and I used instead several timeseries, as suggested in the comments to my question, taking this code as model.

        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);
    
                }
        }