{"id":50719056,"url":"https://github.com/dmang-dev/ca-grid-weather-map","last_synced_at":"2026-06-09T22:01:17.040Z","repository":{"id":358809099,"uuid":"1242286106","full_name":"dmang-dev/ca-grid-weather-map","owner":"dmang-dev","description":"California utility outages, PSPS events, NWS alerts, wildfires, InciWeb incidents, and live NOAA HRRR wind on one Leaflet map.","archived":false,"fork":false,"pushed_at":"2026-06-05T23:53:28.000Z","size":65770,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T01:20:02.010Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmang-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T09:37:37.000Z","updated_at":"2026-06-05T23:53:31.000Z","dependencies_parsed_at":"2026-05-19T08:02:07.169Z","dependency_job_id":null,"html_url":"https://github.com/dmang-dev/ca-grid-weather-map","commit_stats":null,"previous_names":["dmang-dev/ca-grid-weather-map"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dmang-dev/ca-grid-weather-map","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmang-dev%2Fca-grid-weather-map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmang-dev%2Fca-grid-weather-map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmang-dev%2Fca-grid-weather-map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmang-dev%2Fca-grid-weather-map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmang-dev","download_url":"https://codeload.github.com/dmang-dev/ca-grid-weather-map/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmang-dev%2Fca-grid-weather-map/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34127345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-09T22:01:16.216Z","updated_at":"2026-06-09T22:01:17.028Z","avatar_url":"https://github.com/dmang-dev.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# California Grid Weather Map\n\n**Live demo (stale snapshot):** [dmang-dev.github.io/ca-grid-weather-map](https://dmang-dev.github.io/ca-grid-weather-map/)\n\nInteractive map overlaying live NOAA HRRR 10m wind on California utility\nservice territories, active power outages, PSPS event footprints, NWS\nfire/wind alerts, and active wildfire perimeters.\n\n\u003e The hosted demo's `data/` is auto-refreshed every 2 hours by a GitHub\n\u003e Actions workflow (`.github/workflows/refresh-data.yml`). It runs\n\u003e `fetch_data.py` on an Ubuntu runner, then commits the diff back to\n\u003e `main` if anything changed — GitHub Pages redeploys automatically.\n\u003e The refresh button in the UI is disabled on github.io; clone and run\n\u003e `python serve.py` for live on-demand refreshes. To run a refresh\n\u003e manually right now, hit \"Run workflow\" in the Actions tab.\n\nBuilt to answer the question: *what's blowing where, who's in the dark,\nand is anything on fire?*\n\n```\n[ wind ] x [ outages ] x [ PSPS history ] x [ NWS alerts ] x [ wildfires ]\n        all clipped to and colored by California's 53 electric utilities\n```\n\n## Features\n\n- **Animated 10m wind** from NOAA HRRR (3 km, hourly) rendered as flowing\n  particles via `leaflet-velocity`, color-mapped with Viridis (colorblind-safe)\n- **Active power outages** across PG\u0026E (polygon footprints), SCE / SDG\u0026E / SMUD\n  (point markers) — sourced from California OES\n- **PSPS event footprints** from CPUC's official IOU PSPS layer, marked active\n  vs. historical\n- **NWS active alerts** — Red Flag Warning, Fire Weather Watch, Wind Advisory,\n  High Wind Warning, etc. Polygons are reconstructed from UGC forecast-zone\n  unions and clipped to the California state boundary\n- **Active wildfire perimeters** from NIFC's WFIGS interagency aggregate, plus **InciWeb incident origin points** with links to the full incident pages (news releases, evacuation orders, photos, social media)\n- **All 53 CA utility service territories** from CA Energy Commission, with\n  the 8 major utilities (PG\u0026E, SCE, SDG\u0026E, LADWP, SMUD, IID, PacifiCorp,\n  Liberty) styled distinctly in a Paul Tol bright (CVD-safe) palette\n- **Cursor hover panel** reports county, utility (with annual GWh), sampled\n  wind speed/direction, active alerts, outages affecting the point, PSPS\n  status, and wildfire status\n- **Refresh button** kicks off a server-side re-fetch of all datasets;\n  page auto-reloads when done\n- **WCAG 2.1 AA effort** — landmarks, ARIA labels, skip link, visible focus\n  rings, `prefers-reduced-motion` support, no opacity-based dimming, contrast\n  meets 4.5:1 against the dark UI\n\n## Stack\n\n| Layer       | Tech                                                 |\n|-------------|------------------------------------------------------|\n| Data fetch  | Python 3.11, [Herbie](https://herbie.readthedocs.io) (HRRR via AWS S3), `requests`, `shapely`, `xarray`/`cfgrib` |\n| Server      | `http.server` subclass with a `POST /refresh` endpoint |\n| Frontend    | Leaflet 1.9 + leaflet-velocity 2.1 + Turf.js 7 (CDN), no build step |\n| Tiles       | Carto Dark Matter (OpenStreetMap)                    |\n\nNo build system. No npm. Open `index.html` directly behind the bundled HTTP\nserver and you're done.\n\n## Quick start\n\nPrereqs: **Python 3.11** (3.10+ probably works), Windows / macOS / Linux.\n\n```bash\n# 1. Clone\ngit clone https://github.com/dmang-dev/ca-grid-weather-map.git\ncd ca-grid-weather-map\n\n# 2. Create venv + install (one-time, ~5 minutes — eccodes is the heavy bit)\npython3.11 -m venv .venv\n\n# Pick the line matching your OS:\n.venv\\Scripts\\python -m pip install -r requirements.txt   # Windows (PowerShell or cmd)\nsource .venv/bin/activate \u0026\u0026 pip install -r requirements.txt   # macOS / Linux\n\n# 3. Populate ./data with the first dataset fetch (~25 seconds)\npython fetch_data.py        # works after `activate` on macOS/Linux\n.venv\\Scripts\\python fetch_data.py   # Windows without activating\n\n# 4. Start the local server (port 8000) — also handles POST /refresh\npython serve.py\n.venv\\Scripts\\python serve.py        # Windows alternative\n\n# 5. Open the map\n# http://localhost:8000/\n```\n\nClick the **↻ Refresh data** button at any time to re-pull every layer.\n\n## Desktop app\n\nIf you'd rather have a native window than a browser tab:\n\n```bash\npip install -r requirements-desktop.txt\npython app.py\n```\n\n`app.py` runs the data fetcher once if `data/` is empty, starts the same\nHTTP server on a free port, and opens a native window pointed at it via\n[pywebview](https://pywebview.flowrl.com/). No Chromium bundle, no\nElectron — uses the OS's own webview:\n\n| OS      | Backend           | Install footprint |\n|---------|-------------------|--------------------|\n| Windows | Edge WebView2 (preinstalled on Win 10/11 22H2+) | ~10 MB (pythonnet) |\n| macOS   | System WebKit                                    | ~5 MB              |\n| Linux   | PyQt5 + QtWebEngine (via `pywebview[qt]`)        | ~70 MB             |\n\nIf pywebview can't load for some reason, `app.py` falls back to opening\nthe URL in your default browser.\n\n## Mobile\n\nThe site is a **PWA** — mobile Safari and Chrome both support \"Add to Home\nScreen\" / \"Install app\" against the live demo. That's the zero-friction\nmobile install path: no app store, no native build, no signing.\n\nFor native shells, the `mobile/` directory has a [Capacitor](https://capacitorjs.com)\nwrapper that points to the GitHub Pages URL. Two CI workflows build it:\n\n| Workflow | Artifact | Use |\n|---|---|---|\n| `build-android.yml` | Unsigned debug APK | Side-load via `adb install`. **Not** Play Store — that needs a signing keystore and a separate release flow. |\n| `build-ios.yml` | Unsigned simulator `.app` | Runs only in the iOS Simulator on a Mac. **Cannot** install on a real iPhone or ship through TestFlight / App Store without an Apple Developer Program account. |\n\nBoth download as workflow artifacts from the [Actions tab](https://github.com/dmang-dev/ca-grid-weather-map/actions).\nThe iOS job exists primarily as a compile-time regression test until/unless\nreal device distribution becomes a goal.\n\n## File layout\n\n```\nfetch_data.py         All data fetchers (one function per source).\n                      Each writes a file under data/ and is independently\n                      failure-isolated. Re-run any time.\nserve.py              Static-file server + POST /refresh subprocess shim.\nindex.html            The entire frontend. Single file, no build step.\nrequirements.txt      Python deps (Herbie + cfgrib + eccodes + xarray + shapely + requests).\ndata/                 Fetched datasets (gitignored, regenerated by fetch_data.py):\n  wind.json             HRRR 10m U/V in leaflet-velocity grid format\n  outage_points.geojson Active outages (points) — PGE/SCE/SDGE/SMUD\n  outage_areas.geojson  Active outages (polygons) — PGE only\n  psps.geojson          PSPS events, last 365 days, all CA IOUs\n  nws_alerts.geojson    Active fire/wind NWS alerts, clipped to CA\n  wildfires.geojson     NIFC active wildfire perimeters in CA\n  inciweb.geojson       InciWeb incident origin points (CA bbox)\n  pge_territory.geojson CEC utility service territories (53 features)\n  ca_counties.geojson   California county polygons (58 features)\n  ca_boundary.geojson   CA state polygon (used for alert clipping)\n  manifest.json         Run summary for the banner\n```\n\n## Data sources\n\n| Layer            | Source                                                 |\n|------------------|--------------------------------------------------------|\n| Wind (HRRR)      | NOAA via AWS Open Data (`noaa-hrrr-bdp-pds`)           |\n| Outages          | California OES, *Power Outages (Public View)* ArcGIS feature service |\n| PSPS events      | CPUC, *Consolidated PSPS Map* feature service          |\n| NWS alerts       | api.weather.gov                                        |\n| NWS zone polys   | api.weather.gov (`/zones/forecast/\u003cUGC\u003e`)              |\n| Wildfires        | NIFC WFIGS, *Interagency Perimeters Current* (perimeter polygons) |\n| Fire incidents   | InciWeb via the *Wildfire Aware Inciweb* community mirror (origin points + narrative metadata, links to inciweb.wildfire.gov) |\n| Utility territories | California Energy Commission, *Electric Load Serving Entities (IOU \u0026 POU)* |\n| Counties         | CA OES (county subdivision of outage layer)            |\n| CA boundary      | Public ArcGIS service (cached on first fetch)          |\n\nAll data is from public US/CA government open-data feeds. Attributions\nshown on the map and respected per source terms. Tiles \u0026copy; OpenStreetMap\ncontributors \u0026copy; CARTO.\n\n## Limitations (honest list)\n\n- **LADWP, IID, and PacifiCorp outages aren't visible.** They don't publish\n  to the state-aggregated OES feed and instead use vendor-specific outage\n  maps (mostly Kubra) with undocumented, fragile endpoints. Their territory\n  outlines are shown, but outage events from them are not. PG\u0026E participates\n  in OES with polygon footprints; SCE/SDG\u0026E/SMUD with points only.\n- **Wind data is forecast, not observation.** HRRR is a numerical weather\n  prediction model. For *observed* current wind, RTMA would be the better\n  source — at the cost of slightly more complex GRIB processing.\n- **Alert footprints are forecast zones, not \"as drawn.\"** NWS forecasters\n  may have a tighter mental polygon than the UGC zones we union — but the\n  zone polygons are what's actually published.\n- **PSPS feed is post-event filings.** The CPUC layer reflects events\n  reported by IOUs after the fact. For real-time PSPS notification (e.g.\n  during an active event), check the utility's own PSPS portal.\n- **HRRR bbox edges are visible at very low zoom.** Particles \"escape\" the\n  wind data bounding box and shoot in straight lines outside it. The bbox\n  is set generously around CA so this happens off the visible map at\n  normal zoom — but pan far enough out and you'll see it.\n\n## Accessibility\n\nBuilt to WCAG 2.1 AA targets:\n\n- Semantic landmarks (`\u003cmain\u003e`, `\u003cheader\u003e`, `\u003caside\u003e`, `role=\"region\"`)\n- Skip link, visible focus rings (`:focus-visible`)\n- `aria-live=\"polite\"` on the refresh status; deliberately *not* live on the\n  hover panel (would spam screen readers on every mousemove)\n- `prefers-reduced-motion` disables the animated wind particles; the hover\n  panel still reports wind speed/direction in text\n- All color encodings backed by either a non-color signal (dash patterns,\n  text labels) or use a CVD-safe palette (Viridis for sequential wind speed,\n  Paul Tol bright for qualitative utility colors)\n- Tested mentally against NVDA's tab traversal; not yet user-tested with\n  screen reader users — feedback welcome\n\n## Adding a new data source\n\n1. Add a `fetch_\u003cthing\u003e()` function in `fetch_data.py` that writes a JSON or\n   GeoJSON to `data/\u003cthing\u003e.geojson`\n2. Append it to `results` in `main()`\n3. In `index.html`, add a `fetch('data/\u003cthing\u003e.geojson')` block that builds\n   a Leaflet layer and registers it in `overlays`\n4. If the layer should appear in the cursor hover panel, stash the GeoJSON\n   in `HOVER.\u003cthing\u003e` and add a row plus a PIP block in `updateHover()`\n\nEach fetcher is failure-isolated — if a new source breaks, the others still\npopulate.\n\n## License\n\n[MIT](LICENSE). Have fun.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmang-dev%2Fca-grid-weather-map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmang-dev%2Fca-grid-weather-map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmang-dev%2Fca-grid-weather-map/lists"}