""" Downloads BD Charm-50 (BRGM's harmonized 1:50,000 geological maps) for the departments covering the Eure/Risle study area. CONFIRMED (via data.gouv.fr / InfoTerre): free, open (Licence Ouverte), no authentication -- direct per-department ZIP download from InfoTerre. This is a genuinely different access pattern than the WFS sources elsewhere in this project (BD TOPO, BDCavités): no bbox query, no axis- order ambiguity, just a fixed URL per department. Real, working URL pattern (found directly, not guessed): http://infoterre.brgm.fr/telechargements/BDCharm50/GEO050K_HARM_XXX.zip where XXX is the 3-digit department code (e.g. 027 for Eure). NOTE: a separate, DIFFERENT distribution of this same underlying dataset exists that requires CIGAL network membership (seen for at least Alsace regional data) -- that is NOT what this script uses. This script only uses the free InfoTerre download confirmed via data.gouv.fr. DEPARTMENTS: 27 (Eure), 61 (Orne), 28 (Eure-et-Loir) -- 27 and 61 confirmed directly from real INSEE codes seen in this project's own station_list.csv (61342, 27040, 27116, 27468); 28 included because the Eure's own southern tributaries (Voise, Drouette) run through Chartres/ Dreux, unambiguously in Eure-et-Loir. Add more department codes via --departments if the real geographic extent turns out to need them. Usage: python -m scripts.download_bdcharm50 """ import argparse import zipfile from pathlib import Path import requests DEFAULT_DEPARTMENTS = ["027", "028", "061"] BASE_URL = "http://infoterre.brgm.fr/telechargements/BDCharm50" def download_department(dept: str, output_dir: Path) -> bool: url = f"{BASE_URL}/GEO050K_HARM_{dept}.zip" zip_path = output_dir / f"GEO050K_HARM_{dept}.zip" print(f" department {dept}: {url}") try: resp = requests.get(url, timeout=120, stream=True) except requests.RequestException as e: print(f" FAILED: {e}") return False if resp.status_code != 200: print(f" FAILED: HTTP {resp.status_code}") return False content_type = resp.headers.get("Content-Type", "") if "zip" not in content_type and "octet-stream" not in content_type: # A 200 status with an HTML content-type here usually means an # error page or a "department not available" page was returned # instead of the real ZIP -- catching this explicitly rather # than silently saving an HTML file with a .zip extension. print(f" WARNING: Content-Type is {content_type!r}, not zip -- " f"this department's file may not exist at this URL. Saving " f"anyway for inspection, but verify before trusting it.") zip_path.write_bytes(resp.content) size_kb = zip_path.stat().st_size / 1024 print(f" saved {zip_path} ({size_kb:.0f} KB)") extract_dir = output_dir / f"dept_{dept}" try: with zipfile.ZipFile(zip_path) as zf: zf.extractall(extract_dir) shp_files = list(extract_dir.rglob("*.shp")) print(f" extracted to {extract_dir} ({len(shp_files)} .shp file(s) found)") return True except zipfile.BadZipFile: print(f" FAILED: downloaded file is not a valid zip -- likely an error page, " f"not real data. Check {zip_path} directly.") return False def main() -> None: parser = argparse.ArgumentParser(description="Download BD Charm-50 geological maps") parser.add_argument("--departments", nargs="+", default=DEFAULT_DEPARTMENTS, help="3-digit department codes, e.g. 027 028 061") parser.add_argument("--output-dir", type=Path, default=Path("datasets/bdcharm50")) args = parser.parse_args() args.output_dir.mkdir(parents=True, exist_ok=True) print(f"Downloading BD Charm-50 for departments: {args.departments}") print() results = {} for dept in args.departments: results[dept] = download_department(dept, args.output_dir) print() print("=" * 60) ok = [d for d, r in results.items() if r] failed = [d for d, r in results.items() if not r] print(f"Succeeded: {ok}") if failed: print(f"Failed: {failed} -- check the URL pattern still matches by visiting " f"https://infoterre.brgm.fr/page/telechargement-cartes-geologiques directly") if __name__ == "__main__": main()