Public
Edited
Oct 4, 2024
1 fork
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
legend = () => {
const wrapper = d3.create("div")
.attr("class", "legend");

const item = wrapper.selectAll(".legend-item")
.data(Object.keys(colors).map(key => ({key, color: colors[key]})))
.join("div")
.attr("class", "legend-item");

item.append("div")
.attr("class", "legend-swatch")
.style("background", d => d.color);

item.append("div")
.attr("class", "legend-label")
.text(d => d.key);

return wrapper.node();
}
Insert cell
chart = () => {
const svg = d3.create("svg")
.attr("width", chartwidth + margin.left + margin.right)
.attr("height", chartheight + margin.top + margin.bottom)
.style("overflow", "visible");

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

g.append("g")
.attr("class", "axis x-axis")
.attr("transform", `translate(0, ${chartheight})`)
.call(xAxisGenerator);

g.append("g")
.attr("class", "areas")
.selectAll(".area")
.data(series)
.join("path")
.attr("class", "area")
.attr("d", areaGenerator)
.attr("fill", d => colors[d.key])

g.append("g")
.attr("class", "axis y-axis")
.attr("transform", `translate(${chartwidth})`)
.call(yAxisGenerator);
return svg.node();
}
Insert cell
Insert cell
css = `
.legend {
font-family: ${franklinLight};
margin-top: -16px;

.legend-item, .legend-swatch, .legend-label {
display: inline-block;
}

.legend-item {
margin-right: 16px;
}

.legend-swatch {
margin-right: 4px;
width: 12px;
height: 12px;

&:last-of-type {
margin-right: 0px;
}
}
}

.axis {
.domain {
display: none;
}
.tick {
line {
stroke: rgb(118, 118, 118);
}
text {
font-family: ${franklinLight};
font-size: 14px;
}
}

&.x-axis {
.tick {
&:first-of-type {
text {
text-anchor: start;
}
}
&:last-of-type {
text {
text-anchor: end;
}
}
}
}

&.y-axis {
.tick {
line {
stroke: rgba(0, 0, 0, 0.1);
mix-blend-mode: multiply;
}
}
}
}
`
Insert cell
colors = ({
"Coal": "rgb(51, 51, 51)",
"Wind & solar": "rgb(54, 167, 92)",
"Gas": "rgb(234, 178, 70)",
"Other": "rgb(250, 240, 227)"
})
Insert cell
Insert cell
areaGenerator = d3.area()
.x(d => x(d.data[0]))
.y0(d => y(d[0]))
.y1(d => y(d[1]));
Insert cell
xAxisGenerator = d3.axisBottom(x)
.tickFormat(d => d)
.tickValues([...d3.range(1990, 2020, 10), 2023])
Insert cell
yAxisGenerator = d3.axisLeft(y)
.ticks(4)
.tickSize(chartwidth);
Insert cell
Insert cell
x = d3.scaleLinear()
.domain(d3.extent(years)).range([0, chartwidth])
.range([0, chartwidth]);
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.total)])
.range([chartheight, 0])
.nice()
Insert cell
Insert cell
margin = ({ left: 40, right: 1, top: 5, bottom: 20 })
Insert cell
basewidth = Math.min(width, 640)
Insert cell
chartwidth = basewidth - margin.left - margin.right
Insert cell
chartheight = chartwidth * 0.5
Insert cell
Insert cell
// Make a stack
series = d3.stack()
.keys(categories.map(d => d.name))
.value(([, group], key) => group.get(key).value)
(d3.index(area, d => d.year, d => d.category));
Insert cell
area = d3
.cross(years, categories)
.map(([year, {name, fn}]) => {
return {
year,
category: name,
value: fn(data.find(f => f.year === year))
}
});
Insert cell
// We charted 1990-2023
years = d3.range(1990, 2024)
Insert cell
categories = [{
name: "Coal",
fn: d => d?.coal
}, {
name: "Wind & solar",
fn: d => d?.wind + d?.solar
}, {
name: "Gas",
fn: d => d?.gas
}, {
name: "Other",
fn: d => d?.total - (d?.coal + d?.wind + d?.solar + d?.gas)
}]
Insert cell
entities = d3
.groups(data_owid, d => d.entity)
.filter(f => f[1][0].year <= years[0])
Insert cell
codes = d3.groups(data_owid, d => d.code)
Insert cell
data = entity[1]
Insert cell
data_owid = (await FileAttachment("electricity-prod-source-stacked.csv").csv({ typed: true }))
.map(d => {
const o = { entity: d.Entity, code: d.Code, year: d.Year, total: 0 }
Object.keys(cols).forEach(c => {
const v = d[c]
o[cols[c]] = v;
o.total += v
});
return o;
});
Insert cell
cols = ({
"Other renewables excluding bioenergy - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "other_renewables",
"Electricity from bioenergy - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "bioenergy",
"Electricity from solar - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "solar",
"Electricity from wind - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "wind",
"Electricity from hydro - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "hydro",
"Electricity from nuclear - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "nuclear",
"Electricity from oil - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "oil",
"Electricity from gas - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "gas",
"Electricity from coal - TWh (adapted for visualization of chart electricity-prod-source-stacked)": "coal"
})
Insert cell
Insert cell
function toSlugCase(x) {
return x.toString().toLowerCase()
.replace(/\s+/g, "-") // Replace spaces with -
.replace(/[^\w\-]+/g, "") // Remove all non-word chars
.replace(/\-\-+/g, "-") // Replace multiple - with single -
.replace(/^-+/, "") // Trim - from start of text
.replace(/-+$/, ""); // Trim - from end of text
}
Insert cell
Insert cell
import { franklinLight } from "@climatelab/fonts@46"
Insert cell
import { toc } from "@climatelab/toc@44"
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