Skip to content

Instantly share code, notes, and snippets.

@tomshanley
Last active September 5, 2019 03:11
Show Gist options
  • Save tomshanley/60c473ad73a250d5ed467bec3073698b to your computer and use it in GitHub Desktop.
Save tomshanley/60c473ad73a250d5ed467bec3073698b to your computer and use it in GitHub Desktop.
Sankey - highlight links
license: mit
[{
"source": "Leptodora",
"target": "Nors",
"value": 1
}, {
"source": "Heterocope",
"target": "Nors",
"value": 1
}, {
"source": "Heterocope",
"target": "Sikloja",
"value": 1
}, {
"source": "Diaptomus",
"target": "Sikloja",
"value": 1
}, {
"source": "Eurytemora",
"target": "Sikloja",
"value": 1
}, {
"source": "Bosmina",
"target": "Sikloja",
"value": 1
}, {
"source": "Daphnia",
"target": "Sikloja",
"value": 1
}, {
"source": "Bythotrephes",
"target": "Sikloja",
"value": 1
}, {
"source": "Sikloja",
"target": "Gos",
"value": 1
}, {
"source": "Sikloja",
"target": "Lax",
"value": 1
}, {
"source": "Nors",
"target": "Gos",
"value": 1
}, {
"source": "Nors",
"target": "Lax",
"value": 1
}, {
"source": "Bythotrephes",
"target": "Nors",
"value": 1
}, {
"source": "UNKNOWN",
"target": "Nors",
"value": 1
}, {
"source": "Bythotrephes",
"target": "Mort",
"value": 1
}, {
"source": "Bythotrephes",
"target": "Sik",
"value": 1
}, {
"source": "Cyclops",
"target": "Sik",
"value": 1
}, {
"source": "Chironomidae",
"target": "Sik",
"value": 1
}, {
"source": "Pontoporeia",
"target": "Sik",
"value": 1
}, {
"source": "Pontoporeia",
"target": "Gers",
"value": 1
}, {
"source": "Chironomidae",
"target": "Gers",
"value": 1
}, {
"source": "Pallasea",
"target": "Gers",
"value": 1
}, {
"source": "Chironomidae",
"target": "Braxen",
"value": 1
}, {
"source": "Pallasea",
"target": "Braxen",
"value": 1
}, {
"source": "Pallasea",
"target": "Nors",
"value": 1
}, {
"source": "Pallasea",
"target": "Lake",
"value": 1
}, {
"source": "Algae",
"target": "Braxen",
"value": 1
}, {
"source": "Oligochaeta",
"target": "Braxen",
"value": 1
}, {
"source": "Gammaracanthus",
"target": "Nors",
"value": 1
}, {
"source": "Gammaracanthus",
"target": "Lake",
"value": 1
}, {
"source": "UNKNOWN",
"target": "Lake",
"value": 1
}, {
"source": "Gers",
"target": "Lake",
"value": 1
}, {
"source": "Nors",
"target": "Lake",
"value": 1
}]
<!DOCTYPE html>
<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>
html {
min-width: 1040px;
}
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
position: relative;
}
h1 {
font-size: 32px;
font-weight: 250;
letter-spacing: -2px;
margin: .3em 0 .1em 0;
}
h2 {
margin-top: 2em;
}
h1, h2 {
text-rendering: optimizeLegibility;
}
h2 a {
color: #ccc;
left: -20px;
position: absolute;
width: 740px;
}
footer {
font-size: small;
margin-top: 8em;
}
header aside {
margin-top: 88px;
}
header aside,
footer aside {
color: #636363;
text-align: right;
}
aside {
font-size: small;
right: 0;
position: absolute;
width: 180px;
}
.attribution {
font-size: small;
margin-bottom: 2em;
}
body > p, li > p {
line-height: 1.5em;
}
body > p {
width: 720px;
}
body > blockquote {
width: 640px;
}
li {
width: 680px;
}
a {
color: steelblue;
}
a:not(:hover) {
text-decoration: none;
}
pre, code, textarea {
font-family: "Menlo", monospace;
}
code {
line-height: 1em;
}
textarea {
font-size: 100%;
}
body > pre {
border-left: solid 2px #ccc;
padding-left: 18px;
}
.html .value,
.javascript .string,
.javascript .regexp {
color: #756bb1;
}
.html .tag,
.css .tag,
.javascript .keyword {
color: #3182bd;
}
.comment {
color: #636363;
}
.html .doctype,
.javascript .number {
color: #31a354;
}
.html .attribute,
.css .attribute,
.javascript .class,
.javascript .special {
color: #e6550d;
}
svg {
font: 11px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
sup, sub {
line-height: 0;
}
q:before,
blockquote:before {
content: "“";
}
q:after,
blockquote:after {
content: "”";
}
blockquote:before {
position: absolute;
left: 2em;
}
blockquote:after {
position: absolute;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment