geo-heatmap
Generate geographic visualizations from a CSV of street addresses.
Setup
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Usage
python geo_heatmap.py <csv> [options]
Quick examples
# Auto-detect address column, output hexbin heatmap
python geo_heatmap.py addresses.csv
# Multi-column address, specific output file
python geo_heatmap.py addresses.csv \
--address-cols "Street,City,State,Zip" \
--output heatmap.png
# ZIP code choropleth (no geocoding needed)
python geo_heatmap.py addresses.csv \
--mode choropleth \
--zip-col PostalCode \
--output choropleth.png
# Lock viewport for side-by-side comparison of two years
python geo_heatmap.py 2025.csv --mode choropleth --zip-col PostalCode \
--center "38.9,-77.0" --radius 100 --vmax 50 --output 2025.png
python geo_heatmap.py 2026.csv --mode choropleth --zip-col PostalCode \
--center "38.9,-77.0" --radius 100 --vmax 50 --output 2026.png
Options
Address source (pick one, or omit to auto-detect)
| Flag |
Description |
--address-col COL |
Column containing full address strings |
--address-cols COL1,COL2,... |
Columns to join into a full address (e.g. Street,City,State,Zip) |
--lat-col COL |
Latitude column — skips geocoding entirely |
--lon-col COL |
Longitude column — must be used with --lat-col |
--country-col COL |
ISO country code column (e.g. US, CA) — improves geocoding accuracy |
--zip-col COL |
ZIP code column for --mode choropleth (auto-detected if name contains zip/postal) |
--zip-shapefile PATH |
Path to a local ZCTA .shp file — downloaded automatically on first run if omitted |
Output
| Flag |
Description |
--mode hexbin |
Hexagonal bin density map (default) |
--mode choropleth |
ZIP code choropleth — faster, no geocoding required |
--output FILE, -o FILE |
Output file path (default: heatmap.png) |
--title TEXT |
Map title |
--dpi N |
Image resolution in DPI (default: 150) |
--no-points |
Hide the white dot overlay on hexbin maps |
--pin "LAT,LON,Label" |
Add a labeled pin; repeatable for multiple pins |
--interactive |
Output an interactive HTML map (hexbin mode only) |
Viewport
| Flag |
Description |
--center "LAT,LON" |
Fix the map center (e.g. "38.9,-77.0") |
--radius KM |
Radius in km around --center (default: auto-fit to 95% of points) |
--clip PCT |
Percentile trimmed from each edge when auto-fitting the viewport (default: 5) |
Color scale
| Flag |
Description |
--vmin N |
Minimum count for the color scale (default: auto) |
--vmax N |
Maximum count for the color scale (default: auto) |
--gridsize N |
Hexbin grid resolution — higher = finer detail (default: 50) |
Geocoding
| Flag |
Description |
--cache FILE |
Geocoding cache file (default: .geocache.json) |
Geocoding notes
- Uses Nominatim (OpenStreetMap) — free, no API key required
- Rate-limited to ~1 request/second per Nominatim's usage policy
- Results are cached to
.geocache.json; re-runs skip already-geocoded addresses and the cache is saved every 25 lookups and on Ctrl+C
- When a full address isn't found, falls back to city + state + ZIP
--country-col restricts Nominatim's search to the correct country, improving hit rate for international datasets
Choropleth notes
- Uses Census TIGER ZCTA boundaries (2020 vintage)
- The shapefile (~100 MB) is downloaded automatically on first run and cached to
.zcta_cache/
- Only US ZIP codes (5-digit) are supported; other formats are skipped with a warning
- No geocoding is performed — ZIP codes are read directly from the CSV