Search code examples
javachartsjasper-reportsjfreechart

How to stack the chart series on top of each other with vertical separation?


I want to make a jasper report with vertical separation on series in line chart.

I checked all report elements but I didn't find any matching element. I attach sample image what I want

Expected output


Solution

  • The chart is generated by the library and AFIK there is no setting that will allow you to achieve this automatically.

    The solution would be generating multiple charts (one on top of the other) on each series and applying a customizer to the chart so that each chart can be adapted in base of its position.

    csv data example

    +----------+--------+--------+
    | Category | Serie1 | Serie2 |
    +----------+--------+--------+
    | A        | 1      | 0.3    |
    | B        | 0.5    | 0.2    |
    | C        | 0.7    | 0.6    |
    +----------+--------+--------+
    

    jrxml example with two charts (1 for series1 and the other for series2)

    <?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="chartTest" pageWidth="595" pageHeight="842" whenNoDataType="BlankPage" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="597c0716-df6b-42ec-a7c8-863eb1b7174a">
        <queryString>
            <![CDATA[]]>
        </queryString>
        <field name="Category" class="java.lang.String"/>
        <field name="Serie1" class="java.lang.Double"/>
        <field name="Serie2" class="java.lang.Double"/>
        <summary>
            <band height="205" splitType="Stretch">
                <lineChart>
                    <chart customizerClass="ChartCustomizer">
                        <reportElement x="171" y="0" width="200" height="100" uuid="245c4678-0cad-4342-8e54-6355c23a3c72"/>
                        <chartTitle/>
                        <chartSubtitle/>
                        <chartLegend position="Right"/>
                    </chart>
                    <categoryDataset>
                        <categorySeries>
                            <seriesExpression><![CDATA["Serie 1"]]></seriesExpression>
                            <categoryExpression><![CDATA[$F{Category}]]></categoryExpression>
                            <valueExpression><![CDATA[$F{Serie1}]]></valueExpression>
                        </categorySeries>
                    </categoryDataset>
                    <linePlot isShowShapes="false">
                        <plot/>
                        <categoryAxisFormat>
                            <axisFormat verticalTickLabels="false"/>
                        </categoryAxisFormat>
                        <valueAxisFormat>
                            <axisFormat tickLabelColor="#FF0033"/>
                        </valueAxisFormat>
                    </linePlot>
                </lineChart>
                <lineChart>
                    <chart>
                        <reportElement x="171" y="93" width="200" height="111" uuid="b53cb5dc-09cd-448d-93ea-0719c239eafe"/>
                        <chartTitle/>
                        <chartSubtitle/>
                        <chartLegend position="Right"/>
                    </chart>
                    <categoryDataset>
                        <categorySeries>
                            <seriesExpression><![CDATA["Serie 2"]]></seriesExpression>
                            <categoryExpression><![CDATA[$F{Category}]]></categoryExpression>
                            <valueExpression><![CDATA[$F{Serie2}]]></valueExpression>
                        </categorySeries>
                    </categoryDataset>
                    <linePlot isShowShapes="false">
                        <plot>
                            <seriesColor seriesOrder="0" color="#000000"/>
                        </plot>
                        <categoryAxisFormat>
                            <axisFormat verticalTickLabels="true"/>
                        </categoryAxisFormat>
                        <valueAxisFormat>
                            <axisFormat/>
                        </valueAxisFormat>
                        <rangeAxisMinValueExpression><![CDATA[0]]></rangeAxisMinValueExpression>
                        <rangeAxisMaxValueExpression><![CDATA[2]]></rangeAxisMaxValueExpression>
                    </linePlot>
                </lineChart>
            </band>
        </summary>
    </jasperReport>
    

    java class including the ChartCustomizer for chart 1 and main method to run report

    public class ChartCustomizer implements JRChartCustomizer {
    
        @Override
        public void customize(JFreeChart jfchart, JRChart jrchart) {
            CategoryPlot plot = (CategoryPlot) jfchart.getPlot();
            CategoryAxis range = plot.getDomainAxis();
            //Don't show the range axis
            range.setVisible(false);
    
            //Lets remove the 0, in your case you can do a customizer to remove the 2 on the other chart
            NumberAxis rangeAxis = new NumberAxis() {
    
                private static final long serialVersionUID = 3744076016723532336L;
    
                @SuppressWarnings({ "rawtypes", "unchecked" })
                @Override
                public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {
    
                    List allTicks = super.refreshTicks(g2, state, dataArea, edge);
                    NumberTick t0 = new NumberTick(TickType.MAJOR, 0, "", TextAnchor.CENTER_RIGHT, TextAnchor.CENTER_RIGHT, 0);
                    allTicks.set(0, t0);
                    return allTicks;
                }
            };
    
            //Set range and paint, since we replace the rangeAxis
            rangeAxis.setRange(0, 2);
            rangeAxis.setTickLabelPaint(Color.RED); 
            plot.setRangeAxis(rangeAxis);
    
        }
    
        public static void main(String[] args) throws Exception {
            JasperReport report = JasperCompileManager.compileReport("ChartTest.jrxml");
    
            JRCsvDataSource ds = new JRCsvDataSource(new File("ChartData.csv"));
            ds.setNumberFormat(NumberFormat.getInstance(Locale.US)); //. as decimal separator
            ds.setFieldDelimiter(';');
            ds.setUseFirstRowAsHeader(true);
    
            Map<String, Object> paramMap = new HashMap<String, Object>();
            JasperPrint jasperPrint = JasperFillManager.fillReport(report, paramMap, ds);
            JRPdfExporter exporter = new JRPdfExporter();
            exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
            exporter.setExporterOutput(new SimpleOutputStreamExporterOutput("ChartTest.pdf"));
            SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
            configuration.setCreatingBatchModeBookmarks(true);
            configuration.setMetadataAuthor("Petter");
            exporter.setConfiguration(configuration);
            exporter.exportReport();
        }
    }
    

    Result

    Output

    As you can see it not yet perfect, for the legend you can remove it from the chart and create it directly in jasper report, furthermore you can remove the 2.0 on second chart instead of the 0 in first chart, but further implementation I will leave to you.