Wednesday, October 26, 2011

BIRT Chart Palette

We have posted many times about the different facets of scripting (client and server side) for BIRT charts. Some of these posts are shown below. In many cases users want to modify chart colors based on some data value or some external logic. While this is very simple with the standard palette colors, some of the other palette options are not as straight forward. In this post we will discuss the different palette options developers have and how to access and modify them from script.

Previous Chart Scripting Posts


Chart Scripting Overview
Using Script to Modify a BIRT Chart
Dynamically Adding a Series to a BIRT Chart
Calling Client Side JavaScript From a BIRT Chart
More on Chart Interactivity

BIRT Palette Options


The BIRT Chart Palette supports standard and custom colors, gradients, images, positive/negative entries and patterns.

While standard and custom colors are simple some of the other types offer some interesting possibilities. The Gradient palette entry allows a start and end color as well as a rotation angle for the gradient.

The Image Palette entry allows a developer to specify a URL or embed an image directly into the chart model.

The Positive/Negative palette entry uses one palette color for positive values and one for negative values.

The Pattern palette entry allows the developer to define a pattern with foreground and background colors. The pattern can be one of the predefined patterns or one that is customized.

Palette Scripting


Many of the events that are triggered during the creation of the chart are passed a fill object. The actual data type of the fill object will depend on how the palette has been setup. The Fill object will be one of the following types:

org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl – Color/Custom Color Palette Entry
org.eclipse.birt.chart.model.attribute.impl.GradientImpl – Gradient Palette Entry
org.eclipse.birt.chart.model.attribute.impl.ImageImpl – Image URL Entry
org.eclipse.birt.chart.model.attribute.impl.EmbeddedImageImpl – Embedded Image Entry
org.eclipse.birt.chart.model.attribute.impl.MultipleFillImpl – Positive/Negative Entry
eclipse.birt.chart.model.attribute.impl.PatternImageImpl – Pattern Entry

For example assume that you have a Bar chart that uses one bar color and you want to change this color based on the Y-Axis value of the data point. The following beforeDrawDataPoint Script could be used.



function beforeDrawDataPoint(dph, fill, icsc)
{
//Fill implements Fill interface
//ImageImpl
//ColorDefinitionImpl
//GradientImpl
//MultipleFillImpl
//EmbeddedImageImpl
//PatternImageImpl

importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );
val = dph.getOrthogonalValue();
if( fill.getClass().isAssignableFrom(ColorDefinitionImpl)){
if (val < 40){
fill.set(255, 0, 0);
}
}
}


In this particular example the fill object is a ColorDefinitionImpl object that supports setting the R,G,B value of the color. Notice also that we import the org.eclipse.birt.chart.model.attribute.impl package to get access to the ColorDefinitionImpl class. If the palette entry was a gradient the script could be changed to something like the following:


function beforeDrawDataPoint(dph, fill, icsc)
{
//Fill implements Fill interface
//ImageImpl
//ColorDefinitionImpl
//GradientImpl
//MultipleFillImpl
//EmbeddedImageImpl
//PatternImageImpl

importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );
val = dph.getOrthogonalValue();
if( fill.getClass().isAssignableFrom(GradientImpl)){
if (val < 40){
fill.getStartColor().set(255,0,0);
}
}
}


In this example we set the start color of the gradient.

If you are using a Bubble chart with a Positive/Negative palette entry, by default one color will be used for positive values and the other for negative colors. You could alter this behavior for certain data points. For example the following script changes the positive and negative colors for some of the data points.


function beforeDrawDataPoint(dph, fill, icsc)
{
//Fill implements Fill interface
//ImageImpl
//ColorDefinitionImpl
//GradientImpl
//MultipleFillImpl
//EmbeddedImageImpl
//PatternImageImpl

importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );
importPackage( Packages.org.eclipse.birt.chart.extension.datafeed);
//for bubble chart this returns a BubbleEntry
val = dph.getOrthogonalValue();
yval = val.getValue()
if( fill.getClass().isAssignableFrom(MultipleFillImpl)){
if( yval > -500 && yval < 500 ){
fill.getFills().clear();
fill.getFills( ).add( ColorDefinitionImpl.BLUE( ) );
fill.getFills( ).add( ColorDefinitionImpl.WHITE( ) );
}

}
}



Notice that we needed to import the datafeed package as the bubble chart has size and value entries for the y-axis. We also used the ColorDefinitionImpl predefined colors for blue and white. We could have created the color using R,G,B values like ColorDefinitionImpl.create( 255, 255, 225 ).

Palette Model Location


While the above examples illustrate how to manipulate palette entries that are passed to the other events, it is often desirable to alter one or more palette entries. This can be done in the beforeGeneration event. How you access the chart palette will depend on what type of chart it is: Chart with Axes or Chart without Axes. For example suppose you want to replace the chart palette with a set of predefined gradients. To do this with a pie chart the following script could be used.


function beforeGeneration( chart, icsc )
{
importPackage(Packages.org.eclipse.birt.chart.model.attribute.impl);
sd = chart.getSeriesDefinitions( ).get( 0 );
sd.getSeriesPalette( ).getEntries( ).clear( );

sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(220,237,248), ColorDefinitionImpl.create(80,166,218), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(255,213,213), ColorDefinitionImpl.create(242,88,106), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(255,247,213), ColorDefinitionImpl.create(232,172,57), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(213,255,213), ColorDefinitionImpl.create(128,255,128), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(213,255,255), ColorDefinitionImpl.create(64,128,128), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(226,226,241), ColorDefinitionImpl.create(128,128,192), 0, false));


}




In this example we clear the existing palette and replace it with a set of gradients. The palette is attached to the charts first series definition.
For a Bar chart the following script would be used.


function beforeGeneration( chart, icsc )
{
importPackage(Packages.org.eclipse.birt.chart.model.attribute.impl);

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

sd.getSeriesPalette( ).getEntries( ).clear( );

sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(220,237,248), ColorDefinitionImpl.create(80,166,218), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(255,213,213), ColorDefinitionImpl.create(242,88,106), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(255,247,213), ColorDefinitionImpl.create(232,172,57), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(213,255,213), ColorDefinitionImpl.create(128,255,128), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(213,255,255), ColorDefinitionImpl.create(64,128,128), 0, false));
sd.getSeriesPalette().getEntries().add( GradientImpl.create( ColorDefinitionImpl.create(226,226,241), ColorDefinitionImpl.create(128,128,192), 0, false));
}



In this example the palette is attached to the series definition associated with the x-axis.
As a more complex example, and afterDataSetFilled script could be used to calculate the number of data points in a particular bar chart. The beforeGeneration event could then use this value to create a palette that gives entire chart a gradient look by moving a start color to an end color.


cnt = 0;
startColor = null;
endColor = null;
importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );

function afterDataSetFilled(series, dataSet, icsc)
{
importPackage( Packages.java.io );
importPackage( Packages.org.eclipse.birt.chart.model.type.impl );

if( series.getClass() == BarSeriesImpl ){
if( series.getSeriesIdentifier() == "Series 1" ){
var list = dataSet.getValues();
cnt = list.length;
}


}
startColor = ColorDefinitionImpl.RED();
endColor = ColorDefinitionImpl.ORANGE();
}
function buildPalette( mNumber )
{
var sr = startColor.getRed();
var sg = startColor.getGreen();
var sb = startColor.getBlue();

var er = endColor.getRed();
var eg = endColor.getGreen();
var eb = endColor.getBlue();

var nr = ((er-sr)/cnt)*mNumber + sr;
var ng = ((eg-sg)/cnt)*mNumber + sg;
var nb = ((eb-sb)/cnt)*mNumber + sb;

var nc = ColorDefinitionImpl.create( nr, ng, nb );
return nc;


}

function beforeGeneration( chart, icsc )
{
var xAxis = chart.getAxes().get(0);
var yAxis = xAxis.getAssociatedAxes().get(0);
var xSerieDef = xAxis.getSeriesDefinitions().get(0);
var ySerieDef = yAxis.getSeriesDefinitions().get(0);

xSerieDef.getSeriesPalette().getEntries().clear( );
for ( i = 1; i <= cnt; i++ )
{
xSerieDef.getSeriesPalette().getEntries().add( buildPalette(i) );
}
}



The example shown here are available at BIRT Exchange.