Public
Edited
Jun 13, 2023
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
timestamps = d3.range(startTS, endTS + 1, hourDuration)
Insert cell
startTS = 1685750400000 // 2023-06-03T00
Insert cell
endTS = 1686524400000 // 2023-06-11T23
Insert cell
hourDuration = 60 * 60 * 1000
Insert cell
// this is a function because we want to show the map at the top of the page.
map = (width) =>
Plot.plot({
width,
projection: "albers",
color: { ...color, legend: true },
marks: [
// this creates a path to clip the contour mark.
Plot.geo(nation, {
render: (i, s, v, d, c, next) => {
const g = next(i, s, v, d, c).children[0];
return svg`<clipPath id=nation>${g}`;
}
}),

Plot.contour(filteredData, {
x: "lon",
y: "lat",
fill: "PM2.5",
interpolate: "random-walk",
blur: 3,
thresholds: [0, ...color.domain],
stroke: "currentColor",
render: (i, s, v, d, c, next) => {
const g = next(i, s, v, d, c);
g.setAttribute("clip-path", "url(#nation)"); // add clip
return g;
}
}),

Plot.geo(nation, { stroke: "currentColor" }),

Plot.dot(filteredData, {
x: "lon",
y: "lat",
fill: "PM2.5",
stroke: "currentColor",
strokeOpacity: 0.8,
strokeWidth: 0.75,
tip: true
}),

Plot.text([toTimestampString(timestamp)], {
frameAnchor: "top-right",
fontSize: 16,
fontVariant: "tabular-nums"
})
]
})
Insert cell
toTimestampString = (unixTs) =>
d3.min([
`${new Date(unixTs).toISOString().slice(0, 11)}${d3.format("02d")(
new Date(unixTs).getUTCHours()
)}`,
new Date(+new Date() - 3600 * 1000).toISOString().slice(0, 13)
])
Insert cell
color = ({
label: "PM2.5",
type: "threshold",
domain: [50, 100, 150, 200, 300],
range: [
"rgb(156, 216, 78)", // Good
"rgb(250, 207, 57)", // Moderate
"rgb(249, 144, 73)", // Unhealthy for Sensitive Groups
"rgb(246, 94, 95)", // Unhealthy
"rgb(160, 112, 182)", // Very Unhealthy
"rgb(160, 106, 123)" // Hazardous
]
})
Insert cell
filteredData = data
.filter((d) => +d.date - 60 * 60 * 4 * 1000 == timestamp)
.filter((d) => d["PM2.5"] >= 0)
Insert cell
data = FileAttachment("flatdata.csv").csv({ typed: true })
Insert cell
nation = topojson.feature(us, us.objects.nation)
Insert cell
us = FileAttachment("us-counties-10m.json").json()
Insert cell
Insert cell
Insert cell
async function getData(url) {
const res = await d3.text(url);
return d3.csvParse("lat,lon,date,indicator,PM2.5,??\n" + res, d3.autoType);
}
Insert cell
url = (timestampString) =>
`https://www.airnowapi.org/aq/data/?startDate=${timestampString}&endDate=${timestampString}&parameters=PM25&BBOX=-130,25,-65,55&dataType=A&format=text/csv&verbose=0&monitorType=0&includerawconcentrations=0&API_KEY=62F6D106-6781-4DC5-8A63-005044B666E2`
Insert cell
// data = {
// const urls = timestamps.map((d) => url(toTimestampString(d)));
// const list = [];
// let alive = true;
// invalidation.then(() => (alive = false));
// mutable i = 0;
// for (const url of urls) {
// if (!alive) {
// return list;
// }
// mutable i += 1;
// await Promises.delay(1000);
// try {
// const d = await getData(url);
// list.push(d);
// } catch (error) {
// mutable error = error;
// alive = false;
// }
// yield list;
// }
// return list;
// }
Insert cell
// flatdata = data.flat()
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