Published unlisted
Edited
Oct 19, 2020
Insert cell
Insert cell
Insert cell
Insert cell
filename = "data/946.ndjson"
Insert cell
commits = await d3.json(
`https://api.github.com/repos/iandees/usps-collection-boxes/commits?path=${filename}`
)
Insert cell
commitDetails = Promise.all(commits.map(({ url }) => d3.json(url)))
Insert cell
patches = commitDetails
.map(({ commit, files }) => {
const date = commit.committer.date;
const file = files.find(f => f.filename === filename);
if (date && file) {
return { date, patch: file.patch };
} else {
return null;
}
})
.filter(d => d)
Insert cell
lastChangeDate = new Date(
patches.sort((a, b) => d3.descending(a.date, b.date))[0].date
)
Insert cell
Insert cell
Insert cell
changes = patches.flatMap(({ date, patch }) => {
const features = patch
.split("\n")
.filter(d => d.startsWith("+") || d.startsWith("-"))
.map(d => {
try {
const feature = JSON.parse(d.slice(1));
feature.properties.status = d.slice(0, 1) === "-" ? "removed" : "added";
feature.properties.date = date;
return feature;
} catch (e) {
return null;
}
})
.filter(d => d);

const grouped = d3.group(
features,
d => d.properties.ref,
d => d.properties.status
);
return features.filter(
feature => grouped.get(feature.properties.ref).size === 1
);
})
Insert cell
data = {
const grouped = d3.group(changes, d => d.properties.ref);
return Array.from(grouped.values()).map(entries => {
entries.sort((a, b) => d3.ascending(a.properties.date, b.properties.date));
const mailbox = entries[entries.length - 1];
mailbox.properties.descriptor =
entries.length === 1 ? entries[0].properties.status : "changed";
mailbox.properties.summary = entries
.map(d => `${summary(d)}`)
.join("<br/>");
return mailbox;
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
import { table } from "@tmcw/tables/2"
Insert cell
import { tweet } from "@mbostock/tweet"
Insert cell
token = "pk.eyJ1Ijoia2Vycnlyb2RkZW4iLCJhIjoiY2tlYWJtNXdrMDNjazJybzdsdWR1MXQxNiJ9.201r3UVK3fORKawabrfkqw"
Insert cell
mapboxgl = {
const gl = await require("mapbox-gl@1");
if (!gl.accessToken) {
// https://www.mapbox.com/help/how-access-tokens-work/
gl.accessToken = token;
const href = await require.resolve("mapbox-gl@1/dist/mapbox-gl.css");
document.head.appendChild(html`<link href=${href} rel=stylesheet>`);
}
return gl;
}
Insert cell
lastChecked = new Date(
(await d3.text(
"https://raw.githubusercontent.com/iandees/usps-collection-boxes/master/data/last_updated.txt"
)).trim()
)
Insert cell
dateFormat = d3.timeFormat("%B %e, %Y")
Insert cell
summary = feature =>
`${feature.properties.status} ${dateFormat(
new Date(feature.properties.date)
)}`
Insert cell
addedColor = "#3bb2d0"
Insert cell
removedColor = "#ff7f00"
Insert cell
changedColor = "#df44ab"
Insert cell
added = data.filter(d => d.properties.descriptor === "added")
Insert cell
removed = data.filter(d => d.properties.descriptor === "removed")
Insert cell
changed = data.filter(d => d.properties.descriptor === "changed")
Insert cell
inlineDot = color =>
html`<div style="display: inline-block; background: ${color};
border-radius: 50%; width: 12px; height: 12px;"></div>`
Insert cell
addressTable = features =>
features.length > 0
? table(
features.map(d => ({
address: d.properties["addr:full"],
city: `${d.properties["addr:city"]}, ${d.properties["addr:state"]}`,
zip: d.properties["addr:postcode"],
summary: d.properties.summary
}))
)
: md`0`
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