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();
})}
Best regards,
Anthony