D3 力向图 - 删除节点
D3 Force Directed Graph - Removing Nodes
我正在尝试将 this example 合并到力导向图中的 adding/removing 个节点。我可以很好地添加节点,并且出于某种原因,我也可以毫无问题地删除我添加的节点。但是,当我尝试删除任何原始节点时,该节点并没有从显示中删除,而且它还破坏了许多其他节点的链接。但是,根据日志,它及其链接已从 JSON 中正确删除。
This example使用看似与我相同的方法来删除带有拼接的节点。
Here 似乎也使用了 splice 方法,尽管我没有完全理解其余的过滤方法。
There is also this question 问了一些类似的问题,虽然答案只涉及添加。
我在追加后尝试了 exit/removing,但没有成功。我也尝试过使用 .insert
而不是 .append
。 update()
函数中的 console.log
打印正在使用的 JSON 并且它显示节点和链接都已正确删除,但显示并没有反映出来。
相关代码如下
Initializing/updating 图
//Get the SVG element
var svg = d3.select("svg");
var width = 960, height = 600;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var link = svg.append("g").selectAll(".link");
var node = svg.append("g").selectAll(".node");
var label = svg.append("g").selectAll(".label");
//Begin the force simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }).distance(50).strength(0.3))
.force("charge", d3.forceManyBody().strength(-15))
.force("center", d3.forceCenter(width / 2, height / 2));
//Highlight variables
var highlight_color = "blue";
var tHighlight = 0.05;
var config;
var linkedByIndex = {};
//Get the data
d3.json("/../../data.json", function (data) {
//if (!localStorage.graph)
//{
localStorage.graph = JSON.stringify(data);
//}
update();
forms();
});
function update() {
config = JSON.parse(localStorage.graph);
console.log(JSON.stringify(config));
linkedByIndex = {};
//Create an array of source,target containing all links
config.links.forEach(function (d) {
linkedByIndex[d.source + "," + d.target] = true;
linkedByIndex[d.target + "," + d.source] = true;
});
//Draw links
link = link.data(config.links);
link.exit().remove();
link = link.enter().append("line")
.attr("class", "link")
.attr("stroke-width", 2)
.attr("stroke", "#888")
//.attr("opacity", function (d) { if (d.target.radius > 7) { return 1 }; return 0; })
.merge(link);
node = node.data(config.nodes);
node.exit().remove();
node = node.enter().append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.radius; })
.attr("fill", function (d) { return color(d.id); })
.attr("stroke", "black")
// .attr("pointer-events", function (d) { if (d.radius <= 7) { return "none"; } return "visibleAll"; })
// .attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("mouseover", mouseOver)
.on("mouseout", mouseOut)
.merge(node);
label = label.data(config.nodes);
label.exit().remove();
label = label.enter().append("text")
.attr("class", "label")
.attr("dx", function (d) { return d.radius * 1.25; })
.attr("dy", ".35em")
.attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
.attr("font-weight", "normal")
.style("font-size", 10)
.text(function (d) { return d.id; })
.merge(label);
//Add nodes to simulation
simulation
.nodes(config.nodes)
.on("tick", ticked);
//Add links to simulation
simulation.force("link")
.links(config.links);
simulation.alphaTarget(0.3).restart();
}
//Animating by ticks function
function ticked() {
node
.attr("cx", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
.attr("cy", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, 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; });
label
.attr("x", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
.attr("y", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, d.y)); });
}
//Using above array, check if two nodes are linked
function isConnected(node1, node2) {
return linkedByIndex[node1.id + "," + node2.id] || node1.index == node2.index;
}
Adding/removing 个节点和链接。
ADDING 以这种方式对节点和链接都非常有效。
function newNode(name, rad)
{
if (name == "")
{
alert("Must specify name");
return;
}
console.log("Adding node with name: " + name + " and radius: " + rad);
var temp = JSON.parse(localStorage.graph);
temp.nodes.push({ "id": name, "radius": Number(rad) });
localStorage.graph = JSON.stringify(temp);
update();
}
function newLink(source, target)
{
var foundSource = false;
var foundTarget = false;
if (source == "" && target == "")
{
alert("Must specify source and target");
return;
}
else if(source == "")
{
alert("Must specify source");
return;
}
else if (target == "")
{
alert("Must specify target")
return;
}
var temp = JSON.parse(localStorage.graph);
for (var i=0; i < temp.nodes.length; i++)
{
if(temp.nodes[i]['id'] === source)
{
foundSource = true;
}
if(temp.nodes[i]['id'] === target)
{
foundTarget = true;
}
}
if (foundSource && foundTarget) {
temp.links.push({ "source": source, "target": target });
localStorage.graph = JSON.stringify(temp);
update();
}
else {
alert("Invalid source or target");
return;
}
return;
}
function removeLink(linkSource, linkTarget)
{
}
function removeNode(nodeName)
{
var temp = JSON.parse(localStorage.graph);
var found = false;
if (nodeName == "")
{
alert("Must specify node name");
return;
}
for(var i=0; i<temp.nodes.length; i++)
{
if(temp.nodes[i]['id'] === nodeName)
{
console.log("Removing node: " + nodeName);
found = true;
temp.nodes.splice(i, 1);
temp.links = temp.links.filter(function (d) { return d.source != nodeName && d.target != nodeName; });
}
}
if(!found)
{
alert("Node does not exist");
return;
}
localStorage.graph = JSON.stringify(temp);
update();
}
JSON 数据格式为
{
"nodes":[
{
"id": "id1",
"radius": 5},
{
"id: "id2",
"radius": 6}
],
"links":[{
"source": "id1",
"target": "id2"
]
}
默认情况下,d3 通过索引连接数据,因此在删除节点后,它会错误地分配数据。解决方案是将第二个参数传递给 .data
函数。你需要更换
node = node.data(config.nodes);
和
node = node.data(config.nodes, d => d.id);
您也可以对链接执行此操作,但如果它们的样式相同(即它们仅相差 x1
、x2
、y1
和 y2
)它不会有任何区别。
更新:您也应该对标签执行此操作
label = label.data(config.nodes, d => d.id);
我正在尝试将 this example 合并到力导向图中的 adding/removing 个节点。我可以很好地添加节点,并且出于某种原因,我也可以毫无问题地删除我添加的节点。但是,当我尝试删除任何原始节点时,该节点并没有从显示中删除,而且它还破坏了许多其他节点的链接。但是,根据日志,它及其链接已从 JSON 中正确删除。
This example使用看似与我相同的方法来删除带有拼接的节点。
Here 似乎也使用了 splice 方法,尽管我没有完全理解其余的过滤方法。
There is also this question 问了一些类似的问题,虽然答案只涉及添加。
我在追加后尝试了 exit/removing,但没有成功。我也尝试过使用 .insert
而不是 .append
。 update()
函数中的 console.log
打印正在使用的 JSON 并且它显示节点和链接都已正确删除,但显示并没有反映出来。
相关代码如下
Initializing/updating 图
//Get the SVG element
var svg = d3.select("svg");
var width = 960, height = 600;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var link = svg.append("g").selectAll(".link");
var node = svg.append("g").selectAll(".node");
var label = svg.append("g").selectAll(".label");
//Begin the force simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }).distance(50).strength(0.3))
.force("charge", d3.forceManyBody().strength(-15))
.force("center", d3.forceCenter(width / 2, height / 2));
//Highlight variables
var highlight_color = "blue";
var tHighlight = 0.05;
var config;
var linkedByIndex = {};
//Get the data
d3.json("/../../data.json", function (data) {
//if (!localStorage.graph)
//{
localStorage.graph = JSON.stringify(data);
//}
update();
forms();
});
function update() {
config = JSON.parse(localStorage.graph);
console.log(JSON.stringify(config));
linkedByIndex = {};
//Create an array of source,target containing all links
config.links.forEach(function (d) {
linkedByIndex[d.source + "," + d.target] = true;
linkedByIndex[d.target + "," + d.source] = true;
});
//Draw links
link = link.data(config.links);
link.exit().remove();
link = link.enter().append("line")
.attr("class", "link")
.attr("stroke-width", 2)
.attr("stroke", "#888")
//.attr("opacity", function (d) { if (d.target.radius > 7) { return 1 }; return 0; })
.merge(link);
node = node.data(config.nodes);
node.exit().remove();
node = node.enter().append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.radius; })
.attr("fill", function (d) { return color(d.id); })
.attr("stroke", "black")
// .attr("pointer-events", function (d) { if (d.radius <= 7) { return "none"; } return "visibleAll"; })
// .attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("mouseover", mouseOver)
.on("mouseout", mouseOut)
.merge(node);
label = label.data(config.nodes);
label.exit().remove();
label = label.enter().append("text")
.attr("class", "label")
.attr("dx", function (d) { return d.radius * 1.25; })
.attr("dy", ".35em")
.attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
.attr("font-weight", "normal")
.style("font-size", 10)
.text(function (d) { return d.id; })
.merge(label);
//Add nodes to simulation
simulation
.nodes(config.nodes)
.on("tick", ticked);
//Add links to simulation
simulation.force("link")
.links(config.links);
simulation.alphaTarget(0.3).restart();
}
//Animating by ticks function
function ticked() {
node
.attr("cx", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
.attr("cy", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, 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; });
label
.attr("x", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
.attr("y", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, d.y)); });
}
//Using above array, check if two nodes are linked
function isConnected(node1, node2) {
return linkedByIndex[node1.id + "," + node2.id] || node1.index == node2.index;
}
Adding/removing 个节点和链接。 ADDING 以这种方式对节点和链接都非常有效。
function newNode(name, rad)
{
if (name == "")
{
alert("Must specify name");
return;
}
console.log("Adding node with name: " + name + " and radius: " + rad);
var temp = JSON.parse(localStorage.graph);
temp.nodes.push({ "id": name, "radius": Number(rad) });
localStorage.graph = JSON.stringify(temp);
update();
}
function newLink(source, target)
{
var foundSource = false;
var foundTarget = false;
if (source == "" && target == "")
{
alert("Must specify source and target");
return;
}
else if(source == "")
{
alert("Must specify source");
return;
}
else if (target == "")
{
alert("Must specify target")
return;
}
var temp = JSON.parse(localStorage.graph);
for (var i=0; i < temp.nodes.length; i++)
{
if(temp.nodes[i]['id'] === source)
{
foundSource = true;
}
if(temp.nodes[i]['id'] === target)
{
foundTarget = true;
}
}
if (foundSource && foundTarget) {
temp.links.push({ "source": source, "target": target });
localStorage.graph = JSON.stringify(temp);
update();
}
else {
alert("Invalid source or target");
return;
}
return;
}
function removeLink(linkSource, linkTarget)
{
}
function removeNode(nodeName)
{
var temp = JSON.parse(localStorage.graph);
var found = false;
if (nodeName == "")
{
alert("Must specify node name");
return;
}
for(var i=0; i<temp.nodes.length; i++)
{
if(temp.nodes[i]['id'] === nodeName)
{
console.log("Removing node: " + nodeName);
found = true;
temp.nodes.splice(i, 1);
temp.links = temp.links.filter(function (d) { return d.source != nodeName && d.target != nodeName; });
}
}
if(!found)
{
alert("Node does not exist");
return;
}
localStorage.graph = JSON.stringify(temp);
update();
}
JSON 数据格式为
{
"nodes":[
{
"id": "id1",
"radius": 5},
{
"id: "id2",
"radius": 6}
],
"links":[{
"source": "id1",
"target": "id2"
]
}
默认情况下,d3 通过索引连接数据,因此在删除节点后,它会错误地分配数据。解决方案是将第二个参数传递给 .data
函数。你需要更换
node = node.data(config.nodes);
和
node = node.data(config.nodes, d => d.id);
您也可以对链接执行此操作,但如果它们的样式相同(即它们仅相差 x1
、x2
、y1
和 y2
)它不会有任何区别。
更新:您也应该对标签执行此操作
label = label.data(config.nodes, d => d.id);