Add match_station_basin function to identify unique basins for GloFAS stations - #99
Add match_station_basin function to identify unique basins for GloFAS stations#99jmargutt wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new shared geospatial utility to map each GloFAS station to a “unique” HydroBASINS basin (coarsest level where the station is the only one from that country), intended to support flood hazard exposure/admin-division selection logic.
Changes:
- Introduces
match_station_basin()to compute per-station basin assignment across HydroBASINS levels (4–12). - Adds helpers to load basin shapefiles from disk and compute “coarsest unique basin” via spatial joins + per-basin station counts.
- Returns a GeoDataFrame keyed by
stationCodewith basin geometry,HYBAS_ID, and selectedbasin_level.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| # Reproject all basin levels to match the stations CRS so that spatial | ||
| # joins and the output geometries are all in a consistent coordinate system | ||
| stations_crs = stations_with_country.crs | ||
| basins = { | ||
| level: gdf.to_crs(stations_crs) if gdf.crs != stations_crs else gdf | ||
| for level, gdf in basins.items() | ||
| } |
There was a problem hiding this comment.
If stations_gdf has no CRS set (stations_gdf.crs is None), the basin reprojection step will call GeoDataFrame.to_crs(None) and raise. Add an explicit validation/exception for missing station CRS (or require an explicit CRS parameter) before reprojecting basins.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Done in commit 2647331. Added:
- An explicit check that raises
ValueErrorwhenstations_gdf.crs is None, before the reprojection loop. - Per-level basin CRS validation inside the reprojection loop: if a loaded basin GeoDataFrame has no CRS, a
ValueErroris raised with the matching filename(s) to make the failure easy to diagnose.
| for level in range(min_level, max_level + 1): | ||
| # Try to find shapefile matching common naming patterns | ||
| pattern = os.path.join(basins_dir, f"*lev{level:02d}*.shp") | ||
| matches = glob.glob(pattern) | ||
| if matches: | ||
| print(f" Loading level {level} from {matches[0]} …") | ||
| basins[level] = gpd.read_file(matches[0]) | ||
| else: | ||
| print(f" Level {level}: shapefile not found, skipping") |
There was a problem hiding this comment.
_load_basins_from_path() uses glob and silently picks matches[0]. If basins_dir contains multiple HydroBASINS files per level (e.g., multiple regions/continents), this becomes non-deterministic and can select the wrong shapefile. Consider sorting matches and either (a) erroring when len(matches)!=1 or (b) reading all matches and concatenating into one GeoDataFrame for that level.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/rodekruis/IBF/sessions/8c8ef91e-ec45-44f8-96aa-3048b8c044ae Co-authored-by: jmargutt <26323051+jmargutt@users.noreply.github.com>
AB#41554
WIP: will be picked up again later.
Describe your changes
Adds
match_station_basin()todata/shared/to map each GloFAS station to the coarsest HydroBASINS level where it is the only station from its country. Intended for use by flood-hazard-logic to determine exposed administrative divisions.Key implementation details:
n_same_country == 1) and basin area ≤max_basin_area_km2GeoDataFramekeyed bystationCodewith basin geometry,HYBAS_ID, andbasin_levelCRS safety (addressed in review):
ValueErrorearly ifstations_gdf.crs is None— prevents silent failure when reprojecting basins to match station CRSValueErrorper basin level if a loaded shapefile has no CRS metadata, including the matched filename for easy diagnosisChecklist before requesting a code review
Portal preview-deployment
This PR does not have any preview deployments yet.