Public
Edited
Aug 23, 2022
Fork of Networks
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
margin = ({top: 30, right: 0, bottom: 0, left: 50})
Insert cell
visWidth = 100
Insert cell
visHeight = 100
Insert cell
Insert cell
color = d3.scaleSequential()
.domain([0, 100])
.interpolator(d3.interpolateReds)
.unknown("#eeeeee")
Insert cell
y = d3.scaleBand()
.domain(nodes.map(d => d.id))
.range([0, visHeight])
.paddingInner(0.1)
Insert cell
x = d3.scaleBand()
.domain(nodes.map(d => d.id))
.range([0, visWidth])
.paddingInner(0.1)
Insert cell
Insert cell
xAxis = g => g
.call(d3.axisTop(x).tickSize(0))
.call(g => g.selectAll(".domain").remove())
.append("text")
.attr("x", visWidth / 2)
.attr("y", -margin.top + 5)
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "hanging")
.text("Target");
Insert cell
yAxis = g => g
.call(d3.axisLeft(y).tickSize(0))
.call(g => g.selectAll(".domain").remove())
.append("text")
.attr("x", -margin.left)
.attr("y", visHeight / 2)
.attr("fill", "black")
.attr("dominant-baseline", "middle")
.attr("text-anchor", "start")
.text("Source")
Insert cell
legend({ color, title: "Weight"})
Insert cell
{
// set up

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// axes
g.append("g").call(yAxis);
g.append("g").call(xAxis);
// draw points

g.append('g')
.selectAll('g')
.data(links)
.join('rect')
.attr('x', d => x(d.target))
.attr('y', d => y(d.source))
.attr('width', x.bandwidth())
.attr('height', y.bandwidth())
.attr('fill', d => color(d.weight))
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
linkToWeight = new Map(links.map(link => [`${link.source}-${link.target}`, link.weight]))
Insert cell
Insert cell
nodeIds = nodes.map(d => d.id)
Insert cell
Insert cell
Insert cell
{
// set up

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// axes
g.append("g").call(yAxis);
g.append("g").call(xAxis);
// draw square in each cell
g.append('g')
.selectAll('rect')
.data(allLinks)
.join('rect')
.attr('class', 'square')
.attr('x', d => x(d.target))
.attr('y', d => y(d.source))
.attr('width', x.bandwidth())
.attr('height', y.bandwidth())
.attr('fill', d => color(d.weight));
return svg.node();
}
Insert cell
Insert cell
Insert cell
{
// set up
const margin = {top: 50, right: 25, bottom: 5, left: 25};
const visWidth = 150 - margin.left - margin.right;
const visHeight = 200 - margin.top - margin.bottom;

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// create scale

const y = d3.scalePoint()
.domain(nodes.map(d => d.id))
.range([0, visHeight]);
// create and add axes
const leftAxis = d3.axisLeft(y).tickSize(0);
const rightAxis = d3.axisRight(y).tickSize(0);
g.append("g")
.call(leftAxis)
.call(g => g.selectAll(".domain").remove())
.append("text")
.attr("x", -6)
.attr("y", -30)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Source");
g.append("g")
.attr('transform', `translate(${visWidth},0)`)
.call(rightAxis)
.call(g => g.selectAll(".domain").remove())
.append("text")
.attr("x", 6)
.attr("y", -30)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Target");
// draw lines

g.append('g')
.selectAll('line')
.data(links)
.join('line')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => y(d.source))
.attr('y2', d => y(d.target))
.attr('stroke', 'black')
.attr('stroke-width', d => lineWidth(d.weight))
.attr('stroke-linecap', 'round')
return svg.node();
}
Insert cell
Insert cell
// copy the nodes array
nodesForce = nodes.map(d => ({...d}))
Insert cell
nodesForcer = nodes.map(d => ({...d}))
Insert cell
nodes
Insert cell
links.map(d => ({...d}))
Insert cell
// copy the links array
linksForce = links.map(d => ({...d}))
Insert cell
Insert cell
{
// set up
const margin = {top: 5, right: 5, bottom: 5, left: 5};
const visWidth = 200 - margin.left - margin.right;
const visHeight = 200 - margin.top - margin.bottom;

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const nodeRadius = 10;
// create the simulation of the forces
// see the d3-force documentation for more detail about all of the forces that can be added
const simulation = d3.forceSimulation(nodesForcer)
// The first argument is a name for the force, the second argument is the force.
// pull nodes with links together
.force('link', d3.forceLink(linksForce).id(d => d.id))
// set mean position of nodes to be center of vis
.force('center', d3.forceCenter(visWidth / 2, visHeight / 2))
// make nodes repel each other
.force('manyBody', d3.forceManyBody().strength(-400))
// prevent overlapping circles
.force('collision', d3.forceCollide(nodeRadius));
// add lines for edges, we don't need to set their positions yet
const edges = g.append('g')
.selectAll('line')
.data(linksForce)
.join('line')
.attr('stroke', 'black')
.attr('fill', 'none')
.attr('stroke-width', 1);
// add a group for each node, we don't need to set their positions yet
const vertices = g.append('g')
.selectAll('g')
.data(nodesForce)
.join('g');

// add circle to each group
vertices.append('circle')
.attr('r', nodeRadius)
.attr('fill', 'steelblue');
// add text to each group
vertices.append('text')
.attr('fill', 'white')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.attr('font-family', 'sans-serif')
.attr('font-size', '16')
.text(d => d.id);
// update the positions of the vertices and edges on each simulation tick
simulation.on('tick', () => {
// move the group for each vertex
vertices
.attr('transform', d => `translate(${d.x},${d.y})`);
// set the starting and ending coordinates for each edge
edges
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
});

// this line is not needed off of observable
// it stops the current simulation when the cell is re-run
invalidation.then(() => simulation.stop());
return svg.node();
}
Insert cell
Insert cell
{
// margin convention
const margin = {top: 5, right: 5, bottom: 5, left: 5};
const visWidth = 200 - margin.left - margin.right;
const visHeight = 200 - margin.top - margin.bottom;

const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const nodeRadius = 10;
// create the simulation
const simulation = d3.forceSimulation(nodesForce)
// the first argument is a name for the force
// pull nodes with links together
.force('link', d3.forceLink(linksForce).id(d => d.id))
// set mean position of nodes to be center of vis
.force('center', d3.forceCenter(visWidth / 2, visHeight / 2))
// make nodes repel each other
.force('manyBody', d3.forceManyBody().strength(-400))
// prevent overlapping circles
.force('collision', d3.forceCollide(nodeRadius))
// defining a triangle from
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/marker-end
svg.append('defs')
.append('marker')
.attr('id', 'triangle')
.attr('viewBox', '0 0 10 10')
.attr('refX', 1)
.attr('refY', 5)
.attr('markerUnits', 'strokeWidth')
.attr('markerWidth', 7)
.attr('markerHeight', 7)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
.attr('fill', 'black')
// add polyline for edges, we don't need to set their positions yet.
// we are using a polyline here instead of a line so that it will
// be easier to place the arrow in the middle of the line
const edges = g.append('g')
.selectAll('polyline')
.data(linksForce)
.join('polyline')
.attr('stroke', 'black')
.attr('fill', 'none')
.attr('stroke-width', 1)
// put triangle in the middle of the polyline
.attr('marker-mid', 'url(#triangle)')
// add a group for each node, we don't need to set their positions yet
const vertices = g.append('g')
.selectAll('g')
.data(nodesForce)
.join('g');

// add circle to each group
vertices.append('circle')
.attr('r', nodeRadius)
.attr('fill', 'steelblue');
// add text to each group
vertices.append('text')
.attr('fill', 'white')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.attr('font-family', 'sans-serif')
.attr('font-size', '16')
.text(d => d.id);
// update the positions of the vertices and edges on each simulation tick
simulation.on('tick', () => {
vertices
.attr('transform', d => `translate(${d.x},${d.y})`);
edges.attr('points', d => {
// get the midpoint of the line
// this is where the triangle will go
const midX = (d.source.x + d.target.x) / 2;
const midY = (d.source.y + d.target.y) / 2;
// polylines are specified by space sepatated string of x,y coordinates
return `${d.source.x},${d.source.y} ${midX},${midY} ${d.target.x},${d.target.y}`
});
})

// this line is not needed off of observable
// it stops the current simulation when the cell is re-run
invalidation.then(() => simulation.stop());
return svg.node();
}
Insert cell
Insert cell
Insert cell
import {legend} from "@d3/color-legend"
Insert cell
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