Published unlisted
Edited
Nov 18, 2021
1 star
Insert cell
# Learning a Little D3

Insert cell
{
const svg = d3.create("svg");
svg.attr("height", 250);
svg.attr("width", 250);
yield svg.node();
svg
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", 50)
.attr("cy", 50)
.attr("r", 10)
.style("fill", "red")
.transition()
.delay((d, i) => i * 1000)
.duration(1000)
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y)
.style("fill", "blue");
}
Insert cell
{
const svg = d3.create("svg");
yield svg.node();
svg
.append("g")
.attr("id", "g1")
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y)
.attr("r", 10)
.style("fill", "blue");
svg
.append("g")
.attr("id", "g2")
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y)
.attr("r", 10)
.style("fill", "blue");
await Promises.delay(2500);
svg
.selectAll("g#g2")
.transition()
.duration(1500)
.attr("transform", "translate(30, 30)");
}
Insert cell
{
const svg = d3.create("svg").style("border", "1px solid black");
const sWidth = 300;
const sHeight = 300;
const padding = 30;
svg.attr("height", sHeight).attr("width", sWidth);
const xScale = d3
.scaleLinear()
.domain(d3.extent(myData, (d) => d.x))
.range([padding, sWidth - padding]);
const yScale = d3
.scaleLinear()
.domain(d3.extent(myData, (d) => d.y))
.range([sHeight - padding, padding]);
yield svg.node();
svg
.append("g")
.attr("id", "circleGroup")
.selectAll("circle")
.data(myData)
.enter()
.append("circle")
.attr("cx", (d, i) => xScale(d.x))
.attr("cy", (d, i) => yScale(d.y))
.attr("r", 10)
.style("fill", "blue");
svg
.append("g")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0, " + (sHeight - padding) + ")");
svg
.append("g")
.call(d3.axisLeft(yScale))
.attr("transform", "translate(" + padding + ",0)");
}
Insert cell
d3.extent(myData, (d) => d.x)
Insert cell
{
const scale = d3.scaleLinear().domain([0.0, 1.0]).range([100, 200]);
return scale(0.5);
}
Insert cell
{
const sWidth = 400;
const sHeight = 200;
const padding = 30;
const svg = d3.create("svg").attr("height", sHeight).attr("width", sWidth);
yield svg.node();
const xScale = d3
.scaleBand()
.domain(myData.map((d) => d.name))
.range([padding, sWidth - padding])
.padding(0.1);
const yScale = d3
.scaleLinear()
.domain(d3.extent(myData, (d) => d.y))
.range([sHeight - padding, padding]);
svg
.selectAll("rect")
.data(myData)
.enter()
.append("rect")
.attr("x", (d) => xScale(d.name))
.attr("y", (d) => yScale(d.y))
.attr("height", (d) => yScale(0) - yScale(d.y))
.attr("width", xScale.bandwidth())
.style("fill", "black");
svg
.append("g")
.attr("id", "bandScale")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0," + (sHeight - padding) + ")");
}
Insert cell
myData = [
{ x: 100, y: 50, name: "Harry Potter" },
{ x: 10, y: 35, name: "Hermione" },
{ x: 50, y: 10, name: "Tom Riddle" }
]
Insert cell
d3 = require("d3@6")
Insert cell
{
const svg = d3.create("svg").attr("width", 300).attr("height", 300);
yield svg.node();
var xScale = d3
.scaleLinear()
.range([20, 280])
.domain(d3.extent(dataset, (d) => d.x1));
var yScale = d3
.scaleLinear()
.range([280, 20])
.domain(d3.extent(dataset, (d) => d.y1));
svg
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.x1))
.attr("cy", (d) => yScale(d.y1))
.attr("r", (d) => d.radius)
.style("fill", "black");
svg
.selectAll("circle")
.data(dataset)
.on("click", (event, d) => {
d3.select(event.target).transition().duration(3000).style("fill", "red");
});
}
Insert cell
d3.extent(near_earth, (d) => d.discovery_date)
Insert cell
["moid_au", "q_au_1", "q_au_2", "i_deg"].map((d) =>
d3.extent(near_earth, (d2) => parseFloat(d2[d]))
)
Insert cell
<div id="selection"></div>
Insert cell
{
function brushed(event) {
console.log(event.selection);
d3.select("#selection").text(event.selection);
}

const myWidth = 512;
const myHeight = 512;

const svg = d3.create("svg").attr("width", myWidth).attr("height", myHeight);
yield svg.node();

var brush = d3
.brush()
.extent([
[0.0, 0.0],
[512, 512]
])
.on("brush", brushed);
svg.append("g").attr("class", "brush").call(brush);
}
Insert cell
{
const svg = d3.create("svg").style("border", "1px solid black");
const sWidth = 300;
const sHeight = 300;
const padding = 30;
svg.attr("height", sHeight).attr("width", sWidth);
const xScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_1)))
.range([padding, sWidth - padding]);
const yScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_2)))
.range([sHeight - padding, padding]);
yield svg.node();
var myCircles = svg
.append("g")
.attr("id", "circleGroup")
.selectAll("circle")
.data(near_earth)
.enter()
.append("circle")
.attr("cx", (d, i) => xScale(parseFloat(d.q_au_1)))
.attr("cy", (d, i) => yScale(parseFloat(d.q_au_2)))
.attr("r", 5)
.style("fill", "blue");
svg
.append("g")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0, " + (sHeight - padding) + ")");
svg
.append("g")
.call(d3.axisLeft(yScale))
.attr("transform", "translate(" + padding + ",0)");
var brush = d3
.brush()
.extent([
[padding, padding],
[sWidth - padding, sHeight - padding]
])
.on("brush", (event) => {
const newBoundsX = [
xScale.invert(event.selection[0][0]),
xScale.invert(event.selection[1][0])
];
const newBoundsY = [
yScale.invert(event.selection[1][1]),
yScale.invert(event.selection[0][1])
];
myCircles.style("fill", "blue");
myCircles
.filter(
(d) =>
parseFloat(d.q_au_1) >= newBoundsX[0] &&
parseFloat(d.q_au_1) <= newBoundsX[1] &&
parseFloat(d.q_au_2) >= newBoundsY[0] &&
parseFloat(d.q_au_2) <= newBoundsY[1]
)
.transition()
.duration(0)
.style("fill", "black");
});
svg.append("g").attr("class", "brush").call(brush);
}
Insert cell
<style>
.baseCircle { fill: blue; }
.highlighted { fill: black; }
</style>
Insert cell
{
const svg = d3.create("svg").style("border", "1px solid black");
const sWidth = 300;
const sHeight = 300;
const padding = 30;
svg.attr("height", sHeight).attr("width", sWidth);
const xScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_1)))
.range([padding, sWidth - padding]);
const yScale = d3
.scaleLog()
.domain(d3.extent(near_earth, (d) => parseFloat(d.q_au_2)))
.range([sHeight - padding, padding]);
yield svg.node();
var myCircles = svg
.append("g")
.attr("id", "circleGroup")
.selectAll("circle")
.data(near_earth)
.enter()
.append("circle")
.attr("cx", (d, i) => xScale(parseFloat(d.q_au_1)))
.attr("cy", (d, i) => yScale(parseFloat(d.q_au_2)))
.classed("baseCircle", true)
.attr("r", 5);
svg
.append("g")
.call(d3.axisBottom(xScale))
.attr("transform", "translate(0, " + (sHeight - padding) + ")");
svg
.append("g")
.call(d3.axisLeft(yScale))
.attr("transform", "translate(" + padding + ",0)");
var brush = d3
.brush()
.extent([
[padding, padding],
[sWidth - padding, sHeight - padding]
])
.on("brush", (event) => {
const newBoundsX = [
xScale.invert(event.selection[0][0]),
xScale.invert(event.selection[1][0])
];
const newBoundsY = [
yScale.invert(event.selection[1][1]),
yScale.invert(event.selection[0][1])
];
myCircles.classed(
"highlighted",
(d) =>
parseFloat(d.q_au_1) >= newBoundsX[0] &&
parseFloat(d.q_au_1) <= newBoundsX[1] &&
parseFloat(d.q_au_2) >= newBoundsY[0] &&
parseFloat(d.q_au_2) <= newBoundsY[1]
);
});
svg.append("g").attr("class", "brush").call(brush);
}
Insert cell
{
const points = d3.range(256).map((d) => {
return { x: d / 16, y: Math.sin(d / 16) };
});
const myHeight = 300;
const myWidth = 300;
const padding = 30;
const svg = d3.create("svg").attr("height", myHeight).attr("width", myWidth);
var xScale = d3
.scaleLinear()
.domain(d3.extent(points, (d) => d.x))
.range([padding, myWidth - padding]);
var yScale = d3
.scaleLinear()
.domain(d3.extent(points, (d) => d.y))
.range([myHeight - padding, padding]);
var line = d3
.line()
.x((d) => xScale(d.x))
.y((d) => yScale(d.y));
svg
.selectAll("circle")
.data(points)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.x))
.attr("cy", (d) => yScale(d.y))
.style("fill", "black")
.attr("r", 1.5);
svg
.append("path")
.attr("d", line(points))
.style("fill", "none")
.style("stroke", "black");
yield svg.node();
}
Insert cell
{
const binnedValues = d3.bin().value((d) => parseFloat(d.moid_au))(near_earth);
const myWidth = 300;
const myHeight = 300;
const padding = 20;
const svg = d3.create("svg").attr("height", myHeight).attr("width", myWidth);
yield svg.node();
var xScale = d3
.scaleBand()
.range([padding, myWidth - padding])
.domain(d3.range(binnedValues.length))
.padding(0.15);
var yScale = d3
.scaleLinear()
.range([myHeight - padding, padding])
.domain([0, d3.max(binnedValues, (d) => d.length)]);
svg
.selectAll("rect")
.data(binnedValues)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", (d, i) => yScale(d.length))
.attr("width", xScale.bandwidth)
.attr("height", (d, i) => yScale(0) - yScale(d.length));
}
Insert cell
{
const amor = d3.group(near_earth, (d) => d.orbit_class).get("Amor");
const myWidth = 400;
const myHeight = 400;
const padding = 40;
const svg = d3.create("svg").attr("width", myWidth).attr("height", myHeight);
yield svg.node();
const maxQ1 = d3.max(amor, (d) => parseFloat(d.q_au_1));
const maxQ2 = d3.max(amor, (d) => parseFloat(d.q_au_2));
var xScale = d3
.scaleLinear()
.domain([-maxQ1, maxQ1])
.range([padding, myWidth - padding]);
var yScale = d3
.scaleLinear()
.domain([-maxQ2, maxQ2])
.range([myHeight - padding, padding]);
svg
.append("g")
.attr("class", "orbits")
.selectAll("ellipse")
.data(amor)
.enter()
.append("ellipse")
.attr("cx", myWidth / 2)
.attr("cy", myHeight / 2)
.attr("rx", (d) => xScale(parseFloat(d.q_au_1)) / 2)
.attr("ry", (d) => yScale(parseFloat(d.q_au_2)) / 2)
.style("fill", "none")
.style("stroke", "black")
.on("mouseover", (event, d) => {
console.log(d);
});
}
Insert cell
near_earth = d3.json("https://data.nasa.gov/resource/2vr3-k9wn.json")
Insert cell
Insert cell
dataset = [
{ x1: 1.0, y1: 3.0, x2: 3.5, y2: 2.0, radius: 10 },
{ x1: 2.5, y1: 2.0, x2: 0.75, y2: 3.5, radius: 5 },
{ x1: 3.1, y1: 0.6, x2: 4.1, y2: 3.4, radius: 25 }
]
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