Published
Edited
Jul 8, 2022
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
deckgl = {
const props = {
container,
initialViewState: {
target: [width / 2, height / 2, 0],
zoom: 0
},
views: new deck.OrthographicView(),
controller: true,
layers: [grid, xAxis, yAxis, circles],
getTooltip: ({object}) => object && object.name
};
// Avoid creating multiple instances
const prevDeckInstance = this;
if (prevDeckInstance) {
prevDeckInstance.setProps(props);
return prevDeckInstance;
}
return new deck.DeckGL(props);
}
Insert cell
currentData = dataAt(date)
Insert cell
x = d3.scaleLog([200, 1e5], [margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear([14, 86], [height - margin.bottom, margin.top])
Insert cell
radius = d3.scaleSqrt([0, 5e8], [0, width / 24])
Insert cell
color = {
function toRgb(str) {
const c = d3.color(str);
return [c.r, c.g, c.b];
}
return d3.scaleOrdinal(data.map(d => d.region), d3.schemeCategory10.map(toRgb)).unknown([0, 0, 0])
}
Insert cell
circles = new deck.ScatterplotLayer({
id: 'circle',
data: currentData,
stroked: true,
getPosition: d => [x(d.income), y(d.lifeExpectancy)],
getRadius: d => radius(d.population),
getFillColor: d => color(d.region),
getLineColor: [0, 0, 0],
getLineWidth: 1,
lineWidthUnits: 'pixels',
radiusMinPixels: 2,
pickable: true,
autoHighlight: true,
highlightColor: [255, 255, 255, 100]
})
Insert cell
xAxis = new deck.TextLayer({
id: 'x-grid-label',
data: x.ticks(2),
getText: t => `$${t.toString()}`,
getPosition: t => [x(t), y.range()[0]],
getPixelOffset: [0, 20],
getSize: 12,
getColor: [0, 0, 0]
})
Insert cell
yAxis = new deck.TextLayer({
id: 'y-grid-label',
data: y.ticks(),
getText: t => t.toString(),
getPosition: t => [x.range()[0] - 20, y(t)],
getTextAnchor: 'end',
getSize: 12,
getColor: [0, 0, 0]
})
Insert cell
grid = [
new deck.PathLayer({
id: 'x-grid',
data: x.ticks(2),
getPath: t => [[x(t), y.range()[0]], [x(t), y.range()[1]]],
getWidth: 0,
widthMinPixels: 1,
getColor: [100, 100, 100],
opacity: 0.1
}),
new deck.PathLayer({
id: 'y-grid',
data: y.ticks(),
getPath: t => [[x.range()[0], y(t)], [x.range()[1], y(t)]],
getWidth: 0,
widthMinPixels: 1,
getColor: [100, 100, 100],
opacity: 0.1
})
]
Insert cell
function dataAt(date) {
return data.map(d => ({
name: d.name,
region: d.region,
income: valueAt(d.income, date),
population: valueAt(d.population, date),
lifeExpectancy: valueAt(d.lifeExpectancy, date)
}));
}
Insert cell
function valueAt(values, date) {
const i = bisectDate(values, date, 0, values.length - 1);
const a = values[i];
if (i > 0) {
const b = values[i - 1];
const t = (date - a[0]) / (b[0] - a[0]);
return a[1] * (1 - t) + b[1] * t;
}
return a[1];
}
Insert cell
data = (await FileAttachment("nations.json").json())
.map(({name, region, income, population, lifeExpectancy}) => ({
name,
region,
income: parseSeries(income),
population: parseSeries(population),
lifeExpectancy: parseSeries(lifeExpectancy)
}))
Insert cell
interval = d3.utcMonth // interval between animation frames
Insert cell
dates = interval.range(
d3.min(data, d => {
return d3.min([
d.income[0],
d.population[0],
d.lifeExpectancy[0]
], ([date]) => date);
}),
d3.min(data, d => {
return d3.max([
d.income[d.income.length - 1],
d.population[d.population.length - 1],
d.lifeExpectancy[d.lifeExpectancy.length - 1]
], ([date]) => date);
})
)
Insert cell
function parseSeries(series) {
return series.map(([year, value]) => [new Date(Date.UTC(year, 0, 1)), value]);
}
Insert cell
bisectDate = d3.bisector(([date]) => date).left
Insert cell
margin = ({top: 20, right: 40, bottom: 35, left: 60})
Insert cell
height = 600
Insert cell
d3 = require("d3@6.7.0/dist/d3.min.js")
Insert cell
Insert cell
import {Scrubber} from "@mbostock/scrubber"
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