Source code for liesel_gam.io

import logging
from pathlib import Path

import numpy as np

logger = logging.getLogger(__name__)


[docs] def read_bnd( filename: str | Path, delimiter: str = ",", ) -> dict[str, np.typing.NDArray]: """ Reads a .bnd file with geographical information, returns a polys dictionary. Parameters ---------- filename Filename. delimiter Delimiter Notes ----- The expected file format looks like this:: "A", 66 25.6422, -17.8037 25.6477, -17.7607 ... "B", 192 27.276, -17.0096 27.6848, -17.1684 ... Where the lines ``"A",66`` and ``"B",192`` are header lines that indicate the region label, and the number of following lines that define the polygon for the respective region. In this case, the region labelled ``"A"`` is defined by the following 66 lines, and the region labelled ``"B"`` is defined by the following 192 lines. """ polygons = {} with open(filename) as f: while True: header = f.readline() if not header: break # EOF header = header.strip() if not header: continue # skip empty lines parts = header.split(delimiter) if len(parts) != 2: raise ValueError(f"Invalid header line: {header}") label, n_points = parts label = label.strip().strip("\"'") try: n_points_int = int(n_points) except ValueError as e: raise ValueError(f"Invalid number of points in header: {header}") from e coords = [] for _ in range(n_points_int): line = f.readline() if not line: raise ValueError("Unexpected end of file while reading coordinates") line = line.strip() if not line: raise ValueError("Unexpected empty line while reading coordinates") xy = line.split(delimiter) if len(xy) != 2: raise ValueError(f"Invalid coordinate line: {line}") x, y = map(float, xy) coords.append([x, y]) polygons[label] = np.array(coords, dtype=float) for label, poly in polygons.items(): if not polygon_is_closed(poly): msg = ( f"Polygon of region with label '{label}' does not appear to be closed " "(first and last point are not equal)." ) logger.warning(msg) return polygons
[docs] def polygon_is_closed( poly: np.typing.ArrayLike, *, atol: float = 1e-12, rtol: float = 0.0, require_min_points: bool = True, ) -> bool: """ Validate that a polygon is closed: first vertex equals last vertex (within tolerance). Parameters ---------- poly : array-like, shape (n, 2) Polygon vertices. atol, rtol : float Absolute / relative tolerances used by np.allclose. require_min_points : bool If True, require at least 4 points for a closed polygon (first==last + >=3 distinct vertices). Returns ------- True if closed, else False. """ arr = np.asarray(poly, dtype=float) if arr.ndim != 2 or arr.shape[1] != 2: raise ValueError(f"Expected shape (n, 2), got {arr.shape}") n = arr.shape[0] if n == 0: return False if require_min_points and n < 4: return False return np.allclose(arr[0], arr[-1], atol=atol, rtol=rtol)