Обновление/Фильтрация узлов и ссылок в сети D3 Force

#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} ]}  

Заранее спасибо:-)