How do you get the selected data in brush & ldquo; Brush & rdquo; Event Manager?

advertisements

I am attempting to create a vertical timeline using d3.js that is linked to a map so that any item(s) contained in the brush will also be displayed in the map. Kind of like http://code.google.com/p/timemap/ but with d3 instead of SIMILE and a vertical timeline rather than horizontal.

I can successfully create an svg with vertical bars representing time ranges, legend, ticks, and a brush. The function handling brush events is getting called and I can obtain the extent which contains the y-axis start and stop of the brush. So far so good...

How does one obtain the datums covered by the brush? I could iterate over my initial data set looking for items within the extent range but that feels hacky. Is there a d3 specific way of getting the datums highlighted by a brush?

var data = [
  {
     start: 1375840800,
     stop: 1375844400,
     lat: 0.0,
     lon: 0.0
  }
];
var min = 1375833600; //Aug 7th 00:00:00
var max = 1375919999; //Aug 7th 23:59:59
var yScale = d3.time.scale.utc().domain([min, max]).range([0, height])
var brush = d3.svg.brush().y(yScale).on("brush", brushmove);

var timeline = d3.select("#myDivId").append("svg").attr("width", width).attr("height", height);

timeline.selectAll("rect")
  .data(data)
  .enter().append("rect")
    .attr("x", function(datum, index) {return index * barSize})
    .attr("y", function(datum, index) {return yScale(datum.start)})
    .attr("height", function(datum, index) {return yScale(datum.end) - yScale(datum.start)})
    .attr("width", function() {return barSize})

timeline.append("g")
  .attr("class", "brush")
  .call(brush)
    .selectAll("rect")
    .attr("width", width);

function brushmove() {
  var extent = brush.extent();
  //How do I get the datums contained inside the extent????
}


You'll need to do some kind of iteration to figure out what points live inside the brush extent. D3 doesn't automatically do this for you, probably because it can't know what shapes you're using to represent your data points. How detailed you get about what is considered "selected" and what isn't is quite application specific.

There are a few ways you can go about this:

  1. As you suggest, you can iterate your data. The downside to this is that you would need to derive the shape information from the data again the same way you did when you created the <rect> elements.

  2. Do a timeline.selectAll("rect") to grab all elements you potentially care about and use selection.filter to pare it down based on the x, y, height and width attributes.

  3. If performance is a concern because you have an very large number of nodes, you can use the Quadtree helper to partition the surface and reduce the number of points that need to be looked at to find the selected ones.