SYS-CON MEDIA Authors: Elizabeth White, Liz McMillan, William Schmarzo, Yeshim Deniz, Jason Bloomberg

Blog Feed Post

BIRT Charting – Scripting Overview

The BIRT Chart Engine currently supports building and running charts within the BIRT designer or externally. The engine is fully extensible using the Eclipse extension mechanisms and supports client side interactivity and server side event scripting. Fourteen main chart types, such as bar charts, line charts, and scatter charts are supported. Additionally most chart types support many subtypes such as stacked bar charts, 2D with depth, or 3D charts. When using the chart engine within BIRT the charts can be rendered as PNG, JPG, BMP, or SVG.

The chart engine also supports a sophisticated server side event model that allows the developer to customize the charts dynamically. This post is an overview of most of the chart scripting events available.

If you are interested in learning more about client side chart scripting, see these posts:
ClientSide Script:
Calling Client Side JavaScript from a BIRT Chart
More on Chart Interactivity

General BIRT Server Side Scripting
BIRT Chart event scripting is based on the standard BIRT report scripting model. A primer for report scripting is available here. When the BIRT report engine processes reports they are executed in two phases (Generation and Presentation). The generation phase connects to data sources collects and processes the values and creates the report items. The presentation phase renders the content to the particular output (HTML, paginated HTML, PDF, PPT, XLS, DOC, or PS). These phases can be executed in one or two processes, depending on how you call the engine.

If you use the Report Engine API (RE API), the RunAndRenderTask is one process. To run it in two processes you would use a RunTask and then a RenderTask. If you are using the BIRT Viewer, the /Run and /Preview mappings use one process and the /frameset mapping uses two processes. This concept is very important to understand when scripting as it affects the order in which the events are triggered. When using one process the event order for a data item would look something like:

1st instance
onPrepare
onCreate
onRender
2nd instance
onCreate
onRender.

The same data item with two processes would look like:

1st instance
onPrepare
onCreate
2nd instance
onCreate

onRender first instance
onRender second instance

Using two processes offers many advantages. First a binary file called the report document (.rptdocument) is created which can be rendered many times at a later date without re-executing all the queries. It is also required to support TOC bookmarks and paginated HTML.

Chart Server Side Scripting
When using charts in a BIRT report, the engine supports over thirty events. These events can be used to change the data, interactivity, or presentation of the chart. All of these events are fired in the presentation phase (at render time) of the report and can be written in Java or JavaScript.

It is possible to change chart properties at generation time, but these changes must be made in a report event that happens prior to the chart being created, like the beforeFactory event. This approach is described here and here.

It is vital to understand when chart events are fired, if you plan on making script changes to your chart. Take for instance the following chart.



This is a standard Bar chart with a marker line and curve fitting line. If two processes are used to generate the report that contains this chart, the following event firing order will be used.




This diagram shows most of the available events, but not all events are fired for every chart type. For example before and afterDrawMarker events are only fired for chart types that support makers and these events are usually fired in between the before and afterDrawDataPoint scripts. The beforeDrawSeriesTitle event only fires for Pie charts.

Notice the chart events do not start until after the beforeRender report level event.

Chart Script Context
Before getting into each of the events, a good understanding of the chart script context object will be helpful. All BIRT Chart events are passed a chart script context object. This object will be labeled icsc in the function definition. You can use the chart script context object to get the current instance of the chart model by calling:


icsc.getChartInstance();



You can also use it to get the reportContext that is associated with all other report scripting events. See the scripting primer referenced above for more details. This object can be used to get parameter values, resource values, locales, the current http request, global variables, page variables, etc. To access these functions with a JavaScript event use the following:


rpCtx = icsc.getExternalContext().getScriptable();


This is equivalent to the reportContext variable. So to get a report parameter value, use:


rpCtx.getParameterValue(“MyParameter”);


or to access the http request use (this only works in the Viewer unless you put the http request object in the report engines App Context):


rptCtx.getHttpServletRequest().getRequestURL();


If you are using a Java Event handler use:


IReportContext rpCtx = (IReportContext)icsc.getExternalContext().getObject();


Data Set Filled Events
The first set of chart events that fire are the data set filled events. These events are fired when data organizing and generation of runtime series is occurring for the chart. In These events you can change many properties of the chart model, such as series expressions, grouping, labels, formatting, and series visibility. These events are fired once for the category series and once for each runtime series that is generated based on how the chart is designed. Generally you will get one runtime series per design time series and one additional one for the category series. So if you define a bar chart with product code on the x-axis and quantity ordered on the y-axis, this would produce two runtime series. Each series would contain an array of values. For example ProdA, ProdB, and ProdC may be the category series and 55, 98, and 32 may be the value series. If the chart also contained a line series then this would constitute on additional runtime series containing its values. If you use the optional grouping feature this could generate many runtime series, depending on the number of groups the optional grouping expression produced. In the above example, if the following expression was entered for the optional grouping.


if( row["QUANTITYORDERED"] > 50 ){
"high";
}else{
"low";
}


This would produce a total of three runtime series. 1 (ProdA,ProdB,ProdC), 2 ( 55, 98, null) and 3 (null ,null, 32).

Note that all series have to have the same number of points. In the afterDataSetFilled script these values can be check and or modified.

beforeGeneration Event
The beforeGeneration event is executed before chart building begins and after all series data points have been created. You can make changes to the chart model here and can even alter the datasets that the chart uses. In the data set filled events, many runtime series may have been generated. Every series in a chart will have a unique series identifier. You can specify this on the third tab of the chart wizard. If you use optional grouping and many runtime series are generated for each design time series, a new series identifier will be created for each runtime series. To interrogate these series use code similar to the following (Chart With Axis Example):



var xAxis = chart.getAxes().get(0);
var yAxis = xAxis.getAssociatedAxes().get(0);
var xSeriesDef = xAxis.getSeriesDefinitions().get(0);
var ySeriesDef = yAxis.getSeriesDefinitions().get(0);

var ySeries = ySeriesDef.getRunTimeSeries();
var numberofrunseries = ySeries.size();
//get the first runtime series identifier
ySeries.get(0).getSeriesIdentifier();



Computation Events
The before and afterComputations events are called next. These events are called just before and right after all the calculations for the chart’s rendering properties are processed. These computations include all bounds objects for the chart, the series rendering hints, and data point hints, which store the plot location and values for each series. Using these scripts these values can be modified before the chart is actually rendered.

afterGeneration/beforeRender Event
These events are fired after the chart is built but before it renders each part of the chart. When the chart engine is building it stores all information in the GeneratedChartState object. This object is passed to these methods and can be used to make changes before the chart is rendered. You can use the following code to get the plotComputations object that was populated in the computations event:


compu =gcs.getComputations();


You can even change how the chart engine processes interactivity triggers. For example assume you have a class that implements the IActionRenderer interface.



package my.action.handler;
import java.io.Serializable;
import org.eclipse.birt.chart.computation.DataPointHints;
import org.eclipse.birt.chart.event.StructureSource;
import org.eclipse.birt.chart.event.StructureType;
import org.eclipse.birt.chart.model.attribute.ActionType;
import org.eclipse.birt.chart.model.attribute.TooltipValue;
import org.eclipse.birt.chart.model.data.Action;
import org.eclipse.birt.chart.render.ActionRendererAdapter;
public class MyActionHandler extends ActionRendererAdapter implements Serializable{

private static final long serialVersionUID = 1169308658661902989L;

public void processAction( Action action, StructureSource source )
{
if ( ActionType.SHOW_TOOLTIP_LITERAL.equals( action.getType( ) ) )
{
TooltipValue tv = (TooltipValue) action.getValue( );
if ( StructureType.SERIES_DATA_POINT.equals( source.getType( ) ) )
{
final DataPointHints dph = (DataPointHints) source.getSource( );
String MyToolTip = "My Value is " + dph.getDisplayValue() + "--" +dph.getBaseDisplayValue();
tv.setText( MyToolTip );
}
}
}
}


You could instantiate an instance of this class in the beforeFactory event of the report:


importPackage(Packages.my.action.handler);
MyActionRenderer = new MyActionHandler();
reportContext.setPersistentGlobalVariable("myactionrenderer", MyActionRenderer);


And then set it in the beforeRender event like:


mar = icsc.getExternalContext().getScriptable().getPersistentGlobalVariable("myactionrenderer");
gcs.getRunTimeContext().setActionRenderer(mar);



DrawBlock Events
Charts in BIRT are made up of rendering areas called blocks. Each block will have properties such as rendering bounds, anchor points, outline properties, background properties, and child blocks. The whole chart is the main chart block and within this block is the title block, the plot block and the legend block.



The before and afterDraw block events are called multiple times, once for each chart part that constitutes a block.

The before and afterDraw block events are first called for the whole chart, the title block, plot block, and finally for the legend block. Nested within the plot block and legend block other events will fire as illustrated in the event diagram. The plot block events are fired once for each runtime series as well. So if you have 3 runtime series, the before and afterDrawBlock event will be called three times for the plot block.

To know which block the event is firing for you can use a script similar to:


if ( block.isLegend( ) )
{
block.getOutline( ).setVisible( true );
block.getOutline( ).getColor( ).set( 21,244,231 );
block.setBackground( ColorDefinitionImpl.YELLOW( ) );
block.setAnchor( Anchor.NORTH_LITERAL );
}
else if ( block.isPlot( ) )
{
}
else if ( block.isTitle( ) )
{
}
else if ( block.isCustom( ) ) //Main Block
{
}


You can use these events to modify the calculated bounds of any block, but this may result in unattractive charts. To change the location of the legend the following script could be used.


if (block.isLegend())
{
var wid = block.getBounds().getWidth();
var lft = block.getBounds().getLeft();

block.getBounds().setLeft(lft-20.00);
block.getBounds().setWidth(wid+20.00);
}


Marker Range and Line Events
The before and afterDrawMarker range and line events are called before any of the series are drawn. These events are only called for charts with axis that actually contain marker ranges (or lines). Using these events the marker ranges (or lines) can be modified. By default the location of the markers are set in the chart builder, but you can set the location using an index like:


markerLine.setValue(NumberDataElementImpl.create(intcountformaker));


The index is associated with the runtime series data point index and not the data point value. If the index is set to 0 the marker will be set to the first data point. You can use this in combination with other events like the afterDataSetFilled event to make the marker dynamic.

Draw Series Events
For each runtime series that is generated, the before and afterDraw series events will be fired. You can use the series identifier to determine which series is being processed. You can also check the specific type that of series that is being rendered.


if( series.getClass() == LineSeriesImpl ){
series.getLineAttributes().setThickness(5);
}
if( series.getClass() == BarSeriesImpl ){
if( series.getSeriesIdentifier() == “Series 1”){
}
}


Also note that this event is called for the category series.

Within these events you can modify the series before it is rendered. For example if you want to change a drill through hyperlink to point to a different report you could use the following script.


triglen = series.getTriggers().size();
if( triglen >0 ){
mytarget = series.getTriggers().get(0).getAction().getValue().getURLValues( ).get(0).getBaseUrl();
newtarget = mytarget.replace("reporttarget1.rptdesign", "reporttarget2.rptdesign");
series.getTriggers().get(0).getAction().getValue().getURLValues( ).get(0).setBaseUrl(newtarget);

}


You can also get all the data points for the series by using script like the following:


if( series.getClass() == BarSeriesImpl ){
var pts = isr.getSeriesRenderingHints().getDataPoints();
for( i=0; i< pts.length; i++ ){
var mydp = pts[i];
if( mydp.getOrthogonalValue() > 35 ){
//do something
}
}
}



DataPoint and Data Point Label Events
For each runtime series that is not a category series, the before and afterDrawDataPoint and before and afterDrawDataPointLabel events are fired. These occur between the before and afterDrawSeries events. These events can be used to modify how the data point and label are rendered.

The fill object passed to the before and afterDrawDataPoint events will be an instance of one of the following classes depending on how you have setup the palette.

GradientImpl
ColorDefinitionImpl
ImageImpl (EmbeddedImageImpl and PatternImageImpl extend ImageImpl)



It is important to know which type you are dealing with if you plan on making changes to the fill object. You can check the fill type with script similar to this:



if( fill.getClass().isAssignableFrom(GradientImpl)){
fill.setStartColor(ColorDefinitionImpl.create( 0, 0, 255 ));
fill.setEndColor(ColorDefinitionImpl.create(255,255,225 ));
}



You can set the fill object based on the data point value by using a script like (Assumes Color Fill):


val = dph.getOrthogonalValue();
if (val == 10){
fill.set(255, 0, 0);
}


Using the data point label scripts you can modify the label format and value.


var newlabel = label.getCaption().getValue().replace(",", "'");
label.getCaption().setValue(newlabel);

if( dph.getBaseDisplayValue() == 50 ){
label.setVisible(true);

label.getCaption().getFont().setBold(true);
label.getCaption().getFont().setSize(14);
label.getCaption().getFont().setName("Arial");
}else{
label.setVisible(false);
}



Curve Fitting Events
If the series contains a visible curve fitting line, the before and afterDrawFittingCurve events will be trigged after all the data point and data point label scripts have fired. You can use this event to modify the line attributes and label properties for the fitting curve.

Axis Title and Axis Label Events
After all series have been rendered the before and afterDrawAxisLabel events are fired for every label that appears on the axis. The before and afterDrawAxisTitle events are then fired. This process starts with the category axis and then proceeds for each value axis. You can use these events to modify the labels that are rendered. The axis type can be checked to determine which axis fired the event.


//LINEAR_LITERAL
//LOGARITHMIC_LITERAL
//TEXT_LITERAL
//DATE_TIME_LITERAL
if( context.getExternalContext().getScriptable().getParameterValue("NewParameter") == "date"){
if (axis.getType() == AxisType.TEXT_LITERAL)
{
value = label.getCaption().getValue();
var dtf = new SimpleDateFormat("MM/dd/yy");
var dt = new Date(value);
var fn = dtf.format(dt);
label.getCaption().setValue(fn);
}
}
You can also check the axis title to determine which axis fired the event.
if( axis.getTitle( ).getCaption( ).getValue( ) ==
"myYaxisTitle" )
{
label.getCaption( ).setColor(
ColorDefinitionImpl.BLUE( ) );
}
if ( axis.getType( ) == AxisType.DATE_TIME_LITERAL )
{
label.getCaption( ).setColor(
ColorDefinitionImpl.RED( ) );
}



Legend Item Events
The final set of events that are fired that can affect the chart are the before and afterDrawLegendItem events. These events can be used to customize the look of the chart legend.

This event is passed the LegendEntryRenderingHints (lerh) object and the bounds for the specific entry. The bounds object is for legend item graphic and not the text. Using these objects you can customize the legend. You can hide an entry using the bounds object.


if( lerh.getLabel().getCaption().getValue().compareToIgnoreCase("series3") == 0 ){
bounds.setHeight(0);
bounds.setWidth(0);
lerh.getLabel().setVisible(false);
}


You can modify the fill object using the lerh.getFill() function and modify the label using the lerh.getLabel() function.


importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );
label = lerh.getLabel();
labelString = label.getCaption().getValue();
if( labelString == "series2" ){
label.getCaption( ).getColor( ).set( 32, 168, 255 );
label.getCaption().getFont().setItalic(true);
label.getCaption().getFont().setRotation(5);
label.getCaption().getFont().setStrikethrough(true);
label.getCaption().getFont().setSize(12);
label.getCaption().getFont().setName("Arial");
label.getOutline().setVisible(true);
label.getOutline().setThickness(3);

var mycolor = ColorDefinitionImpl.BLUE();
r = mycolor.getRed();
g = mycolor.getGreen();
b = mycolor.getBlue();
lerh.getFill().set(r, g, b);

}



You can check the fill object type before modifying it as described in the DataPoint and Data Point Label Events described earlier.

Chart Scripting Examples
Here are some example reports/posts that illustrate using the chart scripting model.
BIRT Resizing Charts
BIRT - Reversing a Chart Scale
Resize Pie Chart based on optional grouping count
Add spacing to stacked bar optional grouped chart
Turning a Chart series off in script
Set Chart Axis Label Interval with Script
Using Chart Script to Define a specific series color
Chart Script to Alter Palette - Gradient Pie
Adjust BAR Location in Bar Chart

Read the original blog entry...

More Stories By Jason Weathersby

Jason Weathersby is a member of the extended BIRT development team at Actuate Corporation and has backgrounds in both computer science and technical writing. He has many years experience in technical consulting, training, writing, and publishing about reporting, business intelligence tools, and database technologies.

Latest Stories
New competitors, disruptive technologies, and growing expectations are pushing every business to both adopt and deliver new digital services. This ‘Digital Transformation’ demands rapid delivery and continuous iteration of new competitive services via multiple channels, which in turn demands new service delivery techniques – including DevOps. In this power panel at @DevOpsSummit 20th Cloud Expo, moderated by DevOps Conference Co-Chair Andi Mann, panelists examined how DevOps helps to meet the de...
Fact: storage performance problems have only gotten more complicated, as applications not only have become largely virtualized, but also have moved to cloud-based infrastructures. Storage performance in virtualized environments isn’t just about IOPS anymore. Instead, you need to guarantee performance for individual VMs, helping applications maintain performance as the number of VMs continues to go up in real time. In his session at Cloud Expo, Dhiraj Sehgal, Product and Marketing at Tintri, sha...
According to Forrester Research, every business will become either a digital predator or digital prey by 2020. To avoid demise, organizations must rapidly create new sources of value in their end-to-end customer experiences. True digital predators also must break down information and process silos and extend digital transformation initiatives to empower employees with the digital resources needed to win, serve, and retain customers.
In his session at 19th Cloud Expo, Claude Remillard, Principal Program Manager in Developer Division at Microsoft, contrasted how his team used config as code and immutable patterns for continuous delivery of microservices and apps to the cloud. He showed how the immutable patterns helps developers do away with most of the complexity of config as code-enabling scenarios such as rollback, zero downtime upgrades with far greater simplicity. He also demoed building immutable pipelines in the cloud ...
More and more companies are looking to microservices as an architectural pattern for breaking apart applications into more manageable pieces so that agile teams can deliver new features quicker and more effectively. What this pattern has done more than anything to date is spark organizational transformations, setting the foundation for future application development. In practice, however, there are a number of considerations to make that go beyond simply “build, ship, and run,” which changes how...
In his keynote at 18th Cloud Expo, Andrew Keys, Co-Founder of ConsenSys Enterprise, will provide an overview of the evolution of the Internet and the Database and the future of their combination – the Blockchain. Andrew Keys is Co-Founder of ConsenSys Enterprise. He comes to ConsenSys Enterprise with capital markets, technology and entrepreneurial experience. Previously, he worked for UBS investment bank in equities analysis. Later, he was responsible for the creation and distribution of life ...
Smart Cities are here to stay, but for their promise to be delivered, the data they produce must not be put in new siloes. In his session at @ThingsExpo, Mathias Herberts, Co-founder and CTO of Cityzen Data, discussed the best practices that will ensure a successful smart city journey.
A look across the tech landscape at the disruptive technologies that are increasing in prominence and speculate as to which will be most impactful for communications – namely, AI and Cloud Computing. In his session at 20th Cloud Expo, Curtis Peterson, VP of Operations at RingCentral, highlighted the current challenges of these transformative technologies and shared strategies for preparing your organization for these changes. This “view from the top” outlined the latest trends and developments i...
When you focus on a journey from up-close, you look at your own technical and cultural history and how you changed it for the benefit of the customer. This was our starting point: too many integration issues, 13 SWP days and very long cycles. It was evident that in this fast-paced industry we could no longer afford this reality. We needed something that would take us beyond reducing the development lifecycles, CI and Agile methodologies. We made a fundamental difference, even changed our culture...
"Space Monkey by Vivent Smart Home is a product that is a distributed cloud-based edge storage network. Vivent Smart Home, our parent company, is a smart home provider that places a lot of hard drives across homes in North America," explained JT Olds, Director of Engineering, and Brandon Crowfeather, Product Manager, at Vivint Smart Home, in this SYS-CON.tv interview at @ThingsExpo, held Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA.
LogRocket helps product teams develop better experiences for users by recording videos of user sessions with logs and network data. It identifies UX problems and reveals the root cause of every bug. LogRocket presents impactful errors on a website, and how to reproduce it. With LogRocket, users can replay problems.
@CloudEXPO and @ExpoDX, two of the most influential technology events in the world, have hosted hundreds of sponsors and exhibitors since our launch 10 years ago. @CloudEXPO and @ExpoDX New York and Silicon Valley provide a full year of face-to-face marketing opportunities for your company. Each sponsorship and exhibit package comes with pre and post-show marketing programs. By sponsoring and exhibiting in New York and Silicon Valley, you reach a full complement of decision makers and buyers in ...
There are many examples of disruption in consumer space – Uber disrupting the cab industry, Airbnb disrupting the hospitality industry and so on; but have you wondered who is disrupting support and operations? AISERA helps make businesses and customers successful by offering consumer-like user experience for support and operations. We have built the world’s first AI-driven IT / HR / Cloud / Customer Support and Operations solution.
Data Theorem is a leading provider of modern application security. Its core mission is to analyze and secure any modern application anytime, anywhere. The Data Theorem Analyzer Engine continuously scans APIs and mobile applications in search of security flaws and data privacy gaps. Data Theorem products help organizations build safer applications that maximize data security and brand protection. The company has detected more than 300 million application eavesdropping incidents and currently secu...
Rafay enables developers to automate the distribution, operations, cross-region scaling and lifecycle management of containerized microservices across public and private clouds, and service provider networks. Rafay's platform is built around foundational elements that together deliver an optimal abstraction layer across disparate infrastructure, making it easy for developers to scale and operate applications across any number of locations or regions. Consumed as a service, Rafay's platform elimi...