Spaces:
Sleeping
Sleeping
File size: 4,324 Bytes
2e818da | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | """Force-directed network graph template — network family.
Adapted from d3-graph-gallery.com (Yan Holtz) —
https://d3-graph-gallery.com/graph/network_basic.html (d3 v6 version:
d3.forceSimulation() with forceLink/forceManyBody/forceCenter, driven by
data_network.json loaded via d3.json()). Here the JSON is injected inline
as GraphData; nodes are additionally colored by `group` via an ordinal
color scale, ticking uses the "tick" event (not "end") so the simulation
animates into place, and a drag behavior is added so nodes can be
repositioned.
"""
from __future__ import annotations
from app.agents.d3.registry import D3Template, register
from app.agents.d3.templates._graph import GraphData, GraphNode, GraphLink
_HTML = """<!-- Adapted from d3-graph-gallery.com (Yan Holtz) — https://d3-graph-gallery.com/graph/network_basic.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
html, body { margin: 0; padding: 0; width: 100vw; height: 100vh; background: #FAF7F2; color: #1A3557; font-family: Georgia, 'Libre Caslon Text', serif; overflow: hidden; }
#my_dataviz { width: 100vw; height: 100vh; }
.chart-title { fill: #1A3557; font-size: 18px; text-anchor: middle; }
.link { stroke: #1A3557; stroke-opacity: 0.35; }
.node { stroke: #1A3557; stroke-width: 1.5px; cursor: grab; }
</style>
</head>
<body>
<div id="my_dataviz"></div>
<script>
const data = __DATA__;
const margin = {top: 50, right: 30, bottom: 20, left: 30},
width = 960 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.attr("preserveAspectRatio", "xMidYMid meet")
.style("width", "100%")
.style("height", "100%")
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
svg.append("text")
.attr("class", "chart-title")
.attr("x", width / 2)
.attr("y", -20)
.text(data.title);
const groups = Array.from(new Set(data.nodes.map(d => d.group)));
const color = d3.scaleOrdinal(groups, ["#4A7FB5", "#B5764A", "#6FA37A", "#A05A9C", "#C2A03C"]);
function 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);
}
const simulation = d3.forceSimulation(data.nodes)
.force("link", d3.forceLink(data.links).id(d => d.id))
.force("charge", d3.forceManyBody().strength(-250))
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
const link = svg
.selectAll("line")
.data(data.links)
.join("line")
.attr("class", "link")
.style("stroke-width", d => Math.sqrt(d.value));
const node = svg
.selectAll("circle")
.data(data.nodes)
.join("circle")
.attr("class", "node")
.attr("r", 10)
.style("fill", d => color(d.group))
.call(drag(simulation));
function ticked() {
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("cx", d => d.x)
.attr("cy", d => d.y);
}
</script>
</body>
</html>
"""
golden_sample = GraphData(
title="Demo",
nodes=[
GraphNode(id="A", group="x"),
GraphNode(id="B", group="x"),
GraphNode(id="C", group="y"),
],
links=[
GraphLink(source="A", target="B", value=2),
GraphLink(source="B", target="C", value=1),
],
)
register(
D3Template(
id="network_force_graph",
family="network",
title="Force-directed network graph",
when_to_use="Show relationships between named entities as a connected node-link network.",
data_requirements="A list of nodes (id, optional group) and links (source id, target id, optional weight).",
schema=GraphData,
html_template=_HTML,
golden_sample=golden_sample,
)
)
|