PDA

View Full Version : Bar chart colors dynamically changed based on data value. Along with JavaScript vars



and.decarlo
10-04-2013, 01:49 PM
Hey Everyone,

I am sorry if I am asking a question that has been asked before, but I have not been able to get the desired funtionality out of my bar chart.

First my goal. I would like to change the bar chart individual bars to different colors based on the data that they represent. For example, if they are less than 1 the color would be green, if they are between 1 and 3 they would be yellow, and if they were above 3 they would be red.

I have seen two approaches to manipulating the charts, one is through using the extension points and putting a function in the value. The second is by using the post execution scripts.

I have tried some of these, for example with extension points:

bar_fillStyle | function(a){if(a<99.8){return "red"}else{return "green"}}

However this always returns green. I added an alert(a); at the beginning to see what my "a" variable returned, and the message I got was "pvc.visual.Scene". This was clearly not the variable I wanted, because I wanted the data for each bar chart.

I guess this boils down to being new at JavaScript (and CDE), but how do I know what a variable represents? I noticed that a lot of people seem to pass through "d" as a data variable but that also returns "pvc.visual.Scene". Do I need to define the variable somewhere before I can use it, or is there a list of already defined variables? Any help would be appreiated. Thanks!

rahuls
10-04-2013, 02:35 PM
In the latest version i.e. CCC2 you should be able to use the following in post fetch:

function f() {
// Series Value : Color
this.chartDefinition.colorMap = {
"10": 'red',
"12": 'green'
};
}

To get access to the actual data, in the same postfetch:

function f(values)
{
console.log(values);
}

values has metadata and resultset.

and.decarlo
10-04-2013, 04:53 PM
When I do this the log describes values as "undefined". Is there anywhere I can go to see all these variables, like values, metadata, or resultset?

duarte.leao
10-07-2013, 12:49 PM
Hi decarlo,

if I understand well, you want to assign colors to bars according to their value being in value bucket A, B, C,...

See this example (http://jsfiddle.net/duarteleao/5mYw9/) for a clean way to do this. It works well with the legend. Basically, you create an auxiliary data dimension (another column...) that calculates the bucket in which the value falls.
Then, you bind the color role to that new dimension.

In the future, we might provide a way to configure scales to do the quantization directly, given the value ranges.

rahuls
10-07-2013, 04:39 PM
try the f(values) in postfetch

and.decarlo
10-08-2013, 10:01 AM
The function f(values) is working, thanks.

Duarte Leao,

I have seen this jFiddle before but am not too familiar with it too much. I use the Community Dashboard Editor to create my dashboards, and it seems to hide the javascript from the user. How would I go about using the javascript that you showed me?

Thanks!

duarte.leao
10-08-2013, 05:23 PM
Hi decarlo,

most CCC options are available directly as CDE properties.
For the ones that are not, usually complex-valued options, you have to use JavaScript to set them.
See how here (http://forums.pentaho.com/showthread.php?139743-CCC2-BarChart-color-coding&p=337698#post337698), here (http://redmine.webdetails.org/projects/cde/wiki/CCC_Chart_Components_Tips_&_Tricks#Changing-a-previously-defined-extension-point) and here (http://forums.pentaho.com/showthread.php?142974-Help-with-Paired-Bar-Line-Example-on-webdetails-site-(in-CDE)).

and.decarlo
10-10-2013, 02:34 PM
Thanks for these links, they are quite usefull.

So I have been somewhat successful in getting the bars to change colors. I have added this to my post execution

function f(){
var bar = this.chart.barChartPanel.pvBar;
var chart = this.chart;
var resultset = this.query.lastResults().resultset;

for(var i = 0; i < resultset.length; i++){
var dataVal = resultset[i][1];
var child = bar.childIndex[i];

if(dataVal > 0 && dataVal <= 2){
alert('green '+dataVal);
bar.fillStyle("green").root.render();
}
else if(dataVal > 2 && dataVal <= 4){
alert('yellow '+dataVal);
bar.fillStyle("yellow").root.render();
}
else if(dataVal > 4 && dataVal <= 7){
alert('red '+dataVal);
bar.fillStyle("red").root.render();
}
else{
alert('red '+dataVal+" "+bar);
bar.fillStyle("black").root.render();
}

}
}

I have two bars one is about 4.5 and the other is 9.8, so in theory I should have one red bar and one black bar. I get the alert for both red and black but at the end of it both of my bars in the chart are black. I assume this is because when I call the bar.fillStyle("black").root.render(); "bar" refers to the entire bar chart and all the bars in the bar chart. Is there any way I can apply the colors to the separate bars? I havent been successful as of yet, but I think I am getting closer.

duarte.leao
10-11-2013, 07:39 AM
Hi decarlo,

it's good that you experiment with CCC and direct access to the underlying protovis marks, that CCC uses.
But, or I didn't understand what you're trying to do, or you're totally missing the point.

Your traversing the resultset. This is simply an array. But every time you change fillStyle with a constant value, you're changing it in every instance of the mark, whatever its index is.
For what you want, just specify the "bar_fillStyle" extension point, directly in the CDE property, or in code, in preExecution or postFetch.
Pass a function to it that tests the value of this.scene.vars.value.value and returns an appropriate color.

Anyway, apart from learning and experimenting, please don't go, neither in the "postExecution" way, cause you're re-rendering the chart twice, without any need, neither in the extension point way I just described. You're missing and duplicating things that CCC already does, in other ways.

and.decarlo
10-11-2013, 11:53 AM
Duarte, you are a life saver, thanks for taking the time to help me figure this out. I got the desired functionality by adding the following to the PreExecution.

function changeBars(){
var cccOptions = this.chartDefinition;

// For changing extension points, a little more work is required:
var eps = Dashboards.propertiesArrayToObject(cccOptions.extensionPoints);

// add extension points:
eps.bar_fillStyle = function getColor(){
var val = this.scene.vars.value.value;

if(val > 0 && val <= 2){
return 'green';
}
else if(val > 2 && val <= 4){
return 'yellow';
}
else if(val > 4 && val <= 7){
return 'red';
}
else{
return 'black';
}
};

// Serialize back eps into cccOptions
cccOptions.extensionPoints = Dashboards.objectToPropertiesArray(eps);
}


Thanks for all the help resolving this!

and.decarlo
10-14-2013, 04:51 PM
Another question, this is kinda a more in depth usecase for this scenario.

So I can now return different colors based on the data values, but I just learned that sometimes these are not standard across the board. For instance the bars may have different cuttoffs.

Bar 1: green = 0-2, yellow = 2.1-4, red = 4.1-6.0
Bar 2: green = 0-1.5, yellow = 1.6-3.8 red = 3.9-6.0
Bar 3: green = 6.0-4.0, yellow = 3.9-2.0, green = 1.9-0

I feel like this would be easy for me to do if I could get the xaxis label for each bar, and than based on the name I would know how to evaluate the data. The problem is that I cannot for the life of me find the variable that would contain these labels. Any ideas?

pmalves
10-16-2013, 05:50 AM
That's in something like this.scene.series

and.decarlo
10-16-2013, 11:53 AM
thanks for the direction, but am I missing something totally obvious? I am fairly new to Pentaho and saying something like "this.scene.series" means absolutely nothing to me at this point mostly because I dont know where to go from there. Is there any documentation where I could find more information about what this.scene.series contains? Its attributes and stuff, sort of like a javadocs. I feel like many people on this forum know a bunch of different variables to play around with in these things and I am always just lost with them.

duarte.leao
10-22-2013, 07:57 PM
Hi,

you can find some information about the scene structure here (http://redmine.webdetails.org/projects/ccc/wiki/FAQ_Main_Changes_New_Features_CCC_v2).

Generally, when executing code in an extension point, you have a "scene" argument, that is the same as «this.scene».

The scene contains a map of variables, named "vars", so you write «this.scene.vars».

Variables are generally named after the visual roles of each chart type, so you'll have a "series" variable, a "category" variable, a "value" variable, for a typical categorical chart.

A variable is itself an object cause you have, at the minimum, the variable's "value" and the variable's "label".

So, summing it all up, for accessing the series value in an extension point, you write: «this.scene.vars.series.value».

We're working on some sugar functions, accessible from «this» to make your and our wrists type less.

Edit 2013-10-23: Added to master of CDF and CGG new sugar methods for this end.

You can now use the following, from the pvc.visual.Context or the pvc.visual.Scene, to obtain the value of scene variables:


most charts (except, notably, the Pie chart): getSeries()
categorical charts: getCategory()
categorical charts and the legend scenes: getValue()
metric charts: getX(), getY()
cartesian axes panel scenes: getTick()


There are also getSeriesLabel(), getCategoryLabel(), etc., versions of all these.

For those variables/visual-roles for which no sugar method exist, or if you prefer/need a more dynamic approach, the new "get" method can be used as in the following example:

get("category") <=> getCategory()
get("category", "label") <=> getCategoryLabel()

and.decarlo
10-25-2013, 01:17 PM
Thanks, this post has been extremely helpful and I was able to get it to work using the vars.category.label. Thanks again!