4

Here's what I have:

http://jsfiddle.net/bozdoz/Q6A3L/

rickshaw d3.js graph

Code for the background bars:

maxData = 80000000;
maxHeight = 250;
var yScale = d3.scale.linear().
  domain([0, maxData]). // your data minimum and maximum
  range([0, maxHeight]); // the pixels to map to, e.g., the height of the diagram.

var riskpoints = [62000000,48000000,30000000];
var rectDemo = d3.select("#chart").
    append("svg:svg").
    attr("width", 400).
    attr("height", maxHeight).
    attr('class','risk');

rectDemo.append("svg:rect").
    attr("x",0).
    attr("y", maxHeight - yScale(riskpoints[0])).
    attr("height", yScale(riskpoints[0]) - yScale(riskpoints[1])).
    attr("width", '100%').
    attr("fill", "rgba(0,100,0,.3)");

rectDemo.append("svg:rect").
    attr("x",0).
    attr("y", maxHeight - yScale(riskpoints[1])).
    attr("height", yScale(riskpoints[1]) - yScale(riskpoints[2])).
    attr("width", '100%').
    attr("fill", "rgba(100,100,0,.3)");

rectDemo.append("svg:rect").
    attr("x",0).
    attr("y", maxHeight - yScale(riskpoints[2])).
    attr("height", yScale(riskpoints[2])).
    attr("width", '100%').
    attr("fill", "rgba(100,0,0,.3)");

This is exactly what I want, except that I have made the red, yellow, green background bars a fixed height.

When you use the slider, or, more importantly, when the graph is regenerated (which it will with the actual data), I want the background bars to adjust to the correct height.

For example, in the image, the green bar is somewhere between 300M and 200M. If you remove all the graph data except for the red (vertical) bars, the green bar (horizontal) is just above 40M. This is because the position/height of the background bars are not regenerated, whereas the position/height of the graph data are regenerated, via the rickshaw javascript.

How could I put this data into the rickshaw graph so that it is regenerated?

bozdoz
  • 12,550
  • 7
  • 67
  • 96
  • at least you should place them into same scope where graph is, if i understand you correct – zb' Nov 14 '12 at 22:33

2 Answers2

3

The solution is to setup update function to calculate new heights of the background bars, to get displayed bars you can use graph.series.active(); you also may like to keep vars in same scope....

so the solution itself:

var maxHeight;
var background_update = function(graph) {
    //skip 1st time;
    if (maxHeight === undefined) {return;}
    var series=graph.series.active();
    maxHeight=yScale(get_maximum_all(series));
    //console.log(maxHeight);
    var red_riskpoint=get_maximum("y",series[0]);
    var yellow_riskpoint=get_maximum("y",series[1]);
    var green_riskpoint=get_maximum("y",series[2]);
    //console.log(graph.series.active());
    console.log(yScale(red_riskpoint),yScale(yellow_riskpoint),yScale(green_riskpoint));
    
   // return;
    
    b_red.attr("y", maxHeight - yScale(red_riskpoint)).attr("height", yScale(red_riskpoint));
    b_yellow.attr("y", maxHeight - yScale(yellow_riskpoint)).attr("height", Math.abs(yScale(yellow_riskpoint) - yScale(red_riskpoint)));
    b_green.attr("y", maxHeight - yScale(green_riskpoint)).attr("height", Math.abs(yScale(green_riskpoint) - yScale(yellow_riskpoint)));
    console.log(maxHeight-yScale(green_riskpoint));
};
var get_maximum_all=function(series) {
    var max=0;
    for (var i in series) {
        var stack=series[i].stack;
        for (n in stack) {
            if (stack[n].y>max) {max=stack[n].y;}
            if (stack[n].y0>max) {max=stack[n].y0;}
        }
    }
    return max;
}
    var get_maximum=function(tag,object){
    var max=0;
    for (var i in object.stack) {
        if (object.stack[i][tag]>max) {max=object.stack[i][tag];}
    }
return max;
    }
Rickshaw.Graph.RangeSlider = function(args) {

    var element = this.element = args.element;
    var graph = this.graph = args.graph;


    $(element).slider({

        range: true,
        min: graph.dataDomain()[0],
        max: graph.dataDomain()[1],
        values: [
            graph.dataDomain()[0],
            graph.dataDomain()[1]
            ],
        slide: function(event, ui) {

            graph.window.xMin = ui.values[0];
            graph.window.xMax = ui.values[1];
            graph.update();

            // if we're at an extreme, stick there
            if (graph.dataDomain()[0] == ui.values[0]) {
                graph.window.xMin = undefined;
            }
            if (graph.dataDomain()[1] == ui.values[1]) {
                graph.window.xMax = undefined;
            }
        }
    });


    element.style.width = graph.width + 'px';

    graph.onUpdate(function() {

        var values = $(element).slider('option', 'values');

        $(element).slider('option', 'min', graph.dataDomain()[0]);
        $(element).slider('option', 'max', graph.dataDomain()[1]);

        if (graph.window.xMin == undefined) {
            values[0] = graph.dataDomain()[0];
        }
        if (graph.window.xMax == undefined) {
            values[1] = graph.dataDomain()[1];
        }

        $(element).slider('option', 'values', values);
        background_update(graph);
    });
}

other code is almost same you placed to fiddle,

my fiddle here

please note, that i not sure how calculate riskpoints, i just tried to get a maximums. I sure you can make riskpoint calculations from viewing the console.log(series)

b_red,b_green,b_yellow it is d3 selections of initialised background bars.

zb'
  • 8,071
  • 4
  • 41
  • 68
  • Wow, this is a lot more than I expected. I figured it was just a matter of proper placement of the code with variables instead of hardcoded values for the bar heights. Could you explain why all of this is necessary? – bozdoz Nov 15 '12 at 16:34
  • i not understand, what is necessary ? I just added update handler which changes heights of your red,green,yellow bars, and show you a way how you can get current displayed data. I think you can also do a single data graph with maximal datas, in that update function – zb' Nov 15 '12 at 17:09
  • Hi @eicto. I found your functions for getting the maximum data and for the onUpdate handler very useful. I will award you the bounty so long as I don't get a better answer, and I will post my solution as well. One question though: instead of getting the maximum data, how could I get the maximum y-axis height? – bozdoz Nov 15 '12 at 21:44
  • you mean absolute maximum ? i not checked it well, but i think Axis plugin have something about it, as Axis recalculated on scale – zb' Nov 15 '12 at 23:04
1

I found somewhat of a solution, with the help of @eicto's answer.

var maxHeight = 250;
var theSVG = d3.select("#chart svg");

//from @eicto: get the maximum value on the current chart
function get_maximum_all(series) { 
    var max=0;
    for (var i in series) {
        var stack=series[i].stack;
        for (n in stack) {
            if (stack[n].y>max) {max=stack[n].y;}
            if (stack[n].y0>max) {max=stack[n].y0;}
        }
    }
    return parseInt(max);
}

//create a function to call on graph update event
function createRiskPoints(riskpoints){
//these variables need to change on each graph update
   var series = graph.series.active();
//this variable should be get_max_y_axis, but I don't know how to do that
   var maxData = get_maximum_all(series);
   var yScale = d3.scale.linear().
  domain([0, maxData]).
  range([0, maxHeight]);

//using dummy values for now
   var riskpoints = (riskpoints) ? riskpoints : [300000000,200000000,100000000];

//insert the bars so they go behind the data values
theSVG.insert("svg:rect", ":first-child").
    attr("x",0).
    attr("y", maxHeight - yScale(riskpoints[0])).
    attr("height", yScale(riskpoints[0]) - yScale(riskpoints[1])).
    attr("width", '100%').
    attr("fill", "rgba(0,100,0,.2)");

theSVG.insert("svg:rect", ":first-child").
    attr("x",0).
    attr("y", maxHeight - yScale(riskpoints[1])).
    attr("height", yScale(riskpoints[1]) - yScale(riskpoints[2])).
    attr("width", '100%').
    attr("fill", "rgba(100,100,0,.2)");

theSVG.insert("svg:rect", ":first-child").
    attr("x",0).
    attr("y", maxHeight - yScale(riskpoints[2])).
    attr("height", yScale(riskpoints[2])).
    attr("width", '100%').
    attr("fill", "rgba(100,0,0,.2)");
}
//call the function
   createRiskPoints();
//call the function on graph update: thanks to @eicto
   graph.onUpdate( createRiskPoints );​

Now the bars change when the graph changes; however, they are not to scale. This will be my next task.

http://jsfiddle.net/bozdoz/Q6A3L/

bozdoz
  • 12,550
  • 7
  • 67
  • 96
  • I realized that it was only not to scale because the data, being stacked, increased the y-axis to the maximum total of each year, instead of the maximum value of the series. I used graph.renderer.unstack = true, and it unstacked the graph, which is fine for my purposes: http://jsfiddle.net/bozdoz/Q6A3L/11/ – bozdoz Nov 15 '12 at 22:18