d3.js Force Directed Graph: Updating Nodes and Links

1,631 views
Skip to first unread message

Ajantzen1122

unread,
Jun 20, 2016, 5:21:43 PM6/20/16
to d3-js


Hi,


Bear with me as I'm new to d3 and javascript in general. I have a force directed graph setup using a json object. I push the objects into 2 separate arrays (nodes and links) and draw the graph as such:

When a multicolored node is right clicked, a function is called to 'expand' the node. I make a post ajax call which returns another json object. Then, I'm pushing the data into the nodes and links arrays and calling a function to redraw. The graph is turning out like this, however:


I really don't understand why some links go missing because when I log the arrays to the console, the data appears to be correct and intact on the redraw.  I think it's because the existing links aren't being updated properly, but I'm not sure how to resolve this.


Also, would it be more efficient to directly assign nodes to json.nodes and links to json.links? Or would it be better to use d3.json?


Here is my code:

var json = JSON.parse('json_string');
        nodes
= [],
        links
= [];

   
//Format for object: {name: "name", id: "id", id2: "id2", shape: "shape", white: "white"}
   
for (var n = 0; n < json.nodes.length; n++) {
        nodes
.push({name: json.nodes[n]["name"], id: json.nodes[n]["id"], id2: json.nodes[n]["id2"], shape: json.nodes[n]["shape"], white: json.nodes[n]["white"]});
   
}
   
for (var l = 0; l < json.links.length; l++) {
        links
.push({source: json.links[l]["source"], target: json.links[l]["target"]});
   
}

   
var width = 1600,
        height
= 800,
        color
= d3.scale.category10();

   
// Set up canvas dimensions
   
var svg = d3.select("body").append("svg")
       
.attr("width", width)
       
.attr("height", height);

   
var force = d3.layout.force()
       
.nodes(nodes)
       
.links(links)
       
.size([width, height])
       
.charge(-600)
       
.linkDistance(40)
       
.on("tick", tick)
       
.alpha(.1);

   
var link = svg.selectAll(".link"),
        node
= svg.selectAll(".node");

   
// Allow nodes to be dragged
   
var drag = force.drag()
       
.on("dragstart", dragstart);

   
// Fix nodes onto canvas when dragged
   
function dragstart(d) {
        d3
.select(this).classed("fixed", d.fixed = true);
   
}

   
// Unfix node from canvas on double click
   
function unfix(d) {
        d3
.select(this).classed("fixed", d.fixed = false);
   
}

    simulate
();

   
function simulate() {

        link
= link.data(force.links(), function(d) { return d.source + "-" + d.target; });
        link
.enter().insert("line", ".node")
           
.attr("class", "link")
        link
.exit().remove();

        node
= node.data(force.nodes(), function(d) { return d.name; });
        node
.enter().append("g")
           
.attr("class", "node")
           
.style("fill", function(d) { if (d.id == '0') { return 'black'; } else if (d.white == '1') { return '#929292'; } else { return color(d.id); } })
           
.on("dblclick", unfix)
           
.on("contextmenu", function(d) { if (d.id2 != '0') { expand(d); } })
           
.call(drag);

        node
.append("circle")
           
.attr("r", function (d) { if (d.shape == 'True') { return 8; } else { return 0; } })
           
.style("stroke", function(d) { if (d.id2 == '0' || d.id == d.id2) { return 'none'; } else { return color(d.id2); } })
           
.style("stroke-width", "4px");

        node
.append("ellipse")
           
.attr("rx", function (d) { if (d.shape == 'False') { return 5; } else { return 0; } } )
           
.attr("ry", function (d) { if (d.shape == 'False') { return 15; } else { return 0; } } );

        node
.exit().remove();

        force
.start();
   
}

   
function tick() {
        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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
   
}

   
function expand(d) {
        d3
.event.preventDefault();
        $
.ajax({
           
// code for post call
           
var test = JSON.parse(data_returned);
           
for (var n = 0; n < test.nodes.length; n++) {
               nodes
.push({name: test.nodes[n]["name"], id: test.nodes[n]["id"], id2: test.nodes[n]["id2"], shape: test.nodes[n]["shape"], white: test.nodes[n]["white"]});
           
}
           
for (var l = 0; l < test.links.length; l++) {
               links
.push({source: test.links[l]["source"], target: test.links[l]["target"]});
           
}
            simulate
();
       
})}

Any help is greatly appreciated.


Best regards,

Anthony






Ajantzen1122

unread,
Jun 21, 2016, 9:48:31 AM6/21/16
to d3-js
EDIT: Here is a JSfiddle for readability and to see the code in action: http://jsfiddle.net/qAHC2/1288/

As a test, if you click on the multicolored node once, the new node and link is added properly. When clicked on a second time, however, one of the links disappears as data enters.

- Anthony
Reply all
Reply to author
Forward
0 new messages