Reputation: 107
Iam trying to implement forced directed graph for a dataset using d3.js.My code outputs nodes,as tiny dot at top of the page,while edges fail to be drawn.If links are left out of the simulation,nodes are drawn correctly,any hint on where i might be doing it wrong will be helpful!
dataset:
{ nodes": [
{ "country": "East Timor", "code": "tl" },
{ "country": "Canada", "code": "ca" },
{ "country": "Turkmenistan", "code": "tm" },
{ "country": "United States of America", "code": "us" },
{ "country": "Lithuania", "code": "lt" },
{ "country": "Cambodia", "code": "kh" },
{ "country": "Ethiopia", "code": "et" },
{ "country": "Swaziland", "code": "sz" },
],
"links": [
{ "target": 66, "source": 0 },
{ "target": 3, "source": 1 },
{ "target": 100, "source": 2 },
{ "target": 40, "source": 2 },
{ "target": 125, "source": 2 },
{ "target": 147, "source": 2 },
{ "target": 159, "source": 3 },
{ "target": 18, "source": 3 },
}
let request = new XMLHttpRequest();
request.addEventListener("load", loaded);
function loaded() {
const data = JSON.parse(request.responseText);
var nodes = data.nodes;
var links = data.links;
// sets up svg
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
// starts simulation
var simulation = d3
.forceSimulation()
.force(
"link",
d3.forceLink().id(function(d) {
return d.country;
})
)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
// creates lines in graph,
var link = svg
.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter()
.append("line");
var node = svg
.append("g")
.attr("class", "nodes")
.selectAll("circle")
//pass node data
.data(nodes)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", "red")
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
simulation
// pass nodes,on tick call function ticked
.nodes(nodes)
.on("tick", ticked);
// pass links
simulation.force("link").links(links);
function ticked() {
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
request.open(
"GET",
"https://www.cs.mun.ca/~h65ped/Public/country%20data%20for%20force%20directed%20graph/countries.json",
true
);
request.send(null);
Upvotes: 2
Views: 573
Reputation: 102208
Look at your links
array:
"links": [
{ "target": 66, "source": 0 },
{ "target": 3, "source": 1 },
{ "target": 100, "source": 2 },
//etc..
As you can see, there is no country as value, neither for the target nor for the source. The links are using the nodes' indices instead.
Therefore, this...
.force("link", d3.forceLink().id(function(d) {
return d.country;
}));
... is wrong. Just remove the id
:
.force("link", d3.forceLink());
Here is the updated Codepen: https://codepen.io/anon/pen/NwKvbg?editors=0010
Upvotes: 2