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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more