Wednesday, April 23, 2008

BIRT Resizing Charts

Note: I have added the reports that are used in this post to the subversion repository http://longlake.minnovent.com/repos/birt_example
the project is birt_api_example
and the reports can be found in /Reports/charts
Alternatively you can just go to:
http://longlake.minnovent.com/repos/birt_example/birt_api_example/Reports/charts/
navigate into the charts and cut and paste into your report designs

When creating charts in BIRT the size is set automatically. The chart can be resized with the mouse after the chart is in the report as well. This is fine if you know before hand the amount of data your chart is going to display. Currently there are several bugzilla entries tracking chart resize features, but I thought I would offer a work around until those entries are completed. Presented below are two options for resizing the chart. Using the simple chart API, and using the afterDataSetFilled chart event to modify the size of the chart based on the data retrieved.

One way of resizing the chart is to use the chart simple API within the beforeFactory event. The simple api allows simple properties of the chart to be changed before the report is executed.

Some examples include setting titles, chart dimensions, and output formats:


var chart1 = this.getReportElement( "Chart1" )
var chart2 = this.getReportElement( "Chart2" )
var chart3 = this.getReportElement( "Chart3" )
var chart4 = this.getReportElement( "Chart4" )

var color1 = chart1.getTitle().getCaption().getColor()

chart1.setColorByCategory( true );
chart1.getTitle().getCaption().setValue( "Color by Category" );
chart1.setColorByCategory( true );
color1.setRed( 255 );
color1.setGreen( 0 );
color1.setBlue( 0 );
chart1.getTitle().getCaption().setColor( color1 );

chart2.setDimension( "ThreeDimensional" );
chart3.getCategory().setSorting( "Descending" );
chart4.setOutputType("PNG");


In this example the charts must be named. Select the general properties for the chart and in the name field enter a value.

The size of the chart can be set using the following code in the before factory.


var mychart = this.getReportElement( "mychart1" );
if( reportContext.getOutputFormat() == "pdf" ){
mychart.setWidth("3in");
mychart.setHeight("3in");

}



In this example we resize the chart based on output format.

If you want the chart to resize based on the data populated in the chart data sets it requires a little bit of a work around. First, name the chart as with the simple chart API and enter the following code in your beforeFactory event handler.



var mychart = this.getReportElement( "mychart" );
mychart.setWidth("");
mychart.setHeight("");



After you have done that, enter code similar to the following in your afterDataSetFilled chart event handler.


function afterDataSetFilled(series, dataSet, icsc)
{
if( series.getSeriesIdentifier() == "seriesone" ){
if( dataSet.getValues().length > 4 ){
icsc.getChartInstance().getBlock().getBounds().setWidth(800);
icsc.getChartInstance().getBlock().getBounds().setHeight(600);
}else{
icsc.getChartInstance().getBlock().getBounds().setWidth(400);
icsc.getChartInstance().getBlock().getBounds().setHeight(300);

}
}
}


In this example we have named our series “seriesone” on the third tab of the chart wizard.

If the category values contain more than four entries we double the size of the chart. The sizing could be more dynamic, for example we could use the list size to increment a delta per category as well.

This example works with BIRT 2.2.2, but will not work with the chart output type set to SVG, when rendering to HTML.

For Birt 2.3, everything can be done in the beforeGeneration script and it does work with SVG. No other scripts are needed.


function beforeGeneration( chart, icsc )
{
xAxis = chart.getBaseAxes()[0];
yAxis = chart.getOrthogonalAxes( xAxis, true )[0];
seriesDef = yAxis.getSeriesDefinitions().get(0);
runSeries = seriesDef.getRunTimeSeries()[0];
//Retrieve list of data values
list = runSeries.getDataSet().getValues();
if( list.length > 3){
chart.getBlock().getBounds().setHeight(250);
chart.getBlock().getBounds().setWidth(600);
}else{
chart.getBlock().getBounds().setHeight(100);
chart.getBlock().getBounds().setWidth(600);
}
}



19 comments:

Anonymous said...

Jason,

Thanks for another handy example. I am looking to use scripts to annotate a chart by drawing a rectangle around some bars of a stock chart eg to highlight a set of bars forming a high. Is that possible with scripting?

Thank you,
Chris

Jason Weathersby said...

Chris,

You could change the datapoint label to make it different than the others. You may also be able to do something using marker ranges if you do not mind it going across the entire chart.

Jason

Anonymous said...

Hi Jason,

Thank you for the quick response. I tried both, but its not quite the same visually. The closest I got to what I wanted was to create an artificial series to act as a backdrop but even that isn't ideal as being bars, they too have gaps inbetween.

Anyway, thanks for saving me some time searching. BIRT is an amazing tool. Its hard now to think of the time when I had to create reports without it.

Thank you,
CL

Jason Weathersby said...

Chris,

Can you mock up a screenshot of what you would like?

Jason

Anonymous said...

First of all, i will apologize for my poor english.

I use Birt 2.2.2.

When I resize the chart dynamically, the size of texts defined is not respected. Text are too big.

I hope the next version of Birt will correct this.

Anonymous said...

Hi Jason,

I am facing issue in creating a report. Is it possible to show the table horizontally, ie suppose if the number of records exceeds 10 then I am supposed to display the same table adjacent to the previous one instead of displaying it vertically and so on.
For eg.

CustName telNum CustName TelNum ..

abc 2485 xyz 545
cde 4541 sad 545

I have tried it using eventhandler but didnot succeed

Thanks
Alok

Jason Weathersby said...

This can be done by placing two tables side by side in a grid and then apply a filter to each. Filter on rownum.

Anonymous said...

Hi,

I doesn't have such methods in Birt 2.3.1 :

xAxis = chart.getBaseAxes()[0];
yAxis = chart.getOrthogonalAxes( xAxis, true )[0];

Is it normal or not ?

Thanks

Jason Weathersby said...

No that is not normal.

Can you email me your report?

jasonweathersby at alltel.net

Anonymous said...

Sorry for my last post : the methods work when i run the report but they doesn't appear in the list of existing methods for the chart object.

Jason Weathersby said...

Can you please log a bug for this?

Anonymous said...

Hi Jason,

Thanks for the example. Can you post some example on how to change the series marker for the peak value in a scatter chart ?

Thanks,
MS

Jason Weathersby said...

Take a look at this example:
http://www.birt-exchange.org/org/devshare/designing-birt-reports/1225-setting-marker-size-and-type-through-script/

Anonymous said...

That is exactly what I was looking for. Thanks a lot. I am struggling to figure out which event function to use and how to change the properties of charts in it. Is there some detailed documentation available for BIRT scripting API ?

MS

Jason Weathersby said...

The Integrating BIRT Book has detail on chart scripting. You can also find several webinars that show the flow of chart scripting here:
http://www.birt-exchange.com/be/news-events/webinars/archived-webinars/

There are also many chart scripting examples on BIRT-Exchange located in the DevShare.

Also watch this webinar and get the slides, there are some hidden slides that show the chart scripting order.

http://www.birt-exchange.org/org/devshare/deploying-birt-reports/1145-birt-2.5-integration-webinar/

Anonymous said...

Hallo Jason,

shoudn't the function (last one) beforeGeneration( chart, icsc ) have to be placed between the

This example works with BIRT 2.2.2, but will not work with the chart output type set to SVG, when rendering to HTML.

and (here ?)

For Birt 2.3, everything can be done in the beforeGeneration script and it does work with SVG. No script is needed in the beforeFactory.


Cheers.

Jason Weathersby said...

What I should have said is that no afterdatasetfilled script is needed, just the beforeGeneration event.

Jason

kunal ahuja said...

Hi Jason,

Thank you for your contribution. I was facing a problem of different kind with charts. When the charts are being re sized in the eclipse environment (by dragging the edges of the chart object), the size change can be observed in preview mode. When I try to deploy the charts on tomcat the size change cannot be seen.

Please help.

I am new to BIRT

Thank you,
Kunal

Erik Marke said...

if you need to get it even more dynamically you can simly use a multiplication of the number of list items and fully dynamically size the chart.
rgd ErikM
function beforeGeneration( chart, icsc )
{
xAxis = chart.getBaseAxes()[0];
yAxis = chart.getOrthogonalAxes( xAxis, true )[0];
seriesDef = yAxis.getSeriesDefinitions().get(0);
runSeries = seriesDef.getRunTimeSeries()[0];
//Retrieve list of data values
list = runSeries.getDataSet().getValues();
//list.length is the number of items in the yAxis,
//this is then multipled with 15 points and the 40 points for the xAxis and respective labels of the xAxis
//this depends on the size of your labels, ... you might just play with the values
chart.getBlock().getBounds().setHeight((list.length*15)+40)
// if you need to define the width as well
//chart.getBlock().getBounds().setWidth(600);
}