---
name: "depressions-detection"
description: "💧 Depression Analysis: DTM filled difference + 10cm threshold → Kobe_Depressions.shp + Depressions.qml"
model_tier: 1
triggers:
  - "depressioni"
  - "accumulo acqua"
  - "pozze"
  - "sinkholes"
  - "depressions"
  - "chiuse depresse"
  - "DTM filled"
tools:
  - exec
---

# 💧 Depressioni

## REGOLA ASSOLUTA
Quando serve **depression analysis**, segui:
1. RICEVERE: DTM originale + AOI (opzionale)
2. ESEGUIRE il workflow GRASS qui sotto
3. CONSEGNARE: shapefile + raster + GPKG in ZIP via HTTP

## Input

| Parametro | Obbligo | Note |
|-----------|---------|------|
| **DTM** | Richiesto | GeoTIFF, qualsiasi CRS (meglio UTM per aree in metri) |
| **AOI** | Opzionale | Per clipping finale, WGS84 o UTM (riproiettare se CRS diverso dal DTM) |
| **Soglia** | Opzionale | Default 0.10m (10cm). Per DTM 30m, 10cm filtra rumore SRTM/GLO-30 |

DTM filled NON è richiesto come input: `r.fill.dir` lo genera automaticamente.

## Workflow GRASS

### Setup
```bash
# 1. Crea location dal DTM (IMPORTANTE: la location eredita il CRS dal DTM)
grass -c DTM.tif -e grassdata/depressions

# 2. Importa DTM
grass grassdata/depressions/PERMANENT --exec r.in.gdal -o input=DTM.tif output=dem --overwrite
grass grassdata/depressions/PERMANENT --exec g.region raster=dem

# 3. r.fill.dir → genera dem_filled da dem (NON serve DTM filled pre-esistente)
#    NB: è normale vedere "Found N unresolved areas" dopo il primo passaggio.
#    Il secondo passaggio le riduce. Un piccolo residuo (<1%) è accettabile.
grass grassdata/depressions/PERMANENT --exec r.fill.dir input=dem output=dem_filled direction=dir
```

### Calcolo Profondità
```bash
# 4. Depression depth = DTM_filled - DTM_originale
grass grassdata/depressions/PERMANENT --exec r.mapcalc "dep_depth = dem_filled - dem"

# 5. Soglia minima 0.10m (10cm) per filtrare rumore
grass grassdata/depressions/PERMANENT --exec r.mapcalc "dep_mask = if(dep_depth >= 0.10, dep_depth, null())"
```

### Clumping e Classificazione
```bash
# 5. Raggruppa celle contigue
grass grassdata/depressions/PERMANENT --exec r.clump input=dep_mask output=dep_clumps

# 6. Classifica per profondità
# value=1: Shallow 10-50cm, value=2: Medium 50-100cm, value=3: Deep >100cm
grass grassdata/depressions/PERMANENT --exec r.mapcalc \
  "dep_class = if(dep_mask >= 0.10 && dep_mask <= 0.50, 1, \
    if(dep_mask > 0.50 && dep_mask <= 1.0, 2, \
    if(dep_mask > 1.0, 3, null())))"
```

### Volume & Surface Area Calculation (opzionale)
```bash
# 6b. Volume d'acqua: cella 30m = 900 m². Volume = depth × area cella
grass grassdata/depressions/PERMANENT --exec r.mapcalc "water_volume_m3 = dep_depth * 900"
grass grassdata/depressions/PERMANENT --exec r.mapcalc "water_surface_m2 = if(dep_depth >= 0.10, 900.0, null())"

# 6c. Stats aggregate (totale volume m³, superficie m²)
grass grassdata/depressions/PERMANENT --exec r.univar map=water_volume_m3 output=volume_stats.txt --overwrite
grass grassdata/depressions/PERMANENT --exec r.univar map=water_surface_m2 output=surface_stats.txt --overwrite
echo "--- Volume ---"; cat volume_stats.txt | grep -E "sum|mean"
echo "--- Superficie ---"; cat surface_stats.txt | grep -E "sum|mean"
# Esempio: 894 ha depressioni → volume ~2.73M m³, superficie ~894 ha
```

### Vectorizzazione
```bash
# 7. Raster → Poligoni (usare dep_class, non dep_depth — DBF non supporta float64)
grass grassdata/depressions/PERMANENT --exec r.to.vect -s input=dep_class output=dep_v type=area

# 8. Aggiungi attributi (nomi BREVI! DBF max 10 char)
grass grassdata/depressions/PERMANENT --exec v.db.addcolumn map=dep_v columns="dep_cls varchar(10)"
grass grassdata/depressions/PERMANENT --exec db.execute sql="UPDATE dep_v SET dep_cls = \
  CASE WHEN value=1 THEN 'Shallow' WHEN value=2 THEN 'Medium' WHEN value=3 THEN 'Deep' END"
grass grassdata/depressions/PERMANENT --exec v.db.addcolumn map=dep_v columns="area_ha double precision"
grass grassdata/depressions/PERMANENT --exec v.to.db map=dep_v option=area units=hectares columns=area_ha
```

### Export
```bash
# 9. Esporta shapefile + raster
OUT_NAME="${AOI_NAME:-Depressions}"
grass grassdata/depressions/PERMANENT --exec v.out.ogr input=dep_v output="${OUT_NAME}.shp" format=ESRI_Shapefile --overwrite
grass grassdata/depressions/PERMANENT --exec r.out.gdal -c input=dep_class output="${OUT_NAME}.tif" format=GTiff --overwrite

# 10. GPKG con area_ha via SQL (affidabile — v.to.db spesso produce null)
ogr2ogr -f GPKG "${OUT_NAME}.gpkg" "${OUT_NAME}.shp" -nln depressions -overwrite
ogrinfo "${OUT_NAME}.gpkg" -sql "UPDATE depressions SET area_ha = ROUND(ST_Area(geom) / 10000.0, 1)"

# 11. ZIP
zip -9 "${OUT_NAME}.zip" "${OUT_NAME}".*
echo "✅ Output: ${OUT_NAME}.zip / .gpkg / .shp / .tif"
```

## Stile QGIS
Il file `Depressions.qml` usa lo schema:
- **Deep >100cm** (value=3): blu #1F78B4 opaco
- **Medium 50-100cm** (value=2): blu #1F78B4 50%
- **Shallow 10-50cm** (value=1): blu #1F78B4 25%

Lo stile è categorizzato sull'attributo **`value`** (non `dep_cls`).

## Criteri di Qualità
- Soglia minima 0.10m (10cm) per DTM 30m
- Nomi colonne DBF: max 10 caratteri (usare `dep_cls` non `depth_class`)
- Il GPKG non ha limiti di lunghezza colonne
- Valori DTM in METRI (Float32), non centimetri
- `r.fill.dir` lascia un piccolo residuo di celle non risolte (<1%) — normale, non preoccuparsi

## ⚠️ Pitfalls

### 1. area_ha null in shapefile
- `v.to.db` con `units=hectares` spesso non scrive i valori nel DBF.
- **SOLUZIONE**: dopo l'export, ricalcolare via SQL su GPKG:
  ```bash
  ogrinfo "${OUT_NAME}.gpkg" -sql "UPDATE depressions SET area_ha = ROUND(ST_Area(geom) / 10000.0, 1)"
  ```

### 2. r.fill.dir risoluzione incompleta
- **SINTOMO**: "Found N unresolved areas" dopo il secondo passaggio.
- **REALTÀ**: è normale. Le celle non risolte sono < 1% dell'area. Non influiscono sul risultato.
- Se il numero è anomalo (>5%), controllare che il DTM non abbia buchi o NoData.

### 3. DTM e AOI in CRS diversi
- **SINTOMO**: poligoni fuori dall'AOI o coordinate errate.
- **SOLUZIONE**: riproiettare l'AOI prima di clip:
  ```bash
  ogr2ogr -t_srs "$(grass ... --exec g.proj -j)" AOI_reproj.shp AOI_original.shp
  ```

### 4. Troppi poligoni minuscoli
- La soglia 10cm su DTM 30m cattura rumore SRTM.
- **SOLUZIONE**: alzare soglia a 20-30cm per ridurre falsi positivi, oppure filtrare in QGIS per area minima (es. 0.5 ha).

## Esempio output reale (Etiopia Melkadida, 12.06.2026)
- DTM: `DTM_UTM37_100_Croped.tif` (FathomDEM 30m, UTM37N EPSG:32637)
- r.fill.dir: 2,348 → 221 celle non risolte (<0.01%), ignorate
- Depressions totali: 9,579 celle (894 ha, 0.6% dell'AOI)
- Poligoni: 1,664

| Classe | Soglia | Area (ha) | Poligoni | Area media |
|---|---|---|---|---|
| Shallow | 10-50 cm | 660 | 1,376 | 0.5 ha |
| Medium | 50-100 cm | 128 | 261 | 0.5 ha |
| Deep | >100 cm | 106 | 27 | 3.9 ha |

## File Richiesti
- DTM originale (GeoTIFF, qualsiasi CRS. Se EPSG:4326, convertire a UTM per aree in metri)

## Output
- Kobe_Depressions.shp (poligoni con value, dep_cls, area_ha)
- Kobe_Depressions.gpkg (GeoPackage)
- Kobe_Depressions.tif (raster 1-3)
- Depressions.qml (stile QGIS)

## Riferimenti
- Skill: watershed
- GRASS: r.fill.dir, r.clump

## 💧 SCOLPITO NELLA ROCCIA il 09.06.2026 | Rinominato 12.06.2026 | Refined 12.06.2026 (volume calc + ref file)
## NESSUN CAMBIAMENTO SENZA APPROVAZIONE ESPLICITA DI BULDO
