Thursday, December 11, 2008

BIRT - Reversing a Chart Scale.

The Chart Engine that is delivered as part of the BIRT project offers a lot of functionality. Not only can you extend most of its features, the chart engine also offers hooks to do scripting (either in Java or JavaScript). Scripting can be very useful for modifying the chart based on parameters or data. As an example assume you have a chart that is displayed as follows:



Assume these numbers represent percent of sales returns and the lower the bar the better. In some cases larger bars graphically represent better values, so instead of displaying the chart as above the user may wish to see the chart as follows.



One way to achieve this effect is to modify the values and labels in chart script. To modify or add scripts to a chart select the chart and then the script tab within the designer. Bear in mind that although this example illustrates building the event handler in JavaScript it can also be done with Java.

The first thing to setup is the scale for the chart, which we want to tie to a report parameter. So you can enter a script like:


//Global scale
gblscale = 100.00;


//this is the first event called for charting
//So set scale here
function beforeDataSetFilled( series, idsp, icsc )
{
gblscale = icsc.getExternalContext().getScriptable().getParameterValue("Scale");
}

We set the scale for the chart to the parameter value in the beforeDataSetFilled event because this is the first event called for chart scripts. This variable will be used in other scripts for calculating labels.

The next script we need to override is the afterDataSetFilled event handler. This script can be used to modify the values used in a chart.

function afterDataSetFilled(series, dataSet, icsc)
{
if( series.getSeriesIdentifier() == "series1"){
var list = dataSet.getValues();
for ( i=0; i < list.length; i=i+1)
{
list[i] = gblscale - list[i];
}
}
}



This script first checks the series identifier as this call is made for every series, but we only want to change the bar series. The series identifier should be entered in the chart wizard. This can be done in the third tab of the chart wizard.



Once we know that we are working with the correct series, we can modify the values by calling the dataSet.getValues. In the script we are just subtracting the values from the global scale and resetting the value. So if the global scale is 100, a 5 value becomes 95.

We can then set the scale for the entire chart in the beforeGeneration event as follows.


function beforeGeneration(chart, icsc)
{
importPackage( Packages.org.eclipse.birt.chart.model.data.impl );
xAxis = chart.getBaseAxes()[0];
yAxis = chart.getOrthogonalAxes( xAxis, true)[0]
yscale = yAxis.getScale();
yscale.setStep (10);
yscale.setMin( NumberDataElementImpl.create(0) )
yscale.setMax( NumberDataElementImpl.create(gblscale) )
yAxis.setScale(yscale);

}

By default this script sets the Y axis to range from 0 to whatever the parameter value is set to. We now can reverse the labels by adding a beforeDrawAxisLabel script.

function beforeDrawAxisLabel(axis, label, icsc)
{
importPackage(Packages.org.eclipse.birt.chart.model.attribute);
if (axis.getType() == AxisType.LINEAR_LITERAL){
var val = parseFloat(label.getCaption().getValue());
var newval = gblscale - val;
label.getCaption().setValue(newval);
}
}

This script only changes the labels. Internally the chart is still scaled from 0 to the parameter value. So if the scale is 100 and the original value is 5, its new value is 95 and the bar will be drawn from 100 to 5. Internally this is still 0 – 95, but we reversed the labels. The last script we may want to add can change the data point labels. This script is called beforeDrawDataPointLabel and is called whenever the chart renders labels on the bars.

function beforeDrawDataPointLabel(dph, label, icsc)
{
var val = parseFloat(label.getCaption().getValue());
var newval = gblscale - val;
newval = newval.toFixed(2);
label.getCaption().setValue(newval);
}

In this script we just reverse the value label. This example can be downloaded at
BIRT Exchange.