Skip to content

Latest commit

 

History

History
121 lines (86 loc) · 4.4 KB

File metadata and controls

121 lines (86 loc) · 4.4 KB

Canopy-height tile bake (operator guide)

The coverage and scan tools use measured per-pixel canopy heights to refine the ITU-R P.833-9 path-integrated vegetation loss. Tiles are pre-baked from the ETH Global Canopy Height 2020 dataset (Lang et al. 2023, 10 m global) and served as static PNGs under /tiles/canopy/{z}/{x}/{y}.png.

For the propagation model that consumes these tiles, see ../RF-MODEL.md.

Quick start

The bake auto-downloads the relevant 3°×3° COGs from ETH's libdrive share into output/canopy-source/, then bakes z=8..12 PNG tiles into output/canopy/.

Docker (recommended — no host Python deps)

docker compose --profile bake run --rm canopy-bake

Python directly

pip install -r scripts/requirements-canopy.txt
python scripts/canopy_tiles.py

Default scope is CONUS (lower 48). Without canopy tiles the frontend falls back to class-nominal heights from NLCD (e.g. 20 m for Evergreen Forest) — the coverage tool still works, just less accurate over canopy variation.

First-run footprint (CONUS): ~30 GB downloaded COGs + ~1–2 GB output tiles.

Time: ~1–4 hours total depending on the ETH server and CPU. Idempotent — ctrl-C and re-run to resume.

Sub-region or non-CONUS bake

# Single state / metro
python scripts/canopy_tiles.py --bbox -125 32 -113 43

# Full Earth (~50 GB download, hours of bake time)
python scripts/canopy_tiles.py --scope global

# AK only (uses ETH coverage extent N81..S57)
python scripts/canopy_tiles.py --bbox -170 51 -129 71

Including std-dev

ETH publishes a per-pixel standard-deviation file (*_Map_SD.tif) alongside each height file. The frontend can use it to weight uncertainty: where σ exceeds the height, the value is treated as low-confidence and the model falls back toward the class-nominal value.

Off by default (it doubles the download). Enable with:

python scripts/canopy_tiles.py --include-stddev

All flags

--scope conus|global    Convenience preset for --bbox (default: conus).
--bbox W S E N          Geographic bbox in EPSG:4326. Overrides --scope.
--src-dir PATH          Cache for downloaded COGs (default: output/canopy-source).
--out PATH              Tile output directory (default: output/canopy).
--zooms MIN MAX         Zoom range (default: 8 12; ETH is 10 m native).
--workers N             Bake worker count (default: cpu_count).
--download-workers N    Parallel ETH downloads (default: 2; max 4 — be polite).
--include-stddev        Also download std-dev and pack into B channel.
--force                 Overwrite existing tiles instead of skipping.

How the tiles are served

The Meshinfo API mounts output/canopy (configurable via canopy.tile_dir in config.toml) at /tiles/canopy automatically when canopy.enabled = true.

Both compose files bind-mount ./output:/app/output, so tiles baked on the host appear at /app/output/canopy inside the container with no extra config.

The frontend fetches via the Caddy /api/* proxy:

/api/tiles/canopy/{z}/{x}/{y}.png   →   meshinfo:9000/tiles/canopy/{z}/{x}/{y}.png

Tiles respond with Cache-Control: public, max-age=86400 (1 day).

Troubleshooting

  • Missing dependency: rasterio — install scripts/requirements-canopy.txt.
  • All cells absent — bbox doesn't overlap any ETH-published cell. Most often this means the bbox is over open ocean. Check the ETH tile index at https://langnico.github.io/globalcanopyheight/.
  • Slow downloads — leave --download-workers at 2; raising it does not speed things up much against a single server, and will get you rate-limited.
  • Slow bake — bump --workers once downloads are done.

File format

Each output tile is a 256×256 RGBA PNG:

  • R = high byte of canopy height in metres (uint16 packed into R/G).
  • G = low byte of canopy height.
  • B = std-dev in metres (uint8); 0 if --include-stddev was off.
  • A = 255 valid, 0 nodata. Frontend falls back to NLCD class-nominal on nodata.

A=255 with height=0 is "valid measurement, no canopy here" (e.g. clearing in a forest, fire scar) — distinct from A=0 (the bake didn't cover this region).

Refreshing for a new vintage

ETH has not yet published a v2 (as of 2026). When they do, update ETH_BASE_URL and ETH_QUERY_PATH in canopy_tiles.py to point at the new share, delete output/canopy-source/ and output/canopy/, and re-run the bake.