D3 Sankey Diagram Sankey diagrams visualize the magnitude of flow between nodes in a network. This intricate diagram shows a possible scenario for UK energy production and consumption in 2050: energy supplies are on the left, and demands are on the right. Intermediate nodes group related forms of production and show how energy is converted and transmitted before it is consumed or lost. The thickness of each link encodes the amount of flow from source to target. Data: Department of Energy & Climate Change, Tom Counsell
viewof edgeColor = html`<select> <option value=input>Color by input <option value=output>Color by output <option value=path selected>Color by input-output </select>`
chart = { const svg = d3.select(DOM.svg(width, height)) .style("width", "100%") .style("height", "auto"); const {nodes, links} = sankey(data); svg.append("g") .attr("stroke", "#000") .selectAll("rect") .data(nodes) .enter().append("rect") .attr("x", d => d.x0) .attr("y", d => d.y0) .attr("height", d => d.y1 - d.y0) .attr("width", d => d.x1 - d.x0) .attr("fill", d => color(d.name)) .append("title") .text(d => `${d.name}\n${format(d.value)}`); const link = svg.append("g") .attr("fill", "none") .attr("stroke-opacity", 0.5) .selectAll("g") .data(links) .enter().append("g") .style("mix-blend-mode", "multiply"); if (edgeColor === "path") { const gradient = link.append("linearGradient") .attr("id", d => (d.uid = DOM.uid("link")).id) .attr("gradientUnits", "userSpaceOnUse") .attr("x1", d => d.source.x1) .attr("x2", d => d.target.x0); gradient.append("stop") .attr("offset", "0%") .attr("stop-color", d => color(d.source.name)); gradient.append("stop") .attr("offset", "100%") .attr("stop-color", d => color(d.target.name)); } link.append("path") .attr("d", d3.sankeyLinkHorizontal()) .attr("stroke", d => edgeColor === "path" ? d.uid : edgeColor === "input" ? color(d.source.name) : color(d.target.name)) .attr("stroke-width", d => Math.max(1, d.width)); link.append("title") .text(d => `${d.source.name} → ${d.target.name}\n${format(d.value)}`); svg.append("g") .style("font", "10px sans-serif") .selectAll("text") .data(nodes) .enter().append("text") .attr("x", d => d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6) .attr("y", d => (d.y1 + d.y0) / 2) .attr("dy", "0.35em") .attr("text-anchor", d => d.x0 < width / 2 ? "start" : "end") .text(d => d.name); return svg.node(); }
sankey = { const sankey = d3.sankey() .nodeWidth(15) .nodePadding(10) .extent([[1, 1], [width - 1, height - 5]]); return ({nodes, links}) => sankey({ nodes: nodes.map(d => Object.assign({}, d)), links: links.map(d => Object.assign({}, d)) }); }
format = { const f = d3.format(",.0f"); return d => `${f(d)} TWh`; }
color = { const color = d3.scaleOrdinal(d3.schemeCategory10); return name => color(name.replace(/ .*/, "")); }
data = d3.json("https://gist.githubusercontent.com/mbostock/ca9a0bb7ba204d12974bca90acc507c0/raw/398136b7db83d7d7fd89181b080924eb76041692/energy.json")
width = 964
height = 600
d3 = require("d3@5", "d3-sankey@0.7")