#svg #d3.js #data-visualization #force-layout
#svg #d3.js #визуализация данных #компоновка с усилием
Вопрос:
У меня возникли трудности с обновлением моей сети D3 force. Цель состоит в том, чтобы иметь возможность перерисовать график с помощью отфильтрованных данных. Для простоты я привел пример кода ниже с двумя наборами Dummydatasets (полный и отфильтрованный), которые можно выбрать с помощью кнопок. Но вместо того, чтобы перерисовывать график, он просто продолжает добавлять новые svg-элементы помимо уже существующих. Я предполагаю, что это как-то связано с вызовом всей функции showData снова и снова. Хотя я пробовал разные решения, он продолжает добавлять svg, кроме того, или g-элементы друг на друга. Вот код и данные:
index.html
lt;html lang="en"gt; lt;headgt; lt;meta charset="UTF-8"gt; lt;meta http-equiv="X-UA-Compatible" content="IE=edge"gt; lt;meta name="viewport" content="width=device-width, initial-scale=1.0"gt; lt;titlegt;Documentlt;/titlegt; lt;script src="https://d3js.org/d3.v7.min.js"gt;lt;/scriptgt; lt;script src="network.js"gt;lt;/scriptgt; lt;script src="getData.js"gt;lt;/scriptgt; lt;/headgt; lt;bodygt; lt;button onclick="loadData('dummyData1.json')"gt;Data1lt;/buttongt; lt;button onclick="loadData('dummyData2.json')"gt;Data2lt;/buttongt; lt;div id="network"gt;lt;/divgt; lt;/bodygt; lt;/htmlgt;
getData.js
let store = {} function loadData(url) { console.log(url) return Promise.all([ d3.json(url) ]).then(datasets =gt; { store.graph = datasets[0]; showData(store.graph) return store; }) }
network.js
function showData (data){ var toggle = 0; width=500; height=500; var linkedByIndex = {}; data.links.forEach(function(d) { linkedByIndex[d.source ',' d.target] = 1; linkedByIndex[d.target ',' d.source] = 1; }); let simulation = d3.forceSimulation() .force("link", d3.forceLink().id((d) =gt; d.id).distance(10)) .force("charge", d3.forceManyBody().strength(d =gt; -20)) .force("center", d3.forceCenter(width / 2, height / 2)) var color = d3.scaleOrdinal() .domain(data.nodes) .range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628']) svg = d3.select("#network").append("svg") .attr("width", width) .attr("height", height) var g = svg.append("g").attr("class", "everything") var link = g.append("g") .selectAll(".link") var node = g.append("g") .selectAll(".node") var drag_handler = d3.drag() .on("start", drag_start) .on("drag", drag_drag) .on("end", drag_end); drag_handler(node); var zoom_handler = d3.zoom() .on("zoom", zoom_actions); zoom_handler(svg); update(); /** Functions **/ function update() { node = node.data(data.nodes) node.exit().remove() var newNode = node.enter().append("circle") .attr('class', 'node') .attr("r", 5) .attr('fill', function(d) {return(color(d.country))}) .on('click', function(d, i) { if (toggle == 0) { // Ternary operator restyles links and nodes if they are adjacent. d3.selectAll('.link').style('stroke-opacity', function (l) { return l.target == i || l.source == i ? 1 : 0.1; }); d3.selectAll('.node').style('opacity', function (n) { return neighboring(n, i) ? 1 : 0.1; }); d3.select(this).style('opacity', 1); toggle = 1; } else { // Restore nodes and links to normal opacity. d3.selectAll('.link').style('stroke-opacity', '0.3'); d3.selectAll('.node').style('opacity', '1'); toggle = 0; } }) node = node.merge(newNode) link = link.data(data.links) link.exit().remove(); var newLink = link.enter().append("line") .attr('class', 'link') .attr("stroke", "black") .attr("stroke-opacity", 0.3) link = link.merge(newLink) simulation .nodes(data.nodes) .on("tick", tickActions); simulation.force("link") .links(data.links); simulation.alpha(1).alphaTarget(0).restart(); } function drag_start(event, d) { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function drag_drag(event, d) { d.fx = event.x; d.fy = event.y; } function drag_end(event, d) { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } function neighboring(a, b) { return linkedByIndex[a.id ',' b.id]; } //Zoom functions function zoom_actions(event, d){ g.attr("transform", event.transform) } function tickActions() { node .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); 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; }); } }
dummyData1.json
{"nodes": [ {"id":1, "country":"Germany"}, {"id":2, "country":"UK"}, {"id":3, "country":"USA"}, {"id":4, "country":"China"}, {"id":5, "country":"Greece"}, {"id":6, "country":"Brazil"} ], "links": [ {"source": 2, "target": 4}, {"source": 1, "target": 4}, {"source": 3, "target": 5}, {"source": 6, "target": 1}, {"source": 5, "target": 4}, {"source": 1, "target": 2}, {"source": 2, "target": 3} ]}
dummyData2.json
{"nodes": [ {"id":1, "country":"Germany"}, {"id":2, "country":"UK"}, {"id":3, "country":"USA"}, {"id":4, "country":"China"} ], "links": [ {"source": 2, "target": 4}, {"source": 1, "target": 4}, {"source": 1, "target": 2}, {"source": 2, "target": 3} ]}
Заранее спасибо:-)