From 94e881800db42bdd1d138a78390111c656804ab4 Mon Sep 17 00:00:00 2001 From: Jeff Allen Date: Mon, 14 Sep 2026 10:14:02 -0400 Subject: [PATCH] library post content, add footnotes component, tidy up --- .../+page.svelte | 86 ++- .../charts.js | 28 +- .../charts/spatial-access-map.svelte | 183 ++++--- .../charts/travel-time-boxplot.svelte | 513 +++++++++++++----- .../images/toronto-libraries-access-1080.png | Bin 2248502 -> 0 bytes .../toronto-libraries-access-transit-1080.png | Bin 0 -> 1139780 bytes .../toronto-libraries-access-walk-1080.png | Bin 0 -> 1109646 bytes .../meta.json | 17 +- .../2026-09-02/demographics-grid-2160.png | Bin 431233 -> 0 bytes .../published/2026-09-02/embed.json | 14 - .../2026-09-02/spatial-access-map-2160.png | Bin 2364577 -> 0 bytes .../2026-09-02/travel-time-boxplot-2160.png | Bin 81856 -> 0 bytes scripts/validate.js | 14 + src/lib/ChartFrame.svelte | 147 ++++- src/lib/Title.svelte | 18 +- src/lib/footnotes/Footnote.svelte | 104 ++++ src/lib/footnotes/Footnotes.svelte | 112 ++++ src/lib/footnotes/footnoteUtils.js | 25 + src/lib/styles.css | 19 +- src/lib/tags.js | 2 +- 20 files changed, 1012 insertions(+), 270 deletions(-) delete mode 100644 posts/spatial-access-to-public-libraries-toronto/images/toronto-libraries-access-1080.png create mode 100644 posts/spatial-access-to-public-libraries-toronto/images/toronto-libraries-access-transit-1080.png create mode 100644 posts/spatial-access-to-public-libraries-toronto/images/toronto-libraries-access-walk-1080.png delete mode 100644 posts/spatial-access-to-public-libraries-toronto/published/2026-09-02/demographics-grid-2160.png delete mode 100644 posts/spatial-access-to-public-libraries-toronto/published/2026-09-02/embed.json delete mode 100644 posts/spatial-access-to-public-libraries-toronto/published/2026-09-02/spatial-access-map-2160.png delete mode 100644 posts/spatial-access-to-public-libraries-toronto/published/2026-09-02/travel-time-boxplot-2160.png create mode 100644 src/lib/footnotes/Footnote.svelte create mode 100644 src/lib/footnotes/Footnotes.svelte create mode 100644 src/lib/footnotes/footnoteUtils.js diff --git a/posts/spatial-access-to-public-libraries-toronto/+page.svelte b/posts/spatial-access-to-public-libraries-toronto/+page.svelte index f0a77b2..7d0a2ac 100644 --- a/posts/spatial-access-to-public-libraries-toronto/+page.svelte +++ b/posts/spatial-access-to-public-libraries-toronto/+page.svelte @@ -5,12 +5,39 @@ import SpatialAccessMap from './charts/spatial-access-map.svelte'; import DemographicsGrid from './charts/demographics-grid.svelte'; import TravelTimeBoxplot from './charts/travel-time-boxplot.svelte'; + import Footnote from '$lib/footnotes/Footnote.svelte'; + import Footnotes from '$lib/footnotes/Footnotes.svelte'; + import { createFootnoteStore } from '$lib/footnotes/footnoteUtils.js'; + + const footnoteStore = createFootnoteStore(); + + const fBhatt = footnoteStore.addFootnote( + "Bhatt, R. (2010). The impact of public library use on reading, television, and academic outcomes. *Journal of Urban Economics*, 68(2), 148–166. [DOI](https://doi.org/10.1016/j.jue.2010.03.008)." + ); + const fPark = footnoteStore.addFootnote( + "Park, S. J. (2012). Measuring public library accessibility: A case study using GIS. *Library & Information Science Research*, 34(1), 13–21. [DOI](https://doi.org/10.1016/j.lisr.2011.07.007)." + ); + const fAllen = footnoteStore.addFootnote( + "Allen, J. (2019). Mapping differences in access to public libraries by travel mode and time of day. *Library & Information Science Research*, 41(1), 11–18. [DOI](https://doi.org/10.1016/j.lisr.2019.02.001)." + ); + const fCheng = footnoteStore.addFootnote( + "Cheng, W., Wu, J., & Hong, L. (2021). Assessing the spatial accessibility and spatial equity of public libraries' physical locations. *Library & Information Science Research*, 43(2), 101097. [DOI](https://doi.org/10.1016/j.lisr.2021.101097)." + ); + const fDonnelly = footnoteStore.addFootnote( + "Donnelly, F. P. (2014). The geographic distribution of United States public libraries: An analysis of locations and service areas. *Journal of Librarianship and Information Science*, 46(2), 110–129. [DOI](https://doi.org/10.1177/0961000612473099)." + ); +

- + Toronto has 101 public library branches. People who live closer to one visit more often and + borrow more. We mapped how long it takes to reach + the nearest library from across the city, on foot and by public transit. +

+

+ On foot, 35% of residents can reach the nearest library within 15 minutes. The areas least connected to libraries are in more inner-suburban neighbourhoods, areas with fewer libraries and lower population density. When looking at access via public transit, the share of residents unable to access a library within 30 minutes drops to 5%.

@@ -19,10 +46,67 @@ +
+

+ Public libraries offer important programming to serve different population groups, including recent immigrants, youth, and seniors. Research in other contexts has shown that accessibility to libraries varies across travel modes and population groups. Using census data in Toronto, we mapped the geography of a few population groups that often have specific needs for library services and programs, relative to library branch locations. +

+

+ You can view and compare both these sets of maps, including at a more zoomed-in neighbourhood level, on our interactive tool. +

+ +
+ +
+ +

+ Across every group we examined, travel time to the nearest library is close to the citywide figure. Immigrants, low-income households, children, seniors, and visible minority residents all face slightly longer trips on average, but the gaps are small, about 70 seconds at most. In the areas with the longest travel times, some groups are marginally better off than the city as a whole. On this measure, branch locations serve Toronto's population evenly. +

+ +
+ + +
+ +

Methods

+ +

+ We covered the city with a 200 metre hexagon grid and measured outward from each cell. + Travel times were computed in Python using r5py using a pedestrian network from OpenStreetMap and TTC transit schedules, so they follow the routes a person could actually take rather than straight-line distance. Travel times were computed in Python with the r5py package from the centroid of each cell in the 200 metre hexagon grid covering the City of Toronto boundary, for two mode scenarios: walking and public transit. For each mode and hexagon, we estimated the minimum travel time to a branch. Public transit travel times were computed during a late morning window Tuesday (representing a typical weekday) as well as on a Saturday. Weekday and Saturday results were very similar, so we combined them into a single transit metric for the maps on this page. +

+ +

+ Demographic counts from each Dissemination Area (DA) are distributed onto the hexagon grid by area-weighted overlay (most DAs are larger than hexagons, so each DA's population is allocated to the hexagons it covers in proportion to overlap area). For each demographic group, we computed population-weighted means, medians, and percentiles of travel times. For the maps shown above, we used larger census tracts to help visualize neighbourhood differences (some DAs are quite small and would be illegible on maps of this scale, but DAs were more accurate for linking to the hexagon grid). +

+ +

+ Data and code are in our GitHub repository. +

+ +

+ Limitations +

+ +

+ Travel times are computed from hexagon centroids, so results may be sensitive to the choice of grid size and placement. We also only measured travel time to Toronto Public Library branches, so residents near the city boundary may have a closer branch in a neighbouring municipality that offers them at least some services. +

+ +

+ In terms of the demographic data, the 2021 Census data was the most recent available at the time of writing, though population composition in some areas may have shifted since. +

+ +

+ The maps and analysis measures geographic accessibility based on transportation network datasets, not service access. The departure windows were chosen to fall within typical TPL operating hours on both a weekday and a Saturday, but individual branch hours vary and aren't accounted for. We also did not account for the types of services or collection each branch provides, and therefore the population groups it may attract. Filtering through libraries with programming catered to a specific group's needs and interests could give a more nuanced picture of access to library services. +

+ + + +
+ + diff --git a/posts/spatial-access-to-public-libraries-toronto/charts.js b/posts/spatial-access-to-public-libraries-toronto/charts.js index f5655f9..dfd5e86 100644 --- a/posts/spatial-access-to-public-libraries-toronto/charts.js +++ b/posts/spatial-access-to-public-libraries-toronto/charts.js @@ -13,30 +13,34 @@ const embedMeta = import.meta.glob('./published/*/embed.json', { const charts = { 'spatial-access-map': { - title: 'Spatial access to public libraries in Toronto', - subtitle: 'Shortest travel time to the nearest library by public transit and walking', + title: 'Proximity to public libraries in Toronto', + subtitle: + '79% of residents can reach a library within 30 minutes on foot, and 95% of residents can by public transit', source: 'Toronto Public Libraries, City of Toronto, Toronto Transit Commission, OpenStreetMap (2026)', - note: 'Public transit travel times were based on average on Tuesday morning 10-1030am and Saturday 10-1030am', - alt: 'Two maps of Toronto showing travel time to the nearest public library in three bands, under 15 minutes, 15 to 30 minutes, and 30 minutes or more, by walking and by public transit, with library locations and major transit lines overlaid.', + alt: + "Two stacked maps of Toronto covering the same area, the upper one for walking and the lower one for public transit. Both shade a fine hexagon grid into three travel-time bands: dark blue under 15 minutes, mid blue 15 to 30 minutes, and pale blue 30 minutes or more. Yellow dots mark the 101 library branches, thin red lines the major transit routes, and dark outlines the 25 municipal wards, each labelled by number; Etobicoke, North York, East York, Scarborough and Downtown are named. On the walking map dark blue forms a small cluster around each branch, separated by broad pale-blue areas that are largest in western and southern Etobicoke, north-central North York, and northern and eastern Scarborough. On the transit map almost the whole city is mid or dark blue, with pale blue reduced to scattered pockets around the city's outer edges.", widths: [1080], graphicVerb: 'created' }, 'demographics-grid': { - title: "Six demographic groups across Toronto's census tracts, mapped alongside library locations", - subtitle: 'Each map shades census tracts by population share: visible minority, low income, recent and first-generation immigrants, seniors, and children', + title: "Six population groups, mapped relative to library locations", + subtitle: + "Share of each census tract's population, with public library branches marked on every map", source: 'Census of Population, Statistics Canada (2021); Toronto Public Libraries', - alt: 'Six small maps of Toronto census tracts, each shaded by a different demographic measure — visible minority, low income, recent immigrants, first-generation immigrants, seniors, and children — with public library locations marked as dots on every map.', - widths: [1080], + alt: + "Six small maps of Toronto, each shading the city's census tracts into four classes by one population measure, with its own colour ramp and class breaks: visible minority in purple (breaks at 25, 50 and 75 per cent), low income in blue (30, 35, 40), recent immigrants in red (3, 6, 9), first-generation immigrants in teal (35, 50, 65), seniors aged 65 and over in navy (15, 17.5, 20), and children aged 0 to 14 in green (12, 14, 16). Yellow dots mark the public library branches on every panel. Every group is least present in the central corridor running from downtown out to the east end, but concentrations beyond it differ by group: the northwest is highest for low income and recent immigrants, Scarborough for visible minority and first-generation immigrants, and western Etobicoke for seniors.", + widths: [360, 720, 1080], graphicVerb: 'created' }, 'travel-time-boxplot': { title: 'Travel time to the nearest library by demographic group', - subtitle: "Every group's median is at or slightly above the citywide median", - source: 'Census of Population, Statistics Canada (2021); Toronto Public Libraries', - alt: 'Two box-and-whisker charts, walking and public transit, comparing travel time to the nearest library across demographic groups and the overall population, one row per group, showing the 10th, 25th, 50th (median), 75th, and 90th percentiles.', + subtitle: "Every group's median is at or just slightly above the citywide median", + source: 'Census of Population, Statistics Canada (2021); Toronto Public Libraries, City of Toronto, Toronto Transit Commission, OpenStreetMap (2026)', + alt: + "Two panels of horizontal box-and-whisker rows comparing travel time to the nearest library, one panel for walking on an axis running from 0 to about 40 minutes and one for public transit on a 0 to 30 minute axis. Each panel has seven rows: the total population in grey, then visible minority, low income, recent immigrants, first-generation immigrants, seniors aged 65 and over, and children aged 0 to 14, each in that group's own colour from the maps above. Every row shows the 10th and 90th percentiles as whisker ends, the 25th to 75th percentile as a box, and the median as a line inside the box. Within each panel the rows are near-identical: walking medians sit between 19 and 21 minutes and transit medians between 17 and 18, with every group's median within about a minute of the total population's. The walking distributions are much wider than the transit ones, spanning roughly 10 to 38 minutes against 10 to 27.", data: 'travel_time_percentiles.csv', - widths: [1080], + widths: [360, 720], graphicVerb: 'created' } }; diff --git a/posts/spatial-access-to-public-libraries-toronto/charts/spatial-access-map.svelte b/posts/spatial-access-to-public-libraries-toronto/charts/spatial-access-map.svelte index 2a7d5fe..c221f7d 100644 --- a/posts/spatial-access-to-public-libraries-toronto/charts/spatial-access-map.svelte +++ b/posts/spatial-access-to-public-libraries-toronto/charts/spatial-access-map.svelte @@ -1,33 +1,23 @@ -
- - - - {#each wards as ward} - {ward.code.toString().padStart(2, '0')} - {ward.code.toString().padStart(2, '0')} - {/each} - - - {#each legends as legend} -
-
- - Public library -
-
- Travel time by {legend.modeText} to the nearest library (minutes) +
+ {#each panels as panel} +
+ + + + {#each wards as ward} + {ward.code.toString().padStart(2, '0')} + {/each} + {#each places as place} + {place.name} + {/each} + + +
+ Travel time by {panel.modeText} to
the nearest + public library
-
-
- {#each bandColors as color} -
- {/each} - 15 - 30 + +
+
+ + Public library +
+
Travel time (minutes)
+
+
+ {#each bandColors as color} +
+ {/each} + 15 + 30 +
+
+
+ + + + Municipal ward boundaries +
+
+ + Major transit line
-
-
- - - - Municipal ward boundaries
{/each}
diff --git a/src/lib/footnotes/Footnotes.svelte b/src/lib/footnotes/Footnotes.svelte new file mode 100644 index 0000000..a8ec55b --- /dev/null +++ b/src/lib/footnotes/Footnotes.svelte @@ -0,0 +1,112 @@ + + + + + \ No newline at end of file diff --git a/src/lib/footnotes/footnoteUtils.js b/src/lib/footnotes/footnoteUtils.js new file mode 100644 index 0000000..9e08ec7 --- /dev/null +++ b/src/lib/footnotes/footnoteUtils.js @@ -0,0 +1,25 @@ +export function parseMarkdown(text) { + return text + .replace(/\*\*(.+?)\*\*/g, '$1') // bold + .replace(/\*(?!\*)(.+?)\*/g, '$1') // italics + .replace(/\[([^\]]+)]\(([^)]+)\)/g, '$1') // links + .replace(/\n/g, '
'); // line breaks +} + +export function createFootnoteStore() { + let footnotes = []; + + function addFootnote(text) { + const id = footnotes.length + 1; + const parsed = parseMarkdown(text); + footnotes.push({ id, text: parsed }); + return [id, parsed]; + } + + return { + get footnotes() { + return footnotes; + }, + addFootnote + }; +} \ No newline at end of file diff --git a/src/lib/styles.css b/src/lib/styles.css index 89100bc..25201fa 100644 --- a/src/lib/styles.css +++ b/src/lib/styles.css @@ -124,7 +124,7 @@ h1 { font-family: TradeGothicBold; font-weight: normal; font-size: 42px; - color: var(--brandGray90); + color: var(--brandDarkBlue); text-decoration: none; margin-bottom: 0px; padding: 0px; @@ -137,7 +137,7 @@ h2 { font-size: 30px; margin-top: 30px; margin-bottom: 0px; - color: var(--brandBlack); + color: var(--brandDarkBlue); } h3 { @@ -147,7 +147,7 @@ h3 { margin-top: 30px; margin-bottom: 0px; font-size: 25px; - color: var(--brandBlack); + color: var(--brandDarkBlue); } h4 { @@ -155,7 +155,7 @@ h4 { font-family: TradeGothicBold; font-weight: normal; font-size: 20px; - color: var(--brandBlack); + color: var(--brandDarkBlue); } p { @@ -173,7 +173,8 @@ p { } @media screen and (max-width: 600px) { - .text p { + .text p, + .text li { font-size: 17px; line-height: 26px; } @@ -222,10 +223,10 @@ li { font-weight: normal; color: var(--brandGray90); text-align: left; - padding-top: 2px; - padding-bottom: 2px; - font-size: 17px; - line-height: 22px; + padding-top: 4px; + padding-bottom: 4px; + font-size: 18px; + line-height: 30px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; diff --git a/src/lib/tags.js b/src/lib/tags.js index 2499a57..6c6324a 100644 --- a/src/lib/tags.js +++ b/src/lib/tags.js @@ -12,7 +12,7 @@ // existing term over adding a near-synonym. /** Topic tags. Lowercase. */ -export const TAGS = ['urban', 'housing', 'tax', 'policy', 'public transit', 'accessibility', 'demographics', 'public libraries']; +export const TAGS = ['urban', 'housing', 'tax', 'policy', 'public transit', 'accessibility', 'demographics', 'public libraries', 'immigration']; /** * Place names, written as they should appear to a reader — these are published