# FloodLens **Repository Path**: izxj/FloodLens ## Basic Information - **Project Name**: FloodLens - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-05 - **Last Updated**: 2026-08-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # FloodLens > Draw a boundary on a satellite map → get a scientifically defensible flood inundation map and time-stepped simulation for that area. Open-source, runs locally, deploys to any cloud with one command. No paid API keys required. [![Python](https://img.shields.io/badge/Python-3.11%2B-blue?logo=python)](https://www.python.org/) [![FastAPI](https://img.shields.io/badge/FastAPI-0.110%2B-009688?logo=fastapi)](https://fastapi.tiangolo.com/) [![React](https://img.shields.io/badge/React-18-61DAFB?logo=react)](https://react.dev/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![stack](https://img.shields.io/badge/stack-React%20%7C%20FastAPI%20%7C%20Celery-blueviolet)](#architecture) --- ## Table of Contents 1. [What It Does](#what-it-does) 2. [Quick Start](#quick-start) - [Docker (Recommended)](#docker-recommended) - [Bare-Metal Dev](#bare-metal-dev) - [Windows Step-by-Step](#windows-step-by-step) 3. [Configuration](#configuration) 4. [Architecture](#architecture) 5. [Methodology](#methodology) - [Data Sources](#data-sources) - [Simulation Modes](#simulation-modes) - [Solver](#solver) - [Design Rainfall](#design-rainfall) - [Hazard Classification](#hazard-classification) - [Honest Limitations](#honest-limitations) 6. [API Reference](#api-reference) 7. [Validation](#validation) 8. [Cloud Deploy](#cloud-deploy) 9. [Repository Layout](#repository-layout) 10. [Key References](#key-references) 11. [Contributing](#contributing) --- ## What It Does FloodLens lets anyone — with no hydraulic modelling background — produce a flood inundation map for any location on Earth: 1. **Draw a polygon** on the map (or upload a GeoJSON / zipped Shapefile) 2. **Pick flood processes**: rainfall-driven (pluvial), river (fluvial), coastal surge, or rapid screening 3. **Hit Run** — the app fetches open global datasets, conditions the terrain, and runs a 2D shallow-water solver 4. **Get results**: animated depth time-series, max depth/velocity rasters, AIDR H1–H6 hazard map, arrival time, exposed buildings/roads/population, and a downloadable PDF report Every result includes a full **assumptions panel** and lists the exact dataset (including any fallback) used — so the outputs are transparent about what they are and are not. --- ## Quick Start ### Docker (Recommended) **Prerequisites:** [Docker Desktop](https://www.docker.com/products/docker-desktop/) (Windows/Mac) or Docker Engine + Compose (Linux). ```bash git clone https://github.com/SumedhG10/FloodLens.git cd FloodLens cp .env.example .env # edit if needed (all defaults work out-of-the-box) docker compose up --build ``` Open **http://localhost:8080** in your browser. The first run downloads Docker images (~2 GB). Subsequent starts use the cached build. --- ### Bare-Metal Dev No Docker, no Redis — fastest iteration loop. **Prerequisites:** Python 3.11+, Node.js 18+. ```bash # Terminal 1 — backend cd backend pip install -r requirements.txt export FLOODLENS_USE_CELERY=false # Linux/macOS export FLOODLENS_DATA_DIR=./data uvicorn app.main:app --port 8000 --reload # Terminal 2 — frontend cd frontend npm install npm run dev # http://localhost:5173 ``` --- ### Windows Step-by-Step ```powershell # Terminal 1 — backend cd backend pip install -r requirements.txt $env:FLOODLENS_USE_CELERY = "false" $env:FLOODLENS_DATA_DIR = "./data" python -m uvicorn app.main:app --port 8000 --reload # Terminal 2 — frontend cd frontend npm install npm run dev # http://localhost:5173 ``` > **Note:** `uvicorn.exe` may not be on PATH on Windows. Use `python -m uvicorn` instead. --- ## Configuration All configuration is via environment variables (12-factor). Copy `.env.example` to `.env` and edit as needed. Every variable has a sensible default — the app runs fully without any paid keys. | Variable | Default | Description | |----------|---------|-------------| | `FLOODLENS_DATA_DIR` | `/data` | Cache + job results directory | | `FLOODLENS_USE_CELERY` | `true` | `false` = in-process thread (dev); `true` = Celery + Redis (prod) | | `FLOODLENS_REDIS_URL` | `redis://redis:6379/0` | Redis URL (only needed when Celery = true) | | `FLOODLENS_MAX_CELLS` | `4000000` | Hard cap on simulation grid cells | | `FLOODLENS_MAX_AOI_KM2` | `2500` | Max area of interest in km² | | `FLOODLENS_IDF_P100_24H_MM` | `150` | 100-yr/24-h design rainfall depth (mm) — **override with local IDF data for real studies** | | `CDSAPI_KEY` | _(empty)_ | Copernicus CDS key — enables GloFAS discharge; falls back to SCS unit hydrograph otherwise | | `VITE_MAPTILER_KEY` | _(empty)_ | Optional higher-quality satellite basemap | --- ## Architecture ``` ┌────────────────────────────────────────────────────────────┐ │ Browser │ │ React + MapLibre + mapbox-gl-draw │ │ • AOI drawing / Shapefile upload │ │ • Control panel (mode, return period, forcing, params) │ │ • Live progress via WebSocket │ │ • Depth animation + hazard map + results panel │ └────────────────┬───────────────────────────────────────────┘ │ HTTP / WebSocket (Vite proxy → :8000 in dev) ┌────────────────▼───────────────────────────────────────────┐ │ FastAPI (:8000) │ │ POST /api/aoi validates polygon, counts cells │ │ POST /api/simulate creates job, dispatches worker │ │ WS /ws/jobs/{id} streams progress + frame URLs │ │ GET /api/results/* rasters (COG), frames, PDF │ └──────┬──────────────────────────────────────────────────────┘ │ in-process thread (dev) or Celery task (prod) ┌──────▼──────────────────────────────────────────────────────┐ │ Simulation Pipeline │ │ │ │ 1. Fetch DEM FABDEM → GLO-30 → SRTM (auto fallback) │ │ 2. Fetch land cover ESA WorldCover 10 m │ │ 3. Estimate soils SoilGrids v2 → HSG │ │ 4. Condition terrain pit-fill, D8, HAND (pyflwdir) │ │ 5. Build forcing IDF hyetograph / GloFAS hydrograph │ │ 6. Run solver 2D local-inertial (numba JIT) │ │ or rapid screen HAND threshold + bathtub │ │ 7. Compute exposure OSM + WorldPop │ │ 8. Write outputs COG GeoTIFFs, PNG frames, PDF │ └─────────────────────────────────────────────────────────────┘ │ (prod only) ┌──────▼──────────┐ ┌──────────────┐ │ Celery Worker │ │ Redis │ │ (same image) │◄───│ task queue │ └─────────────────┘ └──────────────┘ ``` **Job persistence** — each job lives in `FLOODLENS_DATA_DIR//`: ``` spec.json immutable request parameters status.json atomic progress updates (tempfile + rename) outputs/ COG GeoTIFFs, PNG frames, summary.json, report.pdf ``` --- ## Methodology ### Data Sources Every layer is fetched automatically per AOI from open global datasets. All sources and any fallbacks actually used are recorded in the result. | Layer | Primary Source | Fallback(s) | Notes | |-------|---------------|-------------|-------| | **Terrain (DEM)** | FABDEM v1-2 (30 m bare-earth) | Copernicus GLO-30 → SRTM 1″ | FABDEM is **CC BY-NC-SA** (non-commercial only). Switch to GLO-30 in Advanced for commercial use. Hydrologically conditioned before routing. | | **Land cover** | ESA WorldCover 10 m v200 | Uniform grassland (flagged in UI) | Resampled to model grid; drives Manning's n and SCS-CN tables | | **Soils** | SoilGrids v2 REST API (sand/clay %) | HSG "C" mid-range (flagged) | One HSG per AOI; override in Advanced | | **River discharge** | GloFAS-ERA5 1995–2024 (CDS key) | SCS-CN dimensionless unit hydrograph | UI always states which source was used | | **Rainfall** | Generic IDF scaling (configurable) | User-pasted hyetograph | See [Design Rainfall](#design-rainfall) | | **Exposure** | OSM buildings/roads (Overpass API), WorldPop 2020 100 m | OSM only | Building count, road-km, population at risk | | **Basemap** | Esri World Imagery (keyless) | MapTiler (optional key), OSM tiles | Display only — not used in computation | All fetched data is cached under `FLOODLENS_DATA_DIR/cache` and reused across runs for the same tile. --- ### Simulation Modes | Mode | Method | Speed | Honest Label | |------|--------|-------|--------------| | **Pluvial** (rainfall-driven) | Direct rainfall-on-grid into 2D local-inertial shallow-water solver. Infiltration via SCS-CN. Manning's n from ESA WorldCover. | Minutes–hours (grid-size dependent) | Full 2D hydraulics | | **Fluvial** (river) | Upstream inflow hydrograph at cells with highest DEM-derived catchment area, routed through same 2D domain. GloFAS Gumbel-scaled or SCS unit hydrograph fallback. | Minutes–hours | Full 2D hydraulics, estimated inflow | | **Coastal** (surge/tide/SLR) | Prescribed still-water level on detected sea boundary cells; propagated inland by 2D solver. Surge + SLR + wave-setup offsets configurable. | Minutes–hours | Still-water surge only — no wave dynamics | | **Rapid screening** | HAND thresholding (event-dependent stage heuristic) + connectivity-enforced bathtub for coastal. | Seconds | **First look only — not hydraulics** (labelled everywhere) | Multiple modes can run simultaneously; results are overlaid on the map. --- ### Solver The 2D hydraulic solver (`backend/app/solver/local_inertial.py`) implements the **Bates, Horritt & Fewtrell (2010)** local-inertial formulation — the same physics as the LISFLOOD-FP "acceleration" scheme. **Key design choices:** | Feature | Implementation | |---------|---------------| | **Momentum** | Drops convective acceleration → explicit, stable, fast while preserving inertia | | **Friction** | Implicit friction update: `q* = q·exp(−g·n²·|u|·dt/h^(4/3))` | | **Froude limiter** | Caps face velocity at critical flow (Fr ≤ 1) | | **Mass conservation** | Per-cell outflow flux limiter — exact closure, no negative-depth clipping | | **Timestep** | Adaptive CFL: `dt = 0.7·dx / √(g·h_max)` | | **Boundaries** | Manning normal-flow on cells where terrain descends out of domain; still-water on coastal cells | | **Infiltration** | SCS-CN cumulative transform applied per cell per timestep | | **JIT compilation** | All hot loops compiled with `numba @njit` — typically 50–100× faster than pure Python | | **Fallback scheme** | Diffusive-wave (`backend/app/solver/local_inertial.py::_update_fluxes_diffusive`) selectable in Advanced | **Outputs per cell:** max depth, max velocity, depth time series (saved at `save_interval_min`), arrival time. **Mass-balance closure:** tracked throughout; typically < 0.001%. Reported in every result. --- ### Design Rainfall > **Read this before trusting return periods.** There is no defensible universal IDF curve. FloodLens ships a generic mid-latitude parameterisation: - **Base depth:** `P₁₀₀,₂₄` from `FLOODLENS_IDF_P100_24H_MM` (default 150 mm) - **Return period scaling (Gumbel):** `ratio = 0.35 + 0.1411·ln(T_years)`, normalised so T=100 → 1.0 - **Duration scaling:** `P(D) = P(24h) · (D/24)^0.25` — standard IDF power law - **Temporal pattern:** Alternating-block hyetograph (Chow, Maidment & Mays 1988) — places peak intensity at storm centre **For any real study:** either set `FLOODLENS_IDF_P100_24H_MM` from your national IDF service, or paste a custom measured/design hyetograph in the Advanced panel. The assumptions panel repeats this warning on every result. --- ### Hazard Classification Hazard classes follow the **AIDR (2017) Guideline 7-3** depth × velocity framework: | Class | Condition | Interpretation | |-------|-----------|----------------| | H1 | DV ≤ 0.3, D ≤ 0.3 m, V ≤ 2.0 m/s | Generally safe for people | | H2 | DV ≤ 0.6, D ≤ 0.5 m, V ≤ 2.0 m/s | Unsafe for vehicles | | H3 | DV ≤ 0.6, D ≤ 1.2 m, V ≤ 2.0 m/s | Unsafe for children and elderly | | H4 | DV ≤ 1.0, D ≤ 2.0 m, V ≤ 2.0 m/s | Unsafe for all people and vehicles | | H5 | DV ≤ 4.0, D ≤ 4.0 m, V ≤ 4.0 m/s | Buildings vulnerable | | H6 | Beyond H5 | Building failure possible | D = depth (m), V = velocity (m/s), DV = depth–velocity product (m²/s). --- ### Honest Limitations These limitations are surfaced in the assumptions panel on **every result**: - **30 m DEM resolution** — misses kerbs, walls, culverts, small channels. Street-scale depths are indicative, not engineering-grade. - **Flood defences not modelled** — unless they appear as raised terrain in the DEM surface. - **Urban drainage approximated** — only via CN losses; no sewer network routing. - **Design-event uncertainty** — return periods carry large uncertainty, especially under climate change. Generic IDF adds further uncertainty unless overridden. - **Coastal mode: still-water only** — no wave runup, overtopping, or swash dynamics. - **Rapid screening is not hydraulics** — HAND and bathtub are first-look indicators only. - **Results are a view of risk, not a forecast.** They are not a substitute for site-specific engineering assessment. --- ## API Reference ### POST `/api/aoi` Validate polygon and get cell count / runtime estimate before committing to a simulation. **Request body:** ```json { "geometry": { "type": "Polygon", "coordinates": [[...]] }, "resolution_m": 30 } ``` **Response:** ```json { "aoi_id": "uuid", "area_km2": 12.4, "cell_count": 137000, "runtime_estimate_s": 420, "datasets": ["FABDEM v1-2", "ESA WorldCover v200", "SoilGrids v2"], "warnings": [] } ``` --- ### POST `/api/simulate` Start a simulation job. **Request body:** ```json { "aoi_id": "uuid", "models": ["pluvial", "fluvial"], "forcing": { "return_period_y": 100, "duration_h": 24, "amc": "II" }, "params": { "resolution_m": 30, "sim_duration_h": 36, "save_interval_min": 30, "scheme": "local_inertial" } } ``` **Response:** ```json { "job_id": "uuid", "status": "queued" } ``` --- ### WS `/ws/jobs/{job_id}` Stream real-time progress. Messages: ```json { "pct": 45, "stage": "running solver", "frame_urls": ["/api/results/uuid/frames/0030.png"] } ``` --- ### GET `/api/results/{job_id}` Summary JSON including impact metrics and full assumptions block. --- ### GET `/api/results/{job_id}/raster/{name}` Download a COG GeoTIFF (projected UTM, nodata set). `name` ∈ `max_depth`, `max_velocity`, `hazard`, `arrival_time_h`, `rapid_depth` --- ### GET `/api/results/{job_id}/report.pdf` One-page PDF summary (assumptions, data sources, impact metrics). --- ## Validation ### Physics tests (`validation/run_validation.py`) Run offline — no internet required: ```bash cd validation python run_validation.py ``` | Test | Pass criterion | What it checks | |------|---------------|----------------| | Lake at rest | Max depth drift < 5 mm, max V < 0.01 m/s | Well-balancedness (no spurious flow on flat water) | | Mass balance | Closure error < 0.5% | Exact conservation | | Manning normal depth | Error < 10% | Convergence to analytical h = (qn/√S)^(3/5) | | SCS-CN textbook | CN=75, P=127 mm → Q=62.2 mm | Infiltration transform | | Sensitivity | Monotone response | Manning's n and resolution sensitivity | ### Real-event benchmark (`validation/run_real_aoi.py`) **Carlisle, UK — Storm Desmond (December 2015)** - 341 mm / 24 h recorded; ~2,100 properties flooded - Runs pluvial + fluvial + rapid on a 36-h double-peaked hyetograph (AMC III) - Comparison: order-of-magnitude flooded building count, spatial pattern along Eden corridor and Warwick Road ```bash cd validation python run_real_aoi.py ``` > **Known limitations of the benchmark:** 30 m DEM has no defence data; SCS-CN inflow vs gauged peak of ~1,700 m³/s. See `validation/README.md` for full discussion. --- ## Cloud Deploy The compose stack has no managed-service dependency — the same images run anywhere: ```bash # Any Linux VM, ECS, GKE, AKS, Fly.io, Render, ... docker compose up -d ``` **Scaling workers:** ```bash docker compose up -d --scale worker=4 ``` **Persistent state:** mount an S3-backed volume (s3fs / JuiceFS) or a persistent disk and point `FLOODLENS_DATA_DIR` at it. Workers share only the volume and Redis — stateless horizontal scaling. **Reverse proxy:** place nginx or Caddy in front; the backend listens on `:8000`, frontend on `:8080`. --- ## Repository Layout ``` FloodLens/ ├── backend/ │ ├── app/ │ │ ├── solver/ │ │ │ ├── local_inertial.py 2D local-inertial + diffusive solvers (numba JIT) │ │ │ ├── rapid.py HAND screening + bathtub coastal │ │ │ └── hazard.py AIDR H1–H6 depth×velocity classification │ │ ├── data/ │ │ │ ├── dem.py FABDEM / GLO-30 / SRTM fetcher + fallback chain │ │ │ ├── landcover.py ESA WorldCover + Manning-n / CN tables │ │ │ ├── soils.py SoilGrids v2 → HSG estimation │ │ │ ├── rainfall.py IDF scaling + alternating-block hyetograph │ │ │ ├── discharge.py GloFAS-ERA5 + SCS unit hydrograph │ │ │ └── exposure.py OSM buildings/roads + WorldPop population │ │ ├── hydro/ │ │ │ └── preprocess.py DEM conditioning, D8 routing, HAND, inflow/sea masks │ │ ├── core/ │ │ │ ├── runner.py 8-stage simulation pipeline orchestration │ │ │ ├── jobs.py Filesystem-backed job manager (Celery + thread mode) │ │ │ ├── outputs.py COG GeoTIFF + PNG frame writer │ │ │ └── report.py PDF report generator │ │ ├── models/schemas.py Pydantic API contracts │ │ ├── config.py 12-factor configuration │ │ └── main.py FastAPI gateway (REST + WebSocket) │ ├── requirements.txt │ └── Dockerfile ├── frontend/ │ ├── src/ │ │ ├── App.jsx Main application shell + WebSocket client │ │ ├── api.js REST helpers │ │ └── components/ │ │ ├── MapView.jsx MapLibre map + draw tools │ │ ├── ControlPanel.jsx Mode / forcing / params form │ │ ├── ResultsPanel.jsx Impact metrics + assumptions display │ │ ├── TimeSlider.jsx Depth animation scrubber │ │ └── Legend.jsx Hazard H1–H6 colour scale │ ├── package.json │ ├── vite.config.js │ └── Dockerfile ├── validation/ │ ├── run_validation.py Physics unit tests (offline) │ ├── run_real_aoi.py Carlisle Storm Desmond benchmark │ ├── carlisle_aoi.geojson AOI polygon for Carlisle benchmark │ └── README.md Validation methodology and results ├── docker-compose.yml ├── .env.example └── README.md ``` --- ## Key References - **Bates, P.D., Horritt, M.S. & Fewtrell, T.J. (2010)**. A simple inertial formulation of the shallow water equations for efficient two-dimensional flood inundation modelling. *Journal of Hydrology*, 387(1–2), 33–45. - **Hawker, L. et al. (2022)**. A 30 m global map of elevation with forests and buildings removed. *Environmental Research Letters*, 17(2), 024016. — FABDEM. - **Nobre, A.D. et al. (2011)**. Height Above the Nearest Drainage — a hydrologically relevant new terrain model. *Journal of Hydrology*, 404(1–2), 13–29. - **USDA (2004)**. *National Engineering Handbook Part 630: Hydrology*, Chapter 16 — Hydrographs. — SCS-CN and dimensionless unit hydrograph. - **Chow, V.T., Maidment, D.R. & Mays, L.W. (1988)**. *Applied Hydrology*. McGraw-Hill. — Alternating-block hyetograph. - **AIDR (2017)**. *Guideline 7-3: Guideline on Flood Hazard*. Australian Institute for Disaster Resilience. — H1–H6 classification. - **Yamazaki, D. et al. (2017)**. A high accuracy map of global terrain elevations. *Geophysical Research Letters*, 44(11), 5844–5853. — MERIT DEM precedent. --- ## Contributing Contributions welcome — bug reports, data connector improvements, solver optimisations, or new validation cases. 1. Fork the repository 2. Create a feature branch: `git checkout -b feature/my-improvement` 3. Make your changes and add/update tests in `validation/` 4. Open a pull request with a clear description of what changed and why Please keep the **honest limitations** section accurate — if your change relaxes a limitation, update it; if it adds one, add it. --- *Results are a view of risk, not a forecast. Not a substitute for site-specific engineering assessment.*