Skip to content

Repository files navigation

🛰️ SIAHRA

Spatial Intelligence Atlas for Hazard & Resilience Analytics

แผนที่ข้อมูลเชิงพื้นที่เพื่อการเฝ้าระวังภัยพิบัติของประเทศไทย A 3D geospatial hazard-intelligence platform for Thailand — real-time flood, rainfall, dam and earthquake monitoring rendered as an interactive provincial digital environment.

React TypeScript Three.js Vite Cloudflare Workers Tailwind CSS Status License

thailand disaster-response geospatial webgl three-js react typescript cloudflare-workers durable-objects flood-monitoring earthquake-monitoring digital-twin 3d-visualization open-data


SIAHRA 3D map of Nakhon Ratchasima province: satellite imagery draped over real terrain, GISTDA flood extent picked out in blue across the western districts, a TMD rain-radar sweep, labelled tambon callouts giving flooded area in rai, an earthquake marker, the 72-hour water-level timeline and the layer panel with each layer's data-honesty subtitle

Nakhon Ratchasima in the live 3D viewport — GISTDA satellite flood extent over real terrain, a TMD radar sweep, 49 gauge stations reporting measured levels, and every panel stamped with when its data was fetched.

SIAHRA on a phone-sized viewport (390×844): Chiang Mai province with earthquake markers, weather radar overlay, zoom/compass controls, the vertical-exaggeration switch, the 72-hour water-level timeline and the bottom tab bar SIAHRA mobile bottom sheet opened on the data-layers tab: satellite imagery, TMD radar, GISTDA flood extent, low-lying terrain, alert radii, gauge stations, OSM water and roads — each with its data-honesty subtitle

The same province on a phone-sized viewport (390 × 844) — the desktop panels collapse into a bottom tab bar and a swipe-up sheet, so the map keeps the whole screen; the timeline, exaggeration switch and source-health dots stay reachable with one thumb.


Table of contents

About

SIAHRA renders Thailand's 77 provinces as navigable 3D terrain — real elevation, buildings, roads, rivers and land cover — and overlays it with live hazard intelligence: flood extents, river/rainfall telemetry, dam levels, weather radar and earthquake activity. It exists to make the question "is my area at risk right now, and how did we get here?" answerable in a single interactive view instead of scattered across a dozen agency dashboards.

The project follows a clean separation between three data classes: base geospatial data (terrain, buildings, land cover — slow-changing, pre-processed offline), observation data (rainfall, river levels, radar, seismic events — polled continuously from official sources), and derived data (flood extents, alerts, aggregates — computed by SIAHRA's own edge services). Every panel in the UI shows its data's age and source so nothing is presented as more current or more certain than it actually is.

Features

  • 🗺️ Interactive 3D provincial terrain — Three.js quadtree-tiled terrain with satellite imagery, building extrusions, vegetation and road networks, streamed progressively as you pan and zoom.
  • 🌊 Real-time flood intelligence — GISTDA satellite-derived flood extent overlaid on the terrain, plus live water-level readings from ThaiWater (สสน.) gauge stations with 72-hour history.
  • 🌧️ Rainfall & weather radar — TMD radar composites and rainfall telemetry with a scrubbable timeline.
  • 🏔️ Dam & reservoir monitoring — storage levels and capacity for reservoirs reporting in the selected province.
  • 🌏 Earthquake feed — near real-time seismic events cross-checked across USGS, EMSC and TMD.
  • 📡 Live source-health footer — every panel discloses upstream freshness ("updated N minutes ago") and falls back to "unknown" instead of failing silently when a source is down.
  • 🔗 Shareable permalinks — camera position, province and layer state are encoded into the URL.
  • 📱 Responsive layout — a full desktop control-room view and a condensed mobile sheet for field use.

Tech stack

Layer Technology
3D rendering / client Three.js, React 19, TypeScript, Vite, Tailwind CSS 4
Edge API Cloudflare Workers, Durable Objects (live state / fan-out, SQLite-backed), R2 (immutable geodata artifacts)
ETL / geodata pipeline GDAL-driven Node/TypeScript scripts (tsx), Turf.js, Copernicus GLO-30 DEM, OpenStreetMap
Tooling npm workspaces monorepo, Wrangler, oxlint

Architecture

SIAHRA architecture: five upstream sources (TMD, ThaiWater/HII, GISTDA, USGS/EMSC, and an offline Copernicus DEM + OSM dataset) feed the siahra-api Cloudflare Worker, whose router applies a same-origin guard and rate limiting in front of five SQLite Durable Objects kept warm by a one-minute cron; the offline apps/etl pipeline and the Durable Object archive both write to the R2 bucket siahra-geodata, which the siahra-web Worker reads to serve the tile pyramid to a React 19 and three.js r185 client that calls the API same-origin

Rendered from docs/diagrams/architecture.svg with node docs/diagrams/render.mjs — edit the SVG and re-render.

The Worker's scheduled handler polls upstream sources on a cron and keeps each Durable Object's cache warm, so the first browser request after a quiet period never pays a multi-megabyte upstream fetch inline.

Monorepo layout

SIAHRA/
├── apps/
│   ├── web/     # React + Three.js client (Vite)
│   ├── api/     # Cloudflare Worker — routes, Durable Objects, ingestion
│   └── etl/     # Offline geodata pipeline (terrain/building/feature/land-cover tiles)
├── packages/
│   └── shared-types/   # Types shared between web and api
└── docs/       # Plan, audit & deploy guide

Getting started

Prerequisites: Node.js 20+, npm 10+.

# Install workspace dependencies
npm install

# Run the API (Wrangler, port 8787) and the web client (Vite, port 5173) together
npm run dev

# ...or independently
npm run dev:api
npm run dev:web

Terrain/building tile pyramids are generated by the ETL pipeline and served locally by a Vite middleware:

npm run build:all -w apps/etl        # build tiles for all provinces

The SPA and the API are two separately deployed Cloudflare Workers sharing one origin (siahra-radar.co): siahra-web holds the hostname as a Custom Domain, and siahra-api layers a /api/* route in front of it — Workers on routes run before the origin Worker. So the browser still sees a single origin (no CORS), while a UI change and an API change never have to ship together:

npm run deploy:web                    # build apps/web → dist, deploy siahra-web (static assets only)
npm run deploy:api                    # deploy siahra-api (Durable Objects, cron, R2) — dist not needed

See docs/deploy.md for the full deploy guide.

API

The Worker exposes a versioned JSON API under /api/v1:

Endpoint Description
GET /api/v1/health Freshness/status of every upstream source
GET /api/v1/observations Live rainfall/water-level observations
GET /api/v1/earthquakes/recent · /live Recent and live-polled seismic events
GET /api/v1/flood-extent/summary Nationwide flood-extent summary
GET /api/v1/provinces/:code/flood-extent Flood extent for one province
GET /api/v1/provinces/:code/hazards/latest Latest hazard snapshot for one province
GET /api/v1/dams Reservoir storage levels
GET /api/v1/radar/frames · /radar/frame/:ts.png Weather radar composite frames
GET /api/v1/stations/:id/history Historical readings for one gauge station
GET /api/v1/local-authorities · /:id National local-authority (อปท.) registry, sourced from DLA — static-reference, baked into the build, not live-polled

CI & contributing

ci.yml runs on every push to main and every pull request:

Lint ─────────────┐
TypeScript ───────┼─ independent, always run → required status checks on main
Build ────────────┘
Detect affected ──┬─ Test (api, apps/api)  ┐
                  ├─ Test (web, apps/web)  ├─ only the affected workspaces
                  └─ Test (etl, apps/etl)  ┘
                       └─ Test ─────────────── always reports, gates the legs
  • Lintoxlint over apps/web/src and apps/web/worker
  • TypeScripttsc -b (apps/web), tsc --noEmit (apps/api + its test/ project, apps/etl); the same commands as the pre-push checklist in AGENTS.md
  • Build — production web build, then wrangler deploy --dry-run to bundle both Workers against it, plus a guard on the Workers static-asset limits (≤ 20,000 files, ≤ 25 MiB each)
  • Detect affected — diffs the branch against its merge base and emits the matrix of workspaces whose tests need to run. A change to the lockfile, root package.json, packages/shared-types or ci.yml itself marks all three; an unusable diff base does the same, so the failure mode is "run everything", never "run nothing"
  • Test (name, path) — one vitest run per affected workspace: apps/api in workerd, apps/web and apps/etl as pure modules in node. These names are generated and a leg can be absent, so none of them may ever be a required check
  • Test — the aggregate gate, if: always(). It reports pass/fail for whatever the matrix did, including "nothing was affected", which makes it the only test check that is safe to require. It is not required yet — promoting it is an owner action

Two conventions a branch ruleset can't express — a PR that touches UI files must embed a screenshot (or carry the no-screenshot label), and PR titles/descriptions are English-only — used to live in a pr-rules.yml workflow. They are still the rules, but they are now checked locally by the /implement loop instead of burning Actions minutes on every edit to a PR description. pr-image-cleanup.yml / pr-cache-cleanup.yml tidy up screenshot releases and npm caches when a PR closes; dependabot.yml batches minor/patch bumps weekly and opens majors one-by-one.

Rules on main (.github/rulesets/main.json, applied with scripts/apply-branch-rules.sh): no direct pushes, no force-push or deletion, every change via a pull request with Lint, TypeScript and Build green.

Warning

Only jobs that always run may be required checks. A path-filtered job that never triggers leaves the PR waiting forever.

Agent loop: .claude/ holds the working loop this repo is built with — /implement runs a senior-engineer agent, gates it on a QA agent that runs the same commands as CI plus a headless screenshot pass, loops until the verdict is green, syncs the docs, and then asks before opening a PR (a PreToolUse hook makes sure it never opens one on its own). /review-fix addresses a Codex review in a single batch — P1/P2 only — and closes every thread with a reaction, a reply and a resolve. See AGENTS.md.

Loop engineering diagram: the build loop (/implement) runs Spec, senior-se, qa-verifier with a fail-loop of at most three rounds, then docs-sync, commit and an ask-the-user gate; the review loop (/babysit-prs to /review-fix) runs push and PR, CI checks, Codex review, thread reading, batched fixes and thread closure, re-reviewed on every push with no cap on rounds

The diagram is rendered from docs/diagrams/loop-engineering.svg with node docs/diagrams/render.mjs — edit the SVG and re-render.

Social preview / Open Graph image: docs/images/og-image.png (1280 × 640) is rendered from docs/og/og-image.html with node docs/og/render.mjs — edit the HTML, re-render, and upload the PNG in Settings → General → Social preview.

Contributing: branch, open a PR, and — for anything that touches the UI — put a screenshot in the description (drag-and-drop, or scripts/pr-media.sh "$(git branch --show-current)" shot.png from the CLI and paste the Markdown it prints). Delete the branch once merged. AGENTS.md has the full working agreement, including the data-honesty rules every layer must follow.

Data sources & attribution

ข้อมูลจาก สสน. (ThaiWater), กรมอุตุนิยมวิทยา (TMD), USGS, EMSC, Copernicus DEM และ OpenStreetMap

Layer Source License
Terrain (DEM) Copernicus GLO-30 Open, attribution required
Land cover ESA WorldCover CC BY 4.0
Buildings / roads OpenStreetMap / Geofabrik Thailand extract ODbL
Rainfall / radar / seismic Thai Meteorological Department (TMD) Official API terms
River / rainfall telemetry, dams Hydro-Informatics Institute / ThaiWater (สสน.) Official API terms
Satellite flood extent GISTDA Open Data Commons
Earthquake cross-check USGS, EMSC Public data policy
Local-authority (อปท.) registry Department of Local Administration (DLA) Open Data Common

This is an attribution list, not a claim of authorship or endorsement — these agencies supply the data; they did not build or endorse this application. Several upstream layers referenced in docs/SIAHRA-implement-plan.md (e.g. some LDD soil datasets) carry non-commercial or share-alike terms and are deliberately not wired into the live product; verify licensing before adding any new source.

Project status

SIAHRA is deployed on siahra-radar.co as two independently released Cloudflare Workers — siahra-web (the static SPA and tile proxy) and siahra-api (bound to /api/*). The hazard layers listed in Features above are live: 3D terrain for all 77 provinces, GISTDA flood extent, ThaiWater levels and dams, TMD radar, the earthquake feed and the source-health footer. The Durable-Object-backed API endpoints require a Workers Paid plan — see docs/deploy.md for the deployment prerequisites.

What comes next — the ordered task list, milestones and the work deliberately deferred — is in docs/roadmap.md. docs/SIAHRA-implement-plan.md remains the original research blueprint and data-source inventory; it is not a schedule.

Disclaimer

SIAHRA aggregates and visualizes official hazard data; it is not an early-warning system and does not predict earthquakes. Earthquake science can detect and rapidly characterize events after rupture begins — it cannot forecast the time, place or magnitude of a future quake. Flood extents and levels reflect the freshness and resolution of upstream sources (shown next to every panel) and should not be the sole basis for evacuation or safety decisions — always follow official guidance from TMD, DDPM and local authorities.

License

The code in this repository is released under the MIT License — see LICENSE. That covers the source of the web app, the API Worker and the ETL pipelines, and nothing else.

The data is not ours to license. Every observation, tile and imagery layer SIAHRA fetches or derives stays under the terms of the agency that published it — ThaiWater/HII, TMD, GISTDA, USGS, EMSC, Copernicus DEM, OpenStreetMap, ESA WorldCover and DLA — listed one by one above. MIT applies to the code only; it grants you no right to redistribute those sources, and reusing this code does not transfer their terms to you. If you fork SIAHRA, check each upstream licence for your own use, and keep the attributions.

About

3D geospatial hazard-intelligence platform for Thailand — real-time flood, rainfall, dam and earthquake monitoring on an interactive provincial digital terrain.

Topics

Resources

Security policy

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages