Search code examples
javascriptd3.jsscalecrossfilterbrush

D3.js chart brush and Crossfilter.js scales out of sync


I am trying to highlight selections in my D3 bar chart using the brush extent.

JSFiddle http://jsfiddle.net/Nyquist212/yys2mqkx/

I'm using crossfilter to reduceCount() my data so I can plot score distribution. But this seems to be screwing with my x-scale. The effect is most noticeable when you drag the entire brush extent.

Online example with full dataset is here (takes a few secs to load).

I'm using linear scales but it appears as if the brush coordinates are out of sync with the bar chart coordinate space (see console logging).

enter image description here

My brush extent does not appear to be correctly binding to the rects in front of it.

My problem appears to lie around line# 840 where I'm trying to do something like this,

var extent = brush.extent(), 
    lower = extent[0], 
    upper = extent[1];

d3.selectAll("rect")
  .style("opacity", function(d) {
       return d.key >= lower && d.key <= upper || brush.empty() ? "1" : ".4";
       })

 }

I'm trying to achieve this effect which highlights the selected bars and fades the unselected.

Can someone pls help me understand what I'm doing wrong?

Thanks.


Solution

  • The brush expects to use the x scale you passed in to invert the mouse coordinates to produce the target value in the domain (source). Unfortunately, you're not actually using that x scale to draw the bars, so it looks like the brush is off. In fact, it's your bars that are off, which can be seen by updating line 806 to actually use the x scale that the brush is using. (jsfiddle)

        .attr("x", function (d, i) {
            return x(d.key);
        })
    

    Well, that's great, except it's clearly not at all what you're looking for.

    overlapping bars

    Now you just need to figure out how to produce an x scale that will do what you want for both the bars and the brush. The Linear Scales API documentation will be of great help there.

    EDIT: This should get you started. Update the scale to use an ordinal one with the keys as the domain.

    var keys = byScrGrp.all()
        .map(function (d) { return d.key; });
    
    var x = d3.scale.ordinal()
        .domain(keys)
        .rangeBands([0, width]);
    

    Update the bar rendering to use the new ordinal scale.

        .attr("x", function (d) {
            return x(d.key);
        })
        .attr("width", x.rangeBand())
    

    Update the brushmove method to brute-force which groups are selected.

        svg.selectAll("rect")
            .style("opacity", function(d) {
                var k = x(d.key) + 0.5 * x.rangeBand();
                return  s[0] <= k && k <= s[1] ? "1" : ".2"; 
            });
    

    Also update the brush initializer to use the new coordinates.

        .extent([x(1.1),x(0.9)])
    

    For me Chrome does alright with this but Firefox kind of chokes, at least inside JSFiddle. There are clearly opportunities for optimization here.

    fiddle