Public
Edited
May 17
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
REAL DATA ETC - links no subsidiary (3).csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
pack = data => d3.pack()
.size([width, height])
.padding(3)
(d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
linksonly = await FileAttachment("linksonly.json").json()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
REAL DATA ETC - Copy of Metadata 3 (1)@1.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
data = {
return { nodes: nodes, links: links2 };
}
Insert cell
classes = "blur"
Insert cell
Insert cell
drag = (simulation) => {
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}

function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}

return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
Insert cell
import { h10 } from "04d708900ab1e38b"
Insert cell
createTooltip = {
return d3.select("body")
.append("div")
.attr("class", `tooltip ${classes}`)
.style("position", "absolute")
.style("opacity", 0)
.text("I'm a circle!");
}
Insert cell
ticked = {
const circles = d3.selectAll("circle");
circles.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
}
Insert cell
createSimulation = {
const forceLink = d3.forceLink(links).id((d) => d.id);

return d3
.forceSimulation()
.alpha(0.3)
.force(
"center",
d3
.forceCenter()
.x(width / 2)
.y(height / 2)
)
.force("charge", d3.forceManyBody())
.force(
"collide",
d3.forceCollide((d) => d.radius + padding)
)
.nodes(nodes, (d) => d.id)
.force("x", d3.forceX())
.force("y", d3.forceY())
.force("links", forceLink)
.on("tick", ticked)
.restart();
}
Insert cell
createText = {
const svg = d3.select(DOM.svg(width, height)).attr("id", "chart");
const g = svg.append("g");
return g
.selectAll("text")
.data(nodes)
.join("text")
.attr("opacity", 0.0)
.attr("fill", (d) => color(d.ncolor))
.attr("font-size", "12px")
.text((d) => `${d.id}`);
}
Insert cell
createNode = {
const forceLink = d3.forceLink(links).id((d) => d.id);
const svg = d3.select(DOM.svg(width, height)).attr("id", "chart");
const g = svg.append("g");
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.1).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}

function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
const simulation = d3
.forceSimulation()
.alpha(0.3)
.force(
"center",
d3
.forceCenter()
.x(width / 2)
.y(height / 2)
)
.force("charge", d3.forceManyBody())

.force(
"collide",
d3.forceCollide((d) => d.radius + padding)
)
.nodes(nodes, (d) => d.id)
.force("x", d3.forceX())
.force("y", d3.forceY())
.force("links", forceLink)
.on("tick", ticked)
.restart();
return svg
.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes, (d) => d.$id)
.enter()
.append("circle")
.attr("class", (d) => `cluster${d.cluster}`)
.attr("r", (d) => d.radius)
.attr("fill", (d) => color(+d.ncolor))
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
}
Insert cell
nodeEventHandling = {
const node = svg
.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes, (d) => d.$id)
.enter()
.append("circle")
.attr("class", (d) => `cluster${d.cluster}`)
.attr("r", (d) => d.radius)
.attr("fill", (d) => color(+d.ncolor))
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
node.append("title").text((d) => d.id);

node
.style("cursor", "pointer")
.on("mouseover", function (evt, d) {
const connected = links.filter(
(link) => link.source === d || link.target === d
);
const connectedIds = connected.map((link) =>
link.source === d ? link.target.id : link.source.id
);

tooltip
.html(
`<strong>${d.display_name || d.id}</strong><br/>
Role: ${d.primary_roles || "N/A"}<br/>
Parent: ${d.cluster || "N/A"}`
)
.style("opacity", 1)
.style("left", `${evt.pageX + 10}px`)
.style("top", `${evt.pageY - 10}px`);

node.style("opacity", (n) =>
n.id === d.id || connectedIds.includes(n.id) ? 1 : 0.1
);
link.style("opacity", (l) => (connected.includes(l) ? 1 : 0.1));
text.style("opacity", (t) => (connectedIds.includes(t.id) ? 1 : 0));
hulls.style("opacity", (h) =>
h.nodes.data().some((n) => connectedIds.includes(n.id)) ? 0.2 : 0.05
);
})
.on("mousemove", (evt) => {
tooltip
.style("left", `${evt.pageX + 10}px`)
.style("top", `${evt.pageY - 10}px`);
})
.on("mouseout", () => {
tooltip.style("opacity", 0);
node.style("opacity", 1);
link.style("opacity", 1);
text.style("opacity", 0);
hulls.style("opacity", 0.2);
})
.on("click", (evt, d) => {
const clusterId = d.cluster;
return node.style("cursor", "pointer")
.on("mouseover", handleMouseOver)
.on("mousemove", handleMouseMove)
.on("mouseout", handleMouseOut)
.on("click", handleClick)
}
}

Insert cell
createHulls = {
return hullG
.selectAll("path")
.data(
Object.keys(clusters)
.map((c) => {
return {
cluster: c,
nodes: node.filter((d) => d.cluster == c)
};
})
.filter((d) => d.cluster != 0),
(d) => d.cluster
)
.enter()
.append("path")
.attr("d", (d) => line(d3.polygonHull(hullPoints(d.nodes))))
.attr("fill", (d) => color(+d.cluster))
.attr("opacity", 0.2);
}
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