chart = {
const width = 928;
const height = 680;
const color = d3.scaleOrdinal(d3.schemeCategory10);
const links = data.links.filter((d) => d.target).map((d) => ({ ...d }));
const nodes = data.nodes.map((d) => ({ ...d }));
const simulation = d3
.forceSimulation(nodes)
.force(
"link",
d3.forceLink(links).id((d) => d.id)
)
.force("charge", d3.forceManyBody().strength(-100))
.force(
"x",
d3
.forceX((d) => {
if (d.id === "cmip6plus") return 100;
if (d.id === "mip-cmor-tables") return -100;
return 0;
})
.strength((d) => {
if (d.id === "cmip6plus") return 0.1;
if (d.id === "mip-cmor-tables") return 0.1;
return 0.2;
})
// .strength(1)
)
.force("y", d3.forceY());
// Create the SVG container.
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto;");
// Add a line for each link
const link = svg
.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", (d) => Math.sqrt(d.value));
// Add a group for each node
const node = svg
.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("g")
.data(nodes)
.join("g")
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
// Add a circle to each node group
node
.append("circle")
.attr("r", (d) => 1 + d.weight ** 0.4)
.attr("fill", (d) => color(d.origin));
// Add a text label to each node group
// node
// .append("text")
// .text((d) => d.id)
// .attr("font-size", "10px")
// .attr("dx", 12)
// .attr("dy", 4);
node.append("title").text((d) => d.id);
// Add text labels for links
const linkLabels = svg
.append("g")
.attr("class", "link-labels")
.selectAll("text")
.data(links)
.join("text")
.text((d) => d.value) // Assuming you want to display the 'value' property
.attr("font-size", "8px")
.style("fill", "#666!important");
// Set the position attributes of links and nodes each time the simulation ticks.
simulation.on("tick", () => {
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
node.attr("transform", (d) => `translate(${d.x},${d.y})`);
// linkLabels
// .attr("x", (d) => (d.source.x + d.target.x) / 2)
// .attr("y", (d) => (d.source.y + d.target.y) / 2);
});
// Reheat the simulation when drag starts, and fix the subject position.
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
// Update the subject (dragged node) position during drag.
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
// Restore the target alpha so the simulation cools after dragging ends.
// Unfix the subject position now that it's no longer being dragged.
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
// When this cell is re-run, stop the previous simulation.
invalidation.then(() => simulation.stop());
return svg.node();
}