D3js how to select paths and highlight them with a brush function

65 Views Asked by At

I'm working on a parallel plot that is like this: enter image description here

I'm trying to implement the brush function that selects for every column the number of paths and highlight them if they are in a precise range.

The problem is that I don't know exactly how to take the coordinate of the node of the path in that precise column.

Here is the code in which I try to implement it:

// set the dimensions and margins of the graph
var margin = {top: 30, right: 30, bottom: 10, left: 40},
  width = 560 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

var brushParallelWidth = 50; 

// append the svg object to the body of the page
var svgParallel = d3.select("#parallelPlot")
.append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
.append("g")
  .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");
   

function drawParallelPlot(){
    // Parse the Data
    d3.csv("../../data/processed/ParallelPlotData.csv", function(data) {

      // Here I set the list of dimension manually to control the order of axis:
      dimensions = ["GreenAreaDensity","LowEmission","AutobusStopDensity","CirculatingVehicles","ExposedNoisePollution"]

      // Build the X scale -> it find the best position for each Y axis
      var xParallel = d3.scalePoint()
        .range([0, width])
        .domain(dimensions); 

      // For each dimension, I build a linear scale. I store all in a y object
      var yParallel = {}
      var name;
      for (i in dimensions) {
        name = dimensions[i]
        //console.log(name);
        yParallel[name] = d3.scaleLinear()
        .domain( d3.extent(data, function(d) { 
            //console.log(+d[name] + " is of type: " + typeof(+d[name])) // print the value and the type
              return +d[name];   // --> Different axis range for each group
          }) )
          .range([height, 0])
      }
      
      // Highlight the specie that is hovered
      var highlight = function(d){

        //selected_city = d.City

        // first every group turns grey
        d3.selectAll(".line")
          .transition().duration(200)
          .style("stroke", "lightgrey")
          .style("opacity", "0.5")
        // Second the hovered specie takes its color
        d3.selectAll("." + selected_city)
          .transition().duration(200)
          .style("stroke",function(p){ return( "#4682B4");})
          .style("opacity", "1")
      }

      // Unhighlight
      var doNotHighlight = function(d){
        d3.selectAll(".line")
          .transition().duration(200).delay(1000)
          .style("stroke", function(p){ return( "#AAA");} )
          .style("opacity", "1")
      } 

      // The path function take a row of the csv as input, and return x and y coordinates of the line to draw for this raw.
      function path(d) {
        return d3.line()(dimensions.map(function(column) {
          //console.log("here's the y coordinate of the axes: " + yParallel[column](d[column]));
          //console.log("column is " + column);
          return [xParallel(column), yParallel[column](d[column])]; 
          }));
      } 

      // Draw the lines
      svgParallel
        .selectAll("myPath")
        .data(data)
        .enter()
        .append("path")
          .attr("class", "pt") //here I call my class pt
          .attr("class", function (d) { return "line" + d.City} ) // 2 class for each line: 'line' and the group name
          .attr("d", path)
          .style("fill", "none" )
          .style("stroke", function(d){ return("#AAA")} )
          .style("opacity", 0.5)
          //.on("mouseover", highlight)
          //.on("mouseleave", doNotHighlight ) 

      // Draw the axis:
      svgParallel.selectAll("myAxis")
        // For each dimension of the dataset I add a 'g' element:
        .data(dimensions).enter()
        .append("g")
        .attr("class", "axis")
        // I translate this element to its right position on the x axis
        .attr("transform", function(d) { return "translate(" + xParallel(d) + ")"; })
        // And I build the axis with the call function
        .each(function(d) { d3.select(this).call(d3.axisLeft().ticks(5).scale(yParallel[d])); })
        // Add axis title
        .append("text")
          .style("text-anchor", "middle")
          .attr("y", -9)
          .text(function(d) { return d; })
          .style("fill", "black")
  
        //initialize the brush
        const brushParallel = d3.brushY()
        .extent([
          [-(brushParallelWidth / 2), 0 ],
          [ brushParallelWidth / 2, height]
        ])
        .on("start brush end", brushedParallel)

      //here I append the parallel brush for every axis --- store in a variable?
        svgParallel.append("g")
          .selectAll("g") //select all the graph
          .data(dimensions)
          .enter().append("g")
            .attr("transform", d=>`translate(${xParallel(d)}, 0)`)
          .attr("class", "brush")
          .call(brushParallel);

  //function passed to the brush to highlight the y axis 
  function brushedParallel() { 
      console.log("Prova funzione brushed");
      //var actives = dimensions.filter(function(p){ return !yParallel[p].event.selection.empty(); });
      extent = d3.event.selection; //sarebbe il punto in alto e in basso della selezione brush
      //questi array servono per la colorazione dei path
      var selectedLines=[]; //qui metto tutte i path della selezione 
      var allLines =[]; //qui metto tutti i path

//for every dimension

//I must take the brush associated

//and then color the path that are selected by the brush

//here I scroll through all the paths of the graph
      d3.selectAll(".pt")
      .each(dimensions.map(function(column){
        
        var myLine = d3.select(this); //salvo la linea del path 
        
        //allLines.push(myLine); //here I push all the lines
        
        console.log("dimension map");
        
          if(extent[0][0] > yParallel[column](this.d[column]) && extent[0][1] < yParallel[column](this.d[column])){
            console.log(yParallel[column](this.d[column]));
            console.log("la brush funziona");
            //selectedLines.push(myLine); //here I push only the selected lines
          }
      })); 
    } 

    })
    
}

drawParallelPlot();

I think the problem arises in the brushedParallel function, where I should select the paths and then store them in a variable that is used to change their color.

Here I link a simplyfied version of this plot, with an associated dataset:

https://stackblitz.com/edit/so76371176-cfsvya?file=index.js

Does anyone have any idea how to do this selection or how to fix the code?

Any help is appreciated!

0

There are 0 best solutions below