A demonstration, using D3v4 of Sankey responsible, colors in links (by color's nodes) and nodes font size proportional. github.com/FabricioRHS
Last active
July 19, 2018 11:05
-
-
Save fabric-io-rodrigues/a6410950d8e38e9afefc4ca33a8898fc to your computer and use it in GitHub Desktop.
Responsive D3 v4 Sankey Diagram
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
border: no | |
license: gpl-3.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"/> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Responsive D3 v4 Sankey Diagram</title> | |
<script type="text/javascript" src="http://d3js.org/d3.v4.min.js"></script> | |
<script src="http://unpkg.com/[email protected]/build/d3-sankey.js"></script> | |
<link href="style.css" rel="stylesheet" id="bootstrap-css"> | |
<meta name="description" content="Responsive D3 v4 Sankey Diagram"> | |
<meta name="keywords" content="Responsive,D3,D3v4,Sankey,Diagram"> | |
<meta name="author" content="Fabricio Rodrigues"> | |
<meta itemprop="name" content="Responsive D3 v4 Sankey Diagram"> | |
<meta itemprop="description" content="Responsive D3 v4 Sankey Diagram"> | |
<meta itemprop="image" content="thumbnail.png"> | |
<meta name="og:title" content="Responsive D3 v4 Sankey Diagram"> | |
<meta name="og:description" content="Responsive D3 v4 Sankey Diagram"> | |
<meta name="og:image" content="thumbnail.png"> | |
<meta name="og:site_name" content="Fabricio's bl.ocks"> | |
<meta name="og:type" content="website"> | |
</head> | |
<body> | |
<div id="container"></div> | |
<script src="sankey.fabriciorhs.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const sankeyData = { | |
nodes: [ | |
{ id: 0, name: 'Emergency Admits' }, | |
{ id: 1, name: 'Scheduled Admits' }, | |
{ id: 2, name: 'Telemetry' }, | |
{ id: 3, name: 'Gen Med' }, | |
{ id: 4, name: 'Med Psych' }, | |
{ id: 5, name: 'Orth / ENT / Neuro' }, | |
{ id: 6, name: 'MICU' }, | |
{ id: 7, name: 'ICU' }, | |
{ id: 8, name: 'Discharges Home' }, | |
{ id: 9, name: 'Discharges Acute Care' }, | |
{ id: 10, name: 'Discharges to Unspecified' }, | |
{ id: 11, name: 'Discharges to Nursing Home' }, | |
{ id: 12, name: 'Orth / ENT / Neuro' } | |
], | |
links: [ | |
{source: 0, target: 6, value: 25}, | |
{source: 0, target: 7, value: 15}, | |
{source: 1, target: 6, value: 15}, | |
{source: 1, target: 7, value: 13}, | |
{source: 2, target: 6, value: 8}, | |
{source: 2, target: 7, value: 3}, | |
{source: 3, target: 6, value: 6}, | |
{source: 4, target: 6, value: 2}, | |
{source: 4, target: 7, value: 8}, | |
{source: 5, target: 7, value: 11}, | |
{source: 6, target: 8, value: 20}, | |
{source: 6, target: 9, value: 2}, | |
{source: 6, target: 10, value: 10}, | |
{source: 6, target: 11, value: 13}, | |
{source: 6, target: 12, value: 11}, | |
{source: 7, target: 8, value: 20}, | |
{source: 7, target: 9, value: 10}, | |
{source: 7, target: 10, value: 10}, | |
{source: 7, target: 11, value: 5}, | |
{source: 7, target: 12, value: 5}] | |
}; | |
function getDimensions(container, margin) { | |
const bbox = container.node().getBoundingClientRect(); | |
return { | |
width: bbox.width - margin.left - margin.right, | |
height: bbox.height - margin.top - margin.bottom | |
}; | |
} | |
function updateLinks(linkContainer, linkData) { | |
// Join new data with old elements | |
const links = linkContainer.selectAll('.link') | |
.data(linkData, d => `${d.source}${d.target}`); | |
// Remove elements not present in new data | |
links.exit().remove(); | |
// Update old elements | |
links.attr('d', d3.sankeyLinkHorizontal()); | |
// Enter new elements | |
const enteringLinks = links.enter() | |
.append('path') | |
.attr('class', 'link') | |
.attr('d', d3.sankeyLinkHorizontal()) | |
.style("stroke", function(d){ return d.source.color; }) | |
.style('stroke-width', d => Math.max(1, d.dy)) | |
.sort((a, b) => b.dy - a.dy); | |
// Add the link titles | |
enteringLinks.append('title') | |
.text(d => `${d.source.name} → ${d.target.name}\n${format(d.value)}`); | |
} | |
function updateNodes(nodeContainer, nodeData) { | |
// Join new data with old elements | |
const nodes = nodeContainer.selectAll('.node') | |
.data(nodeData, d => d.id); | |
// Remove elements not present in new data | |
nodes.exit().remove(); | |
// Update old elements | |
nodes.attr('transform', d => `translate(${d.x}, ${d.y})`); | |
// Enter new elements | |
const enteringNodes = nodes.enter().append('g') | |
.attr('class', 'node') | |
.attr('transform', d => `translate(${d.x}, ${d.y})`); | |
// Add the rectangles for the nodes | |
enteringNodes.append('rect') | |
.attr('height', d => d.dy) | |
.attr('width', sankey.nodeWidth()) | |
.style('fill', d => { | |
return (d.color = color(d.name.replace(/ .*/, ''))); | |
}) | |
.style('stroke', d => d3.rgb(d.color).darker(2)) | |
.append('title') | |
.text(d => `${d.name}\n${format(d.value)}`); | |
// Add node names | |
enteringNodes | |
.append('text') | |
.attr('x', -6) | |
.attr('y', d => d.dy / 2) | |
.attr('dy', '.35em') | |
.attr('text-anchor', 'end') | |
.text(d => d.name) | |
.style("fill", function(d) { | |
return d3.rgb(d.color).darker(2.4); | |
}) | |
.style("font-size", function(d) { | |
return Math.floor(fontScale(d.value)) + "px"; | |
}) | |
.filter(d => d.x < width / 2) | |
.attr('x', 6 + sankey.nodeWidth()) | |
.attr('text-anchor', 'start'); | |
} | |
const container = d3.select('#container'); | |
const margin = { top: 10, right: 10, bottom: 10, left: 10 }; | |
const {width, height} = getDimensions(container, margin); | |
const formatNumber = d3.format(',.0f'); | |
const format = d => formatNumber(d); | |
const color = d3.scaleOrdinal(d3.schemeCategory20); | |
// append the svg object to the body of the page | |
const svg = container | |
.append('svg') | |
.attr('width', width + margin.left + margin.right) | |
.attr('height', height + margin.top + margin.bottom); | |
const sankeyContainer = svg.append('g') | |
.attr('transform', `translate(${margin.left}, ${margin.top})`); | |
const {nodes, links} = sankeyData; | |
// Set the sankey diagram properties | |
const sankey = d3.sankey().nodeWidth(36).nodePadding(40).size([width, height]); | |
const graph = sankey.nodes(nodes).links(links)(); | |
const fontScale = d3.scaleLinear() .range([18, 30]); | |
fontScale.domain(d3.extent(graph.nodes, function(d) { return d.value })); | |
const linkContainer = sankeyContainer.append('g'); | |
const nodeContainer = sankeyContainer.append('g'); | |
updateNodes(nodeContainer, graph.nodes); | |
updateLinks(linkContainer, graph.links); | |
function resize() { | |
const updatedDimensions = getDimensions(container, margin); | |
// Resize SVG | |
svg | |
.attr('width', updatedDimensions.width + margin.left + margin.right) | |
.attr('height', updatedDimensions.height + margin.top + margin.bottom); | |
const {nodes, links} = sankeyData; | |
const newSankey = d3.sankey() | |
.nodeWidth(36) | |
.nodePadding(40) | |
.size([updatedDimensions.width, updatedDimensions.height]); | |
const update = newSankey | |
.nodes(nodes) | |
.links(links)(); | |
updateLinks(linkContainer, update.links); | |
updateNodes(nodeContainer, update.nodes); | |
} | |
d3.select(window).on('resize', resize); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: sans-serif; | |
} | |
#container { | |
height: 300px; | |
width: 100%; | |
} | |
.node rect { | |
fill-opacity: .9; | |
shape-rendering: crispEdges; | |
} | |
.node text { | |
pointer-events: none; | |
text-shadow: 0 1px 0 #fff; | |
} | |
.link { | |
fill: none; | |
stroke: #000; | |
stroke-opacity: .16; | |
transition-property: stroke-opacity; | |
transition-duration: 0.5s; | |
} | |
.link:hover { | |
stroke-opacity: .5; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment