"""Ingest an OSM PBF file into a PostGIS database using osm2pgsql.""" from __future__ import annotations import os import shutil import subprocess from pathlib import Path def osm2pgsql_ingest( osm_pbf_path: "str | Path", host: str = "localhost", port: int = 5432, dbname: str = "gis", user: str = "geoserver", password: str = "geoserver", style: "str | None" = None, ensure_hstore: bool = True, ) -> dict: """Ingest an OSM PBF file into PostGIS using osm2pgsql. Verifies osm2pgsql is in PATH (raises RuntimeError if not). If ensure_hstore=True, runs psql to CREATE EXTENSION IF NOT EXISTS hstore. Then runs osm2pgsql with --create --slim --hstore --multi-geometry. Args: osm_pbf_path: Path to the .osm.pbf file to ingest. host: PostGIS host (default: localhost). port: PostGIS port (default: 5432). dbname: Database name (default: gis). user: Database user (default: geoserver). password: Database password (default: geoserver). style: Optional path to a .style file for osm2pgsql. If None, uses osm2pgsql's built-in default style. ensure_hstore: If True, create the hstore extension before ingesting. Returns: dict with keys: - ok (bool): True if ingestion succeeded. - rows_loaded (int | None): Not directly available from osm2pgsql stdout; always None (osm2pgsql does not report row counts). - stderr (str): Combined stdout+stderr output from osm2pgsql. Raises: RuntimeError: If osm2pgsql is not found in PATH. FileNotFoundError: If osm_pbf_path does not exist. """ pbf = Path(osm_pbf_path) if not pbf.exists(): raise FileNotFoundError(f"OSM PBF file not found: {pbf}") if shutil.which("osm2pgsql") is None: raise RuntimeError( "osm2pgsql not found in PATH. Install it before calling this function." ) env = os.environ.copy() env["PGPASSWORD"] = password if ensure_hstore and shutil.which("psql") is not None: psql_cmd = [ "psql", f"--host={host}", f"--port={port}", f"--dbname={dbname}", f"--username={user}", "--command", "CREATE EXTENSION IF NOT EXISTS hstore;", ] subprocess.run(psql_cmd, env=env, capture_output=True, text=True) cmd = [ "osm2pgsql", f"--host={host}", f"--port={port}", f"--database={dbname}", f"--user={user}", "--create", "--slim", "--hstore", "--multi-geometry", ] if style: cmd += ["--style", str(style)] cmd.append(str(pbf)) result = subprocess.run( cmd, env=env, capture_output=True, text=True, ) combined = result.stdout + result.stderr if result.returncode == 0: return {"ok": True, "rows_loaded": None, "stderr": combined} return {"ok": False, "rows_loaded": None, "stderr": combined}