Force layout graphs are a useful tool for understanding large amounts of data, and the relationships between data points within the data. This type of graph consists of elements represented by squares or circles, and the data is arranged by simulating forces pushing and pulling on them. This allows users to easily see the relationships between elements and the most significant element within the dataset.
This guide will demonstrate how to use the d3-force package to create force layout graphs. The guide assumes you have basic understanding of D3.js.
The unique aspect of force layout charts is the ability to simulate pushing and pulling of nodes within geometric constraints, or forces. Forces are functions that allow you to control the size and position of nodes in relation to each other and the simulation. There are, broadly, five categories of forces in d3-force:
charge
and a negative force that pulls nodes closer together is named gravity
.One way to easily get started experimenting with d3-force is using Observable HQ. This website offers an interactive editing experience that doesn't require you to install anything.
Start by forking this simple force layout observable HQ sample. This sample is based on code and data from Daniel Stern's GitHub Repository.
The first section of code imports D3.js and some sample data required to bind to force layout chart. The main part of the chart code is listed below:
1{
2 const simulation = d3.forceSimulation(data.nodes)
3 .force('charge', d3.forceManyBody().strength(-100))
4 .force('link', d3.forceLink(data.links).id(d => d.id)
5 .distance(50))
6 .force('center', d3.forceCenter(300, 300))
7
8 const svg = d3.create("svg").attr("viewBox", [0, 0
9 , 600, 600]);
10
11 const node = svg.selectAll('circle')
12 .data(data.nodes)
13 .enter()
14 .append('circle')
15 .attr('r', 25)
16 .attr('fill', 'blue');
17
18 const link = svg
19 .selectAll('path.link')
20 .data(data.links)
21 .enter()
22 .append('path')
23 .attr('stroke', 'black')
24 .attr('fill', 'none');
25
26 const lineGenerator = d3.line();
27
28 simulation.on('tick', () => {
29 node.attr('cx', d => d.x);
30 node.attr('cy', d => d.y);
31 link.attr('d', d => lineGenerator([
32 [d.source.x, d.source.y],
33 [d.target.x, d.target.y]])
34 )
35 });
36 return svg.node();
37}
The input data has two nodes, and these two nodes have three forces that act upon them:
charge
force is a many-body force that pushes nodes further apart from each other. It is called "charge" because it represents a physical electrical charge that creates repulsion between nodes.link
force illustrates direct links between nodes. This force can read the input data to alter the distance between nodes.center
force pushes nodes to the middle of the viewport. Without this force, nodes would be bunched up on the corner.Finally, the simulation.on('tick', callback)
function is called to tell D3.js how to position your nodes and links after each simulation tick. The rest of the code snippet is standard D3.js code and won't be discussed in this guide for brevity.
In the previous section you have created a simple force layout chart with a few forces applied. Now you can experiment by changing the values of each force to get the desired effect.
Changing the strength of the charge
force influences how much repulsion occurs between all nodes. A greater negative number pushes nodes further apart. A positive strength number pulls nodes closer together.
The center
force accepts an x and y position. You can change them to something like [200, 100]
to move the center of the mass of nodes closer to the top.
Force layout charts are extremely useful when you have a large amount of data and want to understand some insights about it. You can build on this knowledge by looking at more sophisticated force layout graphs on Observable HQ.