Public
Edited
Jun 5
Insert cell
Insert cell
import {us} from "@observablehq/us-geographic-data"
Insert cell
nation = topojson.feature(us, us.objects.nation)
Insert cell
Insert cell
Insert cell
dataByYear = data.map(d => ({
...d,
Year: +d.Year,
"Per Capita Data": +d["Per Capita Data"]
})).filter(d => d.Year === year)
.map(d => ({
...d,
FullName: stateNameByCode.get(d.State)
}))
Insert cell
dataByYear
Insert cell
dataByState = d3.group(data, d => d.State)
Insert cell
stateCodeByName = new Map(
Array.from(stateNameByCode.entries()).map(([code, name]) => [name, code])
)
Insert cell
USstates = topojson.feature(us, us.objects.states).features
Insert cell
Insert cell
viewof selectedStates = Inputs.select(Array.from(stateNameByCode.entries()).map(([code, name]) => code), { multiple: true, value:["AK"] })
Insert cell
viewof year = Inputs.range([2015, 2024], {
step: 1,
label: "Select Year",
value: 2015
})
Insert cell
Plot.plot({
className: "map",
projection: "albers-usa",
width: 800,
color: {
type: "linear",
scheme: "reds",
legend: true,
label: `Overdose Deaths (${year}) per 100,000 residents`
},
marks: [
Plot.geo(USstates, {
stroke: "white",
strokeOpacity: 0.1,
fill: d => {
const entry = dataByYear.find(e => e.FullName === d.properties.name);
return entry ? entry["Per Capita Data"] : undefined;
},
title: d => {
const entry = dataByYear.find(e => e.FullName === d.properties.name);
return entry
? `${entry.FullName} (${entry.State}): ${entry["Per Capita Data"].toLocaleString()} deaths`
: d.properties.name;
},
tip: true,
render: addClick
}),
Plot.text(USstates.map(d => {
const abbrev = stateCodeByName.get(d.properties.name);
return {
...d,
abbrev,
coordinates: d3.geoPath().centroid(d)
};
}),
{
x: d => d.coordinates[0],
y: d => d.coordinates[1],
text: d => d.abbrev,
fontSize: 10,
fill: "black",
textAnchor: "middle"
}),
Plot.geo(USstates, {
filter: d => new Set(selectedStates).has(stateCodeByName.get(d.properties.name)),
stroke: "black",
strokeWidth: 1,
fill: "none"
}),
]
})


Insert cell
addClick = (index, scales, values, dimensions, context, next) => {
const el = next(index, scales, values, dimensions, context);
const path = el.querySelectorAll("path");
for (let i = 0; i < path.length; i++) {
const d = {idx: index[i], name:values.geometry[index[i]].properties.name};
path[i].addEventListener("click", () => {
const clickedState = stateCodeByName.get(d.name)
const newSet = new Set(selectedStates)
if (clickedState) {
if (newSet.has(clickedState)) {
newSet.delete(clickedState)
} else {
newSet.add(clickedState);
}
viewof selectedStates.values = Array.from(newSet); // Update the input’s value?
// mutable clickedState = clickedState;
}

});

}
return el;
}
Insert cell
Plot.plot({
width: 600,
height: 400,
x: { label: "Year" },
y: { label: "Overdose Deaths per 100,000 residents" },
color: {
legend: true,
scheme: "tableau10",
label: "State"
},
marks: [
Plot.line(selectedStates.flatMap(
state => dataByState.get(state).map(
d => ({
Year: d.Year,
State: d.State,
"Per Capita Data": +d["Per Capita Data"]
}))), {
x: "Year",
y: "Per Capita Data",
z: "State",
stroke: "State"
})
]
})

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