D3.js带节点链接的聚类气泡分组失效问题求助
带节点链接的两层聚类气泡图分组失效问题
需求
我需要实现带节点链接的聚类气泡图,完成两层聚类:
- 根据属性水平拆分,形成三个不同堆叠组;
- 每个堆叠组内聚合同类型节点。
实现思路
我结合了两个D3相关的示例来实现该效果:
- 水平拆分节点为堆叠组的力导向图方案
- 堆叠组内聚合节点的聚类气泡图方案
我已完成初步实现,代码可在JSFiddle中查看。
代码实现
JavaScript代码
const m = 10 // number of groups; const l = 3 const [width, height] = [1000, 600]; const color = d3.scaleOrdinal(d3.range(m), d3.schemeCategory10); function renderChart(dataSource) { const svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); const pack = () => d3.pack() .size([width, height]) .padding(1) (d3.hierarchy(dataSource.nodes) .sum(d => d.value)); const nodes = pack().leaves(); const simulation = d3.forceSimulation(nodes) .force("x", d3.forceX(d => { if (d.data.level === 1) { return width / 3 - 100; // width/5 - 50 - 400; } return width / 3 * d.data.level; // width/5 * d.group - 400; }).strength(0.95)) .force("y", d3.forceY(height / 2).strength(0.01)) .force("center", d3.forceCenter(width / 2, height / 2)) .force("cluster", forceCluster()) .force("collide", forceCollide()); const node = svg.append("g") .selectAll("circle") .data(nodes) .join("circle") .attr("cx", d => d.x) .attr("cy", d => d.y) .attr("fill", d => color(d.data.group)); node.transition() .attrTween("r", d => { const i = d3.interpolate(0, d.r); return t => d.r = i(t); }); // links const links = dataSource.links.map(d => Object.create(d)); const link = svg.append("g") .selectAll("line") .data(links) .join("line") .classed('link', true) .style('stroke', '#999') .style("stroke-opacity", 0.75); function ticked() { link .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node .attr("cx", d => d.x) .attr("cy", d => d.y); } simulation .nodes(nodes) .on("tick", ticked); // 启用以下三行代码后分组功能失效 simulation .force("link", d3.forceLink().id(function (d) { return d.data.id; }).strength(0.3)) .force("link").links(links); } d3.json("https://gist.githubusercontent.com/ravengao/a548cc4a7dd38f1afe0fe4b31ba8901b/raw/fd86a4e94e31ca2497ee78e1006dbed798f62e67/sample_data_supp%253E0.4_new.json").then((d) => { const dataSource = { links: d.links, nodes: d.nodes.map(n => { n.group = Math.random() * m | 0; n.value = -Math.log(Math.random()) return n; }) }; dataSource.nodes = ({ children: Array.from( d3.group( dataSource.nodes, d => d.group ), ([, children]) => ({ children }) ) }); renderChart(dataSource); }); function forceCluster() { const strength = 0.2; let nodes; function force(alpha) { const centroids = d3.rollup(nodes, centroid, d => d.data.group); const l = alpha * strength; for (const d of nodes) { const { x: cx, y: cy } = centroids.get(d.data.group); d.vx -= (d.x - cx) * l; d.vy -= (d.y - cy) * l; } } force.initialize = _ => nodes = _; return force; } function forceCollide() { const alpha = 0.4; // fixed for greater rigidity! const padding1 = 2; // separation between same-color nodes const padding2 = 6; // separation between different-color nodes let nodes; let maxRadius; function force() { const quadtree = d3.quadtree(nodes, d => d.x, d => d.y); for (const d of nodes) { const r = d.r + maxRadius; const nx1 = d.x - r, ny1 = d.y - r; const nx2 = d.x + r, ny2 = d.y + r; quadtree.visit((q, x1, y1, x2, y2) => { if (!q.length) do { if (q.data !== d) { const r = d.r + q.data.r + (d.data.group === q.data.data.group ? padding1 : padding2); let x = d.x - q.data.x, y = d.y - q.data.y, l = Math.hypot(x, y); if (l < r) { l = (l - r) / l * alpha; d.x -= x *= l, d.y -= y *= l; q.data.x += x, q.data.y += y; } } } while (q = q.next); return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; }); } } force.initialize = _ => maxRadius = d3.max(nodes = _, d => d.r) + Math.max(padding1, padding2); return force; } function centroid(nodes) { let x = 0; let y = 0; let z = 0; for (const d of nodes) { let k = d.r ** 2; x += d.x * k; y += d.y * k; z += k; } return { x: x / z, y: y / z }; } function generateUUID() { var d = new Date().getTime(); var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); return uuid; };
HTML代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script>
问题描述
未添加节点链接时,两层聚类功能均正常工作;但添加节点间链接后,分组功能失效。注释掉代码中启用forceLink的三行代码时分组正常,启用后分组丢失,恳请协助解决该问题。
内容的提问来源于stack exchange,提问作者Manish Kumar
相关产品推荐
相关产品推荐

