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.
Thursday, December 11, 2008
BIRT - Reversing a Chart Scale.
Posted by
Jason Weathersby
at
2:08 PM
3
comments
Labels: Charts
Monday, November 24, 2008
Friday is the last Day for EclipseCon 2009 proposals
I just wanted to put a post up to let everyone know that the deadline for submissions for EclipseCon 2009 has been extended to Friday (11/28/2008 5pm PST). We are still looking for some use case proposals in the Reporting track and would appreciate feedback on the existing submissions. If you are interested, the EclipseCon 2009 page is located here.
Posted by
Jason Weathersby
at
8:26 AM
3
comments
Wednesday, November 05, 2008
BIRT Connection Pooling Continued Again
With the release of BIRT 2.3.1 connection pooling options have been extended.
In a prior post, I built an example that showed how to implement connection pooling using the driverBridge extension point. While this method is useful, with BIRT 2.3.1 there is a much easier method for passing BIRT an already created connection object.
You can now supply a connection by adding the connection object to BIRT’s application context object. The key for this object is OdaJDBCDriverPassInConnection. So to pass in the connection while using the Report Engine API, use code similar to:
IReportRunnable design = null;
//Open the report design
design = engine.openReportDesign("Reports/passinconnection.rptdesign");
IRunAndRenderTask task = engine.createRunAndRenderTask(design);
HTMLRenderOption options = new HTMLRenderOption();
options.setOutputFileName("output/resample/passinconnobj.html");
options.setOutputFormat("HTML");
task.setRenderOption(options);
task.getAppContext().put("OdaJDBCDriverPassInConnection", this.getConnection());
task.run();
task.close();
This assumes you already have the report engine started. The getConnection method in this example simply creates a java.sql.Connection to my database. You will need to create your own function. Also keep in mind that the BIRT JDBC plugin will close the connection when it has finished with it, so if you plan on using the object in multiple tasks, you will need to reopen it. There is a bug to allow the closing of the connection to be optional -Bugzilla Entry. This application context setting should be applied at the task level.
If you wish to set it in the Example Viewer’s application context take a look at this wiki entry.
These are the ways that a connection can now be manipulated in BIRT:
1-Property binding
2-JNDI
3-Script data set
4-DataSource.beforeOpen() event.
5–driverBridge extension
6-Application Context Ojbect (described here)
Another setting the JDBC plugin now provides is OdaJDBCDriverClassPath, which allows setting the classpath for locating drivers. This prevents the user from having to put JDBC drivers in the drivers directory of the JDBC plugin. This should be set on the EngineConfig object and not on the task object.
config = new EngineConfig( );
config.setBIRTHome("C:\\birt\\birt-runtime-2_3_1\\birt-runtime-2_3_1\\ReportEngine");
config.getAppContext().put("OdaJDBCDriverClassPath", "c:/birt/mysql/mysql-connector-java-5.0.4-bin.jar");
Platform.startup( config );
IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
engine = factory.createReportEngine( config );
Posted by
Jason Weathersby
at
8:15 AM
37
comments
Monday, November 03, 2008
Getting BIRT Source
Back in September I blogged about BIRT Team Project Sets. Last week the BIRT team was able to finish up implementing Team Project Sets. Instructions on how to use a team project set are available here.
The project set to pull from HEAD is available here.
All of the BIRT releases, milestones, and release candidates now have team project sets are also available. To get the team project set files go to the birt download page for the release you are interested in. From there select the full BIRT 2.3.1 Download Page link (each named download has its own link). At the bottom of the full download page, you will find a link to the Project Set File at the bottom of the page.
Posted by
Anonymous
at
9:36 AM
0
comments
Sunday, November 02, 2008
Showing BIRT Reports using the Actuate JSAPI
Actuate is preparing to release a new version of several products that are based on the open source BIRT project. One the coolest new features is a new JavaScript API that can be used to execute and display report content with virtually any front end framework.
This API allows report content to be inserted into any DIV element, and contains methods for parameter manipulation, data extraction, and displaying the viewer. The viewer component has complete functionality for table of contents, exporting reports to different formats, and even the ability to modify sorting, filtering, and grouping from within the browser.
Wenbin, Virgil and I wrote an article on how this API can be used. It is available here. If you are interested in trying out this new API, take a look at the Actuate 10 wiki page located on BIRT Exchange.
Posted by
Jason Weathersby
at
8:52 PM
3
comments
Monday, October 27, 2008
BIRT Properties
If you have done much API level programming with BIRT you have probably found yourself in need of the name and values for a property. This happens if you are working with either the Java or JavaScript modes of BIRT.
The good thing is that the properties that are used in BIRT are defined someplace in the Java code. The hard part is finding exactly where the definition has been placed. I have created a crib sheet with the classes that contain the most common property names on the wiki. You can take a look here .
If you have been working with the BIRT APIs and have found similar information that might be useful, please feel free to update the wiki with your hints.
Posted by
Anonymous
at
12:37 PM
0
comments
Monday, October 20, 2008
Dynamically adding a series to a BIRT Chart
Often it is a requirement to customize report output for specific users. Generally this is accomplished with parameters and some form of conditional logic within the report itself. This approach can be used with BIRT as well. BIRT supports JavaScript expressions and has an event model that allows report customization with handlers written in either Java or JavaScript. For more information on the event model see the scripting primer.
To illustrate modifying a BIRT chart at runtime, assume we have a bar chart that we wish to add additional line series to, based on some report parameter. This can be accomplished by creating an event handler for the beforeFactory event and modifying the report prior to it running.
cht = reportContext.getDesignHandle().findElement(“MyChart”);
This line of code requires that the chart be named MyChart in the General tab of the Properties View. This line of code gets a handle to the Chart report item. Once the chart report item is located we then can retrieve the actual chart model. The chart model is de-serialized from the xml within the report design and stored in a property called chart.instance. So to get the model, you can use code like the following:
mychart = cht.getReportItem().getProperty(“chart.instance”);
Now that we have the actual chart model, we can use standard chart engine API calls to modify it prior to the report running. Remember to import the CE API packages prior to using them. See the final example for more details.
Since we have a bar chart, the chart model will be an instance of the ChartWithAxesImpl class. This class has methods for getting the chart axes and adding series definitions. If we define a static report parameter that allows multi-selection, with values that correspond to the row values we want to map with line series, the following code will add the appropriate series.
//Get the x axis
xAxis =mychart.getAxes().get(0);
//Get the first y axis
yAxis1 = xAxis.getAssociatedAxes().get(0);
alternate_label_position = false;
for( i=0; i < params["SelectSeriesToAdd"].value.length; i++){
var sdNew = SeriesDefinitionImpl.create();
var ls = LineSeriesImpl.create();
ls.getLabel().setVisible(true);
if( alternate_label_position ){
ls.setLabelPosition( Position.BELOW_LITERAL );
alternate_label_position = false;
}else{
ls.setLabelPosition( Position.ABOVE_LITERAL );
alternate_label_position = true;
}
var qry = QueryImpl.create("row[\"" + params["SelectSeriesToAdd"].value[i] + "\"]" );
ls.getDataDefinition().add(qry)
sdNew.getSeries().add( ls );
yAxis1.getSeriesDefinitions().add( sdNew );
The for-loop iterates over the selected values within the parameter and creates a Line series for each value. The value of each parameter is used as the query for the new series. Finally the new series definition is added to the first y axis.
The final code the beforeFactory event handler looks like:
importPackage(Packages.org.eclipse.birt.chart.model.data.impl);
importPackage(Packages.org.eclipse.birt.chart.model.component.impl);
importPackage(Packages.org.eclipse.birt.chart.model.type.impl);
importPackage(Packages.org.eclipse.birt.chart.model.attribute);
//Chart must be named
cht = reportContext.getDesignHandle().findElement("MyChart");
mychart = cht.getReportItem().getProperty( "chart.instance" );
//Get the x axis
xAxis =mychart.getAxes().get(0);
//Get the first y axis
yAxis1 = xAxis.getAssociatedAxes().get(0);
//alternate the series label position of each new series
alternate_label_position = false;
for( i=0; i < params["SelectSeriesToAdd"].value.length; i++){
//create a new series definition for each new series
var sdNew = SeriesDefinitionImpl.create();
//create a new series for each parameter selection
var ls = LineSeriesImpl.create();
ls.getLabel().setVisible(true);
if( alternate_label_position ){
ls.setLabelPosition( Position.BELOW_LITERAL );
alternate_label_position = false;
}else{
ls.setLabelPosition( Position.ABOVE_LITERAL );
alternate_label_position = true;
}
//Create the query. The row value must correlate with the data set row value
var qry = QueryImpl.create("row[\"" + params["SelectSeriesToAdd"].value[i] + "\"]" );
ls.getDataDefinition().add(qry)
sdNew.getSeries().add( ls );
//Add the new series definition to the first y axis
yAxis1.getSeriesDefinitions().add( sdNew );
}
The complete example is located here.
For an example of resizing a chart dynamically see
this.
Posted by
Jason Weathersby
at
4:15 PM
25
comments