Skip to content

Commit f1b598c

Browse files
authored
Merge pull request #151 from chnm/feature/architecture
Updates and fixes to the word cloud and tag URL paths
2 parents c91be72 + 5296f57 commit f1b598c

File tree

115 files changed

+411
-203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+411
-203
lines changed

bom-website/assets/visualizations/wordcloud/main.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ function fetchDataAndRender(startYear, endYear) {
77

88
d3.json(url)
99
.then((data) => {
10+
// Extract unique years from the data
11+
const years = Array.from(new Set(data.map(d => d.year))).sort((a, b) => a - b);
12+
populateYearDropdowns(years);
13+
1014
// Clear the existing word cloud
1115
d3.select("#chart").selectAll("*").remove();
1216

@@ -22,6 +26,32 @@ function fetchDataAndRender(startYear, endYear) {
2226
});
2327
}
2428

29+
// Populate year dropdowns
30+
function populateYearDropdowns(years) {
31+
const startYearSelect = document.getElementById("start-year");
32+
const endYearSelect = document.getElementById("end-year");
33+
34+
// Clear existing options
35+
startYearSelect.innerHTML = "";
36+
endYearSelect.innerHTML = "";
37+
38+
years.forEach((year) => {
39+
const optionStart = document.createElement("option");
40+
optionStart.value = year;
41+
optionStart.text = year;
42+
startYearSelect.appendChild(optionStart);
43+
44+
const optionEnd = document.createElement("option");
45+
optionEnd.value = year;
46+
optionEnd.text = year;
47+
endYearSelect.appendChild(optionEnd);
48+
});
49+
50+
// Set default values
51+
startYearSelect.value = years[0];
52+
endYearSelect.value = years[years.length - 1];
53+
}
54+
2555
// Initial fetch and render
2656
fetchDataAndRender(1648, 1754);
2757

@@ -34,7 +64,7 @@ document.getElementById("update-button").addEventListener("click", () => {
3464

3565
// Add event listener to the reset button
3666
document.getElementById("reset-button").addEventListener("click", () => {
37-
// Reset the input fields to the original values
67+
// Reset the dropdowns to the original values
3868
document.getElementById("start-year").value = 1648;
3969
document.getElementById("end-year").value = 1754;
4070
// Fetch and render the original data

bom-website/assets/visualizations/wordcloud/wordcloud.js

Lines changed: 79 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
1-
import * as d3 from 'd3';
2-
import d3Cloud from 'd3-cloud';
3-
import Visualization from '../common/visualization';
1+
import * as d3 from "d3";
2+
import d3Cloud from "d3-cloud";
3+
import Visualization from "../common/visualization";
44

55
export default class WordCloudChart extends Visualization {
66
constructor(id, data, dim) {
77
const margin = {
8-
top: 0, right: 40, bottom: 40, left: 10,
8+
top: 0,
9+
right: 40,
10+
bottom: 40,
11+
left: 10,
912
};
1013
super(id, data, dim, margin);
1114
}
1215

1316
// Draw the plot
1417
render() {
15-
// causes needs to be an array of objects with a text and size property,
18+
// causes needs to be an array of objects with a text and size property,
1619
// which will group each word together and add together each of their d.count
1720
// values. To do this we'll loop through the data and create a new object
1821
// for each unique word, and add the count to the size property. We use
1922
// d3.rollups to do this.
20-
const causes = d3.rollups(
21-
this.data.causes,
22-
(v) => d3.sum(v, (d) => d.count),
23-
(d) => d.death,
24-
).map(([text, size]) => ({ text, size }));
23+
const causes = d3
24+
.rollups(
25+
this.data.causes,
26+
(v) => d3.sum(v, (d) => d.count),
27+
(d) => d.death,
28+
)
29+
.map(([text, size]) => ({ text, size }));
2530

2631
const wordcloud = WordCloud(causes, {
2732
size: (group) => {
@@ -36,64 +41,81 @@ export default class WordCloudChart extends Visualization {
3641
}
3742

3843
// Word cloud generator
39-
function WordCloud(text, {
40-
size = group => group.length, // Given a grouping of words, returns the size factor for that word
41-
word = d => d, // Given an item of the data array, returns the word
42-
marginTop = 0, // top margin, in pixels
43-
marginRight = 0, // right margin, in pixels
44-
marginBottom = 0, // bottom margin, in pixels
45-
marginLeft = 0, // left margin, in pixels
46-
width = 900, // outer width, in pixels
47-
height = 450, // outer height, in pixels
48-
maxWords = 1200, // maximum number of words to extract from the text
49-
fontFamily = "serif", // font family
50-
fontScale = 14, // base font size
51-
padding = 0, // amount of padding between the words (in pixels)
52-
rotate = 0, // a constant or function to rotate the words
53-
} = {}) {
54-
const words = typeof text === "string" ? text.split(/\W+/g) : Array.from(text);
44+
function WordCloud(
45+
text,
46+
{
47+
size = (group) => group.length, // Given a grouping of words, returns the size factor for that word
48+
word = (d) => d, // Given an item of the data array, returns the word
49+
marginTop = 0, // top margin, in pixels
50+
marginRight = 0, // right margin, in pixels
51+
marginBottom = 0, // bottom margin, in pixels
52+
marginLeft = 0, // left margin, in pixels
53+
width = 900, // outer width, in pixels
54+
height = 450, // outer height, in pixels
55+
maxWords = 10000, // maximum number of words to extract from the text
56+
fontFamily = "serif", // font family
57+
fontScale = 10, // base font size
58+
padding = 0, // amount of padding between the words (in pixels)
59+
rotate = 0, // a constant or function to rotate the words
60+
} = {},
61+
) {
62+
const words =
63+
typeof text === "string" ? text.split(/\W+/g) : Array.from(text);
5564

56-
const data = d3.rollups(words, size, w => w)
65+
const data = d3
66+
.rollups(words, size, (w) => w)
5767
.sort(([, a], [, b]) => d3.descending(a, b))
5868
.slice(0, maxWords)
59-
.map(([key, size]) => ({text: word(key), size}));
60-
61-
const svg = d3.create("svg")
62-
.attr("viewBox", [0, 0, width, height])
63-
.attr("width", width)
64-
.attr("font-family", fontFamily)
65-
.attr("text-anchor", "middle")
66-
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
69+
.map(([key, size]) => ({ text: word(key), size }));
6770

68-
const g = svg.append("g").attr("transform", `translate(${width / 2},${height / 2})`);
71+
72+
const svg = d3
73+
.create("svg")
74+
.attr("viewBox", [0, 0, width, height])
75+
.attr("width", width)
76+
.attr("font-family", fontFamily)
77+
.attr("text-anchor", "middle")
78+
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
79+
80+
const g = svg
81+
.append("g")
82+
.attr("transform", `translate(${width / 2},${height / 2})`);
6983

7084
const cloud = d3Cloud()
71-
.size([width - marginLeft - marginRight, height - marginTop - marginBottom])
72-
.words(data)
73-
.padding(padding)
74-
.rotate(rotate)
75-
.font(fontFamily)
76-
.fontSize(d => Math.max(Math.sqrt(d.size) * fontScale, 12))
77-
.on("end", words => {
78-
const textElements = g.selectAll("text")
79-
.data(words)
80-
.enter().append("text")
81-
.attr("font-size", d => d.size)
82-
.attr("transform", d => `translate(${d.x},${d.y}) rotate(${d.rotate})`)
83-
.text(d => d.text)
84-
.style("cursor", "crosshair");
85+
.size([width - marginLeft - marginRight, height - marginTop - marginBottom])
86+
.words(data)
87+
.padding(padding)
88+
.rotate(rotate)
89+
.font(fontFamily)
90+
.fontSize((d) => Math.max(Math.sqrt(d.size) * fontScale, 12))
91+
.on("end", (words) => {
92+
const textElements = g
93+
.selectAll("text")
94+
.data(words)
95+
.enter()
96+
.append("text")
97+
.attr("font-size", (d) => d.size)
98+
.attr(
99+
"transform",
100+
(d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`,
101+
)
102+
.text((d) => d.text)
103+
.style("cursor", "crosshair");
85104

86-
// Select the <p> element
87-
const infoText = d3.select("#word-info");
105+
// Select the <p> element
106+
const infoText = d3.select("#word-info");
88107

89-
// Add event listeners to update the <p> element
90-
textElements.on("mouseover", function(event, d) {
91-
infoText.html(`Cause of death: <strong>${d.text}</strong>, Count: <strong>${d.size}</strong>`);
108+
// Add event listeners to update the <p> element
109+
textElements
110+
.on("mouseover", function (event, d) {
111+
infoText.html(
112+
`Cause of death: <strong>${d.text}</strong>, Count: <strong>${d.size}</strong>`,
113+
);
92114
})
93-
.on("mouseout", function() {
115+
.on("mouseout", function () {
94116
infoText.text("Mouse over a word to see its count");
95117
});
96-
});
118+
});
97119

98120
cloud.start();
99121
return svg.node();

bom-website/config.yaml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
baseURL: 'https://deathbynumbers.org'
2-
languageCode: 'en-us'
3-
title: 'Death by Numbers'
1+
baseURL: "https://deathbynumbers.org"
2+
languageCode: "en-us"
3+
title: "Death by Numbers"
44
description: >-
55
Death by Numbers is a digital scholarly research project on the London Plague Bills
66
by the Roy Rosenzweig Center for History and New Media
77
copyright: 2021-2023
8-
98
permalinks:
10-
blog: '/:year/:month/:day/:slug/'
11-
archive: '/:slug/'
12-
page: '/:section/:slug/'
9+
blog: "/:year/:month/:day/:slug/"
10+
archive: "/:slug/"
11+
page: "/:section/:slug/"
1312

14-
theme: 'dbn'
13+
theme: "dbn"
1514

1615
author:
1716
name: Roy Rosenzweig Center for History and New Media
@@ -28,7 +27,7 @@ markup:
2827
unsafe: true
2928
tableOfContents:
3029
startLevel: 2
31-
endLevel: 2
30+
endLevel: 3
3231

3332
sitemap:
3433
changefreq: weekly
@@ -38,4 +37,5 @@ sitemap:
3837
taxonomies:
3938
category: categories
4039
tag: tags
41-
author: author
40+
author: author
41+

bom-website/content/analysis/2022-08-16-chimneys-and-the-great-storm-of-1703.md renamed to bom-website/content/analysis/chimneys-and-the-great-storm-of-1703/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tags:
1616

1717
In late November of 1703, a "great storm" or hurricane struck the British Isles. Bad weather began a few days before the heart of the storm made landfall on November 26th, spawning tornadoes, ripping off roofs and chimneys, and destroying entire fleets. One of the most famous tragedies of the storm happened on the Goodwin Sands, a deadly sandbank off the coast of Kent. At least 53 ships were wrecked on the sandbank and over 2,000 men died just six miles from safety.
1818

19-
{{< figure src="/images/image1.png" caption="Figure 1. Ships being tossed about by a storm at sea." alt="ships being tossed about by a storm at sea" >}}
19+
{{< figure src="image1.png" caption="Figure 1. Ships being tossed about by a storm at sea." alt="ships being tossed about by a storm at sea" >}}
2020

2121
The death and destruction continued throughout southern England, including in the capital city of London. As one contemporary report tells:
2222

@@ -36,9 +36,9 @@ Calculating the number of deaths that can actually be attributed to a storm is d
3636

3737
One thing the bills _can_ tell us, however, is where people were dying throughout the city. This is because location data has been recorded for many of the deaths we have identified as storm related. These locations offer another way to analyze the deaths related to the storm. In the first map, we see deaths by week reported. Pale blue is no deaths in any of our three weeks, light blue is one or more deaths in [“week 50” of 1703](https://deathbynumbers.org/2022/02/14/confusion-of-calendars/) (November 23-30), dark blue with stripes is week 51 (November 30-December 7), and turquoise blue is week 52 (December 7-14). In the second map, we see deaths by parish for weeks 50-52 summed. Purple is no deaths, light blue is 1 death, medium blue is 2 deaths, and dark blue is 3 deaths.
3838

39-
{{< figure src="/images/image4.png" caption="Figure." alt="map described in text" >}}
39+
{{< figure src="image4.png" caption="Figure." alt="map described in text" >}}
4040

41-
{{< figure src="/images/image2.png" caption="Figure." alt="map described in text" >}}
41+
{{< figure src="image2.png" caption="Figure." alt="map described in text" >}}
4242

4343
As we can see on the map, the majority of the deaths were recorded in the parishes “without the walls”—that is, outside the London Wall—with the exception of St. Bennet Fink and St. Katherine Coleman. The visualization provided by the location data from the bills tells us that the highest number of deaths occurred in the parishes furthest from the center of London. This geographic pattern can partly be explained by the infrastructure and rebuilding of the city after the Great Fire of London in 1666. The areas of London that burnt down in the fire were mostly contained inside the London wall, a Roman structure that circled a “mass of medieval streets, lanes and alleys,” where the houses were mostly built of timber (Mortimer 2017, 10). These wooden medieval houses which were packed together inside the London wall were one of the reasons the fire spread so quickly (Mortimer 2017, 21). After the fire, the areas that were burnt down had been almost entirely rebuilt in brick. This shift to newer, more durable infrastructure in the parishes inside the London wall meant that these houses would stand up better to other natural phenomena, such as the high winds associated with hurricanes. As the maps show, the area inside the London wall has the least number of reported deaths due to the storm, and it is possible that the reconstruction of the city after the fire of 1666 plays a role in these low numbers.
4444

0 commit comments

Comments
 (0)