Hot questions for Using JasperReports in bar chart

Question:

I have a stacked bar chart in JasperSoft 6.3.1 in eclipse and I am trying to display the colors based on the series. The chart displays random colors instead of assigning a single color for a specific series.

jrxml

<categorySeries>
    <seriesExpression><![CDATA[$F{name}]]></seriesExpression>
    <categoryExpression><![CDATA[$F{time}]]></categoryExpression>
    <valueExpression><![CDATA[$F{value}]]></valueExpression>
</categorySeries>
</categoryDataset>
    <barPlot>
        <plot>
            <seriesColor $F{name}.equals("JANUARY")?color="#756D72":color="" seriesOrder="0" />
            <seriesColor $F{name}.equals("MARCH")?color="#4B5154":color="" seriesOrder="1" />
            <seriesColor $F{name}.equals("JUNE")?color="#090A09":color="" seriesOrder="2"/>
        </plot>
    <itemLabel/>
    <categoryAxisFormat>
    ....

I am trying to assign the chart series color to a specific series name using the if statement. How do I achieve this in jasper reports?.

If the series name is JANUARY the color should be black, and if there is no data for JANUARY the black color should not be used.


Answer:

As I guess that you have noticed, you can not do if statements in the xml tags, the jrxml will simple not compile since it's not valid xml anymore.

The solution is to implement your own JRChartCustomizer

Example

java

Find the different series name and set Paint to the renderer on basis of the name

public class BarColorCustomizer implements JRChartCustomizer {

    @Override
    public void customize(JFreeChart jfchart, JRChart jrchart) {
        //Get the plot
        CategoryPlot  plot = jfchart.getCategoryPlot();
        //Get the dataset
        CategoryDataset dataSet = plot.getDataset();
        //Loop the row count (our series)
        int rowCount = dataSet.getRowCount();
        for (int i = 0; i < rowCount; i++) {
            Comparable<?> rowKey = dataSet.getRowKey(i);
            //Get a custom paint for our series key
            Paint p = getCustomPaint(rowKey);
            if (p!=null){
                //set the new paint to the renderer
                plot.getRenderer().setSeriesPaint(i, p);
            }
        }

    }

    //Example of simple implementation returning Color on basis of value
    private Paint getCustomPaint(Comparable<?> rowKey) {
        if ("JANUARY".equals(rowKey)){
            return Color.BLACK;
        }
        return null;
    }
}

jrxml

Set the customizerClass attribute with full package name on the chart tag

<barChart>
    <chart evaluationTime="Report" customizerClass="my.custom.BarColorCustomizer">
    ....
</barChart>

Question:

I'm trying to designing a report with bar chart, in which I need to add a threshold. I have tried with multi-axis chart, in which the scale in different axis is always different.

Is there any other solution to add line to bar chart?

My expect output is a chart as below:


Answer:

To draw a line on the bar chart you add a ValueMarker to the CategoryPlot.

In jasper report this is done my adding a JRChartCustomizer

public class MyChartCustomizer implements JRChartCustomizer {

    @Override
    public void customize(JFreeChart jfchart, JRChart jrchart) {
        CategoryPlot plot = (CategoryPlot) jfchart.getPlot();
        //Set at what value you like the line, its color and size of stroke
        ValueMarker vm = new ValueMarker(13000,Color.BLUE, new BasicStroke(2.0F));
        //add marker to plot
        plot.addRangeMarker(vm);
    }
}

In jrxml make sure your class is in classpath and set the customizerClass attribute on the chart tag

<barChart>
    <chart customizerClass="MyChartCustomizer">
   ....
    </chart>
   ...
</barChart>

If you are using dynamic-reports you can add it directly in code

chart.addCustomizer(new DRIChartCustomizer() {      
    private static final long serialVersionUID = 1L;
    @Override
    public void customize(JFreeChart chart, ReportParameters arg1) {
        CategoryPlot plot = (CategoryPlot) jfchart.getPlot();
        ValueMarker vm = new ValueMarker(13000,Color.BLUE, new BasicStroke(2.0F));
        plot.addRangeMarker(vm);
    }
});

If you are using dynamic-jasper setCustomizerClass (as in jrxml)

DJBarChartBuilder().setCustomizerClass("MyChartCustomizer");

Example of result

Note: in example no package name is used, if MyChartCustomizer is in a package full package name needs to be indicated in setCustomizerClass example "my.package.MyChartCustomizer"

Question:

I am customizing the default charts generated by Jasper Reports and I cannot find the way to make the axis lines thicker.

So far I easily found examples to change the color: http://www.jfree.org/forum/viewtopic.php?f=3&t=11639 but this is easier to be changed by the editor.

I attach an image to illustrate the expected thickness compared to the current one.

This is my current customizer:

public void customize(JFreeChart jFreeChart, JRChart jrChart) {

    CategoryPlot plot = (CategoryPlot) jFreeChart.getPlot();
    plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);


    BarRenderer barRenderer = (BarRenderer) plot.getRenderer();
    barRenderer.setItemMargin(0.0);

    NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
    NumberFormat numberFormat= NumberFormat.getNumberInstance();
    numberFormat.setMaximumFractionDigits(2);
    numberFormat.setMinimumFractionDigits(2);
    rangeAxis.setNumberFormatOverride(numberFormat);
    rangeAxis.setUpperMargin(0.2);
    rangeAxis.setAutoRange(true); //make sure that fixed range is not set
}

GitHub: https://github.com/MichaelKnight/jaspertest.git


Answer:

To set the Stroke of CategoryAxis and NumberAxis add the following code in your customize:

CategoryAxis categoryAxis = plot.getDomainAxis();
categoryAxis.setAxisLineStroke(new BasicStroke(2f)); //see API link below for BasicStroke

NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setAxisLineStroke(new BasicStroke(2f));

BasicStroke API

Question:

I have designed a bar chart with String value in categorie CRANE_NUMBER and value expression REMAINING_TIME is java.util.Date.

When I run the report, I got error below.

Grave:   java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.Number
    at net.sf.jasperreports.charts.fill.JRFillCategorySeries.evaluate(JRFillCategorySeries.java:141)
    at net.sf.jasperreports.charts.fill.JRFillCategoryDataset.customEvaluate(JRFillCategoryDataset.java:110)
    at net.sf.jasperreports.engine.fill.JRFillElementDataset.evaluate(JRFillElementDataset.java:172)
    at net.sf.jasperreports.engine.fill.JRCalculator.calculateVariables(JRCalculator.java:187)
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillDetail(JRVerticalFiller.java:735)
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReportStart(JRVerticalFiller.java:255)
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport(JRVerticalFiller.java:115)
    at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:582)
    at net.sf.jasperreports.engine.fill.BaseReportFiller.fill(BaseReportFiller.java:414)
    at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:121)
    at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:667)
    at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:648)
    at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:970)

Is it possibile to show remaining time HH:mm in a barchart on the y-axis?


Answer:

To create a bar chart with HH:mm on range axis, I would use a JRChartCustomizer

I will show a full example of how this can be achieved using the chart customizer, the library to renderer charts used by jasper-reports is jfreechart.

The JRChartCustomizer

public class ChartHHmmCustomizer implements JRChartCustomizer {

    @Override
    public void customize(JFreeChart jfchart, JRChart jrchart) {

        //Get the category plot
        CategoryPlot plot = (CategoryPlot) jfchart.getPlot();

        //Crete a date axis
        DateAxis yAxis = new DateAxis();
        //Override the date format
        yAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
        //Customize tick unit
        yAxis.setTickUnit(new DateTickUnit(DateTickUnitType.HOUR, 1));
        //Set it to the range axis
        plot.setRangeAxis(yAxis);

        //adding a customize item label renderer to view the valus on the barchart
        CategoryItemRenderer renderer = ((CategoryPlot) jfchart.getPlot()).getRenderer();
        renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}",new SimpleDateFormat("HH:mm")));
        renderer.setBaseItemLabelsVisible(true);
        ItemLabelPosition position = new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.TOP_CENTER);
        renderer.setBasePositiveItemLabelPosition(position);
    }
}

The jrxml

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="timeBarChart" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="f71aa6b8-70ce-4de9-9e62-f8bd196e40f8">
    <queryString>
        <![CDATA[]]>
    </queryString>
    <field name="CraneNumber" class="java.lang.String"/>
    <field name="RemaningTime" class="java.util.Date"/>
    <columnHeader>
        <band height="20">
            <staticText>
                <reportElement x="0" y="0" width="100" height="20" uuid="af39006a-cd21-46c1-b74f-69fd3c570031"/>
                <textElement verticalAlignment="Middle">
                    <font isBold="true"/>
                </textElement>
                <text><![CDATA[CraneNumber]]></text>
            </staticText>
            <staticText>
                <reportElement x="100" y="0" width="100" height="20" uuid="83918a9e-3cd4-4581-8304-35246f8db5a9"/>
                <textElement verticalAlignment="Middle">
                    <font isBold="true"/>
                </textElement>
                <text><![CDATA[RemaningTime]]></text>
            </staticText>
        </band>
    </columnHeader>
    <detail>
        <band height="20" splitType="Stretch">
            <textField pattern="HH:mm">
                <reportElement x="100" y="0" width="100" height="20" uuid="a699d0ea-03fa-4232-8bc3-7b6c72c8e13c"/>
                <textElement verticalAlignment="Middle"/>
                <textFieldExpression><![CDATA[$F{RemaningTime}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="0" y="0" width="100" height="20" uuid="bbb040e5-4958-435a-90eb-b52f231ed5cd"/>
                <textElement verticalAlignment="Middle"/>
                <textFieldExpression><![CDATA[$F{CraneNumber}]]></textFieldExpression>
            </textField>
        </band>
    </detail>
    <summary>
        <band height="185" splitType="Stretch">
            <barChart>
                <chart isShowLegend="false" customizerClass="my.package.ChartHHmmCustomizer">
                    <reportElement x="66" y="29" width="356" height="141" uuid="60bdb0d1-8c39-4fdc-98b9-d1c5458cb665"/>
                    <chartTitle/>
                    <chartSubtitle/>
                    <chartLegend/>
                </chart>
                <categoryDataset>
                    <categorySeries>
                        <seriesExpression><![CDATA["My cranes"]]></seriesExpression>
                        <categoryExpression><![CDATA[$F{CraneNumber}]]></categoryExpression>
                        <valueExpression><![CDATA[$F{RemaningTime}.getTime()]]></valueExpression>
                    </categorySeries>
                </categoryDataset>
                <barPlot>
                    <plot/>
                    <itemLabel/>
                    <categoryAxisFormat>
                        <axisFormat/>
                    </categoryAxisFormat>
                    <valueAxisLabelExpression/>
                    <valueAxisFormat>
                        <axisFormat/>
                    </valueAxisFormat>
                </barPlot>
            </barChart>
        </band>
    </summary>
</jasperReport>

Note: That I add the customizerClass="my.package.ChartHHmmCustomizer"to chart component and as valueExpression I set $F{RemaningTime}.getTime(), hence the time in milliseconds (a number)

Some data

+-------------+--------------+
| CraneNumber | RemaningTime |
+-------------+--------------+
| Crane1      | 04:20        |
| Crane2      | 02:13        |
| Crane3      | 06:31        |
+-------------+--------------+

Output

Question:

We are using the jfreechart with Jasper reports and we are struggling to put the benchmark line on the bar chart.

How can this be achieved using jasper reports?


Answer:

To customize your bar chart in jasper report create a customizer class (ChartCustomizer) extending the JRChartCustomizer.

public void customize(JFreeChart chart, ChartComponent chartComponent)
{
  //get the ploy
  CategoryPlot plot = (CategoryPlot) chart.getPlot();

  //Now add your markers
  ValueMarker vm = new ValueMarker(200); //200 is the position you like it to be
  vm.setPaint(Color.RED);
  vm.setStroke(new BasicStroke(1));
  vm.setLabel("BeanchMark value"); //The label
  vm.setLabelAnchor(RectangleAnchor.TOP);
  vm.setLabelTextAnchor(TextAnchor.BOTTOM_RIGHT);
  plot.addRangeMarker(vm);
}

add the class to classpath and in jrxml set the customizerClass attribute

<barChart>
    <chart customizerClass="my.package.ChartCustomizer">
   ....
    </chart>
   ...
</barChart>

Question:

I'm using TIBCO Jaspersoft Studio version 6.8.0 and I'm creating a barChart as a subReport of a main report. The barChart takes data from a sub dataset. The report is creating quite well so no complaints about that. The barChart is built dinamically setting the width of the bars based on the number of the elements. But I'd like to set statically the width of the bars, ignoring how many elements there are. So it would be a bar large 10 pixels (for example) both when there are 10 elements or 2 elements. Here it is a picture showing the barChart with the correct bars' width (based on element number in it)

And here we have the same barChart but with less elements, so the bars' width would just resize dynamically (i want to set a static value to their width)

I followed some guide for creating and using a custom function for a barChart, but none of these seems to work (maybe I'm using a newer version and they were referring to an older version of Jasper Report) I was using this, but I dont know if it's good and how to implement it in a report

package com.efarmgroup.sina.sios4.jasper;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.*;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.*;
import org.jfree.chart.ui.TextAnchor;

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


public class customBarWidth implements JRChartCustomizer{

    @Override
    public void customize(JFreeChart chart, JRChart jasperChart){

        CategoryPlot categoryPlot = chart.getCategoryPlot();
        BarRenderer br = (BarRenderer) categoryPlot.getRenderer();
        br.setMaximumBarWidth(.10); // set maximum width to 35% of chart
    }
}

Answer:

I resolved my problem (double problem). Inside the function I wrote

br.setMaximumBarWidth(.01); // set maximum width to 1% of chart

And this worked fine for what I needed. The other problem was about importing the function inside the report. Through an ant file I managed to create the Jar that contains the compilated function (.class file).

<project name="projectName" default="createjar">    
   <target name="createjar">
      <jar destfile="output-jar/jarName.jar" basedir="./bin" />
   </target>    
</project>

After this, I had the .class file I needed to import and use the function in the report.

Question:

I'm creating a barChart in a subReport. I pass the dataset completely constructed via code (nodeJS) and it works pretty fine.

Now I have to give always the same color (of the bar in the barChart) to same element. The number of elements in the barChart is dynamic, so I can't do it static (I did it like that previously).

For example:

  • Item 1 would always have the color black;
  • Item 2 always blue;
  • Item 3 always pink.

And so on. Of course there will be occasion when there is only Item 3, so normally a standard series "0" color would be assigned to it. But I need that Item to be always pink, regardless if it is the first item, the third, etc.

I've come here, but I don't know how to go further. I've found some answers but they don't achieve what I want

package com.efarmgroup.sina.sios4.jasper;


import java.awt.Color;
import net.sf.jasperreports.engine.JRChart;
import net.sf.jasperreports.engine.JRChartCustomizer;
import org.jfree.chart.JFreeChart;
import org.jfree.data.category.*;
import org.jfree.chart.renderer.category.*;
import java.util.HashMap;



public class customColorSeries implements JRChartCustomizer {


    @Override
    public void customize(JFreeChart chart, JRChart jasperChart) {

        //This is the list of the object. Each of them should have a standard color
        Object[] objectList = {"BULLONERIA", "FONDAZIONE o BASAMENTO", "SPALLE", "PILE", "PULVINI", "TRAVI",
                            "TRAVERSI", "IMPALCATI", "CORDOLI", "ARCHI", "APPARECCHI SPECIALI", "SISTEMAZIONE IDRAULICA",
                            "MURO", "APPOGGI", "BARRIERA", "GIUNTI", "Portale", "Piedritto sinistro", "Paramento sinistro",
                            "Volta", "Paramento destro", "Piedritto destro", "Sovrastruttura stradale",
                            "By Pass / Nicchia", "Elementi accessori"};



        // Category Plot: most commonly used to display bar chart
        // BarRenderer: create bar charts from data in a category dataset

        BarRenderer renderer = (BarRenderer) chart.getCategoryPlot().getRenderer();

        // Method required for reading the dataset.
        // Table of values that can be accessed using row and column keys.
        CategoryDataset cd = chart.getCategoryPlot().getDataset();

        // Row gives the series expression
        int rc = cd.getRowCount();
        for (int i = 0; i < rc; i++) {

            //Here I should retrieve the the value of the dataset to perform a simple if
        }
    }

}

This is a simple example of the sub data set

evolution_data: 
  [

     { Building_ID: 1,
       Building_Code: 'F010',
       Building_Name: 'Quiliano',
       Evaluation: 50,
       Object_Name: 'APPOGGI',
       Span_Code: 1,
       seriesOrder: 0,
       color: '#FC6D79' },
     { Building_ID: 1,
       Building_Code: 'F010',
       Building_Name: 'Quiliano',
       Evaluation: 8,
       Object_Name: 'BARRIERA',
       Span_Code: 1,
       seriesOrder: 0,
       color: '#FC6D79' },
     { Building_ID: 1,
       Building_Code: 'F010',
       Building_Name: 'Quiliano',
       Evaluation: 5,
       Object_Name: 'APPOGGI',
       Span_Code: 2,
       seriesOrder: 1,
       color: '#A64186' },
     { Building_ID: 1,
       Building_Code: 'F010',
       Building_Name: 'Quiliano',
       Evaluation: 3,
       Object_Name: 'BARRIERA',
       Span_Code: 2,
       seriesOrder: 1,
       color: '#A64186' },
     { Building_ID: 1,
       Building_Code: 'F010',
       Building_Name: 'Quiliano',
       Evaluation: 0,
       Object_Name: 'APPOGGI',
       Span_Code: 3,
       seriesOrder: 2,
       color: '#9758C4' },
     { Building_ID: 1,
       Building_Code: 'F010',
       Building_Name: 'Quiliano',
       Evaluation: 0,
       Object_Name: 'BARRIERA',
       Span_Code: 3,
       seriesOrder: 2,
       color: '#9758C4' }]

The color should be based on the "Object_Name" field in the dataset.


Answer:

My problem is solved, here's my solution:

public class customColorSeries  extends JRAbstractChartCustomizer  {

    @Override
    public void customize(JFreeChart chart, JRChart jasperChart) {

        BarRenderer renderer = (BarRenderer) chart.getCategoryPlot().getRenderer();
        CategoryDataset catDS = chart.getCategoryPlot().getDataset();
        com.fasterxml.jackson.databind.node.ArrayNode seriesColor = (ArrayNode) getParameterValue("seriesColor_P");
        HashMap<String, String> customMap = new HashMap<>();

        for (int z = 0; z < seriesColor.size(); z++) {  
            customMap.put(seriesColor.get(z).get("Object_Name").textValue(), seriesColor.get(z).get("Color").textValue());
        }

        for (int i = 0; i < catDS.getRowCount(); i++) {
            if(customMap.containsKey(catDS.getRowKey(i).toString())) {
                renderer.setSeriesPaint(i, Color.decode(customMap.get(catDS.getRowKey(i).toString())));
            }
        }
    }        
}

I send an array of object, where every object is built like this

{Object_Name: 'APPOGGI', Color: '#ff0000'} 
{Object_Name: 'SPALLE', Color: '#f2ca79'}

and so on....

as a parameter to the sub report. I then use the databind and the HashMap to treat it and render it at the end.