Public
Edited
Mar 13, 2023
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colors = ({
"China": {
fill: "#d35f4f",
text: "#d35f4f",
},
"Europe": {
fill: "#808080",
text: "#808080",
},
"United States": {
fill: "#5784c5",
text: "#5784c5"
}
})
Insert cell
Insert cell
line = d3.line()
.x(d => x(d.year))
.y(d => y(d.annual_percap))
Insert cell
xAxis = g => {
const generator = d3.axisBottom(x)
.tickFormat(d => d)
.tickValues(d3.range(1950, 2075, 25))
.tickSize(20)

const axis = g
.attr("transform", `translate(0, ${chartheight})`)
.call(generator);

axis.select(".domain").remove();

const tick = g.selectAll(".tick")

tick.select("text")
.attr("font-family", franklinLight)
.attr("font-size", 14);
tick.select("line")
.attr("stroke", "#ccc");

return axis;
}
Insert cell
Insert cell
x = d3.scaleLinear()
.domain(yearExtent)
.range([0, chartwidth])
Insert cell
y = d3.scaleLinear()
.domain([0, 24])
.range([chartheight, 0])
Insert cell
Insert cell
breakpoint = 480
Insert cell
margin = ({left: 29, right: 54, top: 15, bottom: 35})
Insert cell
basewidth = width <= breakpoint ? width : width / 2 - 24
Insert cell
chartwidth = basewidth - margin.left - margin.right
Insert cell
minheight = 250
Insert cell
maxheight = 500;
Insert cell
chartheight = Math.max(Math.min(basewidth * 1/2, maxheight), minheight) - margin.top - margin.bottom
Insert cell
Insert cell
data = ["annual", "annual_percap"].map(prop => {
const lineData = grouped
.map(([entity, entries]) => {
return entries
.map(d => {
return {
prop,
entity,
year: d.year,
value: d[prop]
}
})
});

const y = d3.scaleLinear()
.domain([0, prop === "annual" ? 12e9 : 24])
.range([chartheight, 0]);

const tickFormat = (d, i, e) => prop === "annual" ? `${d / 1e9}${i === e.length - 1 ? " billion tons" : ""}` : `${d}${i === e.length - 1 ? " tons" : ""}`;

const tickValues = prop === "annual" ? d3.range(0, 15e9, 3e9) : d3.range(0, 30, 6);

const yAxisGenerator = g => {
const generator = d3.axisLeft(y)
.tickFormat((d, i, e) => tickFormat(d, i, e))
.tickSize(chartwidth + (prop === "annual" ? 12 : 10))
.tickValues(tickValues)

const axis = g.call(generator)
.attr("transform", `translate(${chartwidth})`);

axis.select(".domain").remove();
const ticks = axis.selectAll(".tick");
const offset = prop === "annual" ? 68 : 29;
ticks.select("text")
.attr("fill", "#808080")
.attr("font-family", franklinLight)
.attr("font-size", 14)
.attr("x", (d, i, e) => {
const currX = +d3.select(e[i]).attr("x");
return currX + (i === e.length - 1 ? offset : 0);
});

ticks.select("line")
.attr("stroke", "#e2e2e2")
.attr("stroke-dasharray", [2, 2])
.attr("x2", (d, i, e) => {
const currX2 = +d3.select(e[i]).attr("x2");
return currX2 + (i === e.length - 1 ? offset : 0);
});

return axis;
}
const line = d3.line()
.x(d => x(d.year))
.y(d => y(d.value));

return {
prop,
lineData,
y,
yAxisGenerator,
line,
tickFormat,
tickValues: prop === "annual" ? d3.range(0, 15e9, 3e9) : d3.range(0, 30, 6),
labelsData: clone(lineData).map((entries) => ({entity: entries[0].entity, position: y(entries[entries.length - 1].value)}))
}

});
Insert cell
grouped = d3
.groups(emissions.filter(d => d.pop), d => d.entity)
.filter(d => Object.keys(colors).includes(d[0]))
Insert cell
yearExtent = [1950, 2050]
Insert cell
clone = data => JSON.parse(JSON.stringify(data))
Insert cell
Insert cell
import { franklinLight } from "1dec0e3505bd3624"
Insert cell
import { toc } from "@harrystevens/toc"
Insert cell
import { emissions } from "10bea703b4f60e4d"
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