Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 Tree layout visualization - Inherit child with multiple parents

Tags:

treeview

d3.js

I'm fresh bee for D3-visulization. Currently working with creating D3 tree layout visualization for data lineage. In a data lineage flow, a child node can be derived from more than one parent. Here is the example. In below example, a 'DevLead' may work with 2 managers.

var data = [
     { "name": "Director", "parent": "null", "depth": 0 },
     { "name": "Manager1", "parent": "Director", "depth": 1 },
     { "name": "Manager2", "parent": "Director", "depth": 1 },
     { "name": "DevLead", "parent": "Manager1", "depth": 2 },
     { "name": "DevLead", "parent": "Manager2", "depth": 2 }
        ];

Getting output refer below image attached.

Output

I'd like to see 'DevLead' children should show only one, and there should be a derivation from 'Manager1' and 'Manager2'. Could any one help with this.

like image 759
Shaan Avatar asked Apr 20 '17 18:04

Shaan


1 Answers

D3 Tree Layout does not exactly supports multiple parents

What Can you do?

  1. Use network graph instead - downside is that node positioning is hard

    I had similar requirements and tried building network graph similar with tree layout, but when there are many nodes, it gets messy ...
    you can check it on codepen

  2. use hack on tree layout - draw additional link from other node
    check this example

  3. another hack using hidden nodes - jsfiddle

Also, I think, these links will help you further :

  • Family Tree in d3.js

  • d-tree library - data with multiple parents

If you go with first option, here, you can play with this snippet by removing and adding nodes in data

<!DOCTYPE html>
<html >

<head>
  <meta charset="UTF-8">
  <link rel="shortcut icon" type="image/x-icon" href="https://production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
  <link rel="mask-icon" type="" href="https://production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
  <title>CodePen - A Pen by  dato</title>
  
  
  
  
  
  
  
  
  
</head>

<body translate="no" >

  
  
  <script src='https://d3js.org/d3.v3.min.js'></script>

    <script>
    var width = window.innerWidth - 20,
  height = window.innerHeight - 20,
  radius = 30;

var min_zoom = 0.1;
var max_zoom = 7;

var zoom = d3.behavior.zoom().scaleExtent([min_zoom, max_zoom])

var fill = d3.scale.category20();

var force = d3.layout.force()

.charge(-8000)
  .linkDistance(200)

.size([width, height]);

force.drag().on("dragstart", dragstarted)

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);

var chart = svg.append('g');

var json = {
  "nodes": [{
    "name": "node0"
  }, {
    "name": "node1"
  }, {
    "name": "node2"
  }, {
    "name": "node3"
  }, {
    "name": "node4"
  }, {
    "name": "node5"
  }, {
    "name": "node6"
  }, {
    "name": "node7"
  }, {
    "name": "node8"
  }, {
    "name": "node9"
  }, {
    "name": "node10"
  }, {
    "name": "node11"
  }, {
    "name": "node12"
  }, {
    "name": "node13"
  }, {
    "name": "node14"
  }, {
    "name": "node15"
  }, {
    "name": "node16"
  }, {
    "name": "node17"
  }, {
    "name": "node18"
  }, {
    "name": "node19"
  }, {
    "name": "node20"
  }, {
    "name": "node21"
  }, {
    "name": "node22"
  }, {
    "name": "node23"
  }, {
    "name": "node24"
  }, {
    "name": "node25"
  }, {
    "name": "node26"
  }, {
    "name": "node27"
  }, {
    "name": "node28"
  }, {
    "name": "node29"
  }, {
    "name": "node30"
  }, {
    "name": "node31"
  }, {
    "name": "node32"
  }, {
    "name": "node33"
  }, {
    "name": "node34"
  }, {
    "name": "node35"
  }, {
    "name": "node36"
  }, {
    "name": "node37"
  }, {
    "name": "node38"
  }, {
    "name": "node39"
  }, {
    "name": "node40"
  }, {
    "name": "node41"
  }, {
    "name": "node42"
  }, {
    "name": "node43"
  }, {
    "name": "node44"
  }, {
    "name": "node45"
  }, {
    "name": "node46"
  }, {
    "name": "node47"
  }, {
    "name": "node48"
  }, {
    "name": "node49"
  }, {
    "name": "node50"
  }, {
    "name": "node51"
  }, {
    "name": "node52"
  }, {
    "name": "node53"
  }, {
    "name": "node54"
  }, {
    "name": "node55"
  }, {
    "name": "node56"
  }, {
    "name": "node57"
  }, {
    "name": "node58"
  }, {
    "name": "node59"
  }, {
    "name": "node60"
  }, {
    "name": "node61"
  }, {
    "name": "node62"
  }, {
    "name": "node63"
  }, {
    "name": "node64"
  }, {
    "name": "node65"
  }, {
    "name": "node66"
  }, {
    "name": "node67"
  }, {
    "name": "node68"
  }, {
    "name": "node69"
  }, {
    "name": "node70"
  }, {
    "name": "node71"
  }, {
    "name": "node72"
  }, {
    "name": "node73"
  }, {
    "name": "node74"
  }, {
    "name": "node75"
  }, {
    "name": "node76"
  }, {
    "name": "node77"
  }, {
    "name": "node78"
  }, {
    "name": "node79"
  }, {
    "name": "node80"
  }, {
    "name": "node81"
  }, {
    "name": "node82"
  }, {
    "name": "node83"
  }, {
    "name": "node84"
  }, {
    "name": "node85"
  }, {
    "name": "node86"
  }, {
    "name": "node87"
  }, {
    "name": "node88"
  }, {
    "name": "node89"
  }, {
    "name": "node90"
  }, {
    "name": "node91"
  }, {
    "name": "node92"
  }, {
    "name": "node93"
  }, {
    "name": "node94"
  }, {
    "name": "node95"
  }, {
    "name": "node96"
  }, {
    "name": "node97"
  }, {
    "name": "node98"
  }, {
    "name": "node99"
  }],
  "links": [ {
    "source": 0,
    "target": 1
  }, {
    "source": 0,
    "target": 2
  }, {
    "source": 1,
    "target": 3
  }, {
    "source": 1,
    "target": 4
  }, {
    "source": 2,
    "target": 5
  }, {
    "source": 2,
    "target": 6
  }, {
    "source": 3,
    "target": 7
  }, {
    "source": 3,
    "target": 8
  }, {
    "source": 4,
    "target": 9
  }, {
    "source": 4,
    "target": 10
  }, {
    "source": 5,
    "target": 11
  }, {
    "source": 5,
    "target": 12
  }, {
    "source": 6,
    "target": 13
  }, {
    "source": 6,
    "target": 14
  }, {
    "source": 7,
    "target": 15
  }, {
    "source": 7,
    "target": 16
  }, {
    "source": 8,
    "target": 17
  }, {
    "source": 8,
    "target": 18
  }, {
    "source": 9,
    "target": 19
  }, {
    "source": 9,
    "target": 20
  }, {
    "source": 10,
    "target": 21
  }, {
    "source": 10,
    "target": 22
  }, {
    "source": 11,
    "target": 23
  }, {
    "source": 11,
    "target": 24
  }, {
    "source": 12,
    "target": 25
  }, {
    "source": 12,
    "target": 26
  }, {
    "source": 13,
    "target": 27
  }, {
    "source": 13,
    "target": 28
  }, {
    "source": 14,
    "target": 29
  }, {
    "source": 14,
    "target": 30
  }, {
    "source": 15,
    "target": 31
  }, {
    "source": 15,
    "target": 32
  }, {
    "source": 16,
    "target": 33
  }, {
    "source": 16,
    "target": 34
  }, {
    "source": 17,
    "target": 35
  }, {
    "source": 17,
    "target": 36
  }, {
    "source": 18,
    "target": 37
  }, {
    "source": 18,
    "target": 38
  }, {
    "source": 19,
    "target": 39
  }, {
    "source": 19,
    "target": 40
  }, {
    "source": 20,
    "target": 41
  }, {
    "source": 20,
    "target": 42
  }, {
    "source": 21,
    "target": 43
  }, {
    "source": 21,
    "target": 44
  }, {
    "source": 22,
    "target": 45
  }, {
    "source": 22,
    "target": 46
  }, {
    "source": 23,
    "target": 47
  }, {
    "source": 23,
    "target": 48
  }, {
    "source": 24,
    "target": 49
  }, {
    "source": 24,
    "target": 50
  }, {
    "source": 25,
    "target": 51
  }, {
    "source": 25,
    "target": 52
  }, {
    "source": 26,
    "target": 53
  }, {
    "source": 26,
    "target": 54
  }, {
    "source": 27,
    "target": 55
  }, {
    "source": 27,
    "target": 56
  }, {
    "source": 28,
    "target": 57
  }, {
    "source": 28,
    "target": 58
  }, {
    "source": 29,
    "target": 59
  }, {
    "source": 29,
    "target": 60
  }, {
    "source": 30,
    "target": 61
  }, {
    "source": 30,
    "target": 62
  }, {
    "source": 31,
    "target": 63
  }, {
    "source": 31,
    "target": 64
  }, {
    "source": 32,
    "target": 65
  }, {
    "source": 32,
    "target": 66
  }, {
    "source": 33,
    "target": 67
  }, {
    "source": 33,
    "target": 68
  }, {
    "source": 34,
    "target": 69
  }, {
    "source": 34,
    "target": 70
  }, {
    "source": 35,
    "target": 71
  }, {
    "source": 35,
    "target": 72
  }, {
    "source": 36,
    "target": 73
  }, {
    "source": 36,
    "target": 74
  }, {
    "source": 37,
    "target": 75
  }, {
    "source": 37,
    "target": 76
  }, {
    "source": 38,
    "target": 77
  }, {
    "source": 38,
    "target": 78
  }, {
    "source": 39,
    "target": 79
  }, {
    "source": 39,
    "target": 80
  }, {
    "source": 40,
    "target": 81
  }, {
    "source": 40,
    "target": 82
  }, {
    "source": 41,
    "target": 83
  }, {
    "source": 41,
    "target": 84
  }, {
    "source": 42,
    "target": 85
  }, {
    "source": 42,
    "target": 86
  }, {
    "source": 43,
    "target": 87
  }, {
    "source": 43,
    "target": 88
  }, {
    "source": 44,
    "target": 89
  }, {
    "source": 44,
    "target": 90
  }, {
    "source": 45,
    "target": 91
  }, {
    "source": 45,
    "target": 92
  }, {
    "source": 46,
    "target": 93
  }, {
    "source": 46,
    "target": 94
  }, {
    "source": 47,
    "target": 95
  }, {
    "source": 47,
    "target": 96
  }, {
    "source": 48,
    "target": 97
  }, {
    "source": 48,
    "target": 98
  }, {
    "source": 49,
    "target": 99
  },{
    "source": 0,
    "target": 99
  }]
}

var link = chart.selectAll("line")
  .data(json.links)
  .enter()
  .append("line")
  .attr("stroke", function(d) {
    return 'blue'
  })

var node = chart.selectAll("circle")
  .data(json.nodes)
  .enter().append("circle")
  .attr("r", radius - .75)
  .style("fill", function(d) {
    return fill(d.group);
  })
  .style("stroke", function(d) {
    return d3.rgb(fill(d.group)).darker();
  })
  .on('mouseover', d => console.log(d))

.call(force.drag);

function dragstarted() {
  d3.event.sourceEvent.stopPropagation();
}

zoom.on("zoom", function(d) {

  var evt = d3.event;
  debugger;
  /*
	var dcx = (window.innerWidth/2-d.x*zoom.scale());
	var dcy = (window.innerHeight/2-d.y*zoom.scale());
  */
  var dcx = evt.translate[0]
  var dcy = evt.translate[1]

  zoom.translate([dcx, dcy]);

  chart.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");;

});

force
  .nodes(json.nodes)
  .links(json.links)
  .on("tick", tick)
  .start();

svg.call(zoom)

function tick(e) {
console.log(e)
  var k = 6 * e.alpha;

  // Push sources up and targets down to form a weak tree.
  link
    .each(function(d,i) {
   
      d.source.y -= k * 60, d.target.y += k * 100;
    /*
    if(i%2==1){
       d.source.x -=  0.4/k  
    }else{
        d.source.x +=  0.4/k  
    }
    */
    
    })
    .attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    });

  node
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });

}
  </script>

  
  

</body>
</html>
 
like image 117
bumbeishvili Avatar answered Nov 06 '22 18:11

bumbeishvili