Published
Edited
Mar 30, 2021
4 stars
Also listed in…
D3 Class
Insert cell
Insert cell
Insert cell
sankeyChart(graph)
Insert cell
Insert cell
Insert cell
graph.links.map(d3.sankeyLinkHorizontal())
Insert cell
Insert cell
Insert cell
createGraph(sampleEnergyFlow)
Insert cell
sampleEnergyFlow = d3.csvParse(
`source,target,value
Coal,Fossil Fuels,25
Coal,Electricity,25
Natural Gas,Fossil Fuels, 20
Oil,Fossil Fuels, 15
Fossil Fuels,Energy, 60
Electricity,Energy, 25`,
d3.autoType
)
Insert cell
Insert cell
Insert cell
Insert cell
showTable(sampleChordData)
Insert cell
Insert cell
Insert cell
feedBack
Insert cell
Insert cell
Insert cell
Insert cell
showTable(migrationChordData)
Insert cell
sankeyChart(migrationSankeyData)
Insert cell
migrationSankeyData
Insert cell
chordChart(migrationChordData)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
N = 25
Insert cell
MaxRadius = 20
Insert cell
Insert cell
md`### Step 1: Getting started with forceSimulation
No Force`
Insert cell
{
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
const myNodes = nodes.map(a => Object.assign({}, a));
const simulation = d3
.forceSimulation()
.nodes(myNodes)
.on('tick', showNodes);
return svgNode;
function showNodes() {
svg
.selectAll("circle")
.data(myNodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", d => d.radius)
.style("stroke", "lightgrey");
}
}
Insert cell
md`### Centering
The centering force translates nodes uniformly so that the mean position of all nodes (the center of mass if all nodes have equal weight) is at the given position ⟨x,y⟩. This force modifies the positions of nodes on each application; it does not modify velocities. `
Insert cell
{
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
const myNodes = nodes.map(a => Object.assign({}, a));
const simulation = d3
.forceSimulation()
.nodes(myNodes)
.force('center', d3.forceCenter(width / 2, height / 2))
.on('tick', showNodes);
return svgNode;
function showNodes() {
svg
.selectAll("circle")
.data(myNodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", d => d.radius)
.style("stroke", "lightgrey");
}
}
Insert cell
md`### Many-Body force
The many-body (or n-body) force applies mutually amongst all nodes. It can be used to simulate gravity (attraction) if the strength is positive, or electrostatic charge (repulsion) if the strength is negative.

Many-body force is global: every node affects every other node, even if they are not connected.`
Insert cell
viewof manyBodyStrength = slider({min:-10,max:10,step: 1, value: 0})
Insert cell
node = {
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
const myNodes = nodes.map(a => Object.assign({}, a));
const simulation = d3
.forceSimulation()
.nodes(myNodes)
.force('center', d3.forceCenter(width / 2, height / 2))
.force('charge', d3.forceManyBody().strength(manyBodyStrength))
.on('tick', showNodes);
svgNode.myNodes = myNodes;
return svgNode;
function showNodes() {
svg
.selectAll("circle")
.data(myNodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", d => d.radius)
.style("stroke", "lightgrey");
}
}
Insert cell
node.myNodes
Insert cell
md`### Collision avoidance
The collision force treats nodes as circles with a given radius, rather than points, and prevents nodes from overlapping. More formally, two nodes a and b are separated so that the distance between a and b is at least radius(a) + radius(b).`
Insert cell
{
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
const myNodes = nodes.map(a => Object.assign({}, a));
const simulation = d3
.forceSimulation()
.nodes(myNodes)
.force('center', d3.forceCenter(width / 2, height / 2))
.force('charge', d3.forceManyBody().strength(manyBodyStrength))
.force('collision', d3.forceCollide().radius(d => d.radius))
.on('tick', showNodes);
return svgNode;
function showNodes() {
svg
.selectAll("circle")
.data(myNodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", d => d.radius)
.style("stroke", "lightgrey");
}
}
Insert cell
md`### Positioning
The x- and y-positioning forces push nodes towards a desired position along the given dimension with a configurable strength. The strength of the force is proportional to the one-dimensional distance between the node’s position and the target position. While these forces can be used to position individual nodes, they are intended primarily for global forces that apply to all (or most) nodes.`
Insert cell
{
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
const myNodes = nodes.map(a => Object.assign({}, a));
const positions = [100, 400, 700];
const simulation = d3
.forceSimulation()
.nodes(myNodes)
//.force('center', d3.forceCenter(width / 2, height / 2))
.force('charge', d3.forceManyBody().strength(manyBodyStrength))
.force('collision', d3.forceCollide().radius(d => d.radius))
.force('x', d3.forceX().x((d, i) => positions[i % 3]))
.force('y', d3.forceY().y((d, i) => height / 2))
.on('tick', showNodes);
return svgNode;
function showNodes() {
svg
.selectAll("circle")
.data(myNodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", d => d.radius)
.style("stroke", "lightgrey");
}
}
Insert cell
{
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
const myNodes = nodes.map(a => Object.assign({}, a));
const positions = [100, 400, 700];
const simulation = d3
.forceSimulation()
.nodes(myNodes)
.force('center', d3.forceCenter(width / 2, height / 2))
.force('charge', d3.forceManyBody().strength(manyBodyStrength))
.force('collision', d3.forceCollide().radius(d => d.radius))
.force('radial', d3.forceRadial(125, width / 2, height / 2))
.on('tick', showNodes);
return svgNode;
function showNodes() {
svg
.selectAll("circle")
.data(myNodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", d => d.radius)
.style("stroke", "lightgrey");
}
}
Insert cell
{
const height = 300;
const svgNode = DOM.svg(width, height);
const svg = d3.select(svgNode);
mutable feedBack = [];
const simulation = d3
.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-100))
.force("link", d3.forceLink().id(d => d.name));
simulation.nodes(sampleForceGraph.nodes).on("tick", function() {
svg
.selectAll("circle")
.data(sampleForceGraph.nodes)
.join("circle")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("r", 4)
.style("stroke", "lightgrey");
svg
.selectAll("line")
.data(sampleForceGraph.links)
.join("line")
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
.style("stroke", "lightgrey");
});
simulation.force("link").links(sampleForceGraph.links);

return svgNode;
}
Insert cell
mutable feedBack = ""
Insert cell
sampleForceGraph = createGraph(sampleEnergyFlow)
Insert cell
sampleEnergyFlow
Insert cell
md`### Functions`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`### External Libraries and Imports`
Insert cell
import { Table } from "@observablehq/table"
Insert cell
import { radio, select, slider, button, checkbox } from "@jashkenas/inputs"
Insert cell
import { soFetch } from '@alecglassford/so-fetch'
Insert cell
xlsx = require('https://bundle.run/xlsx')
Insert cell
d3 = require("d3@6", "d3-sankey@0.12")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more