How do I display a trendline in a stacked bar chart

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
cpb
Posts: 10
Joined: Wed Jul 29, 2015 7:12 am
antibot: No, of course not.

How do I display a trendline in a stacked bar chart

Post by cpb » Tue Sep 08, 2015 2:01 am

Hi,

I'm using Jasper Reports to generate a series of stacked bar charts. As well as the stacked bars, each chart also needs to display a trend-line.

The dataset is formatted using XML. I'm using XPath to access series/category data (this includes trend-line data). Jasper Reports has this concept of a chart customiser that provides access to the JFree API. I've used it to modify things like label positions so far.

Given a org.jfree.chart.plot.CategoryPlot is there a way of cherry picking the trending data and displaying it as a trend-line? Here's what I'm thinking:

Code: Select all

package com.myplace;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Stroke;

import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRChartCustomizer;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYSplineRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.xy.XYDataset;

public class BillingSummaryModifier3 implements JRChartCustomizer {

  public void customize(JFreeChart jfChart, JRChart jrChart) {
    CategoryPlot categoryPlot = jfChart.getCategoryPlot();
    CategoryDataset categoryDataset = categoryPlot.getDataset(5); \\ I'm guessing this is the index to my trend data
    
    // Somehow get categoryDataset into an XYPlot, then render on the chart
    XYSplineRenderer splineRenderer = new XYSplineRenderer();
    splineRenderer.setSeriesShapesVisible( 0, false );
    splineRenderer.setSeriesShapesVisible( 1, false );
    splineRenderer.setSeriesLinesVisible( 1, false );
    splineRenderer.setSeriesStroke(
      0,
      (Stroke) new BasicStroke(4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f, null, 0.0f)
    );
    splineRenderer.setSeriesPaint( 0, new Color( 255, 140, 0 ) );
    splineRenderer.setSeriesVisibleInLegend( 1, Boolean.FALSE );
    
    XYPlot xyPlot = new XYPlot();
    xyPlot.setDataset(0, (XYDataset) categoryDataset);

    XYLineAndShapeRenderer plotRenderer = (XYLineAndShapeRenderer)((XYPlot)jfChart.getPlot()).getRenderer();
    plotRenderer.setSeriesVisible( 0, Boolean.FALSE );
    plotRenderer.setSeriesVisibleInLegend( 0, Boolean.FALSE );
  }
}
Not coming from a Java background, I've relied on samples from various forums to get this far. The above fails with
net.sf.jasperreports.engine.JRException: java.lang.ClassCastException: org.jfree.chart.plot.CategoryPlot cannot be cast to org.jfree.chart.plot.XYPlot
at com.jaspersoft.studio.editor.preview.view.control.ReportControler.fillReport(ReportControler.java:506)
at com.jaspersoft.studio.editor.preview.view.control.ReportControler.access$20(ReportControler.java:481)
at com.jaspersoft.studio.editor.preview.view.control.ReportControler$5.run(ReportControler.java:362)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
Caused by: java.lang.ClassCastException: org.jfree.chart.plot.CategoryPlot cannot be cast to org.jfree.chart.plot.XYPlot
at com.oriel.BillingSummaryModifier3.customize(BillingSummaryModifier3.java:12)
at net.sf.jasperreports.engine.fill.JRFillChart.evaluateChart(JRFillChart.java:861)
Any advice on whether or not this can be done (and if so, how) would be much appreciated.

Regards
Chris

Naxter
Posts: 45
Joined: Thu Jun 26, 2014 8:24 am
antibot: No, of course not.
Location: Germany, Aachen

Re: How do I display a trendline in a stacked bar chart

Post by Naxter » Tue Sep 08, 2015 7:29 am

Your Exception says :
java.lang.ClassCastException: org.jfree.chart.plot.CategoryPlot cannot be cast to org.jfree.chart.plot.XYPlot
And this is true. CategoryPlot and XYPlot are both subclasses of "Plot", but completely different and cannot be casted to each other.
You can check this here by the way: http://www.jfree.org/jfreechart/api/jav ... /Plot.html

Actually I do not really get why you want to cast it this way, do you have any special intention?

cpb
Posts: 10
Joined: Wed Jul 29, 2015 7:12 am
antibot: No, of course not.

Re: How do I display a trendline in a stacked bar chart

Post by cpb » Tue Sep 08, 2015 10:31 am

Hi,

I have no special intention. Since sending my initial post I've been playing around with CategoryLineAnnotation. Given a JFreeChart:

1. get the categoryPlot,
2. use categoryPlot.addAnnotation(new CategoryLineAnnotation( ...))

It's still a work in progress ... It would help if there was a worked example. David Gilbert responded to this question viewtopic.php?f=3&t=23888 in 2008 but I've yet to get it working with my chart.

This is what I'm working with now. I've assumed addAnnotation will draw a line between 2 points. I'm not sure if I need to set up a renderer or if Jasper Reports just "does the right thing" with the customize method (I'll check the Jasper forums for details).

Code: Select all

import java.awt.BasicStroke;
import java.awt.Color;

import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRAbstractChartCustomizer;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.CategoryLineAnnotation;
import org.jfree.chart.plot.*;

public class BillingSummaryModifier2 extends JRAbstractChartCustomizer {

  public void customize(JFreeChart jfChart, JRChart jrChart) {
    CategoryPlot categoryPlot = jfChart.getCategoryPlot();

    categoryPlot.addAnnotation(
      new CategoryLineAnnotation(
        "Jan 2013", 5.0,
        "Feb 2013", 8.0,
        Color.black,
        new BasicStroke(2.0f)
      )
    );
    categoryPlot.addAnnotation(
        new CategoryLineAnnotation(
          "Feb 2013", 8.0,
          "Mar 2013", 2.0,
          Color.black,
          new BasicStroke(2.0f)
        )
      );
  }
}

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: How do I display a trendline in a stacked bar chart

Post by paradoxoff » Tue Sep 08, 2015 6:18 pm

What exactly is not working? Doesn´t the annotation appear on the chart? If yes, you should check whether the category keys ("Jan 2013" etc.) are indeed present in your dataset. if that seems to be the case, you are welcome to post some coede that is showing some unexpected behaviour.

cpb
Posts: 10
Joined: Wed Jul 29, 2015 7:12 am
antibot: No, of course not.

Re: How do I display a trendline in a stacked bar chart

Post by cpb » Tue Sep 08, 2015 10:27 pm

I hate it when this happens. When I gave up last night the annotation was definitely not displaying. First thing this morning I try again (no changes in the interim) and the annotation is there!

It looks like Jasper Report's customizer class is cached.

So, I can hard code a couple of annotations that show up and look correct. My next task is to loop over the values of one of the rows on my dataset creating the annotations as required. Needless to say the code below doesn't work. I get a net.sf.jasperreports.engine.JRException: java.lang.NullPointerException.

Code: Select all

import java.awt.BasicStroke;
import java.awt.Color;
import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRAbstractChartCustomizer;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.CategoryLineAnnotation;
import org.jfree.chart.plot.*;
import org.jfree.data.category.CategoryDataset;

public class BillingSummaryModifier extends JRAbstractChartCustomizer {

  public void customize(JFreeChart jfChart, JRChart jrChart) {
    CategoryPlot categoryPlot = jfChart.getCategoryPlot();
    CategoryDataset categoryDataset = categoryPlot.getDataset(5);

    for (int index=0; index < categoryDataset.getColumnCount(); index++) {
      Comparable c0 = categoryDataset.getColumnKey(index);
      Comparable c1 = categoryDataset.getColumnKey(index+1);
      Double v0 = (Double) categoryDataset.getValue(5, c0);
      Double v1 = (Double) categoryDataset.getValue(5, c1);
      categoryPlot.addAnnotation(
          new CategoryLineAnnotation(
              c0, v0,
              c1, v1,
              Color.black,
              new BasicStroke(2.0f)
          )
      );
    }
  }
}
I should also point out that I'm not a Java programmer any thoughts/comments on why this might be failing would be much appreciated.

Thanks

cpb
Posts: 10
Joined: Wed Jul 29, 2015 7:12 am
antibot: No, of course not.

Re: How do I display a trendline in a stacked bar chart

Post by cpb » Thu Sep 10, 2015 1:05 am

Hi,

Job done.

I realised the Category dataset is created by Jasper (then passed to JFreeChart) and that I needed to include the trend series. I also used DefaultCategoryDataset (instead of CategoryDataset) in order to manipulate my dataset.

Code: Select all

import java.awt.BasicStroke;
import java.awt.Color;

import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRAbstractChartCustomizer;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.CategoryLineAnnotation;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
//import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;

public class BillingSummaryModifier extends JRAbstractChartCustomizer {

  public void customize(JFreeChart jfChart, JRChart jrChart) {
    CategoryPlot categoryPlot = jfChart.getCategoryPlot();
    DefaultCategoryDataset categoryDataset = (DefaultCategoryDataset) categoryPlot.getDataset();

    CategoryItemRenderer renderer = categoryPlot.getRenderer();
    renderer.setSeriesVisible(4, false, true);
    renderer.setSeriesItemLabelsVisible(4, false);
    renderer.setSeriesVisibleInLegend(4, false);
    for (int index=0; index < categoryDataset.getColumnCount()-1; index++) {
      categoryPlot.addAnnotation(
          new CategoryLineAnnotation(
              categoryDataset.getColumnKey(index),
              (Double) categoryDataset.getValue(categoryDataset.getRowKey(4), categoryDataset.getColumnKey(index)),
              categoryDataset.getColumnKey(index+1),
              (Double) categoryDataset.getValue(categoryDataset.getRowKey(4), categoryDataset.getColumnKey(index+1)),
              Color.black,
              new BasicStroke(2.0f)
          )
      );
    }
    
    categoryDataset.removeRow(4);
  }
}

Locked