FloodWise detects the invisible killer β underground floods. While surface floods are visible and tracked by conventional systems, groundwater saturation builds silently beneath the soil over days and weeks. When the ground can absorb no more, a single rainfall triggers catastrophic flash flooding, crop destruction, and infrastructure collapse β with zero visible warning.
FloodWise uses Copernicus and Galileo satellite data to see what the naked eye cannot: the hidden saturation index beneath agricultural land. By combining radar-derived soil moisture, precipitation accumulation, terrain analysis, and machine learning, FloodWise predicts groundwater flooding before it reaches the surface.
Built for the 11th CASSINI Hackathon: EU Space for Water.
- The Problem: Underground Floods
- Overview
- System Architecture
- Satellites & Data Sources
- Data-to-Model Pipeline
- Prediction Workflow
- User Usage Workflow
- Tech Stack
- Getting Started
- API Reference
- License
What happens:
- Weeks of moderate rain slowly saturate clay-heavy soils
- Groundwater table rises β the soil's absorption capacity drops to zero
- The surface looks normal β no puddles, no rivers overflowing
- Then, a single 10mm rainfall hits β instant flash flood, because the ground is already full
- Crops are destroyed, roads are blocked, communities are cut off
Why it's invisible: Conventional weather warnings only trigger on heavy rainfall. But underground floods happen after cumulative moderate rainfall on specific soil types. Without satellite-based subsurface monitoring, there is no warning.
FloodWise solves this by using Sentinel-1 SAR radar to penetrate the soil surface and measure moisture levels that are invisible to optical sensors and human observers. Combined with terrain slope, soil clay content, hydraulic conductivity, and precipitation accumulation, FloodWise's AI model identifies the exact conditions that precede underground flooding β and issues warnings hours to days before the surface shows any sign of danger.
FloodWise acts as a predictive shield for vulnerable landscapes. By synthesizing multi-source European space data, the platform translates raw orbital telemetry into actionable, localized risk insights.
The platform bridges the gap between raw data and emergency action:
- Subsurface Vision: Uses radar to map soil saturation, seeing what optical sensors miss.
- Predictive Lead Time: Generates warnings long before the saturation point is reached.
- Precision Agriculture Focus: Tailored for agricultural parcels where soil health determines the risk.
- Underground flood prediction β detect invisible groundwater saturation before it causes surface flooding
- Flood risk mapping β per-area risk scores computed from live satellite and meteorological data
- Interactive map with polygon drawing β users draw/select agricultural parcels directly on the map
- Economic impact calculator β estimate crop-specific financial losses based on flood probability
- Early warning alerts β configurable threshold triggers with notification support
- NDWI time-series analysis β Sentinel-2 vegetation/water index charting via Google Earth Engine
- Future period forecasting β multi-day flood predictions using Open-Meteo weather forecasts
- Saved zones β persist monitored areas to the database for long-term tracking
- Historical analysis β compare current readings against historical baselines
graph TB
subgraph "π°οΈ Space Data Sources"
S1[Sentinel-1 SAR<br/>Soil Moisture]
S2[Sentinel-2 MSI<br/>NDWI / Vegetation]
ERA5[ERA5 Reanalysis<br/>Precipitation / Temperature]
DEM[Copernicus DEM GLO-30<br/>Elevation]
GAL[Galileo GNSS-R<br/>Reflectometry]
end
subgraph "π External APIs"
CDSE[Copernicus Data Space<br/>Ecosystem API]
GEE[Google Earth Engine]
CDS[Climate Data Store API]
SG[ISRIC SoilGrids API]
OM[Open-Meteo API]
SH[Sentinel Hub API]
end
subgraph "βοΈ Backend Β· Python / FastAPI"
ETL[ETL Pipeline<br/>etl/pipeline.py]
DC[Data Collector<br/>data_collector.py]
NB["Data Modules<br/>notebooks/*.py"]
TM[Model Training<br/>train_model.py]
ML[ML Model<br/>Random Forest .joblib]
API[REST API<br/>main.py]
DB[(PostgreSQL)]
end
subgraph "π₯οΈ Frontend Β· React / Vite"
LAND[Landing Page]
AUTH[Auth Β· Login / Register]
MAP[Risk Map<br/>Leaflet + Geoman]
ECON[Economic Impact Panel]
DASH[Analytical Dashboard]
NDWI[NDWI Chart Viewer]
end
S1 --> CDSE
S2 --> GEE
ERA5 --> CDS
DEM --> CDSE
GAL --> CDSE
CDSE --> ETL
CDSE --> NB
CDS --> NB
SG --> ETL
GEE --> API
OM --> API
SH --> NB
NB --> API
ETL --> DC
DC --> TM
TM --> ML
ML --> API
API --> DB
API --> MAP
API --> DASH
API --> ECON
API --> NDWI
MAP --> ECON
ECON --> DASH
style S1 fill:#1a5276,color:#fff
style S2 fill:#1e8449,color:#fff
style ERA5 fill:#b7950b,color:#fff
style DEM fill:#6c3483,color:#fff
style GAL fill:#ca6f1e,color:#fff
style ML fill:#c0392b,color:#fff
style DB fill:#2980b9,color:#fff
| Satellite / Dataset | Provider | Data Type | Variables Used | Access Method |
|---|---|---|---|---|
| Sentinel-1 SAR | Copernicus / ESA | C-band SAR (GRD) | VV-polarization backscatter β soil moisture estimation | Sentinel Hub Process API |
| Sentinel-2 MSI | Copernicus / ESA | Multispectral imagery | B3 (Green), B8 (NIR) β NDWI calculation | Google Earth Engine |
| ERA5 Reanalysis | C3S / ECMWF | Gridded reanalysis | Total precipitation (m), 2m temperature (K) | CDS API (GRIB/NetCDF) |
| Copernicus DEM GLO-30 | Copernicus / ESA | Digital Elevation Model | Elevation (m) at 30m resolution | Sentinel Hub Process API |
| GLHYMPS | University of Calgary | Hydrogeology | Hydraulic conductivity (m/day) | Local GeoPackage |
| SoilGrids v2.0 | ISRIC | Soil properties | Clay content (g/kg) at 0-5cm depth | REST API |
| Open-Meteo | Open-Meteo GmbH | Weather forecast | Daily precipitation sum, soil moisture (0-7cm) | REST API |
| Galileo GNSS-R | ESA | GNSS reflectometry | Surface moisture via reflected signals | CDSE Catalogue |
All satellite data is accessed via the Copernicus Data Space Ecosystem. No paid licenses required.
The complete pipeline from raw satellite data to a trained ML model:
flowchart LR
subgraph "1 Β· Data Retrieval"
A1[Base Events CSV<br/>ID_Statie, Lat, Lon,<br/>Data, Inundatie_Target]
A2[CDSE OAuth2 Token]
end
subgraph "2 Β· Feature Engineering"
direction TB
B1["ποΈ Static Features"]
B1a[Clay % Β· SoilGrids API]
B1b[Elevation Β· Copernicus DEM raster]
B1c[Slope Β· slope raster]
B1d[Hydraulic Conductivity Β· GLHYMPS]
B2["π§οΈ Dynamic Features"]
B2a[Precipitation Β· ERA5 via CDSE]
B2b[Soil Moisture Β· Sentinel-1 sigma0]
B1 --> B1a & B1b & B1c & B1d
B2 --> B2a & B2b
end
subgraph "3 Β· Training"
C1[Final Dataset CSV<br/>groundwater_ml_dataset_final.csv]
C2["Random Forest Classifier<br/>n_estimators=100<br/>class_weight='balanced'"]
C3[flood_risk_model.joblib<br/>feature_names.joblib]
end
A1 --> B1 & B2
A2 --> B2
B1a & B1b & B1c & B1d & B2a & B2b --> C1
C1 --> C2 --> C3
style A1 fill:#f8d856,color:#1a240f
style C3 fill:#c0392b,color:#fff
| Step | Source | Module | Description |
|---|---|---|---|
| Base events | Local CSV | data/raw/evenimente_baza.csv |
Station ID, coordinates, date, and flood occurrence label |
| Elevation | Copernicus DEM GLO-30 | notebooks/retrieve_elevation.py |
Fetches elevation via Sentinel Hub evalscript (1Γ1px TIFF) |
| Precipitation | ERA5 Single Levels | notebooks/precipitations.py |
Downloads hourly precipitation GRIB, computes daily mean (mm) |
| Soil moisture | Sentinel-1 GRD (VV) | notebooks/soil.py |
Fetches VV backscatter via Sentinel Hub, converts to dB, normalizes to 0β100% using change detection: SM = ((Οβ_now β Οβ_dry) / (Οβ_wet β Οβ_dry)) Γ 100 |
| Clay content | ISRIC SoilGrids | etl/static_features.py |
REST API query β clay g/kg at 0-5cm β converted to % |
| Slope & altitude | Local rasters | etl/static_features.py |
Sampled from GeoTIFF via rasterio with CRS reprojection |
| Hydraulic cond. | GLHYMPS | etl/static_features.py |
Point-in-polygon lookup using geopandas spatial index |
# train_model.py β Summary
Features: ['elevation', 'mean_precipitation_mm', 'soil_moisture']
Target: 'Inundatie_Target' (binary: 0 = no flood, 1 = flood)
Algorithm: RandomForestClassifier(n_estimators=100, class_weight='balanced')
Split: 80/20 stratified train/validation
Output: models/flood_risk_model.joblib + models/feature_names.joblibRun training:
cd backend
python train_model.pyOnce the model is trained, FloodWise offers three prediction modes:
Predict flood risk for a specific location and date by fetching live satellite data:
sequenceDiagram
participant User
participant API as FastAPI
participant DEM as Copernicus DEM
participant ERA5 as CDS ERA5
participant S1 as Sentinel Hub (S1)
participant ML as RF Model
User->>API: POST /predict-location<br/>{lat, lon, date}
API->>DEM: get_elevation_data(lat, lon)
DEM-->>API: elevation (m)
API->>ERA5: download GRIB for date<br/>compute mean precipitation
ERA5-->>API: mean_precipitation_mm
API->>S1: fetch VV backscatter<br/>dB β moisture %
S1-->>API: soil_moisture (0-100%)
API->>ML: predict([elevation,<br/>precipitation, moisture])
ML-->>API: flood_risk (0/1) + probabilities
API-->>User: {flood_risk, confidence,<br/>probabilities, features}
Predict flood risk for multiple days ahead using weather forecast data:
sequenceDiagram
participant User
participant API as FastAPI
participant DEM as Copernicus DEM
participant OM as Open-Meteo API
participant ML as RF Model
User->>API: POST /predict-forecast<br/>{lat, lon, days: 7}
API->>DEM: get_elevation_data(lat, lon)
DEM-->>API: elevation (m) [static β fetched once]
API->>OM: GET /forecast<br/>daily precipitation_sum +<br/>soil_moisture_0_to_7cm_mean
OM-->>API: 7-day forecast array
loop For each forecast day
Note over API,ML: Features: elevation + daily precip + soil moisture
API->>ML: predict(features_day_N)
ML-->>API: risk + probabilities
end
API-->>User: {predictions: [{date, flood_risk,<br/>confidence, features}, ...]}
Soil moisture mapping (Open-Meteo β model input):
Volumetric water content (mΒ³/mΒ³) β Percentage (0-100%)
Formula: min(100, max(0, volumetric_value Γ 200))
The complete journey from landing page to flood risk analysis:
flowchart TD
LANDING[π Landing Page] --> REG[π Register / Login]
REG --> AUTH_OK{Authenticated?}
AUTH_OK -->|No| REG
AUTH_OK -->|Yes| NAV{Navigate to...}
NAV --> MAP[πΊοΈ Risk Map<br/>Interactive Leaflet Map]
NAV --> DASH_EMPTY[π Dashboard<br/>No area selected]
DASH_EMPTY --> MAP
MAP --> DRAW["βοΈ Draw polygon on map<br/>(Geoman: Rectangle or Polygon)"]
MAP --> CLICK["π Click point on map<br/>(Auto-creates small selection)"]
DRAW --> PANEL[π° Economic Impact Panel]
CLICK --> PANEL
PANEL --> CROP[Select crop type:<br/>Wheat, Corn, Sunflower,<br/>Rapeseed, Potatoes, Orchard]
CROP --> AREA[Adjust area in hectares]
AREA --> DURATION[Set hazard duration: 1-14 days]
DURATION --> CALC["Calculate estimated loss:<br/>Exposure Γ Hazard Γ Vulnerability Γ Risk"]
CALC --> ACTION{User action}
ACTION --> SAVE["πΎ Save Zone<br/>POST /api/zones<br/>β generates NDWI graph<br/>β stores to PostgreSQL"]
ACTION --> GOTO_DASH["π Go to Dashboard<br/>(with query params)"]
SAVE --> DASH[π Analytical Dashboard]
GOTO_DASH --> DASH
DASH --> VIEW_RISK[View Flood Risk %]
DASH --> VIEW_CROP[View Crop Impact %]
DASH --> VIEW_GW[View Groundwater Analysis]
DASH --> VIEW_CHART[View Soil Humidity Evolution]
DASH --> VIEW_NDWI[View NDWI Time-Series Graph]
DASH --> VIEW_STATS[View Rainfall + Slope Stats]
DASH --> SELECT_ZONE[Switch between saved zones]
style LANDING fill:#f8d856,color:#1a240f
style MAP fill:#2a3f10,color:#fff
style DASH fill:#2a3f10,color:#fff
style SAVE fill:#c0392b,color:#fff
When a user saves a zone, the backend:
- Receives polygon coordinates, crop type, area, risk metrics
- Runs
ndwi_timeseries.pyvia subprocess β generates NDWI chart from Sentinel-2 (GEE) - Extracts latest NDWI value from the generated CSV
- Encodes the chart PNG as Base64
- Stores everything in the
saved_zonestable (PostgreSQL) - Returns the saved zone with the embedded graph
| Technology | Purpose |
|---|---|
| Python 3.11+ | Core language |
| FastAPI | REST API framework |
| PostgreSQL 16 | Relational database |
| SQLAlchemy 2.0 | ORM & database models |
| Alembic | Database migration management |
| scikit-learn | Random Forest model training & inference |
| joblib | Model serialization |
| pandas / numpy | Data manipulation |
| rasterio | GeoTIFF raster I/O (DEM, slope) |
| geopandas / shapely | Geospatial operations (GLHYMPS lookup) |
| xarray / cfgrib | NetCDF & GRIB file parsing (ERA5) |
| earthengine-api | Google Earth Engine (NDWI time-series) |
| cdsapi | Copernicus Climate Data Store API client |
| matplotlib | Chart generation |
| tenacity | Retry logic with exponential backoff |
| passlib / python-jose | Password hashing & JWT authentication |
| Technology | Purpose |
|---|---|
| React 19 | UI framework |
| TypeScript | Type-safe development |
| Vite 8 | Build tool & dev server |
| Tailwind CSS 4 | Utility-first styling |
| Leaflet | Interactive mapping |
| Leaflet Geoman | Polygon/rectangle drawing tools |
| React Router 7 | Client-side routing |
| Lucide React | Icon library |
| Technology | Purpose |
|---|---|
| Docker Compose | Multi-service orchestration |
| PostgreSQL 16 Alpine | Database container |
| Uvicorn | ASGI server |
- Docker & Docker Compose
- Copernicus Data Space account (register here)
- (Optional) Google Cloud project with Earth Engine API enabled
- (Optional) CDS API key for ERA5 data (register here)
# 1. Clone the repository
git clone https://github.com/your-org/FloodWise.git
cd FloodWise
# 2. Configure environment
cp .env.example .env
# Edit .env with your credentials
# 3. Build and run
docker compose up --buildServices will be available at:
| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:8000 |
| PostgreSQL | localhost:5432 |
curl http://localhost:8000/ # β {"message": "FloodWise API is running"}
curl http://localhost:8000/health # β {"status": "ok"}# Copernicus Data Space (required for satellite data)
COPERNICUS_CLIENT_ID=your_client_id
COPERNICUS_CLIENT_SECRET=your_client_secret
# Google Earth Engine (optional, for NDWI graphs)
EE_PROJECT=your_gcp_project_id
# Database
DATABASE_URL=postgresql+psycopg://floodwise:floodwise@localhost:5432/floodwise
# Security
SECRET_KEY=replace_with_secure_random_string
ALGORITHM=HS256
# Frontend
VITE_API_URL=http://localhost:8000cd backend
# Option A: Run the full ETL pipeline to generate the enriched dataset
python run_groundwater_etl.py
# Option B: Train from an existing dataset
python train_model.pycd backend
alembic upgrade head # Apply all migrations
alembic revision -m "change" # Create a new migrationDirect prediction from raw feature values.
{
"elevation": 85.0,
"mean_precipitation_mm": 42.5,
"soil_moisture": 65.0
}Response:
{
"flood_risk": 1,
"confidence": 0.87,
"probabilities": { "no_flood": 0.13, "flood": 0.87 }
}Fetch live satellite data and predict for a specific date.
{ "lat": 44.4268, "lon": 26.1025, "date": "2026-04-25" }Multi-day forecast prediction using Open-Meteo weather data.
{ "lat": 44.4268, "lon": 26.1025, "days": 7 }| Method | Endpoint | Description |
|---|---|---|
POST |
/api/zones |
Create a zone (generates NDWI graph) |
GET |
/api/zones |
List all zones for the authenticated user |
POST |
/api/ndwi-graph |
Generate NDWI graph for a polygon |
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Create a new account |
POST |
/auth/login |
Login (returns JWT token) |
GET |
/users/me |
Get current user info |
| Method | Endpoint | Description |
|---|---|---|
GET |
/live-flood/assessment |
Live flood assessment for a monitored area |
GET |
/health |
API health check |
MIT Β© 2026 FloodWise Contributors
Built with π°οΈ Copernicus Data Β· π 11th CASSINI Hackathon: EU Space for Water Β· π·π΄ Romania