An adaptation of the Sankey diagram to show traceability. Clicking on a node shows the links up and down the model.
Thanks to Simon Titheridge!
forked from tomshanley's block: Sankey - highlight links by doubleclicking nodes
forked from tomshanley's block: Sankey - highlight links
xxxxxxxxxx
<meta charset="utf-8">
<title>Traceability</title>
<link href="sankey_style.css" rel="stylesheet">
<style media="screen" type="text/css">
.sankey-graph .node rect {
shape-rendering: crispEdges;
pointer-events:all;
fill: none;
stroke: rgb(160, 160,160);
stroke-width: 2px;
}
.sankey-graph .node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
}
.sankey-graph .bar rect {
stroke: none;
}
.sankey-graph .link {
fill: none;
stroke: #000;
}
</style>
<body>
<header>
</header>
<p id="chart" class="sankey-graph">
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/d3-sankey@0"></script>
<script>
console.clear()
var margin = { top: 1, right: 0, bottom: 6, left: 0 }
var width = 1000
// also change CSS file to change Width
var height = 1030 - margin.top - margin.bottom
var node_Width = 200
node_Stroke = 2
var svg = d3
.select('#chart')
.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 + ')')
var sankey = d3
.sankey()
.nodeWidth(node_Width)
.nodePadding(43)
.size([width, height])
.nodeId(function (d) {
return d.name
})
.nodeAlign(d3.sankeyJustify)
d3.json('fish.json').then(function (data) {
// console.log(data)
graph = {}
graph.nodes = []
graph.links = []
data.forEach(function (d) {
graph.links.push({ source: d.source, target: d.target, value: d.value })
graph.nodes.push({ name: d.source })
graph.nodes.push({ name: d.target })
})
graph.nodes = d3
.nest()
.key(function (d) {
return d.name
})
.entries(graph.nodes)
graph.nodes.forEach(function (d, i) {
graph.nodes[i] = { name: d.key }
})
var drag = d3.drag().on('drag', dragmove)
function dragmove (d) {
d3.select(this).attr(
'transform',
'translate(' +
d.x0 +
',' +
(d.y0 = Math.max(0, Math.min(height - d.height, d3.event.y))) +
')'
)
d.y = d.y0
sankey.update(graph)
link.attr('d', d3.sankeyLinkHorizontal())
}
sankey(graph)
var link = svg
.append('g')
.selectAll('.link')
.data(graph.links)
.enter()
.append('path')
.attr('class', 'link')
.attr('d', d3.sankeyLinkHorizontal())
.style('opacity', 0.2)
.style('stroke-width', function (d) {
// d.strokeWidth = d.y1 - d.y0
return d.width
})
.sort(function (a, b) {
return b.dy - a.dy
})
var node = svg
.append('g')
.selectAll('.node')
.data(graph.nodes)
.enter()
.append('g')
.attr('class', 'node')
.attr('transform', function (d) {
d.x = d.x0
d.y = d.y0
return 'translate(' + d.x0 + ',' + d.y0 + ')'
})
.call(drag)
.on('click', handleNodeClick)
node
.append('rect')
.attr('height', function (d) {
d.height = d.y1 - d.y0
return d.height
})
.attr('width', sankey.nodeWidth())
.style('fill', 'white')
node
.append('text')
.attr('y', 11)
.text(function (d) {
return d.name
})
node.append("svg:image")
.attr("x", 0)
.attr("y", 0)
.attr("height", 49)
.attr("xlink:href", function(d){
let suffix = "https://gist.githubusercontent.com/tomshanley/60c473ad73a250d5ed467bec3073698b/raw/4003d8d581423abf4b27fd2ae8425e6ee2a23e44/"
let filename = suffix + d.name + ".png"
return filename
})
// Recursive highlighting of nodes
function handleNodeClick (d) {
if (d3.event.defaultPrevented) return
// Reset colour of nodes to black
d3.selectAll('rect').style('fill', 'white')
d3.selectAll('.link')
.style('stroke', 'black')
.style('opacity', 0.2)
// Highlight clicked node
d3.selectAll('rect').style('fill', function (d2, i) {
return d.name == d2.name ? 'LightCoral' : 'white'
})
iterateLinkedLinksRight(d) // Recurse source direction
iterateLinkedLinksLeft(d) // Recurse target direction
}
function iterateLinkedLinksRight (pStartNode) {
// Select links that have a given source name
d3.selectAll('path.link')
.filter(function (pLinkedLink, i) {
return pLinkedLink.source.name == pStartNode.name
})
.style('stroke', 'LightCoral')
.style('opacity', 0.6)
.each(iterateLinkedNodesRight)
}
function iterateLinkedNodesRight (pStartLink) {
// Select nodes that have a given source name
d3.selectAll('rect')
.filter(function (pLinkedNode, i) {
return pLinkedNode.name == pStartLink.target.name
})
.style('fill', 'LightCoral')
.each(iterateLinkedLinksRight)
}
function iterateLinkedLinksLeft (pStartNode) {
// Select links that have a given source name
d3.selectAll('path.link')
.filter(function (pLinkedLink, i) {
return pLinkedLink.target.name == pStartNode.name
})
.style('stroke', 'LightCoral')
.style('opacity', 0.6)
.each(iterateLinkedNodesLeft)
}
function iterateLinkedNodesLeft (pStartLink) {
// Select nodes that have a given source name
d3.selectAll('rect')
.filter(function (pLinkedNode, i) {
return pLinkedNode.name == pStartLink.source.name
})
.style('fill', 'LightCoral')
.each(iterateLinkedLinksLeft)
}
})
</script>
https://d3js.org/d3.v5.min.js
https://unpkg.com/d3-sankey@0