Interactive demographic + voting explorer for any Michigan State House district. Pulls ACS 5-year data, Detroit precinct shapes + 2024 results, and OSM streets/water; renders a self-contained HTML explorer with map, distributions, sortable table, and precinct results — all in one file suitable for GitHub Pages.
Live demo (HD-9): add your-username.github.io/mi-hd-explorer URL after Pages is configured.
output/district_<N>_explorer.html— single-file interactive site (map, distributions, tracts table, precinct results, saved views, shareable URL state). ~220 KB.output/district_<N>_panels.png— 12-panel matplotlib choropleth deck (population, density, income, race, education, age, tenure, unemployment).output/district_<N>_locator.png— stylized single-panel locator map.- Underlying GeoPackages, CSV summaries, and tract-name tables in
output/.
# 1. Set up
python -m venv .venv && source .venv/bin/activate
pip install -e .
cp .env.example .env # then add your Census API key
# 2. Build everything for HD-9 (Detroit east side)
make all DISTRICT=9 EXCLUDE="26163984100 26163985300"
# 3. Preview locally
open output/district_9_explorer.html
# 4. Publish to GitHub Pages
make publish DISTRICT=9 # copies the explorer into docs/index.html
git add docs/ && git commit -m "Publish HD-9 explorer" && git pushFor any other Michigan house district:
make all DISTRICT=14
make publish DISTRICT=14The pipeline auto-detects the district boundary from TIGER/Line 2024 SLDL and clips tracts/precincts to it.
Seven stages, all idempotent and individually re-runnable:
- fetch_district.py — TIGER district boundary + ACS tract pull (cached) → tracts.gpkg + summary.csv.
- fetch_district_streets.py — OSM streets + water + city boundaries for the cities the district touches.
- fetch_precincts.py — Detroit precinct shapes + 2024 results (joined). Currently Detroit-only.
- name_tracts.py — Auto-name tracts by largest area-overlap with
official Detroit Neighborhoods. Manual overrides via
data/tract_name_overrides.json. - map_district.py — 12-panel matplotlib choropleth PNG.
- make_locator_map.py — stylized website-ready locator PNG.
- make_interactive_map.py — self-contained HTML explorer.
See pipelines/README.md for full details on each script.
- Map tab — choropleth, click to select a tract (⌘-click to add/remove, rectangle tool for bulk-select, click empty area to clear). Selected tracts pop; unselected tracts desaturate to a grayscale dapple that preserves the choropleth pattern as context.
- Distributions tab — 16 histograms, one per metric, with the selection highlighted in navy. Click a bar to select tracts in that bin.
- Tracts tab — sortable table; click row to select. Tract names are shown alongside GEOIDs.
- Precincts tab — separate map, 2024 election results joined to Detroit precinct shapes. Choropleth options: D-margin, election-day turnout, total/D/R votes.
- Sidebar — color-by metric picker, AND-only query builder ("show me tracts where poverty > 30 AND renter % > 60"), live aggregate stats, age histogram, named saved views (localStorage), shareable URL hash.
- After your first
git push, go to repo Settings → Pages. - Source: Deploy from a branch →
main//docs. - Visit
https://<your-username>.github.io/mi-hd-explorer/after a minute or so. - To refresh: re-run
make publish DISTRICT=9and push.
- Python 3.12+
geopandas,pandas,numpy,shapelypytidycensus>=1.2.5(Census API client)osmnx>=2.0(street/water OSM pulls)matplotlib(static map renderers)requests,python-dotenv,pyarrow
A free Census API key is required at build time; the published HTML embeds the resulting data and needs no key to view.
- Coverage: ACS data covers all of Michigan; precinct data currently Detroit-only. Hamtramck / Highland Park / Grosse Pointe Park precincts are a known gap.
- Tract assignment: ≥30% area-overlap with the district.
--exclude <GEOID …>for manual overrides. - Vintage: defaults to ACS 5-year 2020–2024 (released Dec 2025).
Override with
--year 2023for prior vintages. - Aggregates over partial-overlap tracts sum whole-tract counts; fine for a campaign tool, not for statutory precision.
MIT.