Public
Edited
Apr 26, 2024
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sourcesChart = () => {
const svg = d3.create("svg")
.attr("width", chartwidth + margin.left + margin.right)
.attr("height", chartheight + margin.top + margin.bottom);

svg.append("style").html(css);

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

g.append("g")
.call(xAxisGenerator);

g.append("g")
.call(yAxisGenerator);

const lineG = g.selectAll(".line-g")
.data(lineData)
.join("g")
.attr("class", "line-g");

lineG.append("path")
.attr("d", d => line(d[1]))
.attr("fill", "none")
.attr("stroke", d => colors[d[0]])
.attr("stroke-width", 2);

const labelG = g.selectAll(".label-g")
.data(labelData)
.join("g")
.attr("class", "label-g")
.attr("transform", d => `translate(${[chartwidth, d.y]})`);

labelG.append("text")
.attr("fill", d => colors[d.variable])
.attr("x", 4)
.attr("y", 4)
.text(d => d.label)
return svg.node();
}
Insert cell
totalChart = () => {
const svg = d3.create("svg")
.attr("width", chartwidth + margin.left + margin.right)
.attr("height", chartheight + margin.top + margin.bottom);

svg.append("style").html(css);

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

g.append("g")
.call(xAxisGenerator);

g.append("g")
.call(yAxisGenerator);

const lineG = g.append("g")
.datum(dataTotal)
.attr("class", "line-g");

lineG.append("path")
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 2);

const labelG = g.append("g")
.datum(dataTotal)
.attr("class", "label-g")
.attr("transform", d => `translate(${[chartwidth, y(last(d).value)]})`)
labelG.append("text")
.attr("x", 4)
.attr("y", 4)
.text("Total CO2")
return svg.node();
}
Insert cell
Insert cell
colors = ({
"FF": "#e3d61c",
"BB": "#ee242a",
"NEE": "#68c6ba",
"OCN": "#707cc2"
})
Insert cell
css = `
.axis .domain {
display: none;
}
.axis .tick text {
fill: #808080;
font-family: ${franklinLight};
font-size: 14px;
}
.axis.x-axis .tick line {
stroke: #d5d5d5;
}
.axis.y-axis .tick line {
stroke: #e9e9e9;
stroke-width: 0.5px;
}
.axis.y-axis .tick.baseline line {
stroke: #aaaaaa;
stroke-width: 1px;
}

.label-g text {
font-family: ${franklinLight};
}
`
Insert cell
Insert cell
xAxisGenerator = g => {
const months = ["Jan.", "Feb.", "March", "April", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."];
const tickValues = d3.range(1, 13)
.map(d => new Date(`2021-${d.toString().padStart(2, 0)}-01`))
.filter((d, i) => width <= 250 ? i % 6 === 0 : width <= 350 ? i % 3 === 0 : width <= 550 ? i % 2 === 0 : 1);
const generator = d3.axisBottom(x)
.tickFormat(d => months[d.getUTCMonth()])
.tickSize(10)
.tickSizeOuter(0)
.tickValues(tickValues);
g
.attr("class", "axis x-axis")
.attr("transform", `translate(0, ${chartheight})`)
.call(generator);

return g
}
Insert cell
yAxisGenerator = g => {
const generator = d3.axisLeft(y)
.tickFormat((d, i, e) => `${d === 0 ? 0 : d > 0 ? `+${d.toFixed(1)}` : d.toFixed(1)}${i === e.length - 1 ? " ppm CO2" : ""}`)
.tickSize(chartwidth + 10)
.tickValues(d3.range(-5, 7.5, 2.5))
g
.attr("class", "axis y-axis")
.attr("transform", `translate(${chartwidth})`)
.call(generator);

const tickTopWidth = 58;
const tick = g.selectAll(".tick")
.classed("baseline", d => !d)

tick.select("text")
.attr("x", function(d, i, e) {
const currX = +d3.select(this).attr("x");
return i === e.length - 1 ? currX + tickTopWidth : currX;
})

tick.select("line")
.attr("x2", function(d, i, e) {
const currX2 = +d3.select(this).attr("x2");
return i === e.length - 1 ? currX2 + tickTopWidth : currX2;
})

return g;
}
Insert cell
line = d3.line()
.x(d => x(d.date))
.y(d => y(d.value))
Insert cell
Insert cell
x = d3.scaleTime(d3.extent(flatData, d => d.date), [0, chartwidth])
Insert cell
y = d3.scaleLinear(d3.extent(flatData, d => d.value), [chartheight, 0])
Insert cell
Insert cell
margin = ({left: 40, right: 84, top: 8, bottom: 25})
Insert cell
chartwidth = Math.min(width, 640) - margin.left - margin.right
Insert cell
chartheight = Math.max(300, chartwidth * 9 / 16) - margin.top - margin.bottom
Insert cell
Insert cell
flatData = lineData.map(d => d[1]).flat()
Insert cell
lineData = Object.keys(data[0])
.slice(1)
.map(variable => {
return [variable, JSON.parse(JSON.stringify(data)).map(d => ({date: toDate(d.Date), value: d[variable]}))]
})
Insert cell
labelData = {
const labels = ({
FF: "Fossil fuels",
BB: "Fires",
OCN: "Ocean",
NEE: "Land"
})
const Y = lineData.map(d => y(last(d[1]).value))
const dodged = dodge(Y.slice(), 16);
return lineData.map((d, i) => {
return {
variable: d[0],
label: labels[d[0]],
y0: Y[i],
y: dodged[i]
}
})
}
Insert cell
data = (await FileAttachment("GEOS_tagged_CO2_2021.daily.csv").csv({ typed: true }))
Insert cell
dataTotal = data
.map(d => {
return {
date: toDate(d.Date),
value: d.FF + d.BB + d.OCN + d.NEE
}
})
Insert cell
Insert cell
toDate = d => {
const s = d.toString();
return new Date(`${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`);
}
Insert cell
toDateString = d => {
const s = d.toString();
return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;
}
Insert cell
last = arr => arr[arr.length - 1]
Insert cell
Insert cell
import { dodge } from "@climatelab/label-dodging"
Insert cell
import { franklinLight } from "@climatelab/fonts"
Insert cell
import { toc } from "@climatelab/toc"
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