I'm trying to add an arbitrary number of vertical lines to my chart in protovis. Given an array of x-intercept values, I'd like to loop over this array and draw a vertical line for every intercept value. Right now, I'm able to draw a fixed number of lines, but am having trouble generalizing.
I've made a jsfiddle showing how I can add a fixed number of lines to a graph, and also reproduced that code below. Here, I've added 2 lines by explicitly coding x_value0
and x_value1
. In the code, there are two pieces of protovis code that are relevant, which I've labelled Section A
and Section B
. Section A
is a protovis function which defines where the line will be drawn, and Section B
calls those functions.
// X intercept values hard coded
var x_value0 = 30;
var x_value1 = 70;
new pvc.MetricDotChart({
canvas: "cccExample",
width: 500,
height: 400,
axisGrid: true,
axisZeroLine: false,
selectable: true,
extensionPoints: {
plot_add: function() {
var panel = new pv.Panel()
// Quadrant rules above grid, but below dots.
.zOrder(-1)
////
// Section A Begin
////
// Line 1
.def('l0', function() {
var ccc = this.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(x_value0);
})
// Line 2
.def('l1', function() {
var ccc = this.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(x_value1);
})
////
// Section A End
////
;
////
// Section B Begin
////
// Line 1
panel
.add(pv.Rule)
.top(0)
.left (function() { return this.parent.l0(); })
.height(function() { return this.parent.height(); })
.width(null)
;
// Line 2
panel
.add(pv.Rule)
.top(0)
.left (function() { return this.parent.l1(); })
.height(function() { return this.parent.height(); })
.width(null)
;
////
// Section B End
////
return panel;
}
}
})
.setData(testLDot2)
.render();
What I'd like to do instead is define xvalues
as an array, and loop over that. I'm kind of half way there in my attempt. I made a second jsfiddle where I try to move the xvalues
into an array. The problem is that I can't seem to successfully wrap the relevant part in a for-loop. The code from that jsfiddle is:
// X intercept values in an array
var x_values = [30, 60];
new pvc.MetricDotChart({
canvas: "cccExample",
width: 500,
height: 400,
axisGrid: true,
axisZeroLine: false,
selectable: true,
extensionPoints: {
plot_add: function() {
var panel = new pv.Panel()
// Quadrant rules above grid, but below dots.
.zOrder(-1)
////
// Section A Begin - How can I put this inside of a loop?
////
// Line 1
.def('l0', function() {
var ccc = this.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(x_values[0]);
})
// Line 2
.def('l1', function() {
var ccc = this.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(x_values[1]);
})
////
// Section A End
////
;
////
// Section B Begin - I'm able to successfully loop this part
////
for (i = 0; i < x_values.length; i++) {
// The name of the .def function defined above
var fn_name = 'this.parent.l' + i + '()';
// Draw the lines
panel
.add(pv.Rule)
.top(0)
.left (function() { return eval(fn_name); })
.height(function() { return this.parent.height(); })
.width(null)
;
}
////
// Section B End
////
return panel;
}
}
})
.setData(testLDot2)
.render();
I'm able to wrap Section B
inside of a for-loop, and I'd like to do something similar for Section A
:
for (i = 0; i < x_values.length; i++) {
.def('l'+i, function() {
var ccc = this.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(x_values[i]);
})
}
or something similar. But the problem is that protovis doesn't seem to allow me to put any code around this .def
block.
I've also tried generating a string for each item in the x_values
array, which contains the definition of the Section A
function, and then calling it inside of the protovis code using eval()
, but that hasn't worked so far.
Any help here would be greatly appreciated!
I seem to have gotten closer to what I want by eliminating Section A
and moving that function inside of Section B
. See my latest jsfiddle for that code. Previously, in Section B
, the .left
line called one of the functions defined in Section A
. Instead, I moved that function's definition inside of the .left
line of code, like so:
////
// Section B Begin
////
for (var i = 0; i < x_values.length; i++) {
var x = x_values[i];
// Lines
panel
.add(pv.Rule)
.top(0)
.left (function() {
var ccc = this.parent.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(x);
})
.height(function() { return this.parent.height(); })
.width(null)
;
}
This is running now, but still not quite right: it's only drawing the last line in the x_values
array and overwriting all previous ones. Any ideas?
Replace your loop by the following code
panel
.add(pv.Rule)
.data(x_values)
.top(0)
.left (
function(d) {
this.index * 20 + 15
var ccc = this.parent.getContext();
var scale = ccc.chart.axes.base.scale;
var li = ccc.panel._layoutInfo;
return li.paddings.left + scale(d);
})
.height(function() { return this.parent.height(); })
.width(null)
;