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.

6 comments:

ipad black jack said...

Fantastic Birt Chart palette i enjoyed reading it and my knowlege grows after reading your post thanks keep it up.......!!! i love Your post.....!!

iphone roulette said...

Wow, it is really fantastic. I have used it. It is looking so cute. Love your blog.Best wishes for you future blogging career. Thanks

Calvin Jien said...

This is awesome! The color thing worked really well. Hey is there a way to make an example for images? I want to use an image as the palette for one slice of the pie but I can't get it to work...


function beforeDrawDataPoint( dph, fill, icsc )
{
    importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );
    var sValue = lerh.getLabel().getCaption().getValue();
    var fill = lerh.getFill();
    if( sValue == "apple" )
    {
                if (fill.getClass().isAssignableFrom(ImageImpl)){                fill.create(reportContext.getResource("red_and_white.JPG"));
                 }else{
                         fill.set( 0,128,255 );
                 }
    }
}

I put the image inside the Embedded Images as well as in the root folder with the report. Oh and I'm using 2.3.2 with no plans to upgrade right now.

Jason Weathersby said...

in your beforeFactory do something like:
var myimg =reportContext.getDesignHandle().getAllImages().get(0);
importPackage(Packages.org.apache.commons.codec.binary)
importPackage(Packages.java.lang);
var tt = Base64.encodeBase64(myimg.getData());
var mydt = new String( tt, "UTF-8" );
reportContext.setPersistentGlobalVariable("myimagedata", mydt);

This assumes your embedded image is first in the list of embedded images in the report.

Then in the chart script do something like:

if (fill.getClass().isAssignableFrom(EmbeddedImageImpl)){

var rc = icsc.getExternalContext().getScriptable();
fill.setData(rc.getPersistentGlobalVariable("myimagedata"));
}

Anonymous said...

Hi
How to make embedded image chart in html report?

Jason Weathersby said...

Can you give some more detail on what you need?

Jason