chart = {
const tmp = Object.assign(document.createElement("div"), { style: "position:fixed;height:var(--content-padding,0px);visibility:hidden" });
document.body.appendChild(tmp);
const padding = tmp.getBoundingClientRect().height;
document.body.removeChild(tmp);
const h = window.innerHeight - padding * 2;
const margin = { top: 120, right: 20, bottom: 35, left: 55 };
const W = width - margin.left - margin.right;
const H = h - margin.top - margin.bottom;
const xScale = d3.scaleTime()
.domain([
new Date(d3.min(tiles, d => d.week_start).getTime() - 3 * 864e5),
new Date(d3.max(tiles, d => d.week_start).getTime() + 10 * 864e5)
])
.range([0, W]);
const yScale = d3.scaleLinear()
.domain([d3.min(tiles, d => d.mean_temp_max) - 1, d3.max(tiles, d => d.mean_temp_max) + 1])
.range([H, 0]);
const colorScale = d3.scaleSequential()
.domain([-20, 33])
.interpolator(d3.piecewise(d3.interpolateRgb, ["#7700cc","#0088cc","#33aa44","#ddaa00","#ff2200"]));
const tileH = Math.abs(yScale(0) - yScale(2));
const lineGen = d3.line().x(d => xScale(d.week_start)).y(d => yScale(d.mean_temp_max));
const svg = d3.create("svg")
.attr("width", width)
.attr("height", h)
.style("display", "block")
.style("background", "#111111");
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
[0, 10, 20, 30].forEach(y => {
g.append("line").attr("x1", 0).attr("x2", W).attr("y1", yScale(y)).attr("y2", yScale(y))
.attr("stroke", "#111111").attr("stroke-width", 2);
g.append("line").attr("x1", 0).attr("x2", W).attr("y1", yScale(y)).attr("y2", yScale(y))
.attr("stroke", "#333333").attr("stroke-width", 1);
});
g.selectAll(".tile").data(tiles).join("rect").attr("class", "tile")
.attr("x", d => xScale(d.week_start))
.attr("y", d => yScale(d.mean_temp_max + 1))
.attr("width", d => xScale(new Date(d.week_start.getTime() + 7 * 864e5)) - xScale(d.week_start))
.attr("height", tileH)
.attr("fill", d => colorScale(d.mean_temp_max))
.attr("opacity", d => Math.min(1, d.p * 2))
.attr("stroke", "#111111").attr("stroke-width", 0.6);
[[6, "#111111"], [1.5, "#ffffff"]].forEach(([sw, stroke]) =>
g.append("path").datum(line_current)
.attr("fill", "none").attr("stroke", stroke).attr("stroke-width", sw)
.attr("stroke-linejoin", "round").attr("pointer-events", "none").attr("d", lineGen)
);
if (point_current)
g.append("circle")
.attr("cx", xScale(point_current.week_start)).attr("cy", yScale(point_current.mean_temp_max))
.attr("r", 5).attr("fill", "#ffffff").attr("pointer-events", "none");
const tooltip = svg.append("g").attr("display", "none").attr("pointer-events", "none");
const tooltipBg = tooltip.append("rect").attr("rx", 3)
.attr("fill", "#1e1e1e").attr("stroke", "#444").attr("stroke-width", 0.5);
const tooltipTxt = tooltip.append("text")
.attr("fill", "#dddddd").attr("font-size", "0.8rem")
.attr("dx", 6).attr("dy", "1.1em");
const bisect = d3.bisector(d => d.week_start).left;
g.append("rect").attr("width", W).attr("height", H).attr("fill", "none").attr("pointer-events", "all")
.on("pointermove", function(event) {
const [mx] = d3.pointer(event);
const x0 = xScale.invert(mx);
const i = bisect(line_current, x0, 1);
const a = line_current[i - 1], b = line_current[i];
if (!a) return;
const d = b && (x0 - a.week_start > b.week_start - x0) ? b : a;
const weekEnd = new Date(d.week_start.getTime() + 6 * 864e5);
const sameMonth = d.week_start.getMonth() === weekEnd.getMonth();
const range = sameMonth
? `${d.week_start.getDate()} → ${weekEnd.toLocaleDateString("nl-NL", { day: "numeric", month: "long" })}`
: `${d.week_start.toLocaleDateString("nl-NL", { day: "numeric", month: "short" })} → ${weekEnd.toLocaleDateString("nl-NL", { day: "numeric", month: "short" })}`;
tooltipTxt.text(`${range} ~${d.mean_temp_max}°C`);
const bb = tooltipTxt.node().getBBox();
tooltipBg.attr("x", bb.x - 6).attr("y", bb.y - 4).attr("width", bb.width + 12).attr("height", bb.height + 8);
tooltip.attr("display", null).attr("transform", `translate(${xScale(d.week_start) + margin.left + 10},${yScale(d.mean_temp_max) + margin.top - 14})`);
})
.on("pointerleave", () => tooltip.attr("display", "none"));
const axText = ax => ax.selectAll("text").attr("fill", "#888888");
g.append("g").attr("transform", `translate(0,${H})`)
.call(d3.axisBottom(xScale).ticks(d3.timeMonth.every(1)).tickFormat(d3.timeFormat("%b")).tickSize(0))
.call(ax => ax.select(".domain").remove())
.call(ax => axText(ax).attr("font-size", "0.8rem").attr("dy", "1.4em"));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(d => `${d}C°`).tickSize(0))
.call(ax => ax.select(".domain").remove())
.call(ax => axText(ax).attr("font-size", "0.75rem"));
svg.append("foreignObject")
.attr("x", margin.left).attr("y", 0)
.attr("width", W).attr("height", margin.top)
.append("xhtml:div")
.style("color", "#aaaaaa")
.html(`<h2 style="margin:0 0 0.15em">${insights.title}</h2>
<p style="margin:0;opacity:0.7">${insights.subtitle}</p>
<small style="opacity:0.4">${insights.note}</small>`);
return svg.node();
}