ub-movements-test/tests/graph.html
2024-04-05 18:32:15 +02:00

155 lines
No EOL
4.6 KiB
HTML

<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
body {
padding: 0;
margin: 0;
background-color: white;
}
</style>
<script src="../js/d3.v7.js"></script>
</head>
<body>
<div id="graph"></div>
</body>
<script type="module">
fetch("../data/parsed_requests.json")
.then(res => res.json())
.then(out =>
loadData(out))
.catch(err => { throw err });
function loadData(input) {
const data = input['edges'].map((edge) => {
return { 'source': edge['Owning Library Name'], 'target': edge['Pickup Location'], 'value': 1 }
})
const width = 1080;
const height = width;
const innerRadius = Math.min(width, height) * 0.5 - 30;
const outerRadius = innerRadius + 6;
// Compute a dense matrix from the weighted links in data.
const names = Array.from(d3.union(data.flatMap(d => [d.source, d.target])));
const index = new Map(names.map((name, i) => [name, i]));
const matrix = Array.from(index, () => new Array(names.length).fill(0));
for (const { source, target, value } of data) matrix[index.get(source)][index.get(target)] += value;
// // Compute a dense matrix from the weighted links in data.
// const names = data['nodes'].map((node) => node['name'])
// const index = new Map(names.map((name, i) => [name, i]));
// const matrix = Array.from(index, () => new Array(names.length).fill(0));
// for (const edge of data['edges']) {
// const source = edge['Owning Library Name']
// const target = edge['Pickup Location']
// matrix[index.get(source)][index.get(target)] += 1;
// }
const chord = d3.chordDirected()
.padAngle(50 / innerRadius)
.sortSubgroups(d3.descending)
.sortChords(d3.descending);
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
const ribbon = d3.ribbonArrow()
.radius(innerRadius - 5.5) // padding with circle
.padAngle(10 / innerRadius); // padding between arrows
// const colors = d3.schemeCategory10;
const colors = ["#ffc259",
"#20066b",
"#8deb70",
"#ff89f8",
"#90bb2c",
"#bc89ff",
"#ddff7d",
"#584a9f",
"#abff98",
"#980064",
"#00eaa9",
"#ff5aa4",
"#007e22",
"#ff6e70",
"#6ec478",
"#912900",
"#5d7800",
"#ff9350",
"#d9af54",
"#d08500"];
const formatValue = x => `${x.toFixed(0)}`;
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;");
const chords = chord(matrix);
// const textId = DOM.uid("text");
svg.append("path")
.attr("id", "somepath")
.attr("fill", "none")
.attr("d", d3.arc()({ outerRadius, startAngle: 0, endAngle: 2 * Math.PI }));
svg.append("g")
.attr("fill-opacity", 0.75)
.selectAll()
.data(chords)
.join("path")
.attr("d", ribbon)
.attr("fill", d => colors[d.target.index])
.attr("stroke", "black")
.style("mix-blend-mode", "multiply")
.append("title")
.text(d => `${formatValue(d.source.value)} items from ${names[d.source.index]} to ${names[d.target.index]}`);
const g = svg.append("g")
.selectAll()
.data(chords.groups)
.join("g");
g.append("path")
.attr("d", arc)
.attr("fill", d => colors[d.index])
.attr("stroke", "black")
// .attr("stroke", "#fff")
;
g.append("text")
.attr("dy", -3)
.append("textPath")
.attr("xlink:href", "#somepath")
.attr("startOffset", d => d.startAngle * outerRadius)
.text(d => names[d.index]);
// g.append("title")
// .text(d => `${names[d.index]}
// owes ${formatValue(d3.sum(matrix[d.index]))}
// is owed ${formatValue(d3.sum(matrix, row => row[d.index]))}`);
graph.append(svg.node());
}
</script>
</html>