diff --git a/.gitattributes b/.gitattributes
index 833788583edb84d91b020995e84578a6cbb4e15c..c2f074340b722519f777f04954dc4e53a0f360c0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -187,3 +187,29 @@ datasets/draft/era5_2009.nc filter=lfs diff=lfs merge=lfs -text
datasets/draft/era5_2010.nc filter=lfs diff=lfs merge=lfs -text
datasets/draft/era5_2011.nc filter=lfs diff=lfs merge=lfs -text
datasets/draft/era5_2012.nc filter=lfs diff=lfs merge=lfs -text
+datasets/bdcavites/cavite_localisee.geojson filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/Descriptif[[:space:]]des[[:space:]]cartes[[:space:]]géologiques[[:space:]]à[[:space:]]1_50[[:space:]]000[[:space:]]vecteur.pdf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shx filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.lyr filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_028/Descriptif[[:space:]]des[[:space:]]cartes[[:space:]]géologiques[[:space:]]à[[:space:]]1_50[[:space:]]000[[:space:]]vecteur.pdf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/Descriptif[[:space:]]des[[:space:]]cartes[[:space:]]géologiques[[:space:]]à[[:space:]]1_50[[:space:]]000[[:space:]]vecteur.pdf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shx filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shp filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.dbf filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.lyr filter=lfs diff=lfs merge=lfs -text
+datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shp filter=lfs diff=lfs merge=lfs -text
diff --git a/README.md b/README.md
index 21edb0babb636cefe7689929519996ef153760c3..42e30bdd74a9a1f514e2367743717de5b094f632 100644
--- a/README.md
+++ b/README.md
@@ -13,8 +13,10 @@ pinned: true
A physics-informed graph neural network that predicts streamflow (discharge,
water level) at gauged and ungauged points along two Normandy rivers, La Eure
-and La Risle, treated as two separate surface-hydrology graphs with
-station-level covariates pulled from half a dozen independent data sources.
+and La Risle — each modeled as a real reach-based network (confluences,
+braided splits/rejoins, ~4,500 nodes per basin including virtual infill
+points), not a single chain of gauges, with covariates pulled from nine
+independent data sources.
```mermaid
flowchart LR
@@ -22,29 +24,31 @@ flowchart LR
ades["ADES
groundwater levels"]
era5["Copernicus ERA5
climate reanalysis"]
otd["Open Topo Data
station elevation"]
- brgm["BRGM
IDPR"]
- bdtopo["IGN BD TOPO
river centerline geometry"]
-
- hubeau --> loaders
- ades --> loaders
- era5 --> loaders
- otd --> loaders
- brgm --> loaders
- bdtopo --> loaders
-
- loaders["src/data/loaders/"] --> nf["node_features.py
one row per station
inputs kept separate from targets"]
- loaders --> bg["build_graph.py
surface edges, ordered by
real river course"]
-
- nf --> pyg
- bg --> pyg["build_pyg_graphs_per_basin"]
-
- pyg --> eure["La Eure graph"]
- pyg --> risle["La Risle graph"]
-
- eure --> app["src/app.py
Streamlit explorer"]
- risle --> app
- eure --> testsuite["test_build_graph.py
validation"]
- risle --> testsuite
+ brgm["BRGM
IDPR · BD Charm-50 geology"]
+ bdtopo["IGN BD TOPO
real reach topology + catchment polygons"]
+ bdcav["Géorisques
BDCavités (sinkholes)"]
+ wc["ESA WorldCover
landcover · NDVI"]
+
+ bdtopo --> brg["build_reach_graph.py
real confluences, splits/rejoins,
gauge snapping"]
+ brg --> brgs["build_reach_graphs.py
~4,500 nodes/basin"]
+
+ hubeau --> nf
+ ades --> nf
+ era5 --> nf
+ otd --> nf
+ brgm --> nf
+ bdcav --> nf
+ wc --> nf
+ brgs --> nf["node_features.py /
enrich_reach_graph.py
date-filtered 2013-2026"]
+
+ bdtopo --> cc["compute_cumulative_catchment.py
graph-wide catchment area"]
+ cc --> nf
+
+ nf --> pyg["build_pyg_graph
x_static / x_dynamic split"]
+ pyg --> phys["physics_losses.py
confluence · split-rejoin ·
routing · water balance"]
+
+ pyg --> app["src/app.py
Streamlit explorer +
network validation view"]
+ pyg --> testsuite["test_build_graph.py
validation"]
```
---
@@ -53,20 +57,29 @@ flowchart LR
```
PoC_v1/
-├── scripts/ # one-off download / extraction scripts
+├── scripts/ # one-off download / extraction / build scripts
│ ├── download_hubeau.py # discharge + water level, Hub'Eau API v2
│ ├── download_elevation.py # point elevations, Open Topo Data
│ ├── download_era5_sample.py # ERA5 sanity-check pull (Jan 2020 only)
│ ├── download_era5_full.py # ERA5 1960–2026, split instant/accum vars
│ ├── extract_era5.py # unzips CDS API's zipped NetCDF output
-│ ├── download_catchment_area.py # Hub'Eau referentiel/sites -> surface_bv
+│ ├── download_catchment.py # Hub'Eau referentiel/sites -> surface_bv
│ ├── download_bdtopo_hydro.py # IGN WFS -> tronçons, surfaces, catchments
│ ├── analyze_bdtopo_hydro.py # centerline export + karst check
│ ├── run_bdtopo_checks.py # karst + catchment cross-check, one shot
-│ └── cross_check_catchments.py # spatial join: station -> containing polygon
+│ ├── cross_check_catchments.py # spatial join: station -> containing polygon
+│ ├── build_reach_graphs.py # real reach-based topology, both basins
+│ ├── enrich_reach_graph.py # runs node_features.py against the reach graph
+│ ├── compute_cumulative_catchment.py # graph-wide catchment area from BD TOPO polygons
+│ ├── diagnose_confluences.py # verify real vs. artifact confluences
+│ ├── download_bdcavites.py # Géorisques BDCavités (sinkhole/cavity inventory)
+│ ├── download_bdcharm.py # BRGM BD Charm-50 harmonized geology, per department
+│ ├── fetch_idpr_brgm.py # (unused — see §3.5) live IDPR re-fetch attempt
+│ ├── fetch_landcover.py # ESA WorldCover landcover class, real gauges
+│ └── fetch_worldcover_ndvi.py # ESA WorldCover NDVI percentile composite
│
├── src/
-│ ├── app.py # Streamlit river explorer (main UI)
+│ ├── app.py # Streamlit river explorer + network validation view
│ ├── generate_plots.py # batch plot generation across all loaders
│ ├── test_build_graph.py # graph-construction test/validation suite
│ ├── extract_river_centerline.py # digitizes a traced map image into a centerline
@@ -76,7 +89,7 @@ PoC_v1/
│ │ │ ├── base.py # BaseDataLoader — shared load()/get_metadata()
│ │ │ ├── hydrometric.py # discharge & water level (Hub'Eau)
│ │ │ ├── ades.py # groundwater levels (ADES)
-│ │ │ ├── safran.py # ERA5 reanalysis, interpolated to stations
+│ │ │ ├── safran.py # ERA5 reanalysis, vectorized station interpolation
│ │ │ ├── idpr.py # infiltration/runoff tendency (BRGM)
│ │ │ ├── catchment.py # per-station catchment area (Hub'Eau)
│ │ │ ├── bdtopo_hydro.py # IGN BD TOPO hydrography (GeoJSON)
@@ -88,8 +101,10 @@ PoC_v1/
│ │ └── river_centerline.py # real-centerline interpolation + gauge snapping
│ │
│ └── graph/
-│ ├── build_graph.py # surface edges + PyTorch Geometric conversion
-│ └── node_features.py # pulls every loader into one feature table
+│ ├── build_graph.py # PyG conversion: x_static/x_dynamic split, structural columns
+│ ├── build_reach_graph.py # real reach topology: confluences, splits/rejoins, MultiDiGraph
+│ ├── node_features.py # pulls every loader into one feature table
+│ └── physics_losses.py # confluence/split-rejoin/routing/water-balance loss terms
│
├── datasets/ # not checked in; populated by the scripts above
│ ├── station_list.csv # raw station roster (X, Y, names, INSEE, etc.)
@@ -100,10 +115,13 @@ PoC_v1/
│ ├── hydrometric/
│ ├── safran/
│ ├── bdtopo_hydro/
-│ └── centerlines/
+│ ├── bdcavites/
+│ ├── bdcharm50/
+│ ├── centerlines/
+│ └── reach_graph/ # {eure,risle}_{nodes,edges}.csv, _nodes_enriched.csv
```
-`scripts/` talks to the outside world (APIs, WFS);
+`scripts/` talks to the outside world (APIs, WFS, S3);
`src/` doesn't — nothing under `src/` makes a network call, and a script
under `src/` that wants one is a bug. Most of `src/data/loaders/` predates
the graph work — general-purpose readers/plotters for each dataset, with
@@ -115,82 +133,200 @@ way around.
## 2. The graph
This is the part everything else in the repo exists to feed. Two graphs, one
-per river — `H4xx…` stations feed the La Eure graph (196 km of real BD TOPO
-course), `H6xx…` feed La Risle (150 km) — built with no edge between them,
-because there's no surface connection between the two basins to model.
+per river — `H4xx…` stations feed the La Eure graph, `H6xx…` feed La Risle —
+built with no edge between them, because there's no surface connection
+between the two basins to model.
-
+The graph is now built from **real reach topology**, not a single ordered
+chain of gauges. `build_reach_graph.py` constructs it directly from BD TOPO's
+own tronçon-to-node linkage (`lien_vers_noeud_hydrographique_ini/fin`) — the
+NEXT_DOWN-equivalent approach — rather than inferring station order from
+position along a digitized line. That means real branching, real confluences,
+and real braided-channel structure fall directly out of the data instead of
+needing to be modeled separately.
-
+### 2.1 Node types
-### 2.1 Nodes
+Four kinds of node, not one:
-A node is one hydrometric station. The full feature set, as of the last
-`test_build_graph.py` run against real data, is 16 columns wide across all 27
-stations combined (15 per basin, once `basin_id` is dropped — see §2.3):
-
-| Feature | Source | What it is |
+| Type | What it is | Column |
|---|---|---|
-| `latitude`, `longitude` | `station_elevations.csv` | station coordinates |
-| `elevation_m` | Open Topo Data (`scripts/download_elevation.py`) | point elevation at the station |
-| `idpr_value` | BRGM IDPR | infiltration-vs-runoff tendency; direct ID join, not interpolated, for this dataset |
-| `avg_groundwater_level_m`, `avg_groundwater_depth_m` | ADES, radius-averaged | nearest-well groundwater condition |
-| `n_nearby_wells` | ADES | how many wells contributed to the average above — worth checking before trusting the groundwater value, since some stations have zero |
-| `climate_temp_C`, `climate_wind_speed_ms`, `climate_solar_Wm2`, `climate_precip_mm`, `climate_evap_mm`, `climate_snow_mm`, `climate_runoff_mm` | ERA5, interpolated to the station point | mean/sum over the record, by variable |
-| `avg_groundwater_level_m__was_missing`, `avg_groundwater_depth_m__was_missing`, (and similarly for any other feature with real gaps) | auto-generated | 1 if that station had no data for the column before mean-filling, 0 otherwise |
-
-`basin_id` is also computed but excluded from each per-basin graph's own
-feature set, since it's constant there and would just be a zero-variance
-column sitting in `x` for no reason.
+| Real gauge | one of the 27 hydrometric stations | `is_gauged` |
+| Real confluence | a genuinely different, independently-sourced river joins | `is_confluence` |
+| Split / rejoin | a channel divides and later recombines (braiding, an anabranch) — same water, no new mass | `is_split_point` / `is_rejoin_point`, paired via `braid_id` |
+| Virtual (infill) | inserted along long confluence-free stretches so "predict at any point" has real spatial resolution | none of the above |
+
+A **confluence** requires more than a shared node with in-degree ≥ 2 — BD
+TOPO's fine tronçon segmentation produces plenty of same-river multi-inflow
+points with no real branching involved (confirmed against real data:
+incoming-edge distances as short as 4.6 m at some falsely-flagged
+"confluences"). The real test (`find_real_confluences` in
+`build_reach_graph.py`) requires (a) more than one distinct *normalized* river
+name among the incoming edges — river-name normalization strips articles,
+parenthetical qualifiers, and "bras de/du/d'" (arm-of) prefixes, since a named
+secondary channel of the same river ("Bras de la Charentonne") isn't a
+different river — and (b) that those branches don't trace back to a common
+upstream **split** within 15 km, which would mean it's a rejoin, not a
+confluence. Splits themselves need no such disambiguation: out-degree ≥ 2 is
+an unambiguous physical definition on its own, since a split by construction
+has exactly one thing flowing in.
+
+Real branching topology also meant the underlying graph had to move from a
+plain `DiGraph` to a `MultiDiGraph` — two distinct tronçons directly
+connecting the same two hydrographic nodes (exactly the shape a short braid
+takes) is real data, not a collision, and a plain `DiGraph` was silently
+**overwriting** the second such edge's data on `add_edge` rather than keeping
+both. Confirmed as a real bug with real impact, not just a synthetic-test
+concern: fixing it recovered dozens of previously-invisible parallel edges
+per basin on the actual data.
+
+### 2.2 Node and edge features
+
+The feature set now spans several independent sources, each merged onto the
+node table by `node_features.py`'s `add_*_features` functions. Every column
+lands in exactly one of four places once `build_pyg_graph` processes it:
-**Targets are not features.** `target_discharge_m3s_mean/std/count` and
-`target_waterlevel_mm_mean/std/count` exist on the enriched table but never
-enter `x` — they're pulled out into `data.y` separately. A model that could
-see these as inputs would be reading the answer off the label, not learning
-anything.
+```mermaid
+flowchart TD
+ raw["Enriched node table
(node_features.py)"]
+
+ raw --> struct{"structural /
graph-role column?"}
+ struct -->|"is_gauged, is_confluence,
is_split_point, is_rejoin_point,
braid_id, snap_distance_km"| structout["data.is_gauged, data.is_confluence, ...
own Data attribute — never in x"]
-**The missingness flags aren't decorative.** A station with zero nearby wells
-gets its groundwater value silently filled with the basin mean — without the
-flag, that looks identical to a station whose real groundwater level happens
-to be close to average. Given how unevenly some of these datasets cover the
-two basins (the Eure's southern reach has essentially no ADES wells within
-range at all), pretending "filled with the mean" and "actually near the mean"
-are the same thing would be a real information loss, not just tidiness.
+ raw --> tgt{"target_* column?"}
+ tgt -->|"target_discharge_m3s_*
target_waterlevel_mm_*"| y["data.y
never in x — label leakage otherwise"]
-### 2.2 Edges
+ raw --> feat{"real model input"}
+ feat -->|"static: elevation_m, idpr_*,
catchment_area_km2, landcover_*,
geology_*, cavites distance/count"| xstatic["data.x_static"]
+ feat -->|"dynamic: climate_*,
avg_groundwater_*, ndvi_*
(period-aggregate, not a real series yet)"| xdynamic["data.x_dynamic"]
-An edge is "these two gauges are consecutive along the river," in the
-direction water actually flows — upstream to downstream. Three attributes per
-edge:
+ xstatic --> x["data.x — full combined tensor,
z-scored"]
+ xdynamic --> x
+
+ edges["Edge table
(build_reach_graph_tables)"] --> eattr{"numeric edge
attribute?"}
+ eattr -->|"distance_km,
elevation_drop_m,
verified_continuous"| edgeattr["data.edge_attr
[n_edges, 3]"]
+ eattr -->|"toponym, cleabs
(diagnostic metadata)"| meta["not used by build_pyg_graph —
stays in edges_df only"]
+```
+
+**Node features:**
+
+| Feature | Source | Coverage |
+|---|---|---|
+| `latitude`, `longitude`, `elevation_m` | station coords / real BD TOPO tronçon Z | every node |
+| `idpr_value`, `idpr_nearest_point_distance` | BRGM IDPR | every node (spatial fallback for non-gauge codes) |
+| `catchment_area_km2` | Hub'Eau, cumulative, real gauges only | 27 stations |
+| `cumulative_catchment_area_km2` | BD TOPO incremental polygons, summed upstream via real graph topology | graph-wide (~98% of nodes) |
+| `landcover_*` (one-hot) | ESA WorldCover 10 m classification | real gauges only, for now |
+| `ndvi_p10`, `ndvi_p50`, `ndvi_p90` | ESA WorldCover NDVI percentile composite | real gauges only, for now |
+| `geology_*` (one-hot) | BRGM BD Charm-50, point-in-polygon | real gauges only, for now |
+| `distance_to_nearest_cavity_km`, `n_cavities_within_20km` | Géorisques BDCavités, KD-tree + haversine | real gauges only, for now |
+| `avg_groundwater_level_m`, `avg_groundwater_depth_m`, `n_nearby_wells` | ADES, radius-averaged, KD-tree + exact haversine | every node |
+| `climate_*` (temp/wind/solar/precip/evap/snow/runoff) | ERA5, vectorized station interpolation | every node (needs `safran_path`) |
+| `{col}__was_missing` | auto-generated | any feature column with real gaps |
+
+**Edge features** — three numeric attributes per edge, from
+`build_reach_graph.py`'s `build_reach_graph_tables`:
| Feature | Meaning |
|---|---|
-| `distance_km` | along-river distance between the two stations |
-| `elevation_drop_m` | elevation difference, upstream minus downstream (should be positive; negative would flag a real problem) |
-| `verified_continuous` | `False` for any edge deliberately flagged via `known_losing_reaches` |
-
-That last one exists because of a specific, real finding: two stations on La
-Risle are named *"[amont bétoire]"* and *"[aval bétoire]"* in Hub'Eau's own
-data — *bétoire* being the Normandy term for a karst swallow-hole, where a
-river can vanish underground and resurface downstream. Three consecutive
-edges spanning that stretch (`H605641101 → H605022010 → H605641401 →
-H605641201`) are flagged `verified_continuous=False` rather than the graph
-silently assuming water flows continuously through a reach that might not
-carry it at the surface at all. An edge existing in this graph means "these
-stations are sequential along the mapped course," not "surface flow between
-them is guaranteed."
-
-Ordering within a basin comes from the station's position along the real
-digitized river course where one's available (both basins now have real BD
-TOPO-derived centerlines — see §3.8), falling back to elevation or latitude
-otherwise. Every edge is built between *consecutive* stations in that order,
-which means each basin is currently a single chain, not a branching network.
-Tributaries aren't represented. That's the single biggest structural
-simplification in the graph as it stands — see §6.
-
-
-
-### 2.3 Two graphs, not one
+| `distance_km` | along-river distance between the two endpoint nodes |
+| `elevation_drop_m` | elevation difference, upstream minus downstream — negated on the reverse edge when `bidirectional=True` |
+| `verified_continuous` | `False` for any edge deliberately flagged via `known_losing_reaches` (the bétoire stretch — §3.7) |
+
+`toponym` and `cleabs` also live on the real edges table (the tronçon's river
+name and unique BD TOPO ID) but are diagnostic metadata, not model input —
+`build_pyg_graph` selects `edge_attr` columns by explicit name, so extra
+columns like these pass through harmlessly rather than needing to be
+stripped out first.
+
+**Structural columns never enter `x`.** `is_gauged`, `is_confluence`,
+`is_split_point`, `is_rejoin_point`, `snap_distance_km`, `braid_id` describe
+node *role*, not a physical covariate — `build_pyg_graph`'s auto-detection
+excludes them explicitly (confirmed as a real, not hypothetical, bug once:
+pandas treats `bool` as a numeric dtype, so without this exclusion these
+columns were being silently z-scored and fed to the model as if they were
+elevation or precipitation). They're still attached to the returned `Data`
+object as their own typed attributes, for masking supervised loss to gauged
+nodes and for the physics-loss index builders.
+
+**Landcover and geology are one-hot, not a raw class code.** Both are
+nominal categories (10 = Tree cover, 50 = Built-up; a geological formation
+code), not an ordered quantity — leaving either as a raw integer would let
+auto-detection z-score it as if one category were numerically "more" than
+another, the same class of error as the structural-column bug, just subtler
+since these *are* meant to be real model input.
+
+**Targets are not features.** `target_discharge_m3s_mean/std/count` and
+`target_waterlevel_mm_mean/std/count` exist on the enriched table but never
+enter `x` — they're pulled out into `data.y` separately, and attach only to
+real gauge rows (verified: gauge codes, BD TOPO hydrographic node IDs, and
+virtual-node marker strings occupy structurally distinct namespaces, so a
+left-merge on `station_code` can never mislabel a confluence or virtual node).
+
+### 2.3 Static vs. dynamic features
+
+`build_pyg_graph` also splits every feature by physical temporal nature:
+
+- **`data.x_static`** / **`data.static_feature_names`** — genuinely
+ time-invariant: elevation, IDPR, catchment area, landcover, coordinates.
+- **`data.x_dynamic`** / **`data.dynamic_feature_names`** — physically
+ time-varying quantities: climate, groundwater level/depth, NDVI.
+
+`data.x` remains the full combined tensor unchanged; the split is additional,
+not a replacement. **Important limitation, stated plainly**: "dynamic" here
+still means *one period-aggregated number per node* (mean/sum over the whole
+date range, or a single well reading), not a real `[n_nodes, T]` time series.
+The split makes the physical distinction explicit and gives a clean seam for
+a future temporal pipeline to slot into, but building that pipeline — genuine
+daily/whatever-resolution series per node, aligned across sources — is
+separate, larger, unbuilt work. `physics_losses.py`'s `routing_consistency_loss`
+specifically needs that real time dimension and has nothing to consume yet.
+
+### 2.4 Date-range filtering
+
+`build_node_features`/`enrich_reach_graph.py` accept a `date_range` applied
+to every time-varying source (groundwater, climate, hydrometric targets)
+together, so all three describe the same period rather than each silently
+aggregating over its own full, differently-shaped history (ADES wells
+reporting from the 1970s to 2026 on wildly different schedules; ERA5 spanning
+1960–2026; hydrometric records with their own per-station ranges entirely).
+
+Default: **2013-01-01 to 2026-12-31** — computed, not guessed, via a
+brute-force interval-overlap check across all 8 discharge-gauged stations'
+real date ranges. This is the window that maximizes simultaneous station
+coverage: 6 of 8 stations, **8,923 real, quality-filtered observations**
+(`code_qualification >= 16`, the same threshold `HydrometricLoader` itself
+applies — a naive raw count that skips this filter gives 13,084, which is
+what an earlier pass at this analysis originally reported before the
+discrepancy was traced and corrected). Two stations (`H403301101`: 1969–1985,
+`H605022010`: 1970–1980) are permanently excluded by any reasonable window —
+a ~35–40 year dead gap separates them from every other station's record, so
+including them would mean spanning six mostly-empty decades, not a genuine
+improvement.
+
+### 2.5 Physics-informed loss terms (`physics_losses.py`)
+
+Four constraints, each tied to real graph structure, not generic:
+
+| Term | Constraint | Applies to |
+|---|---|---|
+| `confluence_mass_balance_loss` | `Q_confluence ≈ sum(Q_upstream_branches)` — new mass genuinely enters | `is_confluence` nodes |
+| `split_rejoin_conservation_loss` | `Q_split ≈ Q_rejoin` — same water, no new mass | paired `braid_id` nodes |
+| `routing_consistency_loss` | `Q_downstream[t] ≈ Q_upstream[t - lag]`, lag from real `distance_km`/slope | every edge (needs `[n_nodes, T]` — see §2.3) |
+| `water_balance_loss` | `P - ET - Q - ΔS ≈ 0` in volume terms | nodes with `cumulative_catchment_area_km2` |
+
+Confluence and split/rejoin are deliberately different constraints, not one
+generic "conserve mass everywhere" rule — a model that only learned "sum the
+inflows" would get a split/rejoin wrong, since a rejoin's two branches
+together should equal the *split's* value, not add something new on top.
+All four apply graph-wide, not just at the 27 labeled gauges — that's the
+actual mechanism by which sparse supervision generalizes to the ~4,500
+ungauged nodes, not an incidental detail. `ΔS` (storage change) defaults to
+zero, a named steady-state approximation — this project has no direct
+basin-wide storage measurement, only sparse well *levels*, which aren't the
+same thing.
+
+### 2.6 Two graphs, not one
`build_pyg_graphs_per_basin()` returns `{0: eure_graph, 1: risle_graph}`,
each with its own local `0..n-1` node indexing, rather than one merged `Data`
@@ -200,26 +336,15 @@ PyTorch Geometric's own batching (`Batch.from_data_list`) expects a list of
separate small graphs — building two graphs from the start matches that
convention directly.
-### 2.4 What actually goes into `x`
+### 2.7 What it looks like
-`build_pyg_graph()` auto-detects feature columns from whatever table it's
-given, so it works whether that's the plain 4-column base table or the fully
-enriched one. Every column gets z-scored (mean 0, unit variance — verified in
-`test_build_graph.py`, including a check that this used the same `ddof=1`
-convention as the pandas `.std()` call that actually does the standardizing,
-since numpy's default `ddof=0` gives a spuriously "wrong-looking" answer for
-small station counts that isn't actually a bug). Missingness flags, described
-above, get appended after standardization so they stay honest 0/1 values
-rather than being z-scored into something less interpretable.
-
-### 2.5 What it looks like
-
-The Streamlit explorer (`src/app.py`) renders both graphs directly — real
-course geometry, gauges colored by elevation, ADES wells layered underneath
-for context. Clicking anywhere on the line resolves to the nearest gauge and
-pulls up its actual discharge/water-level/rating-curve plots:
-
-
+The Streamlit explorer (`src/app.py`) has two views. "Explore" renders the
+original click-to-read interface over real course geometry. "Network
+validation" renders the full reach graph — confluences as diamonds, gauges as
+elevation-colored circles, every edge as one line trace regardless of edge
+count (verified fast at real scale: 0.29s to build a figure for ~2,900
+edges) — specifically for visually confirming the topology looks like a real
+river network before trusting it as model input.
---
@@ -230,18 +355,20 @@ materially affect what the data means.
### 3.1 Station roster (`station_list.csv`, `station_elevations.csv`)
-27 stations across the two basins, split roughly by Hub'Eau code prefix
-(`H4xx…` for La Eure, `H6xx…` for La Risle — this is a heuristic based on
-observed codes, not a documented rule, so it's worth re-checking if new
-stations show up outside that pattern). Elevation comes from Open Topo Data's
+27 stations across the two basins, spanning three French departments —
+verified directly against the real roster: 12 in Eure (27), 10 in
+Eure-et-Loir (28, the Eure's southern tributaries near Chartres/Dreux — Voise,
+Drouette, and others), 2 in Orne (61). Split roughly by Hub'Eau code prefix
+(`H4xx…` for La Eure, `H6xx…` for La Risle — a heuristic based on observed
+codes, not a documented rule). Elevation comes from Open Topo Data's
`eudem25m` endpoint (`scripts/download_elevation.py`), queried per station
-coordinate — it's a point lookup, not a raster, so there's no slope or
-catchment information hiding in it.
+coordinate — a point lookup, not a raster, so there's no slope or catchment
+information hiding in it.
The row order in `station_list.csv` does **not** follow the river's course —
verified directly, it jumps around in both latitude and elevation. Anything
that needs upstream/downstream ordering has to derive it from elevation,
-latitude, or real centerline position; never from file order.
+latitude, or real centerline/graph position; never from file order.
Not every station in this list is actively gauged. Cross-referencing station
names against the hydrometric data turned up three categories worth knowing
@@ -255,9 +382,10 @@ about:
- Everything else with no data is unexplained from the name alone and worth a
direct check on Hub'Eau's site before assuming it's just a gap.
-In total, **14 of the 27 stations have no discharge or water-level records at
-all** in the current Hub'Eau export. That's not a bug in the loader — it's
-confirmed against the raw files, not just the merged table.
+Of the 8 stations with any `QmnJ` discharge data at all, only **6 have real
+observations within the project's 2013–2026 date window** (§2.4) — worth
+knowing before assuming "8 gauged stations" translates directly into training
+examples.
### 3.2 Hydrometric data (`hydrometric/`, via `scripts/download_hubeau.py`)
@@ -269,6 +397,15 @@ has `HIXnJ`/`HIXM` (water-level codes) sitting right alongside `QmnJ`
down to its intended `grandeur` code explicitly (`QmnJ` from the discharge
file, `HIXnJ` from the water-level file) rather than trusting the filename.
+`HydrometricLoader` also filters on Hub'Eau's own `code_qualification` field,
+keeping only `>= 16` (their "acceptable"/"good" threshold) and dropping lower-
+quality/provisional readings. This is real and meaningful, not a rounding
+detail — traced directly against the raw discharge file for the 2013–2026
+window (§2.4): 13,084 raw `QmnJ` rows in range, of which 4,022 have
+`code_qualification == 12` (below the threshold) and get correctly excluded,
+leaving 8,923. Any manual read of the raw CSVs that skips this filter will
+overcount real usable observations by close to a third.
+
Only 8 of the 27 stations have any `QmnJ` (daily mean discharge) rows at all.
Several others report water level only. This isn't evenly distributed and
matters a lot for anything downstream that assumes "gauged" means "has both
@@ -283,18 +420,28 @@ merges the levels file against the stations file on `code_bss` and renames
`x`/`y` to `lon`/`lat` — those columns are already in degrees in this dataset,
not a projected CRS, so no reprojection happens or is needed.
-Groundwater is used as a **station-level input covariate** (radius-averaged via
-`aggregate_to_stations`), not as a graph edge. Well proximity alone isn't
-sufficient grounds for a subsurface/karst connectivity edge between the two
-basins — two wells being close together doesn't mean they're hydraulically
-connected. That would need either correlated well hydrographs over time or a
-shared BDLISA aquifer-unit code (the `groundwater_stations.csv` file has a
-`codes_bdlisa` column available for exactly this kind of check).
+`node_features.py`'s `add_groundwater_features` does **not** use
+`ADESLoader.aggregate_to_stations` — that method loops per station and does a
+full haversine `.apply()` over the entire groundwater dataframe for each one.
+At 27 stations against ~272k readings that's slow but tolerable; at the reach
+graph's ~4,500 nodes it's over a billion row-wise Python calls, confirmed as a
+genuine, not hypothetical, multi-hour hang. The fix (reduce to each well's
+latest reading first, then a KD-tree coarse prefilter + exact haversine on the
+small candidate set) turned out to also fix a real accuracy bug: the old
+method required wells to share the *exact same reporting date* before
+averaging, but real wells report on wildly different schedules (18 real
+wells within 20 km of one station spanned 13 different "latest dates," one
+from 1972) — silently discarding most real coverage every time.
+
+Groundwater is used as a **station-level input covariate**, not as a graph
+edge. Well proximity alone isn't sufficient grounds for a subsurface/karst
+connectivity edge — that would need either correlated well hydrographs over
+time or a shared BDLISA aquifer-unit code (`groundwater_stations.csv` has a
+`codes_bdlisa` column available for exactly this kind of check; still unused).
Well coverage is not uniform across the two basins. The Eure's southern reach
(south of roughly 48.68°N, toward Chartres) has essentially zero wells within
-range in this extract — any groundwater estimate south of there will come back
-empty, and that's a property of the source data, not a bug in the interpolation.
+range in this extract.
### 3.4 Climate (`safran/`, via `download_era5_sample.py` / `download_era5_full.py`)
@@ -305,107 +452,203 @@ instantaneous variables (temperature, wind) from accumulated ones
(precipitation, evaporation, radiation, snowfall, runoff) at the API level —
`download_era5_full.py` downloads each set separately per year and merges them,
because the CDS API rejects mixed requests. The full pull spans 1960–2026 and
-is genuinely slow (a `time.sleep` and per-year retry loop is in there for a
-reason).
+is genuinely slow.
-`SAFRANLoader` interpolates the ERA5 grid to each station's coordinate
-(nearest or linear, configurable) rather than requiring stations to sit
-exactly on a grid cell — this is standard practice for reanalysis data at
-0.25° resolution, not an approximation specific to this project.
+`SAFRANLoader` interpolates the ERA5 grid to every station **in one
+vectorized xarray call per file**, not one `.sel()` + `.to_dataframe()` call
+per station — the per-station loop version does real per-call work (an index
+lookup, then a full DataFrame conversion) that's tolerable at 27 stations
+(~1,800 calls across ~67 year-files) but was confirmed to actually hang at the
+reach graph's ~4,500 nodes (~193,000 calls). Vectorized indexing with
+DataArray indexers sharing a `station` dimension does every station in one
+call per file instead.
### 3.5 IDPR (`idpr.csv`)
BRGM's *Indice de Développement et de Persistance des Réseaux* — an
infiltration-vs-runoff tendency index, and the closest thing this project has
-to a real soil/drainage covariate. The actual file used here is already
-one row per station (`station_id` matching `station_code` exactly, verified
-1:1 against all 27 stations, no duplicates, no nulls), not a raw spatial point
-cloud. `node_features.py` checks for this and does a direct ID join when
-possible rather than nearest-neighbor search — nearest-neighbor is only the
-fallback path for a generic IDPR export that doesn't come pre-matched to
-stations.
-
-There is no soil texture, permeability, or hydraulic conductivity dataset in
-this project. IDPR is a tendency index, not a texture measurement — worth
-keeping that distinction in mind if it ever gets described as "soil data" in a
-writeup.
-
-### 3.6 Catchment area (`catchment_area.csv`, via `scripts/download_catchment_area.py`)
-
-Hub'Eau publishes catchment (drainage basin) area on the **site** referentiel,
-not the station referentiel — `surface_bv` on `hydrometrie/referentiel/sites`,
-in km². Since one site can have several stations, the download script does two
-passes: station → `code_site`, then `code_site` → `surface_bv`, and joins them
-back. 16 of 27 stations have a value; Hub'Eau simply doesn't publish this field
-for every site, and the missing ones correlate with the observer/SEBV stations
-from §3.1 but aren't identical to that set.
-
+to a real soil/drainage covariate (see §3.9 for why it's standing in for soil
+data specifically, not just conveniently similar). The file used here is
+already one row per station (`station_id` matching `station_code` exactly,
+verified 1:1 against all 27 stations), so `node_features.py` does a direct ID
+join when possible rather than nearest-neighbor search, falling back to
+spatial nearest-neighbor for any station code that isn't an exact match
+(which is every non-gauge reach-graph node, and — a real, minor precision
+trade-off worth knowing — every gauge too, once the table also contains
+non-gauge codes, since the exact-match path requires the *entire* table to
+match IDPR's station list).
+
+A live re-fetch from BRGM's own geoservice (`scripts/fetch_idpr_brgm.py`,
+`geoservices.brgm.fr/geologie`, `GetFeatureInfo` point queries against the
+`IDPR_50M` raster layer) was attempted, to get fresher values and eventually
+cover the full reach graph rather than just the 27 gauges. **It failed** in
+testing and the project is currently using the original, already-uploaded
+`idpr.csv` instead — not resolved further, since the existing file already
+gives real, usable IDPR coverage for every gauge.
+
+### 3.6 Catchment area — two independent sources
+
+**Hub'Eau (`catchment_area.csv`, via `scripts/download_catchment.py`)**:
+published on the **site** referentiel, not the station referentiel —
+`surface_bv` on `hydrometrie/referentiel/sites`, in km². Since one site can
+have several stations, the download script does two passes: station →
+`code_site`, then `code_site` → `surface_bv`. 16 of 27 stations have a value.
This number is **cumulative** — the total catchment area draining to that
-point, all the way to the source. That distinction matters once you bring BD
-TOPO into the picture (§3.7), because BD TOPO's catchment polygons are the
-opposite: incremental, reach-by-reach areas. Comparing the two directly without
-accounting for that gives area ratios that look like measurement error but
-aren't — a spatial join between the 27 stations and the 135 BD TOPO polygons
-gave ratios from about 0.015 near the river mouth up to 0.67 near the source,
-which is exactly the pattern you'd expect from cumulative-vs-incremental, not
-a data quality problem.
+point, all the way to the source.
+
+**BD TOPO, graph-wide (`cumulative_catchment_area_km2`, via
+`scripts/compute_cumulative_catchment.py`)**: sums BD TOPO's incremental
+catchment polygons upstream of any node, via the real graph topology —
+distinct polygons counted once even when many nodes/edges share the same
+coarse polygon (verified with a hand-computed test case specifically checking
+this). Covers ~98% of nodes graph-wide, not just the 27 gauges — the actual
+fix for the "confluences and virtual nodes have no catchment area at all" gap.
+
+**Cross-checked against Hub'Eau's real values on real gauges — and there's a
+real, identified bias, not a clean match.** Ratio (BD-TOPO-summed ÷ Hub'Eau)
+runs from about 0.75 to 1.25 for smaller catchments (< ~800 km², plausibly
+normal polygon-boundary/digitization precision) but drops to 0.75–0.89 for
+the largest catchments (> ~3,500 km²) — a clean, monotonic pattern, not noise.
+Most likely cause: **bounding-box truncation** — the original BD TOPO pull
+bbox had only a 9.6 km margin on its southern edge (the tightest of all four
+directions, and south is exactly where the Eure's longest upstream
+tributaries run, toward Chartres/Dreux), not a safe margin for real watershed
+extent. The bbox in `download_bdtopo_hydro.py` was widened afterward (from
+`(0.3, 48.3, 1.7, 49.5)` to `(-0.1, 47.7, 2.1, 49.9)`, ~2.9x the area) — the
+full `download_bdtopo_hydro.py → build_reach_graphs.py → enrich_reach_graph.py
+→ compute_cumulative_catchment.py` chain needs re-running against the wider
+box to actually resolve this, which had not yet happened as of the last
+verified run in this project.
### 3.7 BD TOPO hydrography (`bdtopo_hydro/`, via `scripts/download_bdtopo_hydro.py`)
IGN's BD TOPO / BD TOPAGE hydrographic network, pulled from the Geoplateforme
WFS (`https://data.geopf.fr/wfs`) rather than downloaded as a national bulk
file — the download script queries a bounding box around the two basins
-instead. Three layers, all scoped to that bbox:
-
-- `troncon_hydrographique.geojson` — river centerline reaches. 30,045 features
- in the current pull, which sounds like a lot for two rivers because it
- includes every stream, ditch, and canal in the box, not just the Eure and
- Risle.
+instead (see §3.6 for why that box was widened). Three layers, all scoped to
+that bbox:
+
+- `troncon_hydrographique.geojson` — river centerline reaches, now the
+ primary source for graph *topology* too (§2), via `lien_vers_noeud_
+ hydrographique_ini/fin` and `sens_de_l_ecoulement`. Real per-vertex
+ altitude data doubles as a fine-grained elevation profile, denser than
+ anything derivable from the 27 gauge points alone.
- `surface_hydrographique.geojson` — hydrographic surfaces, including a
`Nature` attribute that's supposed to flag karst-influenced reaches. IGN
- documents this attribute as **provisional and incomplete** — filled in with
- a limited, sometimes-default set of values pending refinement by the water
- agencies. A negative karst search here is not strong evidence of anything;
- it may just mean that stretch hasn't been annotated yet.
+ documents this attribute as **provisional and incomplete**.
- `bassin_versant_topographique.geojson` — catchment polygons, incremental
- (see §3.6). 135 polygons in the current bbox.
-
-**WFS axis order**: when a `BBOX` parameter's CRS is given via the URN form
-(`urn:ogc:def:crs:EPSG::4326`), the OGC spec requires latitude, longitude axis
-order — the opposite of the lon,lat order most GIS tools use by default.
-Getting this backwards doesn't raise an error; it silently matches zero real
-features, which looks identical to "no data in this area." `download_bdtopo_hydro.py`
-tries lon,lat first and automatically retries with the axes swapped if that
-comes back empty — for this project's bounding box, lat,lon is the order that
-returns real data.
-
-**Z-coordinate in tronçon geometry**: `troncon_hydrographique` geometries
-carry a third coordinate — real altitude at every vertex — not just lon/lat.
-Code handling this data needs to account for 3D coordinates explicitly (e.g.
-slicing to the first two values before building a 2-column DataFrame from
-`geometry.coords`). This altitude data doubles as a genuinely useful
-fine-grained elevation profile along the river, denser than anything
-derivable from the 27 gauge points alone (`BDTopoHydroLoader.get_elevation_profile`).
+ (see §3.6).
+
+**WFS axis order**: when a `BBOX` parameter's CRS is given via the URN form,
+the OGC spec requires latitude, longitude axis order — the opposite of the
+lon,lat order most GIS tools use by default. Getting this backwards doesn't
+raise an error; it silently matches zero real features. `download_bdtopo_hydro.py`
+and `scripts/download_bdcavites.py` both try lon,lat first and automatically
+retry with the axes swapped if that comes back empty.
+
+**Real branching topology fixed a naive assumption.** Filtering 30,045
+tronçons down to a single named river and building a graph from their
+endpoints does **not** give one connected line — for "Risle" alone, 1,195
+name-matched tronçons split into 132 disconnected components. Broadening the
+name filter to include known tributaries (§2.1) initially made this *worse*
+(487/214 components), traced to short/generic tributary names ("Bec", "Avre")
+matching unrelated streams elsewhere within the ~100×130 km bbox — fixed by
+requiring every name-matched tronçon to also fall within a real distance of a
+known gauge (`load_troncons_for_basin`'s `anchor_radius_km`), and by selecting
+the connected component actually containing the most real gauges rather than
+the component with the most raw tronçons (`best_component_for_stations`) —
+proven to matter, not just theoretically: a synthetic adversarial test showed
+the naive "biggest component" approach picking a larger but entirely
+unrelated decoy network over the real one.
**The bétoire finding**: two stations in the roster are explicitly named
*"[amont bétoire]"* and *"[aval bétoire]"* in Hub'Eau's own site names —
-*bétoire* being the Normandy dialect term for a karst swallow-hole. That's
-independent, deliberate naming from the data provider, not a guess. It's the
-strongest evidence in this project for a real losing reach on La Risle around
-Grosley-sur-Risle/Ajou (`H605641101` → `H605641201`), even though the BD TOPO
-karst layer doesn't currently confirm it (see the provisional-attribute note
-above). `build_surface_edges`'s `known_losing_reaches` parameter exists
-specifically for this — the current recommendation is to flag all three edges
-spanning that stretch as `verified_continuous=False`, not just guess at one.
-
-Also worth knowing: filtering the 30,045 tronçons down to a named river and
-building a graph from their endpoints does **not** give one connected line.
-For "Risle," 1,195 name-matched tronçons split into 132 disconnected
-components; the extraction keeps only the largest. That's a real topology gap
-in the source data (tile boundaries, tributaries sharing a base name without
-literally connecting), not a bug in the graph-walking code.
-
-### 3.8 Centerline generation
+*bétoire* being the Normandy dialect term for a karst swallow-hole. Three
+edges spanning that stretch on La Risle (`H605641101 → H605022010 →
+H605641401 → H605641201`) are flagged `verified_continuous=False`. BD TOPO's
+own karst attribute doesn't currently confirm it (see the provisional-
+attribute note above) — `scripts/download_bdcavites.py` (§3.8) exists
+specifically to get an independent, purpose-built second check on this,
+rather than relying only on naming inference.
+
+### 3.8 BDCavités (`bdcavites/`, via `scripts/download_bdcavites.py`)
+
+BRGM's national underground cavity inventory (sinkholes, quarries, natural
+cavities), via Géorisques' WFS (`georisques.gouv.fr/services`, typeName
+`CAVITE_LOCALISEE`, confirmed live and GeoJSON-capable directly against the
+real service). Built specifically as an independent check on the bétoire
+finding (§3.7) — a purpose-built cavity dataset, not inference from station
+naming or a provisional BD TOPO attribute. One real caveat: departments
+75/78/91/92/93/94/95 (Paris region, unrelated to this project) are excluded
+from BDCavités entirely, and the Eure department's own inventory was among
+the later batches of the national 2001–2013 completion program — worth
+checking coverage density before treating a sparse result as a negative
+finding rather than incomplete data.
+
+### 3.9 Geology (`bdcharm50/`, via `scripts/download_bdcharm.py`)
+
+BRGM's BD Charm-50, harmonized 1:50,000 geological maps — free, open
+(Licence Ouverte), no authentication, direct per-department ZIP download from
+InfoTerre (a genuinely different access pattern than the WFS sources
+elsewhere in this project: fixed URL per department, no bbox query, no axis-
+order ambiguity). Departments **27 (Eure), 28 (Eure-et-Loir), 61 (Orne)** —
+verified directly against the real, complete station roster (§3.1), not
+guessed. A separate, CIGAL-membership-gated distribution of similar data
+exists for at least one other French region; this project only uses the free
+InfoTerre path.
+
+### 3.10 Landcover and NDVI (`scripts/fetch_landcover.py`, `scripts/fetch_worldcover_ndvi.py`)
+
+ESA WorldCover, sampled at real gauge points from the public AWS S3 Cloud-
+Optimized GeoTIFFs — **not** the Terrascope WMS, which a source dated within
+the last month of this project's active development reported actively resets
+connections from non-browser HTTP clients (TLS fingerprinting, confirmed
+across multiple tools and User-Agents, not a coding problem to work around),
+and separately was slated for full phase-out already past by the time this
+was checked.
+
+Landcover classification uses the product's 3°×3° tile grid; every real
+station coordinate falls inside exactly one tile (`N48E000`), verified
+directly against all 27 real coordinates. NDVI uses the *annual composites'*
+1°×1° tile grid instead — genuinely different from the classification grid,
+looked up per-station via VITO's own authoritative tile-index grid file
+(`esa_worldcover_grid_composites.fgb`) rather than a second hand-guessed S3
+key pattern. Both need `AWS_NO_SIGN_REQUEST=YES` for `s3://`-scheme tile URLs
+specifically — a plain HTTPS URL to the same public bucket needs no signing
+at all, which is why the landcover script (HTTPS) worked without this while
+the NDVI grid's returned URLs (`s3://`) initially failed on AWS credential
+errors despite the bucket being fully public.
+
+Both currently cover only the 27 real gauges (exact `station_code` match),
+same limitation as Hub'Eau's `catchment_area_km2` before the cumulative-BD-
+TOPO fix (§3.6) — extending either script to the full reach graph is
+unstarted work, not a design decision.
+
+### 3.11 What was tried and didn't work
+
+**SoilGrids (ISRIC)** — confirmed non-functional directly, not from a stale
+search result: even a bare `lon`/`lat` query to the live REST API returned
+`422` consistently, consistent with ISRIC's own currently-posted "temporarily
+paused" service notice.
+
+**INRAE's national soil survey (RRP/BDGSF)** — not a uniformly-accessible
+source at all. Access is explicitly described as depending on regional/
+departmental "référents" (varies by department, sometimes needs a formal
+agreement with the regional chamber of agriculture), and INRAE's own
+documentation states outright that the more detailed scale "n'est pas encore
+en accès libre." IDPR (§3.5) and BD Charm-50 geology (§3.9) are this
+project's actual substitutes for the hydrologically-relevant part of what
+soil data would otherwise provide — not literal soil texture data, but IDPR
+specifically is an *integrated hydrological behavior* indicator (infiltration
+tendency), arguably more directly useful for a streamflow model than a raw
+soil property map would be on its own.
+
+### 3.12 Centerline generation
+
+**Only relevant to the older single-chain pipeline** (`build_surface_edges`,
+still available for direct comparison/debugging) — the reach graph (§2)
+derives its topology directly from BD TOPO's own node linkage and doesn't use
+these centerline files at all.
`centerlines/eure_centerline.csv` and `centerlines/risle_centerline.csv` — the
geometry `build_surface_edges` orders stations against — are generated by
@@ -433,80 +676,82 @@ vector source isn't available, not the method used for the current
## 4. Applications
-Click-to-explore UI over the river network (`src/app.py`, Streamlit): pick a
+`src/app.py` (Streamlit) has two views, selected by a radio at the top:
+
+**Explore** — the original click-to-read UI over real course geometry: pick a
river, click (or slide) along its course, see interpolated elevation,
estimated groundwater level, and — for whichever real gauge is nearest that
point — water level, discharge, and rating-curve plots pulled directly from
`HydrometricLoader`'s own plotting methods rather than reimplemented.
Click support uses Streamlit's native chart-selection
-(`st.plotly_chart(..., on_select="rerun")`, requires `plotly` alongside
-`streamlit`), not a third-party click-handling package. The click handler and
-the position slider share a single source of truth by design: Streamlit only
-honors a slider's `value=` argument the first time that widget is created, and
-on every later rerun returns whatever's stored under that widget's own
-session-state key — so the click handler writes directly into the slider's
-own key before it's instantiated, rather than a separate key, keeping the two
-controls in sync. It also de-duplicates incoming click events, since
+(`st.plotly_chart(..., on_select="rerun")`), not a third-party click-handling
+package. The click handler and the position slider share a single source of
+truth by design: Streamlit only honors a slider's `value=` argument the first
+time that widget is created, and on every later rerun returns whatever's
+stored under that widget's own session-state key — so the click handler
+writes directly into the slider's own key before it's instantiated, rather
+than a separate key. It also de-duplicates incoming click events, since
Streamlit's chart-selection state persists across reruns caused by *other*
-widgets and would otherwise re-fire on every unrelated interaction, not just
-the click that caused it.
+widgets and would otherwise re-fire on every unrelated interaction.
+
+**Network validation** — renders the full reach graph (§2): every edge as one
+Plotly line trace regardless of edge count (a trace-per-edge approach doesn't
+hold up at ~5,000+ edges; verified fast at real scale — 0.29s to build a
+figure for ~2,900 edges), real confluences as diamond markers, real gauges as
+elevation-colored circles. Metrics card reports node/edge/confluence/gauge
+counts and, when available, IDPR and cumulative-catchment coverage. Virtual
+infill nodes are deliberately not drawn individually — at ~2,400 per basin,
+markers for each would bury the actual validation signal (do confluences sit
+where a tributary visibly joins the line? do gauges sit on the network, not
+offset from it?) rather than help it. Reads directly from `reach_graph/
+{basin}_nodes_enriched.csv`, keyed on file modification time so a re-run of
+`build_reach_graphs.py`/`enrich_reach_graph.py` is picked up automatically —
+`st.cache_data` otherwise keys purely on function arguments, not file
+contents, and this was confirmed to actually cause stale numbers once during
+development, not just a theoretical risk.
---
## 5. Testing (`src/test_build_graph.py`)
-Not a unit test suite in the pytest sense — a single script that runs the full
-`node_features → build_surface_edges → build_pyg_graph(s)` pipeline against
-real data under `--data-root` and checks the result is actually sane: no
-NaN/Inf in the feature tensor, no accidental cross-basin edges, targets
-genuinely excluded from the model input, edge indices within bounds,
-bidirectional edge count exactly double the directed count, per-basin node
-counts summing to the combined total, standardized features actually landing
-near zero mean / unit variance, and the `known_losing_reaches` flag actually
-taking effect on the edge it's supposed to.
-
-It also prints mean/max `snap_distance_km` per basin against whatever
-centerline is currently in `centerlines/` — this is the number that would
-catch "one basin got a real BD TOPO centerline and the other is still on the
-old digitized approximation" without anyone having to notice by eye. As of the
-last run, that threshold is deliberately loose (2 km) so it won't hard-fail on
-a legitimately-imperfect-but-usable centerline; it's a reported number to
-watch, not yet an enforced standard. Worth tightening once both basins are on
-real geometry.
-
-Exits 0 on a clean pass, 1 otherwise, so it's usable as a pre-commit or CI
-gate if that's ever set up.
-
----
-
-## 6. Known limitations and open questions
-
-- **Each basin is modeled as a single chain, not a branching network.** Tributaries
- aren't represented. Fixing this means pulling BD TOPO's confluence-node
- layer and reworking `build_surface_edges`'s core assumption, not a small
- patch.
-- **The karst losing-reach flag is based on naming evidence, not a confirmed
- BD TOPO classification.** The provisional state of the `Nature` attribute
- means this may firm up (or not) as IGN's data matures — it's not something
- this project can resolve unilaterally.
-- **Catchment area exists in two incompatible forms** (Hub'Eau cumulative vs.
- BD TOPO incremental) and nothing currently derives a true independent
- cumulative catchment from BD TOPO alone by summing incremental polygons
- upstream of each gauge. Doable, not done.
-- **13 of 27 stations have some hydrometric data; 8 have discharge
- specifically.** Any model trained on discharge as a target has a real,
- uneven gauge-density problem to contend with, not just a "some nodes are
- unlabeled" abstraction.
-- **No true soil or permeability dataset.** IDPR is a tendency index, useful
- but not a substitute.
-- **The groundwater-well BDLISA aquifer-unit field is unused.** If a
- subsurface connectivity edge is ever justified with real evidence rather
- than proximity, this is the first place to look.
+Not a unit test suite in the pytest sense — a script with two independent
+sections, both run from `main()`.
+
+**`run_checks`** — the original single-chain pipeline: runs `node_features →
+build_surface_edges → build_pyg_graph(s)` against real data and checks the
+result is sane — no NaN/Inf in the feature tensor, no accidental cross-basin
+edges, targets genuinely excluded from the model input, edge indices within
+bounds, bidirectional edge count exactly double the directed count, per-basin
+node counts summing to the combined total, standardized features actually
+landing near zero mean / unit variance, the `known_losing_reaches` flag
+actually taking effect, and mean/max `snap_distance_km` per basin against
+whatever centerline is currently in `centerlines/`.
+
+**`run_reach_graph_checks`** — the reach graph pipeline, gracefully skipped
+(not a failure) if `reach_graph/` doesn't exist yet. Mostly regression tests
+for three bugs found and fixed during development, kept here specifically so
+they can't silently reintroduce themselves:
+
+- structural columns (`is_gauged`/`is_confluence`/etc.) never leak into
+ `feature_names`, but remain accessible as their own `Data` attributes
+- target values never attach to a non-gauge node, and target coverage never
+ exceeds the real gauge count
+- `edge_attr` stays exactly 3 columns despite extra edge metadata
+ (`toponym`, `cleabs`) sitting on the real edges table
+- `physics_losses.py`'s `build_confluence_index`/`build_braid_index` produce
+ counts matching `is_confluence`/`is_rejoin_point` sums, with every index
+ within node bounds and every confluence having ≥ 2 upstream branches
+- IDPR and `cumulative_catchment_area_km2` presence/coverage are reported
+ explicitly (the latter compared against the Hub'Eau-only baseline it's
+ meant to exceed)
+
+Exits 0 on a clean pass across both sections, 1 otherwise — usable as a
+pre-commit or CI gate if that's ever set up.
---
-## 7. Running things
+## 6. Running things
Data acquisition (from repo root, in roughly dependency order):
@@ -515,12 +760,35 @@ python -m scripts.download_hubeau
python -m scripts.download_elevation
python -m scripts.download_era5_full # slow; download_era5_sample.py first if just testing
python -m scripts.extract_era5
-python -m scripts.download_catchment_area
+python -m scripts.download_catchment
python -m scripts.download_bdtopo_hydro --check # verify typeNames before the real pull
python -m scripts.download_bdtopo_hydro
+python -m scripts.download_bdcavites --check
+python -m scripts.download_bdcavites
+python -m scripts.download_bdcharm
+```
+
+Build and validate the reach graph:
+
+```bash
+python -m scripts.build_reach_graphs --data-root datasets
+python -m scripts.enrich_reach_graph --data-root datasets # --skip-climate if that step hangs
+python -m scripts.compute_cumulative_catchment --data-root datasets
+python -m scripts.diagnose_confluences --data-root datasets --basin eure
+python -m scripts.diagnose_confluences --data-root datasets --basin risle
+```
+
+Landcover / NDVI, real gauges only (needs `rasterio`, and `geopandas` for NDVI's
+tile lookup):
+
+```bash
+python -m scripts.fetch_landcover --check
+python -m scripts.fetch_landcover
+python -m scripts.fetch_worldcover_ndvi --check
+python -m scripts.fetch_worldcover_ndvi
```
-Validate the graph pipeline against whatever's actually in `datasets/`:
+Validate everything against whatever's actually in `datasets/`:
```bash
python -m src.test_build_graph --data-root datasets
diff --git a/datasets/bdcavites/cavite_localisee.geojson b/datasets/bdcavites/cavite_localisee.geojson
new file mode 100644
index 0000000000000000000000000000000000000000..595d43877a1d105c73f7d90f5fc4f17442caa630
--- /dev/null
+++ b/datasets/bdcavites/cavite_localisee.geojson
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9ad0770c09a15992f31f3ece16afeb9097f3a048f66c431379a78c9305bc204
+size 24931685
diff --git a/datasets/bdcharm50/GEO050K_HARM_027.zip b/datasets/bdcharm50/GEO050K_HARM_027.zip
new file mode 100644
index 0000000000000000000000000000000000000000..cc5031209757d975c4a4ecb0072fd79980a07e16
--- /dev/null
+++ b/datasets/bdcharm50/GEO050K_HARM_027.zip
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef28692e01362400bb0201d0fa5940f1a20f7630f90b9258f4d99bc0744ea944
+size 16009427
diff --git a/datasets/bdcharm50/GEO050K_HARM_028.zip b/datasets/bdcharm50/GEO050K_HARM_028.zip
new file mode 100644
index 0000000000000000000000000000000000000000..227ab65ff0b110543449a98e80e0ffb8d8ce6d48
--- /dev/null
+++ b/datasets/bdcharm50/GEO050K_HARM_028.zip
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d486b6bea0b24b2004adad79afc7be2ddef86b67567467b015cf663ddeadeb7
+size 21530663
diff --git a/datasets/bdcharm50/GEO050K_HARM_061.zip b/datasets/bdcharm50/GEO050K_HARM_061.zip
new file mode 100644
index 0000000000000000000000000000000000000000..06213e38823b475665d22dd9755d8e19c479eb86
--- /dev/null
+++ b/datasets/bdcharm50/GEO050K_HARM_061.zip
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c900e51ff795863b505c3545f8bd8068aa7591f039d69f7e3b135812b35f0947
+size 24293693
diff --git "a/datasets/bdcharm50/dept_027/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf" "b/datasets/bdcharm50/dept_027/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf"
new file mode 100644
index 0000000000000000000000000000000000000000..74bbc83d6bd0b949635a727eb8c01a89f29d3627
--- /dev/null
+++ "b/datasets/bdcharm50/dept_027/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf"
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1c5773cc3af2552b4a21629d05bed2195293a7b924a5d696e1d0e250fbf3e86
+size 465834
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.dbf b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..6757a9862148ad09748ce554e8d7e5146f6d2efd
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.dbf differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.lyr b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..2f4e323182b76dca4f60306bf2d07f3c544226a2
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.prj b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.qml b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..608666bc5a1a36b0fa53ba669e4c7c3454e1eba3
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.shp b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..cd8a1470c8bec278c4a8c41ed35a075b513d20f9
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.shp differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.shx b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..03ce799ca5995f7ee317aacc1494adf31fbc5cff
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_DIVERS_2154.shx differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.dbf b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..aba449ffd7284a468a50acd934316b636d8eb83b
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7919155134fb9c8fcce3ba7f4a4e47a75a6d02499efb82215bc9375ed8a67d34
+size 6620665
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.lyr b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..9ac53c06303f76b5f1d689ca3ea1842f97fb0329
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.prj b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.qml b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..4344ca61acd186ab576018dc7a5f2d7cc620a663
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shp b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..da2602a8fd8d16223c00d62c74c1341c96582702
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:15626b809bc52ed27ba554c0c6ebae2933d42f4795f2ae4fd0bb7e5974f0d380
+size 7438780
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shx b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..51d475a994e5a8a78130e6693ef47aeb0cae9f86
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_FGEOL_2154.shx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:573e2b4d674d42526cbf89a2295f2571cdf4ac7fe1615f308bea04bf5ec66564
+size 154060
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.dbf b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..f6f3d55aca786932851cd84021c0b9f86e12248c
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.dbf differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.lyr b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..b8824d4d7fadb8e0984c318b6f3005aca34897b8
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.prj b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.qml b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..2648c172baee540462b48b6fc9f7f3c94a10abe9
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.shp b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..1fad6bcfab9894af628b2fff3fdfd2d52cfc0148
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.shp differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.shx b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..c7aea67626f00e6efb69876f8ece32ed92709b16
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_L_STRUCT_2154.shx differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.dbf b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..9badabfaeb58e4086f59a384fa5e873a73847bae
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7f9f02aa66fdd0a0df715a6ff43d48fd3030dc42895747976c584edff25d196
+size 3125317
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.lyr b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..8708929a7d41d3a5926631549c052ef26d766408
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.prj b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.qml b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..225557495de1f30db7dee9449faa15affd03a53a
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shp b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..084e2caa5ea14c796cf75619a01f719ddf180fb0
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e7696bc4eeeaf7d2b499040cba69508aca65b5493e7ab9c99b7bd41c44c9a35a
+size 153064
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shx b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..14601c184e6fc0e426c6d48b8cc220e9a637bfdf
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_DIVERS_2154.shx differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.dbf b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..8b60f60448f0858b8683ef1192cc1ec97b7e9ac1
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.dbf differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.lyr b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..3b90f90802d847b85aa17d8a4f7cc17b92445d6c
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.prj b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.qml b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..329f846cb71e9fa561f98bc393cf6c54ff7947ed
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.shp b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..4b08c51bca1f6542d717807d7edf762bc739371d
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.shp differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.shx b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..3d31c4a6e15d42aeadae1dfd38f18c8bc56123d6
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_P_STRUCT_2154.shx differ
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.dbf b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..6ce68a9f601861f83dc5c4aa66288835e821f14e
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1da5e6b0d60c08a41862a83b94606d31ef06ec729883a7a0f2ce465fb923443
+size 3622421
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.lyr b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..a0aac032d304e2605360de30469b463317247e2c
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.lyr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0874de865d408583ecfc7048837e63bb65046e6eb3b5892e3f2fb4272744a8f6
+size 143360
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.prj b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.qml b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..50f71d35f47d981251051ae799c3b5dd8bbc6490
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shp b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..f867cfe9a93c56ab836456aef863b959d450b9f4
--- /dev/null
+++ b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:06a8f10eee38b43494ed457c05c416cbbf07e024911858815ee1151d1068e802
+size 12602632
diff --git a/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shx b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..2ea9196cd1b8452eaa244ef5c4655a099eacd7f5
Binary files /dev/null and b/datasets/bdcharm50/dept_027/GEO050K_HARM_027_S_FGEOL_2154.shx differ
diff --git "a/datasets/bdcharm50/dept_028/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf" "b/datasets/bdcharm50/dept_028/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf"
new file mode 100644
index 0000000000000000000000000000000000000000..74bbc83d6bd0b949635a727eb8c01a89f29d3627
--- /dev/null
+++ "b/datasets/bdcharm50/dept_028/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf"
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1c5773cc3af2552b4a21629d05bed2195293a7b924a5d696e1d0e250fbf3e86
+size 465834
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.dbf b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..ce7c94fd09941103a57cafa059ed7e110cd9e431
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:869d8ce6ca5b251aa03cb61066ffdba2325874bd785dc9755bfb0821bfe53c2f
+size 3912353
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.lyr b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..61387daf37f1c74d7f6679f76f398649e6ce7e21
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.prj b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.qml b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..7205f5ed85d8244271e64a5b972b8e851521163f
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shp b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..86c9ba885640d2bb1cdf91885ad88c88d092054d
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d0d181998e534cee707edf55432f83031accf30b73bbe6f80ccc55b452106c6f
+size 9529460
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shx b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..19f88652f54f70df0f7000b6223844f669696e43
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_FGEOL_2154.shx differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.dbf b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..8affbd23c2a74f30b610b5646887c1d77c5b365c
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.dbf differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.lyr b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..7a38d6dd4679a6fb540c3e217377f6d881574585
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.prj b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.qml b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..1c0667dd2a7642a7acc39826971d54826bd5d82c
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.shp b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..542fcd3f449a177591536983cbef085c6e787612
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.shp differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.shx b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..17070ff0aa4b96242dfa095fff65302a285fe87f
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_L_STRUCT_2154.shx differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.dbf b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..3720679c71e1fb69c7e1aa9b798e60cd1c58cc66
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9c4cbffa396ea0fc4fc96fef11eca4c0980cd5ce2c07fd5094cbc5de0b979b43
+size 1930585
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.lyr b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..9b89473496866e01da3c8adeeeacbdc05284ad26
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.prj b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.qml b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..90f285fb1067d0a11e72eee258e76d2309823f93
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shp b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..9bf82f4f6b356f61e43a51cad01eaffa903fe169
--- /dev/null
+++ b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:358c987e15a91f0b7f3d476c79c149f70b5a4e9c3718e62e917966524369bc77
+size 17651700
diff --git a/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shx b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..94ce5596ea126d993b9bc4ebf9a38a9bbb928361
Binary files /dev/null and b/datasets/bdcharm50/dept_028/GEO050K_HARM_028_S_FGEOL_2154.shx differ
diff --git "a/datasets/bdcharm50/dept_061/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf" "b/datasets/bdcharm50/dept_061/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf"
new file mode 100644
index 0000000000000000000000000000000000000000..74bbc83d6bd0b949635a727eb8c01a89f29d3627
--- /dev/null
+++ "b/datasets/bdcharm50/dept_061/Descriptif des cartes g\303\251ologiques \303\240 1_50 000 vecteur.pdf"
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1c5773cc3af2552b4a21629d05bed2195293a7b924a5d696e1d0e250fbf3e86
+size 465834
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.dbf b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..917b2cd36d3a6ad1e7c8714d5fe3d02005196595
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a64312aac232f869dfb05bc0044fe33d7cb1760fedfca22dee5b1d21f93ff92
+size 8971561
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.lyr b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..90c39b45da5a9be60c028b8ec0feaf67a3896a89
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.prj b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.qml b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..7205f5ed85d8244271e64a5b972b8e851521163f
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shp b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..bf5e9823a1baeb72828cbe4f586783967a2ddc53
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb7fc8bb198bf56e8c8993ee3ab4b675a380e7392e27887d30da99ca2045e547
+size 11149532
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shx b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..c2828d4cb3787d7b2bf67f482d1c239eaacbcd27
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_FGEOL_2154.shx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aabb3fc5e83c69af2ca961a0466417231a08337c23026f9f3effeb7448c21058
+size 208732
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.dbf b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..06d1913ed03b2bbe0564c1964a19ba83823407ee
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:501b25f12190b431308ebcb748551d2d686d57a702cb00a4d21dd34ae09d52b0
+size 1189111
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.lyr b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..59a5466b2673f6ccf777fe8eb14ccac7b2d60bcd
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.prj b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.qml b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..abee182a61feadd98c9250d08dbb479e174b1354
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shp b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..75912dc05a5c9ee441babe8d37603f56a4f9b840
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0bc45892fa789d16fcb73735f88fb6e8bc25a11a9f1ce80023b14d7c3d49c071
+size 491220
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shx b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..7ab9d7784fa60da43091cee961508f04a3d765ad
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_L_STRUCT_2154.shx differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.dbf b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..287b8ea9445f40ec26eb1468408982f9ee0bbbb1
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7f115a8dffa8e416d6f0c8796d4fb4537619cbb422ee74b33370a67dc6f1896
+size 368277
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.lyr b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..08113fba331befa42ffbc5d270f9a8b5646c9f89
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.prj b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.qml b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..25d667ae3d38821c18167e60cf8482da511052d4
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.shp b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..74b243a811c216f739c17cc2285e6cb3fd6e7b70
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.shp differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.shx b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..b2c11b3061dcc4b5a83129d958b2221f0a793179
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_DIVERS_2154.shx differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.dbf b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..14dc0a37891e6674029cc018f1950c4728d54b6d
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:188a00c47b6b7d17e1b3f47b3c317ddbd4524a0c98d2131bc12c1d3182b512cd
+size 645857
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.lyr b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..3f68e77c89cd684ab8aca8cdbb61fe13e3a639bb
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.lyr differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.prj b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.qml b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..7a6b918f0b246caceca90f44f1036431836d41b8
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.shp b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..31d6fc176edf17624c36a25210df35476a6ca614
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.shp differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.shx b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..f76d81e9ad2838351688873413ceeb4860b31f99
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_P_STRUCT_2154.shx differ
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.dbf b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.dbf
new file mode 100644
index 0000000000000000000000000000000000000000..b61ed017e95de3b5ce1f24094193b4d3e5ff772b
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.dbf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd38bf36b15c89a63a74d8bef4bcebb4afb1534fcd98860e3a96d23046f87c98
+size 4080749
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.lyr b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.lyr
new file mode 100644
index 0000000000000000000000000000000000000000..885efbca6bb72b68041a4d9b1e63b98aa33d2ada
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.lyr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1cb99ebd851db7f701957f4dd7b1c78ffb20212e15b035094fce871c3948cec2
+size 666112
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.prj b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.prj
new file mode 100644
index 0000000000000000000000000000000000000000..56757fc434d98d5eddf8c29389e42675dd4845ab
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.prj
@@ -0,0 +1 @@
+PROJCS["RGF93_Lambert_93",GEOGCS["GCS_RGF_1993",DATUM["D_RGF_1993",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",700000.0],PARAMETER["False_Northing",6600000.0],PARAMETER["Central_Meridian",3.0],PARAMETER["Standard_Parallel_1",44.0],PARAMETER["Standard_Parallel_2",49.0],PARAMETER["Latitude_Of_Origin",46.5],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.qml b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.qml
new file mode 100644
index 0000000000000000000000000000000000000000..f81974d3f83faa510f54ef3d1238fb0e759efda1
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.qml
@@ -0,0 +1,2 @@
+
+255
\ No newline at end of file
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shp b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shp
new file mode 100644
index 0000000000000000000000000000000000000000..229924591b6fd7c538d8122c4c895eac0ae9cbaf
--- /dev/null
+++ b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aef56e596d16181cc954119b898e3e442671cb47e1045203e9162a245ab21c6a
+size 19079416
diff --git a/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shx b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shx
new file mode 100644
index 0000000000000000000000000000000000000000..c3aa4e088dc7b7a60765a95b924d21a282c73bda
Binary files /dev/null and b/datasets/bdcharm50/dept_061/GEO050K_HARM_061_S_FGEOL_2154.shx differ
diff --git a/datasets/idpr_brgm_live.csv b/datasets/idpr_brgm_live.csv
new file mode 100644
index 0000000000000000000000000000000000000000..422a6724370f8f232a79f84503d628acd9d6194b
--- /dev/null
+++ b/datasets/idpr_brgm_live.csv
@@ -0,0 +1,28 @@
+station_code,idpr_value_brgm_live,latitude,longitude
+H602021010,,48.748705218,0.58156195
+H605641401,,49.034118991,0.808378297
+H620021010,,49.183882705,0.714759598
+H623021010,,49.242018124,0.694854113
+H600021010,,48.718036538,0.430946285
+H603021010,,48.822095344,0.710239413
+H605021010,,48.952759299,0.743674684
+H605022010,,49.030154418,0.814920403
+H605641101,,48.984918738,0.789022568
+H605641201,,49.047068778,0.799839826
+H606000101,,49.091746041,0.759834916
+H623022010,,49.340856041,0.58089832
+H402021010,,48.44823808,1.205047075
+H403301101,,48.386346937,1.477223775
+H404021010,,48.441954003,1.498980903
+H420021010,,48.77678274,1.379648722
+H420021011,,48.776718416,1.379891235
+H403021010,,48.449158817,1.291192655
+H404021101,,48.46726995,1.483793322
+H414000201,,48.645793808,1.542543476
+H416021010,,48.700852684,1.442183941
+H420021012,,48.777083556,1.379365749
+H426021010,,48.792029301,1.376915068
+H426021201,,48.792090349,1.37670564
+H431021010,,49.01307446,1.378224742
+H432021010,,49.114382066,1.208807304
+H438021010,,49.224003868,1.177680812
diff --git a/datasets/worldcover_landcover.csv b/datasets/worldcover_landcover.csv
new file mode 100644
index 0000000000000000000000000000000000000000..f47f24b6f69840ecb55b2d8a06ed9556a522bff1
--- /dev/null
+++ b/datasets/worldcover_landcover.csv
@@ -0,0 +1,28 @@
+station_code,landcover_class_code,landcover_class_name
+H602021010,10,Tree cover
+H605641401,50,Built-up
+H620021010,10,Tree cover
+H623021010,10,Tree cover
+H600021010,10,Tree cover
+H603021010,50,Built-up
+H605021010,30,Grassland
+H605022010,10,Tree cover
+H605641101,10,Tree cover
+H605641201,10,Tree cover
+H606000101,10,Tree cover
+H623022010,10,Tree cover
+H402021010,10,Tree cover
+H403301101,10,Tree cover
+H404021010,10,Tree cover
+H420021010,10,Tree cover
+H420021011,10,Tree cover
+H403021010,30,Grassland
+H404021101,50,Built-up
+H414000201,10,Tree cover
+H416021010,10,Tree cover
+H420021012,10,Tree cover
+H426021010,10,Tree cover
+H426021201,10,Tree cover
+H431021010,10,Tree cover
+H432021010,10,Tree cover
+H438021010,10,Tree cover
diff --git a/datasets/worldcover_ndvi.csv b/datasets/worldcover_ndvi.csv
new file mode 100644
index 0000000000000000000000000000000000000000..1076fbf7b854b618e1f3aa0bd251a4f868f53f45
--- /dev/null
+++ b/datasets/worldcover_ndvi.csv
@@ -0,0 +1,28 @@
+station_code,ndvi_p90,ndvi_p50,ndvi_p10
+H602021010,223.0,210.0,181.0
+H600021010,210.0,200.0,181.0
+H603021010,172.0,168.0,149.0
+H605021010,220.0,213.0,186.0
+H605641101,236.0,213.0,194.0
+H402021010,214.0,202.0,183.0
+H403301101,227.0,214.0,187.0
+H404021010,205.0,195.0,166.0
+H420021010,235.0,221.0,196.0
+H420021011,230.0,207.0,176.0
+H403021010,236.0,228.0,174.0
+H404021101,160.0,156.0,150.0
+H414000201,193.0,186.0,176.0
+H416021010,213.0,206.0,191.0
+H420021012,222.0,202.0,176.0
+H426021010,197.0,188.0,170.0
+H426021201,226.0,212.0,201.0
+H605641401,180.0,170.0,164.0
+H620021010,239.0,216.0,189.0
+H623021010,200.0,181.0,172.0
+H605022010,226.0,205.0,193.0
+H605641201,218.0,205.0,178.0
+H606000101,223.0,198.0,176.0
+H623022010,212.0,200.0,163.0
+H431021010,199.0,176.0,161.0
+H432021010,204.0,189.0,162.0
+H438021010,215.0,195.0,172.0
diff --git a/scripts/__pycache__/download_bdcavites.cpython-311.pyc b/scripts/__pycache__/download_bdcavites.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..47145c20a51e42a636d4c3f8c3d37562605f59e9
Binary files /dev/null and b/scripts/__pycache__/download_bdcavites.cpython-311.pyc differ
diff --git a/scripts/__pycache__/download_bdcharm.cpython-311.pyc b/scripts/__pycache__/download_bdcharm.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a0768dc3cba04bf14e39a9678ce54aee82ee830c
Binary files /dev/null and b/scripts/__pycache__/download_bdcharm.cpython-311.pyc differ