D3 Force Directed Graph - Removing Nodes












0















I am trying to incorporate this example for adding/removing nodes from a Force Directed Graph. I am able to add nodes just fine, and for some reason I am also able to remove the nodes I have added without issues. However when I try to remove any of the original nodes, the node is not removed from the display, and it also breaks many other nodes' links. However, it and it's links are properly removed from the JSON based on the logs.



This example uses seemingly the same method as I do for removing nodes with splice.



Here also seems to use splice method though I don't fully follow the rest of what it is doing with filtering.



There is also this question that asks a somewhat similar thing though the answers only address adding.



I have tried exit/removing after the append which did not work. I have also tried using .insert instead of .append. The console.log during the update() function prints the JSON being used and it shows that nodes and links are all properly removed, but the display doesn't reflect that.



Relevant code is below.



Initializing/updating graph



//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 nodes and links.
ADDING works perfectly fine for both nodes and links this way.



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 Data is in the format



{
"nodes":[
{
"id": "id1",
"radius": 5},
{
"id: "id2",
"radius": 6}
],

"links":[{
"source": "id1",
"target": "id2"
]
}









share|improve this question























  • Hi! I am trying to reproduce your problem here: stackblitz.com/edit/q53397252. I've made functions newNode, newLink, removeNode, and removeLink global, so you can call them in console. In my browser I do not see any errors after removing any of initial nodes.

    – Yaroslav Sergienko
    Nov 22 '18 at 11:39











  • I'm not able to use this site for some reason it just says "starting dev server" and never loads. How do you call functions from the console? Right now I am using some buttons so I can test using console to see if it works

    – A Zibuda
    Nov 26 '18 at 12:48











  • Update: I tried running remove node from the console and it still doesn't properly update display. To clarify @YaroslavSergienko there are no errors that show up...but the display is not correct. Nodes will get jumbled and the 'removed' node which is no longer in the JSON is usually still there.

    – A Zibuda
    Nov 26 '18 at 14:15


















0















I am trying to incorporate this example for adding/removing nodes from a Force Directed Graph. I am able to add nodes just fine, and for some reason I am also able to remove the nodes I have added without issues. However when I try to remove any of the original nodes, the node is not removed from the display, and it also breaks many other nodes' links. However, it and it's links are properly removed from the JSON based on the logs.



This example uses seemingly the same method as I do for removing nodes with splice.



Here also seems to use splice method though I don't fully follow the rest of what it is doing with filtering.



There is also this question that asks a somewhat similar thing though the answers only address adding.



I have tried exit/removing after the append which did not work. I have also tried using .insert instead of .append. The console.log during the update() function prints the JSON being used and it shows that nodes and links are all properly removed, but the display doesn't reflect that.



Relevant code is below.



Initializing/updating graph



//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 nodes and links.
ADDING works perfectly fine for both nodes and links this way.



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 Data is in the format



{
"nodes":[
{
"id": "id1",
"radius": 5},
{
"id: "id2",
"radius": 6}
],

"links":[{
"source": "id1",
"target": "id2"
]
}









share|improve this question























  • Hi! I am trying to reproduce your problem here: stackblitz.com/edit/q53397252. I've made functions newNode, newLink, removeNode, and removeLink global, so you can call them in console. In my browser I do not see any errors after removing any of initial nodes.

    – Yaroslav Sergienko
    Nov 22 '18 at 11:39











  • I'm not able to use this site for some reason it just says "starting dev server" and never loads. How do you call functions from the console? Right now I am using some buttons so I can test using console to see if it works

    – A Zibuda
    Nov 26 '18 at 12:48











  • Update: I tried running remove node from the console and it still doesn't properly update display. To clarify @YaroslavSergienko there are no errors that show up...but the display is not correct. Nodes will get jumbled and the 'removed' node which is no longer in the JSON is usually still there.

    – A Zibuda
    Nov 26 '18 at 14:15
















0












0








0


1






I am trying to incorporate this example for adding/removing nodes from a Force Directed Graph. I am able to add nodes just fine, and for some reason I am also able to remove the nodes I have added without issues. However when I try to remove any of the original nodes, the node is not removed from the display, and it also breaks many other nodes' links. However, it and it's links are properly removed from the JSON based on the logs.



This example uses seemingly the same method as I do for removing nodes with splice.



Here also seems to use splice method though I don't fully follow the rest of what it is doing with filtering.



There is also this question that asks a somewhat similar thing though the answers only address adding.



I have tried exit/removing after the append which did not work. I have also tried using .insert instead of .append. The console.log during the update() function prints the JSON being used and it shows that nodes and links are all properly removed, but the display doesn't reflect that.



Relevant code is below.



Initializing/updating graph



//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 nodes and links.
ADDING works perfectly fine for both nodes and links this way.



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 Data is in the format



{
"nodes":[
{
"id": "id1",
"radius": 5},
{
"id: "id2",
"radius": 6}
],

"links":[{
"source": "id1",
"target": "id2"
]
}









share|improve this question














I am trying to incorporate this example for adding/removing nodes from a Force Directed Graph. I am able to add nodes just fine, and for some reason I am also able to remove the nodes I have added without issues. However when I try to remove any of the original nodes, the node is not removed from the display, and it also breaks many other nodes' links. However, it and it's links are properly removed from the JSON based on the logs.



This example uses seemingly the same method as I do for removing nodes with splice.



Here also seems to use splice method though I don't fully follow the rest of what it is doing with filtering.



There is also this question that asks a somewhat similar thing though the answers only address adding.



I have tried exit/removing after the append which did not work. I have also tried using .insert instead of .append. The console.log during the update() function prints the JSON being used and it shows that nodes and links are all properly removed, but the display doesn't reflect that.



Relevant code is below.



Initializing/updating graph



//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 nodes and links.
ADDING works perfectly fine for both nodes and links this way.



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 Data is in the format



{
"nodes":[
{
"id": "id1",
"radius": 5},
{
"id: "id2",
"radius": 6}
],

"links":[{
"source": "id1",
"target": "id2"
]
}






javascript html d3.js force-layout






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 20 '18 at 16:20









A ZibudaA Zibuda

388




388













  • Hi! I am trying to reproduce your problem here: stackblitz.com/edit/q53397252. I've made functions newNode, newLink, removeNode, and removeLink global, so you can call them in console. In my browser I do not see any errors after removing any of initial nodes.

    – Yaroslav Sergienko
    Nov 22 '18 at 11:39











  • I'm not able to use this site for some reason it just says "starting dev server" and never loads. How do you call functions from the console? Right now I am using some buttons so I can test using console to see if it works

    – A Zibuda
    Nov 26 '18 at 12:48











  • Update: I tried running remove node from the console and it still doesn't properly update display. To clarify @YaroslavSergienko there are no errors that show up...but the display is not correct. Nodes will get jumbled and the 'removed' node which is no longer in the JSON is usually still there.

    – A Zibuda
    Nov 26 '18 at 14:15





















  • Hi! I am trying to reproduce your problem here: stackblitz.com/edit/q53397252. I've made functions newNode, newLink, removeNode, and removeLink global, so you can call them in console. In my browser I do not see any errors after removing any of initial nodes.

    – Yaroslav Sergienko
    Nov 22 '18 at 11:39











  • I'm not able to use this site for some reason it just says "starting dev server" and never loads. How do you call functions from the console? Right now I am using some buttons so I can test using console to see if it works

    – A Zibuda
    Nov 26 '18 at 12:48











  • Update: I tried running remove node from the console and it still doesn't properly update display. To clarify @YaroslavSergienko there are no errors that show up...but the display is not correct. Nodes will get jumbled and the 'removed' node which is no longer in the JSON is usually still there.

    – A Zibuda
    Nov 26 '18 at 14:15



















Hi! I am trying to reproduce your problem here: stackblitz.com/edit/q53397252. I've made functions newNode, newLink, removeNode, and removeLink global, so you can call them in console. In my browser I do not see any errors after removing any of initial nodes.

– Yaroslav Sergienko
Nov 22 '18 at 11:39





Hi! I am trying to reproduce your problem here: stackblitz.com/edit/q53397252. I've made functions newNode, newLink, removeNode, and removeLink global, so you can call them in console. In my browser I do not see any errors after removing any of initial nodes.

– Yaroslav Sergienko
Nov 22 '18 at 11:39













I'm not able to use this site for some reason it just says "starting dev server" and never loads. How do you call functions from the console? Right now I am using some buttons so I can test using console to see if it works

– A Zibuda
Nov 26 '18 at 12:48





I'm not able to use this site for some reason it just says "starting dev server" and never loads. How do you call functions from the console? Right now I am using some buttons so I can test using console to see if it works

– A Zibuda
Nov 26 '18 at 12:48













Update: I tried running remove node from the console and it still doesn't properly update display. To clarify @YaroslavSergienko there are no errors that show up...but the display is not correct. Nodes will get jumbled and the 'removed' node which is no longer in the JSON is usually still there.

– A Zibuda
Nov 26 '18 at 14:15







Update: I tried running remove node from the console and it still doesn't properly update display. To clarify @YaroslavSergienko there are no errors that show up...but the display is not correct. Nodes will get jumbled and the 'removed' node which is no longer in the JSON is usually still there.

– A Zibuda
Nov 26 '18 at 14:15














1 Answer
1






active

oldest

votes


















0














By default d3 joins data by index, so after you remove node, it assigns data incorrectly. The solution is to pass second argument to .data function. You need to replace



node = node.data(config.nodes);


with



node = node.data(config.nodes, d => d.id);


You can also do this for links, but if their styles are equal (i.e. they only differ by x1, x2, y1 and y2) it will make no difference.



Update: you should do this for labels as well



label = label.data(config.nodes, d => d.id);





share|improve this answer


























  • I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

    – A Zibuda
    Nov 26 '18 at 16:22











  • The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

    – Yaroslav Sergienko
    Nov 26 '18 at 16:32











  • The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

    – A Zibuda
    Nov 26 '18 at 16:47













  • I haven't noticed, that you also have labels, so you should pass second argument for them as well

    – Yaroslav Sergienko
    Nov 26 '18 at 17:00











  • That fixed it, thank you!

    – A Zibuda
    Nov 26 '18 at 17:47











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53397252%2fd3-force-directed-graph-removing-nodes%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














By default d3 joins data by index, so after you remove node, it assigns data incorrectly. The solution is to pass second argument to .data function. You need to replace



node = node.data(config.nodes);


with



node = node.data(config.nodes, d => d.id);


You can also do this for links, but if their styles are equal (i.e. they only differ by x1, x2, y1 and y2) it will make no difference.



Update: you should do this for labels as well



label = label.data(config.nodes, d => d.id);





share|improve this answer


























  • I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

    – A Zibuda
    Nov 26 '18 at 16:22











  • The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

    – Yaroslav Sergienko
    Nov 26 '18 at 16:32











  • The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

    – A Zibuda
    Nov 26 '18 at 16:47













  • I haven't noticed, that you also have labels, so you should pass second argument for them as well

    – Yaroslav Sergienko
    Nov 26 '18 at 17:00











  • That fixed it, thank you!

    – A Zibuda
    Nov 26 '18 at 17:47
















0














By default d3 joins data by index, so after you remove node, it assigns data incorrectly. The solution is to pass second argument to .data function. You need to replace



node = node.data(config.nodes);


with



node = node.data(config.nodes, d => d.id);


You can also do this for links, but if their styles are equal (i.e. they only differ by x1, x2, y1 and y2) it will make no difference.



Update: you should do this for labels as well



label = label.data(config.nodes, d => d.id);





share|improve this answer


























  • I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

    – A Zibuda
    Nov 26 '18 at 16:22











  • The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

    – Yaroslav Sergienko
    Nov 26 '18 at 16:32











  • The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

    – A Zibuda
    Nov 26 '18 at 16:47













  • I haven't noticed, that you also have labels, so you should pass second argument for them as well

    – Yaroslav Sergienko
    Nov 26 '18 at 17:00











  • That fixed it, thank you!

    – A Zibuda
    Nov 26 '18 at 17:47














0












0








0







By default d3 joins data by index, so after you remove node, it assigns data incorrectly. The solution is to pass second argument to .data function. You need to replace



node = node.data(config.nodes);


with



node = node.data(config.nodes, d => d.id);


You can also do this for links, but if their styles are equal (i.e. they only differ by x1, x2, y1 and y2) it will make no difference.



Update: you should do this for labels as well



label = label.data(config.nodes, d => d.id);





share|improve this answer















By default d3 joins data by index, so after you remove node, it assigns data incorrectly. The solution is to pass second argument to .data function. You need to replace



node = node.data(config.nodes);


with



node = node.data(config.nodes, d => d.id);


You can also do this for links, but if their styles are equal (i.e. they only differ by x1, x2, y1 and y2) it will make no difference.



Update: you should do this for labels as well



label = label.data(config.nodes, d => d.id);






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 26 '18 at 16:59

























answered Nov 26 '18 at 16:15









Yaroslav SergienkoYaroslav Sergienko

40016




40016













  • I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

    – A Zibuda
    Nov 26 '18 at 16:22











  • The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

    – Yaroslav Sergienko
    Nov 26 '18 at 16:32











  • The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

    – A Zibuda
    Nov 26 '18 at 16:47













  • I haven't noticed, that you also have labels, so you should pass second argument for them as well

    – Yaroslav Sergienko
    Nov 26 '18 at 17:00











  • That fixed it, thank you!

    – A Zibuda
    Nov 26 '18 at 17:47



















  • I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

    – A Zibuda
    Nov 26 '18 at 16:22











  • The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

    – Yaroslav Sergienko
    Nov 26 '18 at 16:32











  • The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

    – A Zibuda
    Nov 26 '18 at 16:47













  • I haven't noticed, that you also have labels, so you should pass second argument for them as well

    – Yaroslav Sergienko
    Nov 26 '18 at 17:00











  • That fixed it, thank you!

    – A Zibuda
    Nov 26 '18 at 17:47

















I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

– A Zibuda
Nov 26 '18 at 16:22





I tried changing this but I'm seeing the same result. Is there some documentation regarding the way data is joined that I can reference in this case? It does seem something like that could be the cause as colors are mixed up as well when removing.

– A Zibuda
Nov 26 '18 at 16:22













The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

– Yaroslav Sergienko
Nov 26 '18 at 16:32





The most comprehensive about joins it this: bost.ocks.org/mike/selection/#key. Can you share example with colors and steps to reproduce the problem?

– Yaroslav Sergienko
Nov 26 '18 at 16:32













The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

– A Zibuda
Nov 26 '18 at 16:47







The color of the node is defined by its ID. Here is standard graph that is drawn by my code: imgur.com/CcTL4mL Next I execute removeNode('CCC') This is result: imgur.com/a/d0ThkhU Most nodes stay the same, some change color and name. (KYC changes to MAL and MAL changes to CCC) However JSON shows no nodes with name "CCC" being loaded.

– A Zibuda
Nov 26 '18 at 16:47















I haven't noticed, that you also have labels, so you should pass second argument for them as well

– Yaroslav Sergienko
Nov 26 '18 at 17:00





I haven't noticed, that you also have labels, so you should pass second argument for them as well

– Yaroslav Sergienko
Nov 26 '18 at 17:00













That fixed it, thank you!

– A Zibuda
Nov 26 '18 at 17:47





That fixed it, thank you!

– A Zibuda
Nov 26 '18 at 17:47




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53397252%2fd3-force-directed-graph-removing-nodes%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Xamarin.form Move up view when keyboard appear

Post-Redirect-Get with Spring WebFlux and Thymeleaf

Anylogic : not able to use stopDelay()