Public
Edited
Feb 28, 2023
2 stars
Insert cell
Insert cell
sankeyChord(sampleData)
Insert cell
function sankeyChord (data, {
title = null,
bind = '.chart',
height = 500,
_width = width * 0.6, // use observable width
sourceCols = ["#ffd700",
"#fa8775",
"#ea5f94",
"#9d02d7"],
targetCols = ['#ccc'],
margin = {top: 20, right: 100, bottom: 20, left: 100},
} = {}) {
const w = _width - margin.left - margin.right
const h = height - margin.top - margin.bottom
const sourceColors = d3.scaleOrdinal()
.range(sourceCols)
const targetColors = d3.scaleOrdinal()
.range(targetCols)
const _data = nodesAndLinks(data)

console.log('data', data)
console.log('_data formatted', _data)


// set up dom
const sel = d3.create('div').attr('class', 'js-vis')
createDOM(sel)

// create svg in passed in div
const svg = sel.select('.js-svg')
.append('svg')
.attr('width', _width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')

const sankey = d3Sankey.sankey()
.nodeWidth(40)
.nodePadding(10)
.extent([[1, 1], [w - 1, h - 6]])
sankey(_data)
// console.log('sankey(_data)', sankey(_data))

const link = svg.append('g')
.attr('class', 'links')
.attr('fill', 'none')
.attr('stroke-opacity', 0.6)
.selectAll('path')
.data(_data.links)
.enter().append('path')
.attr('class', d => {
// console.log('d', d)
return `source-${d.source.node} target-${d.target.node}`
})
.attr('d', d3Sankey.sankeyLinkHorizontal())
.attr('stroke', d => sourceColors(d.source.node))
.attr('stroke-width', d => Math.max(1, d.width))

const node = svg.append('g')
.attr('class', 'nodes')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.selectAll('g')
.data(_data.nodes)
.enter().append('g')

node.append('rect')
.attr('x', d => d.x0)
.attr('y', d => d.y0)
.attr('class', d => d.type + ' bar')
.attr('height', d => d.y1 - d.y0)
.attr('width', d => d.x1 - d.x0)
// use colours depending on source or target
.attr('fill', d => (d.type === 'source')
? sourceColors(d.subIndex)
: targetColors(d.subIndex)
)
.on('click', function (e, d) {
if (d.type === 'source') {
highlightSourcePaths(svg, d.index, d.type)
updateTitle(sel, d.label, d.value)
listInfo(sel, d.sourceLinks, {
type: 'source',
colorScale: sourceColors
})
} else {
highlightSourcePaths(svg, d.index, d.type)
updateTitle(sel, d.label, d.value)
listInfo(sel, d.targetLinks, {
type: 'target',
colorScale: sourceColors
})
}
})
.on('dblclick', function (d) {
svg.selectAll('path')
.attr('stroke-opacity', 0.4)
});

node.append('text')
.attr('x', d => d.x0 + 50)
.attr('y', d => (d.y1 + d.y0) / 2)
.attr('dy', '0.35em')
.attr('class', 'label')
.attr('text-anchor', 'start')
.text(d => d.label)
.filter(d => d.x0 < _width / 2)
.attr('x', d => d.x1 - 50)
.attr('text-anchor', 'end');

function highlightSourcePaths (sel, index, type) {
sel.selectAll('path')
.attr('stroke-opacity', 0.05)
// pass in type of source/target
.filter(`path.${type}-${index}`)
.attr('stroke-opacity', 0.6);
}

return sel.node();

}
Insert cell
Insert cell
Insert cell
Insert cell
// using http://basscss.com/ classes
function createDOM (selection) {
const container = selection.append('section')
.attr('class', 'clearfix mx-auto my2 js-wrap')
// create DOM for svg chart
const chart = container.append('div')
.attr('class', 'col col-7')
chart.append('div')
.attr('class', 'js-svg')
// create DOM for displaying info
const info = container.append('div')
.attr('class', 'col col-4 mt2')
info.append('h2')
.attr('class', 'h3 regular js-info-title')
info.append('ul')
.attr('class', 'list-reset js-info-ul')
}

// this will produce the below elements:
/*
<section class="clearfix mx-auto my2">
<div class="col col-9">
<div class="js-svg"></div>
</div>
<div class="col col-3">
<h2 class="h3 regular js-info-title"></h2>
<ul class="list-reset js-info-ul"></ul>
</div>
</section>
*/

Insert cell
Insert cell
function updateTitle (selection, label, value) {
selection.select('.js-info-title')
.html(`${label} <span class='js-total'>(total: ${value})</span>`)
}
Insert cell
d3Sankey = require.alias({"d3-array": d3, "d3-shape": d3, "d3-sankey": "d3-sankey@0.12.3/dist/d3-sankey.min.js"})("d3-sankey")
Insert cell
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<link href="https://unpkg.com/basscss@8.0.2/css/basscss.min.css" rel="stylesheet">
<style>
.js-vis, text {
font-family:'Space Mono',monospace;
fill: #130C0E;
}
.node rect {
cursor: move;
fill-opacity: .9;
shape-rendering: crispEdges;
}
.node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
}
rect.source {
opacity: .8;
cursor: pointer;
}
.label {
font-size: 14px;
fill: #454545
}
.link {
fill: none;
stroke: #000;
stroke-opacity: .2;
}
.link:hover {
stroke-opacity: .5;
}
.js-total {
padding-left: 5px;
color: #888;
}

</style>
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more