I'm new to D3, and am trying to make a graph similar to this example but a few things confuse me. I'm unsure when the example is referring to things built in to D3, or just the data set they are using.
Like here, I'm not sure about the id in d.id. as the example's data looks like this, which makes me think id is from the data. Or does it represent an index value?
"nodes": [
{"id": "Myriel", "group": 1},
{"id": "Napoleon", "group": 1}
],
// etc
"links": [
{"source": "Napoleon", "target": "Myriel", "value": 1},
{"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
{"source": "Mme.Magloire", "target": "Myriel", "value": 10},
//etc
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; })) <-- where is this from?
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
SO! In my code I get an error repeating thousands of times, 'Uncaught Error: missing: X', where X is the value of the first source in my links array. I can console.log my data and it looks fine, and the elements are rendering to the DOM, but all bunched up to the top left of the SVG. I don't know what is wrong. I guess I have 2 questions.
Could someone clarify about the example id thing?
What does my error mean?
Any help would be appreciated.
My code;
var graph
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
d3.json("./mock.json", function(json) {
var graph = json
console.log(graph);
console.log(graph.nodes);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink(graph.links))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width/2, height/2));
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
//.attr("stroke-width", function(d) {return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
//.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) {return d.text });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
});
Data:
{
"nodes":[
{"reference":5, "year": 0, "text":"The amount of time spent on video gaming is related negatively to academic achievement", "tags":["Academic disturbance"]},
{"reference":5, "year": 0, "text":"Digital addiction ranges from <1% and 38%", "tags":["Addiction"]},
{"reference":58, "year": 0, "text":"Patological video game play impacts negativelly academic achievement", "tags":["Addiction"]},
{"reference":77, "year": 2009, "text":"74% of adults have Internet access at home", "tags":["Adults"]},
{"reference":64, "year": 0, "text":"Apathetic users spend short times on web pages, follow no logical order, and make random selections", "tags":["Apathetic hypertext users3"]},
{"reference":8, "year": 0, "text":"49.8% of sessions are shorter than 5 seconds", "tags":["App usage"]}
],
"links": [
{"source":0,"target":2},
{"source":0,"target":6},
{"source":1,"target":6},
{"source":1,"target":3},
{"source":1,"target":2}
]
}
There are two issues with your code:
The index of 6, see in your links array, is not available in the nodes array. nodes has a length of 6, which means the largest index is 5. This causes an error even with the correct code. I have change 6 to 5 in my working example below, and I believe that is what you want.
Since the links are based on the index, you can simply return the index instead of the ID, i.e. d3.forceLink().id(function(d,i) { return i; }.
Here is the proof-of-function example:
var graph
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var json = {
"nodes": [{
"reference": 5,
"year": 0,
"text": "The amount of time spent on video gaming is related negatively to academic achievement",
"tags": ["Academic disturbance"]
}, {
"reference": 5,
"year": 0,
"text": "Digital addiction ranges from <1% and 38%",
"tags": ["Addiction"]
}, {
"reference": 58,
"year": 0,
"text": "Patological video game play impacts negativelly academic achievement",
"tags": ["Addiction"]
}, {
"reference": 77,
"year": 2009,
"text": "74% of adults have Internet access at home",
"tags": ["Adults"]
}, {
"reference": 64,
"year": 0,
"text": "Apathetic users spend short times on web pages, follow no logical order, and make random selections",
"tags": ["Apathetic hypertext users3"]
}, {
"reference": 8,
"year": 0,
"text": "49.8% of sessions are shorter than 5 seconds",
"tags": ["App usage"]
}],
"links": [{
"source": 0,
"target": 2
}, {
"source": 0,
"target": 5
}, {
"source": 1,
"target": 5
}, {
"source": 1,
"target": 3
}, {
"source": 1,
"target": 2
}
]
};
var graph = json;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) {
return i;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
//.attr("stroke-width", function(d) {return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
//.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) {
return d.text
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
.links line {
stroke: #aaa;
}
.nodes circle {
pointer-events: all;
stroke: none;
stroke-width: 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.8/d3.min.js"></script>
<svg width="500" height="200"></svg>
Related
Please advise me what was wrong in the following code.
I want to create foce-directed graph by using D3.js
I cannot find where to correct in the following code.
Please advise me what was wrong in the following code.
I want to create foce-directed graph by using D3.js
I cannot find where to correct in the following code.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 v5 force simulation</title>
</head>
<body>
<svg width="400" height="300"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
var nodesData = [
{id:"Korea", "group": 1},
{id:"USA", "group": 1},
{id:"France", "group": 1},
{id:"UK", "group": 1},
{id:"Japan", "group": 1},
{id:"Turkey", "group": 1},
]
var linksData = [
{ "source": 'Korea', "target": 'USA'},
{ "source": 'UK', "target": 'USA' },
{ "source": 'France', "target": 'Turkey' },
{ "source": 'Korea', "target": 'UK' },
{ "source": 'Japan', "target": 'Turkey' },
{ "source": 'Japan', "target": 'Korea' }
]
var link = d3.select("svg")
.selectAll("line")
.data(linksData)
.enter()
.append("line")
.attr("stroke-width", 1)
.attr("stroke", "black");
var node = d3.select("svg")
.selectAll("circle")
.data(nodesData)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "LightSalmon")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(200, 150));
simulation
.nodes(nodesData)
.on("tick", ticked);
simulation.force("link")
.links(linksData);
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;
}
</script>
I am studying d3.js and would like to know if it is possible to grow the nodes outward as generation of nodes goes down.
For example, in example below, I am forcing the location of node 0.
I would like to place the nodes so child nodes 1,2,3,4 are surrounding node 0, and grand child nodes 5,6,7,8 are placed at the outskirts of nodes 1,2,3,4 farther and farther away from node 0.
In the example below, the grandchild nodes 5,7 are closer to node 0 than child nodes.
Would it be possible to achieve what I am looking for?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 v5 force simulation</title>
</head>
<body>
<svg width="600" height="400"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
var nodesData = [
{},
{},
{},
{},
{},
{},
{},
{},
{}
]
var linksData = [
{ "source": 0, "target": 1 },
{ "source": 0, "target": 2 },
{ "source": 0, "target": 3 },
{ "source": 0, "target": 4 },
{ "source": 1, "target": 5 },
{ "source": 1, "target": 6 },
{ "source": 1, "target": 7 },
{ "source": 2, "target": 8 }
]
var link = d3.select("svg")
.selectAll("line")
.data(linksData)
.enter()
.append("line")
.attr("stroke-width", 1)
.attr("stroke", "black");
var node = d3.select("svg")
.selectAll("circle")
.data(nodesData)
.enter()
.append("circle")
.attr("r", 7)
.attr("fill", "LightSalmon");
const svg = d3.select("svg");
const sWidth = +svg.attr("width");
const sHeight = +svg.attr("height");
nodesData[0].fx = sWidth / 2;
nodesData[0].fy = sHeight / 2;
var simulation = d3.forceSimulation()
.force("collide", d3.forceCollide()
.radius((d)=>{return 10})
.strength(1.0)
.iterations(1))
.force("x", d3.forceX().strength(0.7))
.force("y", d3.forceY().strength(0.7))
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody().strength(-6000))
.force("center", d3.forceCenter(sWidth/2, sHeight/2));
simulation
.nodes(nodesData)
.on("tick", ticked);
simulation.force("link")
.links(linksData);
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; });
}
</script>
</body>
</html>
Looking at the documentation there is a distance method you can pass to force a fixed length for the links. Although you will probably need to play with other values to accomplish exactly the kind of equilibrium you are after.
.distance(function(d) {return d.distance;})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 v5 force simulation</title>
</head>
<body>
<svg width="600" height="400"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
var nodesData = [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{}
]
var linksData = [
{ "source": 0, "target": 1 },
{ "source": 0, "target": 2 },
{ "source": 0, "target": 3 },
{ "source": 0, "target": 4 },
{ "source": 1, "target": 5 },
{ "source": 1, "target": 6 },
{ "source": 1, "target": 7 },
{ "source": 2, "target": 8 },
{ "source": 2, "target": 9 },
{ "source": 2, "target": 10 },
{ "source": 2, "target": 11 }
]
var link = d3.select("svg")
.selectAll("line")
.data(linksData)
.enter()
.append("line")
.attr("stroke-width", 1)
.attr("stroke", "black");
var node = d3.select("svg")
.selectAll("circle")
.data(nodesData)
.enter()
.append("circle")
.attr("r", 15)
.attr("fill", "LightSalmon");
const svg = d3.select("svg");
const sWidth = +svg.attr("width");
const sHeight = +svg.attr("height");
nodesData[0].fx = sWidth / 2;
nodesData[0].fy = sHeight / 2;
var simulation = d3.forceSimulation()
.force("collide", d3.forceCollide()
.radius(40)
.strength(1)
.iterations(1))
.force("x", d3.forceX().strength(0))
.force("y", d3.forceY().strength(0))
.force("link", d3.forceLink().distance(function(d) {return 30;}))
.force("charge", d3.forceManyBody().strength(0))
.force("center", d3.forceCenter(sWidth/2, sHeight/2));
simulation
.nodes(nodesData)
.on("tick", ticked);
simulation.force("link")
.links(linksData);
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; });
}
</script>
</body>
</html>
How can i disable the animation while dragging the current node in the force simulation in the d3 version 4
Below is the code which is used for drag the node
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function dragstarted(d) {
if (!d3.event.active) force.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) force.alphaTarget(0.3);
// d.fx = null;
//d.fy = null;
}
When i tried to stop the force while in dragged method it doesn't work out.
Please suggest me the best way to stop the animation
Please check this fiddle
It's not exactly clear what you mean by "disable the animation". I suppose that you're talking about the movement of the other nodes... well, that's quite the expected behaviour, since you're reheating the simulation.
A possible solution is setting the fx and fy property of all nodes when the simulation ends:
simulation.on("end", function() {
node.each(function(d) {
d.fx = d.x;
d.fy = d.y;
})
})
Here is your code with that change. Wait until the simulation ends (aprox. 5 seconds) and then drag the nodes:
var nodes = [{
"id": 1,
"name": "server 1"
}, {
"id": 2,
"name": "server 2"
}, {
"id": 3,
"name": "server 3"
}, {
"id": 4,
"name": "server 4"
}, {
"id": 5,
"name": "server 5"
}, {
"id": 6,
"name": "server 6"
}, {
"id": 7,
"name": "server 7"
}, {
"id": 8,
"name": "server 8"
}, {
"id": 9,
"name": "server 9"
}]
var links = [{
source: 1,
target: 2
}, {
source: 1,
target: 3
}, {
source: 1,
target: 4
}, {
source: 2,
target: 5
}, {
source: 2,
target: 6
}, {
source: 3,
target: 7
}, {
source: 5,
target: 8
}, {
source: 6,
target: 9
}, ]
var index = 10;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
update();
function update() {
link = svg.selectAll(".link")
.data(links, function(d) {
return d.target.id;
})
link = link.enter()
.append("line")
.attr("class", "link");
node = svg.selectAll(".node")
.data(nodes, function(d) {
return d.id;
})
node = node.enter()
.append("g")
.attr("class", "node")
.on("click", click)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("circle")
.attr("r", 2.5)
node.append("title")
.text(function(d) {
return d.id;
});
node.append("text")
.attr("dy", 3)
.text(function(d) {
return d.name;
});
simulation
.nodes(nodes)
.on("tick", ticked)
.on("end", function() {
node.each(function(d) {
d.fx = d.x;
d.fy = d.y;
})
})
simulation.force("link")
.links(links);
}
function click(d) {
nodes.push({
id: index,
name: "server " + index
});
links.push({
source: d.id,
target: index
});
index++;
update();
}
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("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
}
.link {
stroke: #aaa;
}
.node {
pointer-events: all;
stroke: none;
stroke-width: 40px;
cursor: pointer;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="400"></svg>
Take a look at this example: http://bl.ocks.org/norrs/2883411 as it seems to accomplish what you are after.
As indicated in the example and the related SO question (D3 force directed graph with drag and drop support to make selected node position fixed when dropped) you will probably be better off creating and using your own drag listener to achieve this specific behaviour.
Its actually very easy. Simply don't define :
.call(d3.drag().on("start", drag_start)
.on("drag", drag_drag)
.on("end", drag_end))
This is what is controlling the animation part, and yet you find problem in late loading of your graph due to huge data or something then i suggest add a property called .alphaDecay(0.9), this particular thing will decrease the link length thus decreasing your load time of graph.
I'm trying to figure out a way to space out the nodes in my network graph for my d3.js code. I don't necessarily care about how the shape of the network will be when I load the page since I can just click and drag around the nodes to make any kind of shape I want. But I'm not really sure where I start in trying to space out my nodes. I searched around and nothing I found seems to work for me. Help is very much appreciated.
Here is a picture of what the network looks like when I load the page:
https://i.gyazo.com/919ad4bde39d9fe6a6b6c91548dbcc2f.png
Here is what I'd like for it to look like roughly (again, shape does not really matter, I'm just looking to get a little distance on the inital load):
https://i.gyazo.com/fefa29cf861e204bc83f34cbc2d1a17d.png
(I only have 8 rep so I can't upload pictures sorry)
Here's my code so far:
<!DOCTYPE html>
<style>
.links line {
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Group Comments</title>
<script src="http://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p> Not Ready: Group 6 Comments </p>
<svg width="960" height="600"></svg>
<script>
//fetches the svg
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
//Sets a color scale
var color = d3.scaleOrdinal(d3.schemeCategory20);
var strokeColor = d3.scaleLinear()
.domain([0, 1, 2])
.range(["white", "red", "green"]);
//Creates a force simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
//reads the JSON file
d3.json("NR6comments.json", function (error, graph) {
if (error) throw error;
//sets up the "links" between the nodes
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function (d) { return Math.sqrt(d.value) })
.attr("stroke", function (d) { return strokeColor(d.value) });
//sets up the nodes
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 10)
.attr("fill", function (d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
//displays the ID number on a node when hovering over
node.append("title")
.text(function (d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
</script>
<p>Likes Chart</p>
</body>
</html>
I would greatly appreciate it if I could get some help with this problem. Thank you!
There are different ways to achieve what you want. The easiest one is setting the strength of your manyBody method. According to the API:
If strength is specified, sets the strength accessor to the specified number or function, re-evaluates the strength accessor for each node, and returns this force. A positive value causes nodes to attract each other, similar to gravity, while a negative value causes nodes to repel each other, similar to electrostatic charge.
Since I don't have access to your data, this is a simplified demo. The first version has no strength, just like your code:
var width = 400;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}, {
name: "foofoo",
color: "yellow"
}, {
name: "foobar",
color: "blue"
}, {
name: "foobaz",
color: "green"
}, {
name: "barfoo",
color: "red"
}, {
name: "barbar",
color: "yellow"
}, {
name: "barbaz",
color: "blue"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 0,
"target": 3
}, {
"source": 1,
"target": 3
}, {
"source": 1,
"target": 4
}, {
"source": 2,
"target": 5
}, {
"source": 3,
"target": 6
}, {
"source": 1,
"target": 7
}, {
"source": 6,
"target": 8
}, {
"source": 0,
"target": 7
}, {
"source": 2,
"target": 6
}, {
"source": 3,
"target": 8
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", function(d) {
return d.r = 10;
})
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d) {
return d.color
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
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
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
The second version, however, has the strength set to a negative value:
.force("charge", d3.forceManyBody().strength(-500))
Here it is:
var width = 400;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}, {
name: "foofoo",
color: "yellow"
}, {
name: "foobar",
color: "blue"
}, {
name: "foobaz",
color: "green"
}, {
name: "barfoo",
color: "red"
}, {
name: "barbar",
color: "yellow"
}, {
name: "barbaz",
color: "blue"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 0,
"target": 3
}, {
"source": 1,
"target": 3
}, {
"source": 1,
"target": 4
}, {
"source": 2,
"target": 5
}, {
"source": 3,
"target": 6
}, {
"source": 1,
"target": 7
}, {
"source": 6,
"target": 8
}, {
"source": 0,
"target": 7
}, {
"source": 2,
"target": 6
}, {
"source": 3,
"target": 8
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody().strength(-500))
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", function(d) {
return d.r = 10;
})
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d) {
return d.color
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
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
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
I am a beginner at d3. One of the diagrams I find particularly helpful and fun is the d3 force diagram. I've been playing around with it, but for some reason I've had a lot of trouble with this particular data set. Specifically, I've been trying forever to get the labels to show up on my d3 force diagram, but it just won't work. Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("links.json", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 1});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){
return 3*d.size;
})
.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("svg:title")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
</script>
thanks so much for your help
edit: here is my links.json
{
"nodes": [
{"id": "AK","size": "1.414213562"},
{"id": "AL","size": "1.414213562"},
{"id": "AR","size": "1.414213562"},
{"id": "AZ","size": "2.828427125"}],
"links": [
{"source": "ABC","target": "CO","value": "A"},
{"source": "ABC","target": "CO","value": "A"},
{"source": "ABC","target": "CO","value": "A"},
{"source": "ABC","target": "FL","value": "A"}
}
Added Edit below :
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
graph = {
"nodes": [
{"id": "AK","size": "1.414213562"},
{"id": "AL","size": "1.414213562"},
{"id": "AR","size": "1.414213562"},
{"id": "AZ","size": "2.828427125"}],
"links": [
{"source": "AK","target": "AL","value": "A"},
{"source": "AL","target": "AZ","value": "A"},
{"source": "AL","target": "AR","value": "A"},
{"source": "AZ","target": "AR","value": "A"}]
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 1});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){
return 5;
})
.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("svg:title")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
var labels = svg.append("g")
.attr("class", "label")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 6)
.attr("dy", ".35em")
.style("font-size",10)
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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; });
labels
.attr("x", function(d) { return d.x; })
.attr("y", 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;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="600"></svg>
Original answer :
Your json is not "proper". First of all there is no source named ABC or targets named CO or FL.
Source and target values should be an id from the nodes. You cannot link something that is not defined.
Also you are not using the size and value attribute.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
graph = {
"nodes": [
{"id": "AK","size": "1.414213562"},
{"id": "AL","size": "1.414213562"},
{"id": "AR","size": "1.414213562"},
{"id": "AZ","size": "2.828427125"}],
"links": [
{"source": "AK","target": "AL","value": "A"},
{"source": "AL","target": "AZ","value": "A"},
{"source": "AL","target": "AR","value": "A"},
{"source": "AZ","target": "AR","value": "A"}]
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 1});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){
return 5;
})
.attr("fill", function(d) { return color(d.id); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("svg:title")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.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;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="600"></svg>