A playful map of what California grows, from field-level crop data.
Live: crops.sabhyachhabria.com
Every farm field in the state, coloured by crop, on a hand-tuned pastel basemap. Emoji markers bob over the regions famous for each crop, the "What's in view" legend tallies the fields on screen, clicking a field opens a card with its crop, acreage and county, and search lights up where a crop grows statewide (try "almonds"). Demo videos: laptop, mobile.
A static site with no backend: Vite + TypeScript + MapLibre GL v5, with every map layer read straight from PMTiles archives over HTTP range requests.
| Path | What |
|---|---|
site/ |
The web app (npm run dev, npm run build → site/dist/) |
site/public/data/ |
Small runtime data (crop list, regions, rasters, labels); the .pmtiles are built, not committed |
pipeline/ |
Python + GDAL/tippecanoe pipeline that turns the DWR shapefile into tiles and regions |
data/lookup/ |
Hand-curated crop lookup (DWR code → name, emoji, colour, harvest months) and place names |
assets-src/ |
Icon sources |
docs/ |
Notes on crop codes, regions, icons, fonts, harvest sources, and screenshots |
The app expects four PMTiles archives in site/public/data/: basemap.pmtiles, fields.pmtiles,
overview.pmtiles and fields-low.pmtiles. Build them with the pipeline below, then:
cd site
npm install
npm run dev # http://localhost:5173Needs uv, GDAL (ogr2ogr), tippecanoe and the
pmtiles CLI (brew install gdal tippecanoe pmtiles on macOS).
make setup # Python deps + checks the CLI tools
make download # DWR Statewide Crop Mapping 2023 shapefile (~172 MB)
make all # fields, overview, low-zoom fields, regions -> site/public/data/
pipeline/basemap.sh # California extract of the latest Protomaps daily build (~640 MB at z14)npm run build deliberately leaves the .pmtiles out of dist/: they are too big for most static hosts, so host
them anywhere that serves HTTP range requests with CORS (Cloudflare R2, S3, a plain nginx/Caddy box) and point the
build at them:
VITE_BASEMAP_URL=https://tiles.example.com/basemap.pmtiles \
VITE_FIELDS_URL=https://tiles.example.com/fields.pmtiles \
VITE_OVERVIEW_URL=https://tiles.example.com/overview.pmtiles \
VITE_FIELDS_LOW_URL=https://tiles.example.com/fields-low.pmtiles \
npm run buildThe live site runs on Cloudflare Pages with the tiles on R2. .github/workflows/deploy.yml builds every push:
main deploys to production and other branches to <branch>.<project>.pages.dev previews. It reads the repo
variables TILES_BASE and PAGES_PROJECT and the secrets CLOUDFLARE_API_TOKEN (Cloudflare Pages: Edit) and
CLOUDFLARE_ACCOUNT_ID; forks without them skip the deploy. deploy.sh does the same from a terminal,
r2-cors.json is the bucket's CORS policy, and site/public/_headers sets the Pages cache headers.
- Crop fields: California Department of Water Resources, Statewide Crop Mapping 2023, produced by Land IQ.
- Water (field popup): California Department of Water Resources:
water agency boundaries,
Bulletin 118 groundwater basins and the
critically overdrafted basins. Rebuilt into
site/public/data/water.jsonbynode site/scripts/build-water.mjs. They describe the area a field sits in, not that field's own water supply. - Basemap: © OpenStreetMap contributors (ODbL), via Protomaps.
- Terrain shading: AWS Terrain Tiles (Terrarium).
- Crop icons: Microsoft Fluent Emoji (MIT), plus original SVGs for crops without an emoji (docs/icons.md).
- Font: Figtree (SIL OFL 1.1,
site/public/webfonts/OFL.txt).
Code and original artwork: MIT. Third-party data, icons and fonts keep their own licenses, listed above.
