Question

I am using d3.js, and I'm working on a brushed area chart by modifying this example. In addition to the x-axis changing based on the brush, I'd like chart's y-axis to be redrawn, based on the y-values of the data that fall within the brush (similar to the behavior of a Google Finance chart).

I have gotten the functionality working, but only in a way that enables the brush to be drawn in both x- and y-space. I did this by first adding a y scale to the brush variable:

var brush = d3.svg.brush()
    .x(x2)
    .y(y2)
    .on("brush", brush);

This makes brush.extent() return the following multi-dimensional array: [ [x0, y0], [x1, y1] ]. I then use this data in the brush() function to redefine the x- and y- domain for the focus chart:

function brush() {
  var extent = brush.extent();
  x.domain(brush.empty() ? x2.domain() : [ extent[0][0], extent[1][0] ]);
  y.domain(brush.empty() ? y2.domain() : [ extent[0][1], extent[1][1] ]);
  focus.select("path").attr("d", area);
  focus.select(".x.axis").call(xAxis);
  focus.select(".y.axis").call(yAxis);
}

This works, but by defining the y scale in the brush variable, the user can now drag 'boxes' in the focus chart, rather than only being able to drag west to east, as in the original chart.

Essentially, my question is: how do I get the range of values that fall within a brush's area, rather than the range of the brush's area itself? Is this even possible?

d3's brush documentation is here.

Was it helpful?

Solution

I came up with a solution.

I used the brush-filtered x.domain to filter down my original data set. This new filtered data set has only the values that fall within the brush:

// Use x.domain to filter the data, then find the max and min duration of this new set, then set y.domain to that
  x.domain(brush.empty() ? x2.domain() : brush.extent());
  var dataFiltered = data.filter(function(d, i) {
    if ( (d.date >= x.domain()[0]) && (d.date <= x.domain()[1]) ) {
      return d.duration;
    }
  })
  y.domain([0, d3.max(dataFiltered.map(function(d) { return d.duration; }))]);

Finally, be sure to redraw the y-axis as well as the x-axis:

focus.select("path").attr("d", area);
focus.select(".x.axis").call(xAxis);
focus.select(".y.axis").call(yAxis);

OTHER TIPS

A shorter possibility would be to use d3.scale.invert() on your brush.extent() like so:

var domainExtent = brush.extent().map(function(d){return scale.invert(d);});
var filteredData = data.filter(function(d){return ((d <= domainExtent[1]) && (d >= domainExtent[0]));});

However, by now d3 has gained d3.brushX(), which only allows brushing in East/West direction by design.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top