diff --git a/CLAUDE.md b/CLAUDE.md index 162f553..ad4beb1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,7 +76,7 @@ Contents (in addition to `base`): These same four are **also** refreshed to latest on every Codespace create by codespace-starter's `postCreateCommand`. Baking them here pre-installs the dependency tree (so that refresh is a quick update, not a cold build) and is a fallback if GitHub is unreachable. Add further course packages to **both** places (here and the postCreateCommand) following the same pattern. - A basic **Python data-science stack** so students can work in Python alongside R (numpy, pandas, matplotlib, seaborn, scikit-learn, statsmodels, jupyter, ipykernel). Installed with `uv` into a venv at `/opt/venv` from a fully-pinned lockfile — `student/requirements.lock`, generated from `student/requirements.txt` via `uv pip compile` (edit the `.txt` and recompile to bump). Notable choices, all build-test-verified: the venv uses the **system** CPython (`uv venv --python /usr/bin/python3`, not a managed download); it's the default `python` on `PATH`, `--seed`ed (has its own `pip`), and made **group-writable** (`staff`, like R's site-library) so a student can `pip install` more packages. A `python3` Jupyter kernel ("Python (data science)") is registered system-wide, so Python works in VS Code notebooks and in Quarto `.qmd` chunks — Quarto runs Python-only docs via the **jupyter** engine, so **reticulate is not needed** (and isn't installed; `RETICULATE_PYTHON` is still set so reticulate bridges to this venv if someone does install it). Footprint: ~150 MB compressed / negligible Codespace-creation time (baked into the image, restored by the prebuild). - **Observable** for interactive JavaScript visualisation. Quarto's bundled `{ojs}` cells render Observable Plot / OJS inside `.qmd` documents (pass R/Python data in via `ojs_define()`) — this needs **nothing extra**, it's part of the baked Quarto (build-test verified). Separately, **Observable Framework** (`@observablehq/framework`, pinned; adds the `observable` CLI for `create`/`preview`/`build`) is installed for building standalone data-app/dashboard projects — ~84 MB on disk, smoke-tested via `observable --version`. -- A **mapping / census stack** for NYT-style tract maps: `sf` (vector geodata — links against the GDAL/PROJ/GEOS/udunits system libs that `base` bakes for exactly this) and `tidycensus` (ACS/decennial data + geometry in one call; pulls in `tigris`). Live `tidycensus` queries need a free per-student Census API key. Interactive maps come free from the pieces already here: `leaflet` draws `sf` objects, with CARTO basemap tiles instead of Mapbox (no token). The smoke test does an offline `st_sample()` on a CRS-tagged polygon to verify the GDAL/PROJ/GEOS linkage end to end. +- A **mapping / census stack** for NYT-style tract maps: `sf` (vector geodata — links against the GDAL/PROJ/GEOS/udunits system libs that `base` bakes for exactly this) and `tidycensus` (ACS/decennial data + geometry in one call; pulls in `tigris`). Live `tidycensus` queries need a free per-student Census API key — the Census API rejects keyless requests. Interactive maps come free from the pieces already here: `leaflet` draws `sf` objects, with CARTO basemap tiles instead of Mapbox (no token). The smoke test does an offline `st_sample()` on a CRS-tagged polygon to verify the GDAL/PROJ/GEOS linkage end to end. Rounded out by the rest of the Kyle Walker toolkit: `mapgl` (MapLibre/Mapbox GL htmlwidgets — WebGL vector maps with no token via `maplibre()` + CARTO styles), `crsuggest` (suggests the right projected CRS), and `idbr` (Census International Data Base; same Census API key as tidycensus). `mapboxapi` is deliberately excluded — it needs a per-student Mapbox account/token. - **Interactive web-viz + Shinylive**, for "fancy" interactive websites that still publish to **static** hosting (GitHub Pages). R htmlwidgets — `plotly`, `leaflet`, `DT`, `crosstalk` (installed via pak, ~70 MB) — and their Python counterparts `plotly`/`altair`/`folium`/`itables` (in `requirements.lock`) emit self-contained client-side JS. `shiny` + `shinylive` (both R and Python) compile Shiny apps to WebAssembly (webR/Pyodide) so even reactive apps run with no server. (Interactive charts *inside* Quarto docs work via these widgets or `{ojs}`; Shinylive embeds via the per-project `quarto-shinylive` extension.) Does NOT include: diff --git a/README.md b/README.md index c7cd4d0..5a75122 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The `student` image carries both languages students use: - **R** — the rocker/tidyverse foundation, plus the PPBDS course packages (`tutorial.helpers`, `vscode.tutorials`, `misc.tutorials`, `primer.tutorials`), the `arf` console, Quarto, and `httpgd`. - **Python** — a basic data-science stack installed with [`uv`](https://docs.astral.sh/uv/) into `/opt/venv` from a pinned lockfile (`student/requirements.lock`): numpy, pandas, matplotlib, seaborn, scikit-learn, statsmodels, jupyter, ipykernel. It's the default `python` on `PATH`, with a registered Jupyter kernel — so students can use Python in VS Code notebooks or in Quarto `.qmd` chunks (Quarto runs Python via the jupyter engine; no reticulate needed). The venv is group-writable, so students can `pip install` more packages. - **Observable** — Quarto's `{ojs}` cells render interactive [Observable Plot](https://observablehq.com/plot/) / OJS in `.qmd` documents out of the box (no install); plus the [Observable Framework](https://observablehq.com/framework/) CLI (`observable`) for building standalone data-app projects. -- **Mapping / census** — `sf` + `tidycensus` (with `tigris`) for census-tract maps: pull ACS data and geometry in one call, render static maps with `ggplot2` or interactive ones with `leaflet` over free CARTO basemap tiles (no Mapbox token). Live census queries need a [free Census API key](https://api.census.gov/data/key_signup.html). +- **Mapping / census** — `sf` + `tidycensus` (with `tigris`) for census-tract maps: pull ACS data and geometry in one call, render static maps with `ggplot2` or interactive ones with `leaflet` over free CARTO basemap tiles (no Mapbox token). Plus the rest of the [Kyle Walker](https://walker-data.com) toolkit: `mapgl` (WebGL vector maps via MapLibre — token-free with `maplibre()` + CARTO styles), `crsuggest` (pick the right CRS), and `idbr` (international Census data). Live census queries need a [free Census API key](https://api.census.gov/data/key_signup.html). - **Interactive web viz + Shinylive** — for fancy interactive sites on static hosting: R htmlwidgets (`plotly`, `leaflet`, `DT`, `crosstalk`) and Python (`plotly`, `altair`, `folium`, `itables`) emit self-contained client-side JS; **Shinylive** (`shiny` + `shinylive`, R & Python) runs Shiny apps as WebAssembly, so they too deploy to GitHub Pages with no server. Both are baked into the image, so they cost ~nothing at Codespace-creation time (the prebuild restores them). diff --git a/student/Dockerfile b/student/Dockerfile index 5d3c106..369b979 100644 --- a/student/Dockerfile +++ b/student/Dockerfile @@ -82,8 +82,18 @@ RUN R -q -e 'pak::pkg_install(c("plotly", "leaflet", "DT", "crosstalk", "shiny", # API key for live tidycensus queries. P3M Linux binaries, so this is fast. # The interactive path needs nothing more: leaflet (above) draws sf objects, # and free CARTO basemap tiles substitute for Mapbox. -RUN R -q -e 'pak::pkg_install(c("sf", "tidycensus"))' \ - && R --vanilla -e 'for (p in c("sf","tidycensus","tigris")) if (!requireNamespace(p, quietly = TRUE)) stop("smoke test failed to load: ", p)' \ +# +# Plus the rest of the Kyle Walker toolkit: +# - mapgl: MapLibre/Mapbox GL htmlwidgets — WebGL vector maps (the NYT +# look) with NO token via maplibre() + CARTO styles; publishes +# static to GitHub Pages like every other htmlwidget here. +# - crsuggest: suggests the right projected CRS for a dataset — the one- +# function answer to the #1 beginner mapping confusion. +# - idbr: Census International Data Base (population pyramids, country +# time series); uses the same Census API key as tidycensus. +# Deliberately NOT mapboxapi: it requires a per-student Mapbox account/token. +RUN R -q -e 'pak::pkg_install(c("sf", "tidycensus", "mapgl", "crsuggest", "idbr"))' \ + && R --vanilla -e 'for (p in c("sf","tidycensus","tigris","mapgl","crsuggest","idbr")) if (!requireNamespace(p, quietly = TRUE)) stop("smoke test failed to load: ", p)' \ && R --vanilla -e 'library(sf); p <- st_sfc(st_polygon(list(rbind(c(0,0), c(1,0), c(1,1), c(0,1), c(0,0)))), crs = 4326); pts <- st_sample(p, 10); stopifnot(length(pts) == 10); cat("sf geometry ops OK\n")' USER root