How to add label values and error bars to a JFreeChart line chart?
Asked Answered
A

1

1

I'm using JFreeChart to create line charts that are exported as images and embedded into automatically generated documents. For instance one simple line chart would look like this:

simple line plot

The code I'm using to display the values would be:

LineAndShapeRenderer renderer = new LineAndShapeRenderer(true, false);
chart.getCategoryPlot().setRenderer(renderer);
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", NumberFormat.getNumberInstance()));
renderer.setBaseItemLabelsVisible(true);

I'd also like to add error bars to display the standard deviation of each point, this can be done by using a StatisticalLineAndShapeRenderer (and of course adding the error values to the data set), so that the chode above now becomes this:

StatisticalLineAndShapeRenderer renderer = new StatisticalLineAndShapeRenderer(true, false);
chart.getCategoryPlot().setRenderer(renderer);
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", NumberFormat.getNumberInstance()));
renderer.setBaseItemLabelsVisible(true);

With this code the chart is generated with the error bars, but the labels have disappeared. As it can be seen in this image:

line plot with error bars

I've tried to find an online example featuring both error bars and labels but I've failed to do so. Why are the labels disappearing when using the Statustical Renderer, is there any way around this?

EDIT: Added minimal and self contained example.

DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset();
dataset.add(1, 0.1, "serie", "A");
dataset.add(2, 0.4, "serie", "B");
dataset.add(2, 0.2, "serie", "C");

JFreeChart chart = ChartFactory.createLineChart("Chart", null, null, dataset, PlotOrientation.VERTICAL, false, true, true);
LineAndShapeRenderer renderer = new LineAndShapeRenderer(true, false);
chart.getCategoryPlot().setRenderer(renderer);
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", NumberFormat.getNumberInstance()));
renderer.setBaseItemLabelsVisible(true);

JFreeChart chartErrorBars = ChartFactory.createLineChart("ErrorBars", null, null, dataset, PlotOrientation.VERTICAL, false, true, true);
StatisticalLineAndShapeRenderer statisticalRenderer = new StatisticalLineAndShapeRenderer(true, false);
chartErrorBars.getCategoryPlot().setRenderer(statisticalRenderer);
statisticalRenderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", NumberFormat.getNumberInstance()));
statisticalRenderer.setBaseItemLabelsVisible(true);

int width = 1500;
int height = 400;

try {
    FileOutputStream fos = new FileOutputStream(new File("chart.png"));
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ChartUtilities.writeChartAsPNG(baos, chart, width, height);
    baos.writeTo(fos);
    baos.close();
    fos.close();

    fos = new FileOutputStream(new File("chartErrorBars.png"));
    baos = new ByteArrayOutputStream();
    ChartUtilities.writeChartAsPNG(baos, chartErrorBars, width, height);
    baos.writeTo(fos);
    baos.close();
    fos.close();

} catch (IOException e) {
    e.printStackTrace();
}

And the two images generated by that code.

simple Example with Values Simple Example with Error Bars

Arnhem answered 27/6, 2016 at 14:13 Comment(3)
Please edit your question to include a minimal reproducible example that shows your current approach.Ripleigh
@Ripleigh I've just added the example, should I add anything else?Arnhem
I've suggested an approach below.Ripleigh
R
1

StatisticalLineAndShapeRenderer displays labels when the parent LineAndShapeRenderer method getItemShapeVisible() returns true. In the example below, I've eschewed the chart factory and used the explicit StatisticalLineAndShapeRenderer constructor that enables both shapes and lines.

StatisticalLineAndShapeRenderer renderer
        = new StatisticalLineAndShapeRenderer(true, true);

Is there any specific reason to avoid the ChartFactory?

While repurposing the ChartFactory is expedient, it's reminiscent of ordering chicken on toast, hold the chicken, to get toast. More critically, the discarded LineAndShapeRenderer takes with it the requested tooltip and URL generators, perhaps puzzling a future maintainer.

Any way to keep the shapes disabled?

As you suggest, an empty Shape is effective, e.g.

renderer.setSeriesShape(0, new Rectangle2D.Double(0, 0, 0, 0));

image

import java.awt.Dimension;
import java.awt.EventQueue;
import java.text.NumberFormat;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.StatisticalLineAndShapeRenderer;
import org.jfree.data.statistics.DefaultStatisticalCategoryDataset;

/**
 * @see https://mcmap.net/q/1163582/-how-to-add-label-values-and-error-bars-to-a-jfreechart-line-chart
 */
public class Test {

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DefaultStatisticalCategoryDataset dataset
            = new DefaultStatisticalCategoryDataset();
        dataset.add(1, 0.1, "series", "A");
        dataset.add(2, 0.4, "series", "B");
        dataset.add(2, 0.2, "series", "C");

        CategoryAxis domain = new CategoryAxis();
        ValueAxis range = new NumberAxis();
        StatisticalLineAndShapeRenderer renderer
            = new StatisticalLineAndShapeRenderer(true, true);
        CategoryPlot plot = new CategoryPlot(dataset, domain, range, renderer);
        JFreeChart chart = new JFreeChart(
            "ErrorBars", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
        renderer.setBaseItemLabelGenerator(
            new StandardCategoryItemLabelGenerator("{2}",
                NumberFormat.getNumberInstance()));
        renderer.setBaseItemLabelsVisible(true);
        renderer.setSeriesShape(0, new Rectangle2D.Double(0, 0, 0, 0));
        new StandardChartTheme("JFree").apply(chart);
        f.add(new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(600, 300);
            }
        });

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Test()::display);
    }
}
Ripleigh answered 28/6, 2016 at 15:47 Comment(1)
I've tested as you suggested to enable the shapes in the StatisticalLineAndShapeRenderer and this already enables the labels. Is there any specific reason to avoid the ChartFactory? I believe this solves my question, but do you thing there may be any way to keep the shapes disabled? Alternatively I'll just resize them. statisticalRenderer.setSeriesShape(0, new Ellipse2D.Double(0, 0, 0, 0));Arnhem

© 2022 - 2024 — McMap. All rights reserved.