viewof heatmap = {
const margin = { top: 60, right: 120, bottom: 80, left: 140 };
const width = cols.length * 20;
const height = rows.length * 20;
const svg = d3.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("text")
.attr("x", margin.left + width / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size", "20px")
.text("Mean Snow Depth by Country and Year");
const x = d3.scaleBand().domain(cols).range([0, width]);
const y = d3.scaleBand().domain(rows).range([0, height]);
const color = d3.scaleSequential()
.domain([d3.min(grid.flat()), d3.max(grid.flat())])
.interpolator(d3.interpolateCividis);
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
rows.forEach((country, i) => {
cols.forEach((year, j) => {
g.append("rect")
.attr("x", x(year))
.attr("y", y(country))
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.attr("fill", color(grid[i][j]));
});
});
const colaxisG = g.append("g")
.attr("transform", `translate(0,${height})`)
d3.axisBottom(x)(colaxisG)
colaxisG.selectAll("text")
.attr("transform", "rotate(-45)")
.style("text-anchor", "end");
g.append("text")
.attr("x", width / 2)
.attr("y", height + 50)
.attr("text-anchor", "middle")
.text("Year");
d3.axisLeft(y)(g.append("g"))
g.append("text")
.attr("x", -height / 2)
.attr("y", -100)
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.text("Country");
const gradient = svg.append("linearGradient")
.attr("id", "legend-gradient")
.attr("x1", "0%").attr("y1", "100%")
.attr("x2", "0%").attr("y2", "0%");
[0, 0.5, 1.0].forEach(t => {
gradient.append("stop")
.attr("offset", `${t*100}%`)
.attr("stop-color", d3.interpolateCividis(t));
});
const legendX = margin.left + width + 40;
const legendY = margin.top;
const legend = svg.append("g")
.attr("transform", `translate(${legendX},${legendY})`);
const legendHeight = 200;
const legendWidth = 20;
legend.append("rect")
.attr("width", legendWidth)
.attr("height", legendHeight)
.style("fill", "url(#legend-gradient)");
const legendScale = d3.scaleLinear()
.domain(color.domain())
.range([legendHeight, 0]);
d3.axisRight(legendScale)(
legend.append("g").attr("transform", `translate(${legendWidth},0)`)
);
legend.append("text")
.attr("x", -height / 2)
.attr("y", -10)
.attr("text-anchor", "start")
.attr("transform", "rotate(-90)")
.attr("font-size", "15px")
.text("Precipitation scale");
svg.append("a")
.attr("href", "https://www.ecmwf.int/")
.append("text")
.attr("x", margin.left)
.attr("y", margin.top + height + 70)
.attr("font-size", "12px")
.text("Data source: ERA5 (ECMWF, https://www.ecmwf.int/)");
return svg.node();
}