Public
Edited
Nov 25, 2023
Insert cell
Insert cell
[[1800, 1900]].map(x=> x.map(ytd))
Insert cell
cents = [...Array((2100+500)/100).keys()].map(x => (x*100)-500)
Insert cell
centSpans = [...Array((2100+500)/100).keys()].map(x => (x*100)-500).map((d, i) => [d, cents[i+1]]).filter((_, i) => (i % 2) == 0)
Insert cell
Insert cell
Insert cell
Insert cell
viewof startYear = Inputs.range([-1000, 1980], {label: "Start date", step: 20, value: -600})
Insert cell
viewof endYear = Inputs.range([-1000, 2020], {label: "End date", step: 10, value: 2020})
Insert cell
timeline = {
const startY = ytd(startYear);
const endY = ytd(endYear);

const catFilter = picked.map(p => p.category);
const occFilter = pickedOcc.map(p => p.category);
const localEvents = events.filter(e => e.end > startY).filter(e => e.start < endY).filter(e => catFilter.includes(e.type))
const localPoi = poi.filter(p => p.end > startY).filter(e => e.start < endY)
.filter(e => occFilter.includes(e.occupation))
.filter(e => (e.hpi > minHpi)) ;
const ydomain = d3.sort(localEvents.concat(localPoi.filter(p => p.type == "life")), e => e.start).map(e => e.label);


return Plot.plot({
marginLeft: 120,
width: width*.8,
x: {domain: [startY, endY]},
//color: {domain: ["war"], range: ["darkred"]},
y: {
axis: null,
domain: ydomain,
},
marks: [
// century bars
Plot.barX(centSpans.map(x=> x.map(ytd)), {x1: d => d[0], x2: d=> d[1], fill: "#f7f7f7"}),
Plot.ruleX([ytd(0)]),
//Plot.ruleY(eras, {x1: "start", x2: "end", y: () => "Cold War", stroke: 'name'}),



Plot.barX(d3.sort(events, e=> e.start), {x1: "start", x2: "end", y: "label", fill: "type",
}),
Plot.text(events.filter(e => e.type != "inner"), {
x: "start",
y: "label",
//fill: "label",
text: "label",
textAnchor: "end",
href: e => `https://en.wikipedia.org/wiki/${e.label.replace(" ", "_")}`,
dx: -6
}),

Plot.barX(d3.sort(poi, e=> e.start), {x1: "start", x2: "end", y: "label", fill: "type"
}),
Plot.text(poi.filter(i => i.type == "life"), {
x: "start",
y: "label",
//fill: "hpi",
//fill: "label",
text: "label",
textAnchor: "end",
dx: -6
})
]
})

}
Insert cell
// {
// timeline

// return Array.from(document.querySelectorAll("g a"))[0].target = "_blank"
// }
Insert cell
"a b".replace(" ", "_")
Insert cell
// from https://api.pantheon.world/era
eras = ystd([{"id":1,"name":"Scribal Era","slug":"scribal","description":"The Scribal Era is defined by...","start_year":-500,"end_year":1449,"img_link":"https://flic.kr/p/bqwCMj","img_author":"Walters Art Museum Illuminated Manuscripts"},
{"id":2,"name":"Printing Era","slug":"printing","description":"The Printing Era is defined by...","start_year":1450,"end_year":1699,"img_link":"https://flic.kr/p/PNn6xc","img_author":"groenling"},
{"id":3,"name":"Newspaper Era","slug":"newspaper","description":"The Newspaper Era is defined by...","start_year":1700,"end_year":1899,"img_link":"https://flic.kr/p/qgTvEG","img_author":"Ashley Van Haeften"},
{"id":4,"name":"Radio & Film Era","slug":"radio_and_film","description":"The Radio and Film Era is defined by...","start_year":1900,"end_year":1949,"img_link":"https://flic.kr/p/nDQ7RM","img_author":"simpleinsomnia"},
{"id":5,"name":"Television Era","slug":"television","description":"The Television Era is defined by...","start_year":1950,"end_year":1989,"img_link":"https://flic.kr/p/fTmSpk","img_author":"totallymystified"},
{"id":6,"name":"Personal Computer Era","slug":"personal_computer","description":"The Personal Computer Era is defined by...","start_year":1990,"end_year":2016,"img_link":"https://flic.kr/p/rofpfh","img_author":"Jody Sticca"}], "start_year", "end_year")
Insert cell
JSON.stringify(Array.from(new Set(poi.map(p => p.label))))
Insert cell
ytd = (y) => {
if (typeof y === "object") {
return y;
}

if (y < 100) {
return lowyear(y);
}
return new Date(y.toString());
}

Insert cell
events = manualEvents.concat(ystd(empire_subset).map(d => ({...d, type: "empire"})));
Insert cell
Insert cell
ystd(empire_subset)
Insert cell
Insert cell
ystd = (d, s="start", end="end") => d.map(e => ({...e, start: ytd(e[s]), end: ytd(e[end]) }))
Insert cell
lowyear = (y) => {
const d = new Date("0000-1-1");

d.setUTCFullYear(y);

return d;
}
Insert cell
nn = {

// shorter names than the wikipedia ones
const nicknames = {
"George III of the United Kingdom": "George III",
"Louis XVI of France": "Louis XVI",
"Henry VIII of England": "Henry VIII",
}

return (n) => {
return nicknames[n] || n;
}
}
Insert cell
poi = {
const makeDate = (d) => {
if (d.includes("BC")) {
return luxon.DateTime.fromFormat(d, "yyyy-mm-dd G")
}
return luxon.DateTime.fromISO(d);
}

const lifespans = pantheon_p.concat(pantheon_p2).map(p => {
// Overrides
let birth = makeDate(p.birthdate);
let death = (p.deathdate != "") ? makeDate(p.deathdate) : birth.plus({year: 50});
return {
occupation: p.occupation,
hpi: parseFloat(p.hpi),
start: birth,
end: death,
type: "life",
label: p.name,
}
});


return lifespans.concat(ystd(p_intervals)).map(d => ({...d, label: nn(d.label)}));
}
Insert cell
luxon = require("luxon@1/build/amd/luxon.js")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
new Date("00001-1-1")
Insert cell
ytd(new Date("0000-01-01"))
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