River_Network / scripts /fetch_idpr_brgm.py
ageraustine's picture
Upload folder using huggingface_hub (part 2)
4bb7968 verified
Raw
History Blame Contribute Delete
5.76 kB
"""
Fetches fresh IDPR (Indice de Développement et de Persistance des
Réseaux) values directly from BRGM's live geoservice, replacing/
supplementing the manually-uploaded idpr.csv.
CONFIRMED (fetched GetCapabilities directly): endpoint
http://geoservices.brgm.fr/geologie, WMS 1.3.0, live and responding.
IDPR is raster/grid data at five resolutions (IDPR_50M down to
IDPR_5000M) -- not a vector feature type, so WFS GetFeature (the
pattern used everywhere else in this project) doesn't apply. The
correct WMS operation for extracting a value at a specific point from a
raster layer is GetFeatureInfo: request a small map image centered on
the point, then ask "what's the value at this pixel."
This queries ONE point at a time (GetFeatureInfo has no bulk/vectorized
equivalent the way BD TOPO's WFS BBOX queries did) -- scoped to the 27
real gauge stations, matching what "redownload IDPR" means (fresh
values at known station points), not the full ~4,500-node reach graph
(which would mean thousands of individual HTTP requests -- a different,
much larger undertaking than this).
Usage:
python -m scripts.fetch_idpr_brgm --check
python -m scripts.fetch_idpr_brgm --stations datasets/station_elevations.csv
"""
import argparse
import re
from pathlib import Path
import pandas as pd
import requests
WMS_URL = "http://geoservices.brgm.fr/geologie"
LAYER = "IDPR_50M" # highest resolution; use --layer to try a coarser one if this fails
def get_feature_info_at_point(lat: float, lon: float, layer: str = LAYER,
half_extent_deg: float = 0.001) -> "float | None":
"""
Query IDPR value at a single point via WMS GetFeatureInfo: request a
tiny 3x3 pixel map centered on (lat, lon), then ask for the value at
the center pixel.
"""
minx, miny = lon - half_extent_deg, lat - half_extent_deg
maxx, maxy = lon + half_extent_deg, lat + half_extent_deg
params = {
"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetFeatureInfo",
"LAYERS": layer, "QUERY_LAYERS": layer, "STYLES": "",
"BBOX": f"{minx},{miny},{maxx},{maxy}", "SRS": "EPSG:4326",
"WIDTH": 3, "HEIGHT": 3, "X": 1, "Y": 1,
"INFO_FORMAT": "text/plain",
}
try:
resp = requests.get(WMS_URL, params=params, timeout=30)
except requests.RequestException as e:
print(f" request failed: {e}")
return None
if resp.status_code != 200:
print(f" HTTP {resp.status_code}")
return None
return parse_getfeatureinfo_value(resp.text)
def parse_getfeatureinfo_value(text: str) -> "float | None":
"""
MapServer's text/plain GetFeatureInfo output is typically a small
block like:
Layer 'IDPR_50M'
Feature 0
value = '885'
Parsing defensively: find any line with 'value' (case-insensitive)
and extract the first number on it, rather than assuming an exact
format -- MapServer's plain-text output format has enough real
variation across deployments that a strict parser is more likely to
silently return nothing than a permissive one is to return a wrong
number.
"""
for line in text.splitlines():
if "value" in line.lower():
match = re.search(r"[-+]?\d*\.?\d+", line)
if match:
return float(match.group())
return None
def check_service() -> bool:
# A real point known to be in mainland France, well within IDPR coverage
test_lat, test_lon = 49.03, 0.79
print(f"Testing {LAYER} at ({test_lat}, {test_lon})...")
value = get_feature_info_at_point(test_lat, test_lon)
if value is not None:
print(f" OK: value = {value}")
return True
print(" FAILED: no value returned. Try --layer IDPR_100M or IDPR_500M "
"(coarser resolutions), or check GetCapabilities directly:")
print(f" {WMS_URL}?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities")
return False
def fetch_for_stations(stations_path: Path, layer: str, output_path: Path) -> None:
stations = pd.read_csv(stations_path)
lat_col = "latitude" if "latitude" in stations.columns else "lat"
lon_col = "longitude" if "longitude" in stations.columns else "lon"
rows = []
for _, row in stations.iterrows():
code = row["station_code"]
value = get_feature_info_at_point(row[lat_col], row[lon_col], layer=layer)
print(f" {code}: {value}")
rows.append({"station_code": code, "idpr_value_brgm_live": value,
"latitude": row[lat_col], "longitude": row[lon_col]})
out = pd.DataFrame(rows)
n_ok = out["idpr_value_brgm_live"].notna().sum()
print()
print(f"{n_ok}/{len(out)} stations got a real value")
out.to_csv(output_path, index=False)
print(f"Saved to {output_path}")
if n_ok < len(out):
print("Some stations got no value -- this could mean they're just outside "
"IDPR's coverage, or the layer/resolution needs adjusting. Compare "
"against the existing idpr.csv for those specific stations before "
"assuming the live fetch is wrong.")
def main() -> None:
parser = argparse.ArgumentParser(description="Fetch fresh IDPR values from BRGM's live geoservice")
parser.add_argument("--check", action="store_true")
parser.add_argument("--stations", type=Path, default=Path("datasets/station_elevations.csv"))
parser.add_argument("--layer", type=str, default=LAYER)
parser.add_argument("--output", type=Path, default=Path("datasets/idpr_brgm_live.csv"))
args = parser.parse_args()
if args.check:
check_service()
return
fetch_for_stations(args.stations, args.layer, args.output)
if __name__ == "__main__":
main()