How to rescale Y axis when changing the selected X axis range through a rangechart

2,579 views
Skip to first unread message

Peter Coppens

unread,
Jul 12, 2013, 2:16:00 AM7/12/13
to dc-js-us...@googlegroups.com
Hello

Does anyone have a tip on how it would be possible to rescale the y axis when the x axis range selection changes?

The "problem" is illustrated on http://nickqizhu.github.io/dc.js/ - if you select some x range the y axis does not rescale. However, when using one of the other charts to change the selected data (e.g. clicking on one of the "days", the y axis does rescale).

I would like to have the y axis rescale on x axis range selection changes. Can that be achieved in some way?

Tx

Peter

Travis

unread,
Jul 12, 2013, 4:53:39 AM7/12/13
to dc-js-us...@googlegroups.com
I'd like to know this too.

The focus method is being used by the bar chart when a filtered event occurs :

.on("filtered", function(chart) {
            dc
.events.trigger(function() {
                moveChart
.focus(chart.filter());
           
});

This moves the chart to the given filter range and looks like it doesn't rescale the y-axis.

Looking at the api it seems the elasticY attribute only works called on a redraw event. Is there a workaround?

Nick Zhu

unread,
Jul 12, 2013, 11:31:22 AM7/12/13
to Travis, dc-js-us...@googlegroups.com
Currently you have to recalculate the new scale and set it in onfocus listener, however this is planned to be addressed in v1.5.

Peter Coppens

unread,
Jul 15, 2013, 1:53:01 PM7/15/13
to dc-js-us...@googlegroups.com, Travis
Sorry for my ignorance, but is it possible to be a bit more specific as how to such an onfocus listener is created and what the arguments are it will get for input. I could not find anything in dc.js at first sight

Many thanks indeed

Peter

Nick Zhu

unread,
Jul 16, 2013, 4:43:10 PM7/16/13
to Peter Coppens, dc-js-us...@googlegroups.com, Travis
This workaround a bit involved and needs fair a bit of knowledge of crossfilter and d3:

1. turn off elasticY
2. set your y domain manually
3. in your onFocus listener calculate the max and min for your data set within the focused range
4. set the y axis domain to the newly calculated [min, max]
5. redraw


Nick

Peter Coppens

unread,
Jul 16, 2013, 5:11:22 PM7/16/13
to dc-js-us...@googlegroups.com, Peter Coppens, Travis
Thanks Nick

Believe it or not, but I have everything going except for declaring the onfocus listener. I am not sure how to do that. I understand this is not related to dc, d3 or crossfilter, but nevertheless any help would be greatly appreciated.

Tx

Peter

Nick Zhu

unread,
Jul 16, 2013, 5:14:41 PM7/16/13
to Peter Coppens, dc-js-us...@googlegroups.com, Travis
The brute-force approach will be simply get all your data that fall within focused range then use d3.max and d3.min or d3.extent (all O(n)) to extract the min and max value so you can reset the y domain.

Peter Coppens

unread,
Jul 16, 2013, 5:26:32 PM7/16/13
to dc-js-us...@googlegroups.com, Peter Coppens, Travis
Determining the new Y limits works fine. I tested this is in a renderlet. It's just that at that point it is too late to set them. So, it is getting an focus event listener installed is what I am struggling with now

Peter

Peter Coppens

unread,
Jul 17, 2013, 2:35:35 AM7/17/13
to dc-js-us...@googlegroups.com, Peter Coppens, Travis
I have no idea how much bad hacking the "solution" I came up with is, but for the interested, here is roughly how I made it work 


            function resetYAxis() {

               
var firstKeyVal = myChart.dimension().bottom(1)[0].CounterDay;

               
var lastKeyVal = myChart.dimension().top(1)[0].CounterDay
               
var allVals = counterSumPerDay.all();
               
var maxValue = d3.max(allVals, function (v) {
                   
if (v.key >= firstKeyVal && v.key <= lastKeyVal) {
                       
return v.value;
                   
} else {
                       
return -Infinity;
                   
}
               
});
               
var currMaxY = myChart.y().domain()[1];
               
if (currMaxY !== maxValue) {
                   
var newYAxisScale = d3.scale.linear().domain([0,maxValue]).range(myChart.yAxis().scale().range());
                    myChart
.elasticY(false).yAxis().scale(newYAxisScale);
                    myChart
.y(d3.scale.linear().domain([0, maxValue]));
                    myChart
.renderYAxis(myChart.g());
                    dc
.redrawAll();
               
}
           
}
            rangeChart
.renderlet(function (chart) {
                dc
.events.trigger(function () {
                   
resetYAxis();
                    myChart
.focus(chart.filter());
               
});
           
});


Doing it like this, there is still an extra redraw involved which I do find annoying but I was unable to get rid of it until now.

Peter

Nick Zhu

unread,
Jul 17, 2013, 12:43:11 PM7/17/13
to Peter Coppens, dc-js-us...@googlegroups.com, Travis
Peter, thanks for sharing the work around, I am sure someone else will find it useful =) one minor possible improvement:

* instead of calling dc.redrawAll() you might be able to just call myChart.redraw()

Nick

Peter Coppens

unread,
Jul 19, 2013, 1:57:22 AM7/19/13
to dc-js-us...@googlegroups.com, Peter Coppens, Travis
Thanks for the tip. 

Sharing the hack seems the least I could do in return for being to able to use dc.js. 

Peter
Reply all
Reply to author
Forward
0 new messages