Unlisted
Edited
Mar 20
Insert cell
Insert cell
prodviz = html`<div style="display: flex; justify-content: center;">${Plot.plot((() => {
const n = 3;
const customProvOrder = ["Alberta", "Saskatchewan", "Manitoba", "Ontario", "Quebec"];
const provKeys = customProvOrder.filter(prov => production.some(d => d.prov === prov));
const provIndex = new Map(provKeys.map((key, i) => [key, i]));
const fx = (key) => provIndex.get(key) % n;
const fy = (key) => Math.floor(provIndex.get(key) / n);

const customGrainOrder = ["Wheat", "Canola", "Corn", "Barley", "Soybeans", "Oats", "Lentils"];
const grainIndex = new Map(customGrainOrder.map((key, i) => [key, i]));

const sortedProduction = production.slice().sort((a, b) => (grainIndex.get(a.grain) ?? Infinity) - (grainIndex.get(b.grain) ?? Infinity));

return {
width,
height: 600,
color: { legend: true, domain: customGrainOrder },
title: "Production trends of major grains and legumes in Canada by province",
subtitle: "Regional differences exist in the type and volume of grain produced.",
caption: "Source: Own calculation using data from Statistics Canada",
fx: { padding: 0.08, axis: null },
fy: { padding: 0.08, axis: null },
marks: [
Plot.frame({ strokeOpacity: 0.1 }),
Plot.areaY(sortedProduction, {
x: (d) => +d.year,
y: "value",
fill: "grain",
fx: (d) => fx(d.prov),
fy: (d) => fy(d.prov),
title: (d) => `${d.prov} produced ${(d.value / 1_000_000).toFixed(1)} million tonnes of ${d.grain.toLowerCase()} in ${d.year}.`,
tip: true
}),
Plot.text(provKeys, {fx, fy, frameAnchor: "top-left", dx: 6, dy: 6}),
],
x: {
label: "Year of production",
domain: [2000, 2023],
ticks: [2000, 2023],
tickFormat: (d) => d.toString(),
},
y: {
label: "Production in metric tonnes",
ticks: [5000000, 10000000, 15000000, 20000000, 25000000, 30000000, 35000000],
tickFormat: (d) => Math.round(d / 1_000_000) + 'M'
}
};
})())}</div>`
Insert cell
storageviz = html`<div style="display: flex; justify-content: center;">
${Plot.plot({
color: { legend: true },
width,
title: "Grain storage capacity in Canada: Provincial breakdown by elevator type and railway network connection",
subtitle: "Elevator capacity is influenced by the geography of production and transportation networks for exports to foreign markets.",
caption: "Source: Own calculation using data from Statistics Canada",
fx: { padding: 0.08, label: null },
height: 500,
marks: [
Plot.frame({ strokeOpacity: 0.1 }),
Plot.barY(
storage.map(d => ({
...d,
Railway: d.Railway === "Canadian Pacific" ? "Canadian Pacific Kansas City" : (d.Railway === "Not on Railway" ? "Not connected to railway" : d.Railway),
sum: storage
.filter(item =>
item.Province === d.Province &&
item.Elevator_type === d.Elevator_type &&
(item.Railway === "Canadian Pacific" ? "Canadian Pacific Kansas City" : item.Railway) ===
(d.Railway === "Canadian Pacific" ? "Canadian Pacific Kansas City" : d.Railway)
)
.reduce((acc, item) => acc + item.Capacity_tonne, 0)
})),
Plot.groupX(
{ y: "sum" },
{
fx: "Elevator_type",
x: "Province",
y: "Capacity_tonne",
fill: "Railway",
title: (d) =>
`${d.Province} has ${(d.sum / 1_000_000).toFixed(2)} million tonnes of ${d.Elevator_type.toLowerCase()} storage elevator capacity connected to the ${d.Railway} network.`,
tip: true
}
)
),
],
x: {
label: null,
domain: ["British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Ontario", "Quebec", "Nova Scotia"],
tickFormat: (d) => ({
"British Columbia": "BC",
"Alberta": "AB",
"Saskatchewan": "SK",
"Manitoba": "MB",
"Ontario": "ON",
"Quebec": "QC",
"Nova Scotia": "NS"
}[d] || d)
},
y: {
label: "Storage capacity in metric tonnes",
ticks: [1000000, 2000000, 3000000, 4000000, 5000000],
tickFormat: (d) => Math.round(d / 1_000_000) + 'M'
}
})}
</div>`
Insert cell
transportviz = html`<div style="display: flex; justify-content: center;">
${Plot.plot({
color: {
domain: ["CPKC", "CN"],
range: ["#4F67CD", "#E6B533"],
legend: false
},
height: 400,
title: "Transportation of grains by CPKC and CN across destination region in 2023",
subtitle: "The vast majority of grain transported by rail was towards Western Canada.",
caption: "Source: Own calculation using data from CPKC and CN annual reports",
fx: {
padding: 0.08,
label: null,
domain: ["Western Canada", "Eastern Canada", "US"]
},
x: {
domain: ["CPKC", "CN"],
label: null
},
y: {
label: "Grains transported in metric tonnes",
ticks: [5000000, 10000000, 15000000, 20000000, 25000000, 30000000, 35000000],
tickFormat: (d) => Math.round(d / 1_000_000) + 'M'
},
marks: [
Plot.frame({ strokeOpacity: 0.1 }),
Plot.barY(
transportation.map(d => ({
...d,
rail: d.rail === "CP" ? "CPKC" : d.rail
})),
{
fx: "Region",
x: "rail",
y: "Tonnes",
fill: "rail",
title: (d) => `${d.rail} transported ${(d.Tonnes / 1_000_000).toFixed(2)} million metric tonnes of grain towards ${d.Region} in 2023.`,
tip: true
}
),
Plot.ruleY([0])
]
})}
</div>`
Insert cell
production@1.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
storage.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
transportation@1.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

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