Published
Edited
Jun 8, 2021
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.select(DOM.svg(myWidth, height))
.style("-webkit-tap-highlight-color", "transparent")
.style("overflow", "visible");

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
svg.append("path")
.datum(serie)
.attr("fill", "none")
.attr("stroke", "#08a6bf")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line);

const tooltip = svg.append("g");

svg.on("touchmove mousemove", function(event) {
const {year, value} = bisect(d3.pointer(event, this)[0]);

tooltip
.attr("transform", `translate(${x(year)},${y(value)})`)
.call(callout, `${year}: ${formatValue(value)}`);
});

svg.on("touchend mouseleave", () => tooltip.call(callout, null));

return svg.node();
}
Insert cell
Insert cell
Insert cell
callout = (g, value) => {
if (!value) return g.style("display", "none");

g
.style("display", null)
.style("pointer-events", "none")
.style("font", "10px sans-serif");

const path = g.selectAll("path")
.data([null])
.join("path")
.attr("fill", "white")
.attr("stroke", "grey");

const text = g.selectAll("text")
.data([null])
.join("text")
.call(text => text
.selectAll("tspan")
.data((value + "").split(/\n/))
.join("tspan")
.attr("x", 0)
.attr("y", (d, i) => `${i * 1.1}em`)
.style("font-weight", (_, i) => i ? null : "bold")
.text(d => d));
const {x, y, width: w, height: h} = text.node().getBBox();
text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}
Insert cell
Insert cell
x = d3.scaleLinear()
.domain([d3.min(serie, d => d.year), d3.max(serie, d => d.year)])
.range([margin.left, myWidth - margin.right])
Insert cell
y = d3.scaleLinear()
.domain([d3.min(serie, d => d.value), d3.max(serie, d => d.value)]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = g => g
.attr('class', 'axes xAxes')
.attr("transform", `translate(0,${height - margin.bottom / 1.5})`)
.call(d3.axisBottom(x).ticks(myWidth / 80).tickSizeOuter(0).tickFormat(d => d))
Insert cell
yAxis = g => g
.attr('class', 'axes yAxis')
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y)
.tickSize(-myWidth + margin.right)
.ticks(5)
.tickFormat(d => formatValue(d)))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(serie.y))
Insert cell
line = d3.line()
.defined(d => !isNaN(d.value))
.x(d => {
return x(d.year)
})
.y(d => y(d.value))
Insert cell
function formatValue(value) {
return value.toLocaleString(language, {
style: "decimal"
});
}
Insert cell
function formatDate(date) {
return date.toLocaleString(language, {
year: "numeric",
timeZone: "UTC"
});
}
Insert cell
bisect = {
const bisect = d3.bisector(d => d.year).left;
return mx => {
const year = x.invert(mx);
const index = bisect(serie, year, 1);
const a = serie[index - 1];
const b = serie[index];
return b && (year - a.year > b.year - year) ? b : a;
};
}
Insert cell
Insert cell
maxWidth = 660
Insert cell
myWidth = +Math.min(width, maxWidth)
Insert cell
height = 450
Insert cell
margin = ({top: 20, right: 30, bottom: 30, left: 20})
Insert cell
Insert cell
Insert cell
Insert cell
md`## Appendix`
Insert cell
d = d3.csv('https://data.civio.es/medicamentalia/mental-health/psychiatric-care-beds.csv', parseRow)
Insert cell
Insert cell
data = (dataSort(d))
Insert cell
parseRow = d => {
Object.keys(d).forEach(key => {
if (!d[key]){
d[key] = null
} else if (!isNaN(d[key])){
d[key] = +d[key]
}
})
d.country_name = locale[language].countries[d.country_code]
return d
}
Insert cell
Insert cell
Insert cell
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