Search code examples
javascriptsvgd3.jscrossfilterbrush

D3.js Brush controls with Ordinal Crossfilter Scales


I'm aiming to subset my choropleth like this wonderful example. I've reviewed the API documentation and Mike's Ordinal Brushing example.

Unfortunately, I'm still not getting my thick head around how brushes are supposed to work.

The examples are sophisticated and somewhat inaccessible for newcomers (lots of turnery operators and arrays of chart objects etc).

I've chosen not to use dc.js due to lack of support for things I'll have to deal with later. Nice work though and props to Nick Qi Zhu.

Can someone pls review my (native d3) approach and show me what I'm doing wrong?

I've setup my bar chart which renders fine. Basically a count of values in a normal distribution.

// Setup Crossfilter dimensions and groups
var nation      = crossfilter(MSPB),
    byScr       = nation.dimension(function(d){ return d.score; }),
    byScrGrp    = byScr.group().reduceCount(),
    byHosp      = nation.dimension(function(d){ return d.FIPS; }); 

// Histogram X-Axis ordinal scale
var x = d3.scale.ordinal()
        .domain(d3.range(0,2,0.1))
        .rangeBands([0, width, 0.5, 0.5 ]);

// Histogram Y-Axis linear scale
var y = d3.scale.linear()
        .domain([0, d3.max(byScrGrp.all(), function(d){ return d.value; })])
        .range([height/2, 0]);

// Histogram SVG containiner    
var svg = d3.select("#histogram").append("svg:svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height/2 + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // Histogram bars
    svg.selectAll("rect")
        .data(byScrGrp.all())
        .enter().append("rect")
        .attr("x", function(d,i){ return i * (width / byScrGrp.size() ); })
        .attr("width", width / byScrGrp.size() - barPadding )
        .attr("y", function(d){ return  y(d.value) ; })
        .attr("height", function(d){ return ((height/2) -y(d.value)); })
        .attr("fill", function(d){ return color(d.key); })
        .on("mouseover", function(d){ d3.select(this).attr("fill", "teal"); })
        .on("mouseout", function(d) { d3.select(this).attr("fill", function(d){return color(d.key);}) } );

Immediately after, I try to add my brush controls like this. Initially I just want to get the drag selection working on the bar chart. I'll worry about getting xAxis extent values for filtering the choropleth later.

 var brush = d3.svg.brush()
            .x(x)
            .on("brushstart", brushstart)
            .on("brush", brushmove)
            .on("brushend", brushend)

var brushg = svg.append("g")
            .attr("class", "brush")
            .call(brush)

    brushg.selectAll("rect")
                .attr("height", height/2)
                .attr("width", width);


function brushstart() {
  svg.classed("selecting", true);
}

function brushmove() {
  var s = brush.extent();
  brushg.classed("selected", function(d) { return s[0] <= (d = x(d)) && d <= s[1]; });
}

function brushend() {
  svg.classed("selecting", !d3.event.target.empty());
}

I'm seeing the crosshair drag handles but not seeing what I'd expect and certainly not getting the desired effect.

It's slow on this webserver but the full js file is here and partially working example here.

EDIT: I've tried to create a JSFiddle here.

Any pointers would be much appreciated.

Thanks


Solution

  • Hard to say without experimenting (any chance of creating a JSfiddle or similar?). I suspect the issue is that you are setting the width of all the rects in the brush. You only need to set the height, I believe. This is probably what is causing your extents to go all crazy.

    Once you get that straightened out, you'll need to filter your Crossfilter dimension based on the brush extent.

    Also, Crossfilter's group.reduceCount function doesn't take an argument, and if it is possible you are missing data you need to make sure to test/cast your data in your dimension accessors to make sure they always return naturally ordered numbers or strings.