Question

I am learning the brush concept of d3 and want to know if it is possible to use brush on sequence of rectangles instead of x-axis. I have 12 rectangles created and would like to stretch over the rectangles using the mouse.

My code is :

    var margin = {top: 4, right: 50, bottom: 20, left: 50},
    width = 960 - margin.left - margin.right,
    height = 120 - margin.top - margin.bottom;

var svg = d3.select("body").append("svg").attr("id","svgtimer")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.json("TimerData.json", function(data) {
       CreateLegend('#timer',svg,"rectangle",data,'Jan','Dec');
    })


svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")




  var brush = d3.svg.brush()
    .x(d3.scale.identity().domain([0, width]))
    .y(d3.scale.identity().domain([0, height]))
    .on("brush", brushed);

    svg.append("g").call(brush);


 function brushed() {
    console.log(brush.extent());
}




function CreateLegend(div,svg,svgid,data,header,trail)
{

      console.log(data);
      var traillength=0;
      var svgelem;
    //alert("Non-empty");

    //d3.json(filepath, function(data) {
    console.log(" the svg id is "  +svgid); 

    jsondata = data;

    rectangle= svg.selectAll("rect").data(data).enter().append("rect");
      var RectangleAttrb = rectangle

                        .attr("id", function (d,i) { return svgid + "id" + i ; })
                        .attr("x", function (d) { return d.x_axis; })
                       .attr("y", function (d) { return d.y_axis; })
                       .attr("width",function(d) { return d.width; } )
                   .attr("height",function(d) { return d.height; })
                       .style("stroke", function (d) { return d.border;})
                       .style("fill", function(d) { return d.color; });





            var textparam = svg.selectAll("text").data(data).enter().append("text");

            var yearheader = d3.select("#header");

        if(yearheader.empty()) 
        {

            var textheader = svg.append("text").attr("dx",20).attr("dy",5).text(header).attr("id",header).attr("style","margin-bottom:21px;border-bottom: solid 2px #ffd97f; font-size:12px;")
        }

            if (trail.length == 0)
        {
              //console.log(textheader);
              d3.select(header).attr("style","font-size:15.1px;text-decoration:underline");
          //svg.attr("style","text-decoration:underline");
        }


        var text = textparam .attr("x", function (d) { traillength = d.x_axis + d.width +10; return d.x_axis + d.width +10; })
                       .attr("y", function (d) { return d.y_axis + d.height-5; })
                       .attr("width",30 )
                       .attr("height",20)
                       .attr("style", "text-decoration:none")
                       .text(function(d) { return d.text; });


    var yearheader = d3.select("#trail");


    if (trail.length > 0 && yearheader.empty() )
      {

        svg.append("text").attr("id","trail").attr("dx",traillength-10).attr("dy",5).text(trail).attr("style","margin-bottom:21px;border-bottom: solid 2px #ffd97f; font-size:12px;" )
      }


    //});


}

My timerdata is :

[

   { "x_axis":40, "y_axis": 10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":60, "y_axis": 10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":80, "y_axis": 10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":100, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":120, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":140, "y_axis":10,"width":20,"height":15,"color": "#ffffff","border":"#000000"},
   { "x_axis":160, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":180, "y_axis":10,"width":20,"height":15,"color": "#ffffff","border":"#000000"},
   { "x_axis":200, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":220, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":240, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"},
   { "x_axis":260, "y_axis":10,"width":20,"height":15,"color" : "#ffffff","border":"#000000"}



]

How do i use the brush to stretch on the rectangles. I looked at some samples and found that brush is associated with x-axis. Is it not possible to use brush on these rectangles?

Was it helpful?

Solution

You should be able to use d3.svg.brush() without any special modifications. I've taken your code and implemented it here. The code that initialises and attaches the brush is as follows.

var brush = d3.svg.brush()
  .x(d3.scale.identity().domain([0, width]))
  .y(d3.scale.identity().domain([0, height]))
  .on("brush", brushed);
svg.append("g").call(brush);

This initialises the brush and assigns identity scales to the x and y dimensions. In your code, you're not actually using scales to convert the user to screen coordinates, but taking the user coordinates directly. This is what the identity scales does. The domain of each is set to the respective dimension of the graph to tell the brush how big the brushed area can be.

You can specify an initial extent of the brush using the .extent() function. The implementation of the handler looks like this in your case.

function brushed() {
  var e = brush.extent(),
    selected = svg.selectAll("rect").filter(function(d) {
    return d.x_axis <= e[1][0] && d.x_axis + d.width >= e[0][0] && d.y_axis <= e[1][1] && d.y_axis + d.height >= e[0][1];
  })
  console.log(selected);
}

It first gets the current extent of the brush and then filters the drawn rectangles by it. That is, for each rectangle, the code checks whether it overlaps with the brush rectangle. If it does, it is retained in the list selected. Note that this implementation is not particularly efficient as it iterates over all the rectangles. This is not a problem in your case as you only have a few, but if you want to use this with many more rectangles in two dimensions, I recommend using a more efficient data structure, such as a quadtree.

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