I'm a D3.js beginner working on a network visualization, and I can't figure out how to properly specify my links for the force layout. The problem is that by default, d3 interprets the "source" and "target" of the links to be the indices of the nodes in the "nodes" array. However, in my data, the source and target refer to the id numbers of the nodes, which I've stored in a node attribute. How do I get the links to point to node.id
instead of node.index
? Here is where I guess I should do it:
var nodes = data.nodes;
var edges = data.edges;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(edges)
.size([w, h])
.linkDistance(80)
.charge(-500)
.gravity(0.2)
.on("tick", tick)
.start();
... but I'm not sure if I should be modifiying the .nodes()
part or the .links()
part. I tried adding this just before it:
var nodes = data.nodes;
for (var i = 0; i < nodes.length; i++) {
nodes[i].index = nodes[i].id;
console.log(i + " " + nodes[i].index + " " + nodes[i].id);
}
... but the index
attribute just gets overwritten when I create the force.
I read this question, but the only answer there seems like a bit of a hack. They also mention "data keys", but I can't find much on those, and I don't know how I would incorporate them, since I'm not actually using the enter()
function.
I don't think that you can force D3 to use the attribute node.id
as index, but you can process the links to have the right references:
var edges = [];
data.edges.forEach(function(e) {
// Get the source and target nodes
var sourceNode = data.nodes.filter(function(n) { return n.id === e.source; })[0],
targetNode = data.nodes.filter(function(n) { return n.id === e.target; })[0];
// Add the edge to the array
edges.push({source: sourceNode, target: targetNode});
});
var force = d3.layout.force()
.nodes(data.nodes)
.links(edges)
// the rest of your code here
.start();
The force layout overwrite the index if you set it, but will keep the references for the source and target of each node in the links.