Search code examples
javascriptd3.jslinechartinteractionsankey-diagram

How to create an interaction between charts in d3


I want to make two charts interactive. The code for the charts can be found in the following links: https://github.com/irbp005/d3SankeyAndLineInteraction

The visual representation of the charts is as follows:

Sankey and Line Chart visualization

The lines represent the trend value of the node A, B & C.

What I want to do is that when hovering over the A, B & C nodes the corresponding lines in the line chart get highlighted and vice versa, when hovering over the lines in the line chart the nodes in the sankey chart get highlighted.

Any ideas on how I can achieve this interaction.

Thanks


Solution

  • Here's some quick modifications to achieve your aims:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <title>SANKEY Static Sankey Visualization</title>
    <style>
    	.node rect {
    	  cursor: move;
    	  fill-opacity: .9;
    	  shape-rendering: crispEdges;
    	}
    	.node text {
    	  pointer-events: none;
    	  text-shadow: 0 1px 0 #fff;
    	}
    	.link {
    	  fill: none;
    	  stroke: #000;
    	  stroke-opacity: .2;
    	}
    	.link:hover {
    	  stroke-opacity: .5;
    	}
    	.axis path,
    	.axis line{
    	  fill: none;
    	  stroke: black;
    	}
    	
    	#A{
    	  fill: none;
    	  stroke: rgb(15, 58, 88);
    	  stroke-width: 5px;
    	}
    	
    	#B{
    	  fill: none;
    	  stroke: rgb(85, 97, 113);
    	  stroke-width: 5px;
    	}
    	
    	#C{
    	  fill: none;
    	  stroke: rgb(124, 62, 6);
    	  stroke-width: 5px;
    	}
    	.tick text{
    	  font-size: 12px;
    	}
    	.tick line{
    	  opacity: 0.2;
    	}
    </style>
    <body>
    
    <p id="chartSankey"></p>
    <p id="chartLine"></p>
    
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="https://rawgit.com/d3/d3-plugins/master/sankey/sankey.js"></script>
    
    <script> 
    // ## Datasets for Sankey visualization
    var graph = 
    	{
    	"nodes":[
    	{"node":0,"name":"A"},
    	{"node":1,"name":"B"},
    	{"node":2,"name":"C"},
    	{"node":3,"name":"D"},
    	{"node":4,"name":"E"},
    	{"node":4,"name":"F"}
    	],
    	"links":[
    	{"source":0,"target":3,"value":200},
    	{"source":0,"target":4,"value":300},
    	{"source":0,"target":5,"value":500},
    	{"source":1,"target":3,"value":400},
    	{"source":1,"target":4,"value":600},
    	{"source":1,"target":5,"value":1000},
    	{"source":2,"target":3,"value":600},
    	{"source":2,"target":4,"value":900},
    	{"source":2,"target":5,"value":1500}
    	]};
    // ## Datasets for linechart	
    var dataset = [
      {x: 0, y: 0},
      {x: 1, y: 28},
      {x: 2, y: 131},
      {x: 3, y: 152},
      {x: 4, y: 187},
      {x: 5, y: 245},
      {x: 6, y: 345},
      {x: 7, y: 481},
      {x: 8, y: 559},
      {x: 9, y: 632},
      {x: 10, y: 820},
      {x: 11, y: 800},
      {x: 12, y: 1000}
    ];
    var dataset2 = [
      {x: 0, y: 0},
      {x: 1, y: 74},
      {x: 2, y: 174},
      {x: 3, y: 393},
      {x: 4, y: 593},
      {x: 5, y: 814},
      {x: 6, y: 1038},
      {x: 7, y: 1276},
      {x: 8, y: 1529},
      {x: 9, y: 1756},
      {x: 10, y: 1860},
      {x: 11, y: 1900},
      {x: 12, y: 2000}
    ];
    var dataset3 = [
      {x: 0, y: 0},
      {x: 1, y: 95},
      {x: 2, y: 295},
      {x: 3, y: 406},
      {x: 4, y: 518},
      {x: 5, y: 869},
      {x: 6, y: 1282},
      {x: 7, y: 1403},
      {x: 8, y: 1504},
      {x: 9, y: 1840},
      {x: 10, y: 2050},
      {x: 11, y: 2500},
      {x: 12, y: 3000}
    ];
    	
    </script>
    
    <script>
    // ## Original source for Sankey https://gist.github.com/d3noob/c2637e28b79fb3bfea13
    // ## Option to move nodes eliminated
    // ## Order of nodes disabled on sankey.js (//nodes.sort(ascendingDepth);) to maintain the order of data, which is alphabetical and prevent for the nodes to be ordered by size 
    // ## Codigo inicial adquirido para el Sankey https://gist.github.com/d3noob/c2637e28b79fb3bfea13
    // ## La posibilidad de mover los nodos se ha eliminado
    // ## La ordenacion en el archivo sankey.js se ha desbilitado (//nodes.sort(ascendingDepth);) para mantener el ordenen de los datos y evitar que los nodos se ordenen por tama~o 
    var units = "$";
    var margin = {top: 10, right: 10, bottom: 40, left: 20},
        width = 700 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;
    var formatNumber = d3.format(",.0f"),    // zero decimal places
        format = function(d) { return formatNumber(d) + " " + units; },
        color = d3.scale.category20();
    // append the svg canvas to the page
    var svg = d3.select("#chartSankey").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 + ")");
    // Set the sankey diagram properties
    var sankey = d3.sankey()
        .nodeWidth(36)
        .nodePadding(40)
        .size([width, height]);
    var path = sankey.link();
      sankey
          .nodes(graph.nodes)
          .links(graph.links)
          .layout(32);
    // add in the links
      var link = svg.append("g").selectAll(".link")
          .data(graph.links)
        .enter().append("path")
          .attr("class", function(d){
             return "link " + d.source.name;
          })
          .attr("d", path)
          .style("stroke-width", function(d) { return Math.max(1, d.dy); })
          .sort(function(a, b) { return b.dy - a.dy; })
          .on("mouseover", function(d){
            var s = d.source.name;
            d3.selectAll(".line").style('opacity', .1);
            d3.select("#" + s).style('opacity',  1);
          })
          .on("mouseout", function(d){
            d3.selectAll(".line").style('opacity', 1);
          });
          
          
          
    // add the link titles
      link.append("title")
            .text(function(d) {
        		return d.source.name + " → " + 
                    d.target.name + "\n" + format(d.value); });
    // add in the nodes
      var node = svg.append("g").selectAll(".node")
          .data(graph.nodes)
        .enter().append("g")
          .attr("class", "node")
          .attr("transform", function(d) { 
    		  return "translate(" + d.x + "," + d.y + ")"; })
    // add the rectangles for the nodes
      node.append("rect")
          .attr("height", function(d) { return d.dy; })
          .attr("width", sankey.nodeWidth())
          .style("fill", function(d) { 
    		  return d.color = color(d.name.replace(/ .*/, "")); })
          .style("stroke", function(d) { 
    		  return d3.rgb(d.color).darker(2); })
    		  .on("mouseover", function(d){
            var s = d.name;
            d3.selectAll(".line").style('opacity', .1);
            d3.select("#" + s).style('opacity',  1);
          })
          .on("mouseout", function(d){
            d3.selectAll(".line").style('opacity', 1);
          });
    		  
        node.append("title")
          .text(function(d) { 
    		  return d.name + "\n" + format(d.value); });
    // add in the title for the nodes
      node.append("text")
          .attr("x", -6)
          .attr("y", function(d) { return d.dy / 2; })
          .attr("dy", ".35em")
          .attr("text-anchor", "end")
          .attr("transform", null)
          .text(function(d) { return d.name; })
        .filter(function(d) { return d.x < width / 2; })
          .attr("x", 6 + sankey.nodeWidth())
          .attr("text-anchor", "start");
    	  
    </script>
    
    <script>
    var margin = {top: 10, right: 10, bottom: 40, left: 40},
        width = 700 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;
    	
    var xScale = d3.scale.linear()
        .domain([0, d3.max(dataset3, function(d){ return d.x; })])
        .range([0, width]);
    var yScale = d3.scale.linear()
        .domain([0, d3.max(dataset3, function(d){ return d.y; })])
        .range([height, 0]);
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .tickSize(-height);
    var yAxis = d3.svg.axis()
        .scale(yScale)
    	.orient("left")
        .ticks(10);
    	
    var line = d3.svg.line()
        .x(function(d) { return xScale(d.x); })
        .y(function(d) { return yScale(d.y); });
    	
    var line2 = d3.svg.line()
        .x(function(d) { return xScale(d.x); })
        .y(function(d) { return yScale(d.y); });
    var line2 = d3.svg.line()
        .x(function(d) { return xScale(d.x); })
        .y(function(d) { return yScale(d.y); });
    	
    var svg = d3.select("#chartLine").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 + ")");
      svg.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis)
      svg.append("g")
          .attr("class", "y axis")
          .call(yAxis)
      svg.append("path")
          .data([dataset])
          .attr("class", "line")
          .attr("d", line)
          .attr("id", "A")
          .on("mouseover", highlight)
          .on("mouseout", function(){ d3.selectAll(".link").style('opacity', 1);   });
      svg.append("path")
          .data([dataset2])
          .attr("class", "line")
          .attr("d", line)
          .attr("id", "B")
          .on("mouseover", highlight)
          .on("mouseout", function(){ d3.selectAll(".link").style('opacity', 1);   });
      svg.append("path")
          .data([dataset3])
          .attr("class", "line")
          .attr("d", line)
          .attr("id", "C")
          .on("mouseover", highlight)
          .on("mouseout", function(){ d3.selectAll(".link").style('opacity', 1);   });
          
      function highlight(){
        var id = this.id;
        d3.selectAll(".link").style('opacity', 0.1);
        d3.selectAll("." + id).style('opacity', 1);
      }
    	  
    </script>
    
    </body>
    </html>