Skip to content

Instantly share code, notes, and snippets.

@briantjacobs
Last active March 1, 2019 22:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briantjacobs/9608831 to your computer and use it in GitHub Desktop.
Save briantjacobs/9608831 to your computer and use it in GitHub Desktop.
Sankey plugin with schema function
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Sankey Diagram</title>
<style>
body {font-family: sans-serif;}
#chart {
height: 500px;
}
.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;
}
</style>
</head>
<body>
<p id="chart"></p>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.2/d3.min.js"></script>
<script src="https://rawgithub.com/briantjacobs/d3-plugins/master/sankey/sankey.js"></script>
<script>
var margin = {top: 1, right: 1, bottom: 6, left: 1},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatNumber = d3.format(",.0f"),
format = function(d) { return formatNumber(d) + " TWh"; },
color = d3.scale.category20();
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(15)
.nodePadding(10)
.size([width, height])
.schema({
id: "type_id",
source: "fuel_origin",
target: "fuel_dest",
value: "val"
});
var path = sankey.link();
//incomplete extract from
//http://bost.ocks.org/mike/sankey/energy.json
//http://www.decc.gov.uk/en/content/cms/tackling/2050/calculator_on/calculator_on.aspx"
var energy = {"nodes":[
{"type_id": 12, "name":"Agricultural 'waste'"},
{"type_id": 16, "name":"Bio-conversion"},
{"type_id": 17, "name":"Liquid"},
{"type_id": 10, "name":"Losses"},
{"type_id": 19, "name":"Solid"},
{"type_id": 13, "name":"Gas"},
{"type_id": 14, "name":"Biofuel imports"}
],
"links": [
{"fuel_origin":12,"fuel_dest":16,"val":124.729},
{"fuel_origin":16,"fuel_dest":17,"val":0.597},
{"fuel_origin":16,"fuel_dest":10,"val":26.862},
{"fuel_origin":16,"fuel_dest":19,"val":280.322},
{"fuel_origin":16,"fuel_dest":13,"val":81.144},
{"fuel_origin":14,"fuel_dest":17,"val":35}
]
};
sankey
.nodes(energy.nodes)
.links(energy.links)
.layout(32);
var link = svg.append("g").selectAll(".link")
.data(energy.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
link.append("title")
.text(function(d) { return d.fuel_origin.name + " → " + d.fuel_dest.name + "\n" + format(d.value); });
var node = svg.append("g").selectAll(".node")
.data(energy.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.call(d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove));
node.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) {console.log(d); return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
.append("title")
.text(function(d) { return d.name + "\n" + format(d.value); });
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");
function dragmove(d) {
d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
sankey.relayout();
link.attr("d", path);
}
</script>
</body></html>
@mg1075
Copy link

mg1075 commented Mar 1, 2019

Thanks for writing the blog post and plugin.
After searching quite a while for any way to better understand the cryptic d3 sankey data structure so that I could try pulling data from a database to fit the structure, your post and solution are the best material I've come across.

@mg1075
Copy link

mg1075 commented Mar 1, 2019

One issue that comes to mind, though, is when the nodes involve more than one category or table of data such that the id's from the database could overlap. What would be the best approach in this scenario?

@mg1075
Copy link

mg1075 commented Mar 1, 2019

I suppose one approach is, if your database id columns are integer data points, you could add a varchar prefix to the id (uniquely differentiating each of the source tables), and thus each identifier field in a node would be unique.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment