- JavaScript 51.8%
- Python 19.4%
- CSS 14.6%
- HTML 13.6%
- Nix 0.6%
|
All checks were successful
Deploy to Forgejo Pages / build-and-deploy (push) Successful in 18s
|
||
|---|---|---|
| .forgejo/workflows | ||
| content | ||
| data | ||
| layouts | ||
| scripts | ||
| static | ||
| .gitignore | ||
| hugo.yaml | ||
| README.md | ||
| requirements.txt | ||
| shell.nix | ||
| TODO.txt | ||
NPS Mortality Dashboard
A static Hugo site that visualizes deaths in U.S. National Parks using the public NPS mortality dataset (CY 2007 — present).
Live site: https://nps.pub.opworks.org/
Disclaimer
This project is independent and not affiliated with, endorsed by, or sponsored by the U.S. National Park Service or any government agency. It is a personal visualization of data the NPS publishes openly at https://www.nps.gov/aboutus/mortality-data.htm.
Each row in the source spreadsheet represents a person who died. The intent of this dashboard is informed risk awareness and transparency — not spectacle. The source dataset does not contain names or other personally identifying information, and none is added here. If anything on the deployed site is inaccurate or feels misrepresented, please open an issue.
If you are in crisis, contact the U.S. Suicide & Crisis Lifeline by calling or texting 988.
What it shows
- Total deaths (filtered)
- An interactive OpenStreetMap of parks, sized by death count, with click filtering and "filter to visible parks" by current map bounds
- Deaths by year (bar) — click a year to drill into months
- Top 5 recreational activities (bar)
- Deaths by age range (bar)
- Unintentional deaths by cause group (bar)
- Medical deaths by type (bar)
- Intentional deaths by cause group (bar)
- Deaths by intent (pie)
- Deaths by sex (pie)
All charts and the map respond to the filter bar at the top — year range, intent, sex, age range, activity, park. Every chart is itself clickable to toggle the corresponding filter.
How it works
scripts/build_data.pyreads the source.xlsxfromdata/and writes a normalizedstatic/data/mortality.json. Categorical values are lower-cased and trimmed during normalization so variants like"Driving "and"Driving"collapse to a single key. The frontend re-capitalizes the first letter for display.scripts/geocode_parks.pylooks up unique park names against the OpenStreetMap Nominatim API (≤1 request/second per their ToS) and caches results indata/park_locations.json. Subsequent runs only geocode parks that aren't in the cache.build_data.pythen copies the cache intostatic/data/park_locations.jsonfor the frontend.- Hugo ships the JSON as a static asset. The dashboard (
layouts/index.html - CI (
.forgejo/workflows/pages.yml) re-runs the build script, geocodes any new parks, builds the site, and deploys to Forgejo Pages on every push tomain.
Local development
Nix shell (recommended)
nix-shell
python3 scripts/build_data.py # one-time: generate the JSON
hugo server -D --bind 0.0.0.0 # http://localhost:1313/
Without Nix
You need Hugo (extended, ≥ 0.147) and Python 3.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python3 scripts/build_data.py
hugo server -D --bind 0.0.0.0
Project layout
.
├── data/ # source spreadsheet (committed)
│ ├── NPS-Mortality-Data-...xlsx
│ └── park_locations.json # geocoded park lat/lon cache
├── scripts/
│ ├── build_data.py # xlsx → static/data/mortality.json
│ └── geocode_parks.py # Nominatim → data/park_locations.json
├── static/
│ ├── css/style.css
│ ├── js/dashboard.js
│ ├── js/map.js
│ └── data/ # generated, git-ignored
├── layouts/
│ ├── _default/baseof.html
│ ├── partials/{head,header,footer}.html
│ └── index.html # the dashboard
├── content/_index.md # placeholder home content
├── hugo.yaml # Hugo config
├── shell.nix # Nix dev shell
├── requirements.txt # Python deps (openpyxl)
└── .forgejo/workflows/pages.yml # CI deploy
Updating the data
When NPS publishes a new spreadsheet:
- Drop the new file into
data/(replace the old one). - Update the path in
scripts/build_data.py(SRC = ...) if the filename changed. - Commit and push — CI rebuilds and redeploys.
Deployment
Pushed to main triggers .forgejo/workflows/pages.yml, which:
- Checkout code
- Installs Hugo, runs
hugo --minify - Build Site Data
python3 scripts/build_data.py - Build static site
- Publishes
public/to Forgejo Pages atnps.pub.opworks.org:8889
Data source
National Park Service Mortality Data, Calendar Years 2007 – present.