Skip to content

Services

Services handle specific domains of logic: networking, geospatial operations, the index repositories, and data management.

Index & lookups (consumer)

The repositories that resolve a query against the local or remote index.

IndexRepository

IndexRepository(settings: Settings | None = None)

Locate index artifacts locally or from a remote published index.

Bind to a settings object (defaults to get_settings).

Methods:

Name Description
index_source

Return the path/URI to open the index COG with rasterio.

open_index

Return an open rasterio dataset for the index COG, cached per thread.

local_index_missing

True when the COG is neither local nor available remotely (hard error).

ensure_tables

Mirror the small tables (dict/events/meta/manifest) once, if hosted.

mirror

Download the full published bundle to the cache (offline / HPC mirror).

Attributes:

Name Type Description
is_remote bool

True when the hosted index may be used (mode allows it + a base URL exists).

Attributes

is_remote property

is_remote: bool

True when the hosted index may be used (mode allows it + a base URL exists).

Methods:

index_source

index_source() -> str

Return the path/URI to open the index COG with rasterio.

A local file if present; otherwise a /vsicurl URL so the COG is read remotely, one ROI window at a time, without downloading it.

open_index

open_index() -> Any

Return an open rasterio dataset for the index COG, cached per thread.

Opening a remote COG re-reads its tile index over /vsicurl each time; caching the open handle lets a repeat floods() in one process reuse it (and GDAL's in-process block cache), so the second query is near-instant instead of paying the re-open. Local COGs are cached too (cheap, harmless). Callers must not close the returned dataset — use close_cached_datasets to release the cache.

local_index_missing

local_index_missing() -> bool

True when the COG is neither local nor available remotely (hard error).

ensure_tables

ensure_tables() -> None

Mirror the small tables (dict/events/meta/manifest) once, if hosted.

No-op when reading a local bundle, or once the dictionary + events table are cached. Leaves the big COG remote (streamed via /vsicurl).

mirror

mirror(*, include_cog: bool = True) -> None

Download the full published bundle to the cache (offline / HPC mirror).

DictionaryRepository

DictionaryRepository(settings: Settings | None = None)

Keyed lookup of flood events by combo_id (Parquet, with JSON fallback).

Detect the dictionary format; raise if none is present.

Methods:

Name Description
lookup_combos

Return {str(combo_id): {flood_ids[, events]}} for the given ids.

Methods:

lookup_combos

lookup_combos(
    combo_ids: list[int],
) -> dict[str, dict[str, Any]]

Return {str(combo_id): {flood_ids[, events]}} for the given ids.

The normalized Parquet path returns flood_ids only; the legacy JSON path may also carry an embedded events list.

EventsRepository

EventsRepository(settings: Settings | None = None)

Keyed lookup of flood-event metadata by global_id (from events.parquet).

Bind to a settings object; the table is read lazily per lookup.

Methods:

Name Description
lookup_events

Return {global_id: {metadata…}} for the given ids (present ones only).

Attributes:

Name Type Description
available bool

Whether the normalized events table exists in the cache.

Attributes

available property

available: bool

Whether the normalized events table exists in the cache.

Methods:

lookup_events

lookup_events(
    global_ids: list[int],
) -> dict[int, dict[str, Any]]

Return {global_id: {metadata…}} for the given ids (present ones only).

Reads only the matching rows. Missing table / ids resolve to {} / omission rather than raising, matching the old "skip unresolved" behaviour.

Geospatial

LocationResolver

LocationResolver(
    settings: Settings | None = None,
    geocoder: GeocodingService | None = None,
)

Resolve a region specification to a single WGS84 shapely geometry.

Initialize the resolver.

Parameters:

Name Type Description Default
settings Settings | None

Optional configuration. Defaults to get_settings.

None
geocoder GeocodingService | None

Optional injected GeocodingService (for place names).

None

Methods:

Name Description
resolve

Resolve exactly one location input to a WGS84 geometry.

buffer_metric

Buffer a WGS84 geometry by metres (project → buffer → project back).

Methods:

resolve

resolve(
    region: (
        str
        | BaseGeometry
        | GeoDataFrame
        | tuple[float, float, float, float]
        | None
    ) = None,
    *,
    point: tuple[float, float] | None = None,
    radius_m: float = 0.0,
    bbox: tuple[float, float, float, float] | None = None,
    shapefile: str | Path | None = None,
    buffer_m: float = 0.0,
    level: int | None = None,
    shape: str = "exact"
) -> BaseGeometry

Resolve exactly one location input to a WGS84 geometry.

Parameters:

Name Type Description Default
region str | BaseGeometry | GeoDataFrame | tuple[float, float, float, float] | None

A place name, shapely geometry, GeoDataFrame, or bbox tuple.

None
point tuple[float, float] | None

A (lat, lon) point; combine with radius_m.

None
radius_m float

Radius in metres around point.

0.0
bbox tuple[float, float, float, float] | None

A (minx, miny, maxx, maxy) bounding box in WGS84.

None
shapefile str | Path | None

Path to a vector file (read via geopandas, unioned).

None
buffer_m float

Optional extra metric buffer applied to the result.

0.0
level int | None

Optional NUTS level filter for place-name resolution.

None
shape str

How to regularize the resolved geometry before buffering: "exact" (default, the raw boundary), "bbox" (its bounding rectangle), or "hull" (its convex hull). Applied to every input mode, so buffer_m then grows the chosen shape.

'exact'

Returns:

Name Type Description
BaseGeometry BaseGeometry

The resolved geometry in EPSG:4326.

Raises:

Type Description
GeocodingError

If zero or more than one input is given, the input type is unsupported, or shape is not a known mode.

buffer_metric staticmethod

buffer_metric(
    geom: BaseGeometry,
    meters: float,
    *,
    join_style: str = "round"
) -> BaseGeometry

Buffer a WGS84 geometry by metres (project → buffer → project back).

join_style controls corners: "round" (default) for organic shapes, "mitre" to keep a bounding box / convex hull sharp-cornered so it grows into a larger polygon of the same kind rather than a rounded one.

GeocodingService

GeocodingService(
    settings: Settings | None = None,
    downloader: DownloadService | None = None,
)

Resolve administrative-area place names to WGS84 geometries.

The backend is chosen by settings.geocoder_backend:

  • "online_first" (default) — query OpenStreetMap Nominatim, then fall back to the offline Eurostat NUTS dataset on any network failure or empty result.
  • "nominatim" — online only (no offline fallback).
  • "local" — the offline NUTS dataset only (fully offline). Tolerant of accents/case, bilingual and suffixed names, common exonyms, and close typos.

Attributes:

Name Type Description
settings

The active configuration.

backend

The resolved backend name (see above).

Initialize the GeocodingService.

Parameters:

Name Type Description Default
settings Settings | None

Optional configuration. Defaults to get_settings.

None
downloader DownloadService | None

Optional injected DownloadService used to fetch the local boundary dataset on first use.

None

Methods:

Name Description
get_geometry

Resolve a place name to a single shapely geometry in EPSG:4326.

Methods:

get_geometry

get_geometry(
    query: str, level: int | None = None
) -> BaseGeometry

Resolve a place name to a single shapely geometry in EPSG:4326.

Parameters:

Name Type Description Default
query str

Place name, e.g. "Cologne, Germany". Only the part before the first comma is matched against the local dataset.

required
level int | None

Optional NUTS level filter (0=country ... 3=small region).

None

Returns:

Name Type Description
BaseGeometry BaseGeometry

The (possibly multi-) polygon in EPSG:4326.

Raises:

Type Description
GeocodingError

If the place cannot be resolved.

RasterOps

Utilities for manipulating raster files.

Methods:

Name Description
project_geometry

Reproject a shapely geometry to a different Coordinate Reference System.

crs_of

Return a raster's CRS without materializing a band (a cheap header read).

read_array

Read one band of a raster into memory with its georeferencing.

crop_raster

Crop a raster to a given geometry.

mosaic_and_crop

Mosaic one or more rasters over an ROI window, then crop to a geometry.

Methods:

project_geometry staticmethod

project_geometry(
    geom: BaseGeometry, to_crs: CRS, from_crs: CRS = WGS84
) -> BaseGeometry

Reproject a shapely geometry to a different Coordinate Reference System.

Parameters:

Name Type Description Default
geom BaseGeometry

The input shapely geometry.

required
to_crs CRS

The target CRS (e.g., CRS("EPSG:3857")).

required
from_crs CRS

The source CRS. Defaults to CRS("EPSG:4326").

WGS84

Returns:

Name Type Description
BaseGeometry BaseGeometry

The reprojected geometry.

crs_of staticmethod

crs_of(source_path: str | Path) -> Any

Return a raster's CRS without materializing a band (a cheap header read).

Lets callers branch on the CRS (e.g. reproject-if-geographic) before deciding how to read, so the band is read exactly once instead of once to inspect the CRS and again to actually use it.

read_array staticmethod

read_array(
    source_path: str | Path,
    *,
    band: int = 1,
    to_crs: Any = None,
    resampling: Resampling = bilinear
) -> tuple[Any, Any, Any, float | None]

Read one band of a raster into memory with its georeferencing.

Returns the (array, transform, crs, nodata) triple that plotting needs — the same data crop_raster / mosaic_and_crop compute but write to disk. The raster is assumed to already be on disk (e.g. a .download() output).

Parameters:

Name Type Description Default
source_path str | Path

Path (or /vsicurl/ URL) of the raster.

required
band int

1-based band index to read (default 1).

1
to_crs Any

If given (e.g. "EPSG:4326") and the source is in a different CRS, reproject the band to it in-memory. Downloaded depth GeoTIFFs keep their source CRS (a metric projection), so plotting/overlaying them needs this to land in lat/lon.

None
resampling Resampling

Resampling method used when to_crs triggers a reproject. Defaults to bilinear (smooth, for display); pass Resampling.nearest to preserve exact pixel values (for stats).

bilinear

Returns:

Type Description
Any

The 2-D array, the affine transform, the crs, and the

Any

nodata sentinel (None if unset).

crop_raster staticmethod

crop_raster(
    source_path: Path,
    output_path: Path,
    geometry: BaseGeometry,
    crop_to_poly: bool = True,
) -> bool

Crop a raster to a given geometry.

This method reads a source raster, masks it using the provided geometry, and writes the result to a new file. It handles CRS mismatches by reprojecting the geometry to match the raster's CRS.

Parameters:

Name Type Description Default
source_path Path

Path to the input TIF file.

required
output_path Path

Path where the cropped TIF will be saved.

required
geometry BaseGeometry

The shapely geometry defining the crop area (usually in WGS84).

required
crop_to_poly bool

If True, sets pixels outside the polygon to NoData. If False, crops to the bounding box of the geometry. Defaults to True.

True

Returns:

Name Type Description
bool bool

True if successful and data was written, False otherwise (e.g., no overlap or empty result).

mosaic_and_crop staticmethod

mosaic_and_crop(
    sources: Sequence[str | Path],
    output_path: Path,
    geometry: BaseGeometry,
    *,
    nodata: float | None = None,
    crop_to_poly: bool = True
) -> bool

Mosaic one or more rasters over an ROI window, then crop to a geometry.

Opens each source (a local path or a /vsicurl/ URL), merges them with rasterio.merge.merge(bounds=geometry.bounds) so only the ROI window of each source is read (bounded memory regardless of full tile size), optionally masks pixels outside the polygon to nodata, and writes an LZW GeoTIFF. Handles 1..N sources uniformly (a single source is a no-op mosaic).

Parameters:

Name Type Description Default
sources Sequence[str | Path]

Local paths or /vsicurl/ URLs, all in the same CRS.

required
output_path Path

Where to write the cropped mosaic.

required
geometry BaseGeometry

ROI geometry (usually WGS84); reprojected to the source CRS.

required
nodata float | None

NoData value for merge/masking and the emptiness check.

None
crop_to_poly bool

If True, set pixels outside the polygon to nodata (requires nodata); otherwise keep the bbox-cropped mosaic.

True

Returns:

Name Type Description
bool bool

True if non-empty data was written, False otherwise (no overlap

bool

or an entirely-NoData result).

HazardTileIndex

HazardTileIndex(
    settings: Settings | None = None,
    downloader: DownloadService | None = None,
)

Cache and query the GLOFAS tile-extents GeoJSON.

Build the index.

Parameters:

Name Type Description Default
settings Settings | None

Optional configuration. Defaults to get_settings.

None
downloader DownloadService | None

Optional injected DownloadService. Defaults to one writing into settings.get_hazard_dir() so the index lands at settings.get_hazard_index_path().

None

Methods:

Name Description
tiles_for

Return the tiles whose extent intersects roi for return_period.

all_tiles

Return every tile for return_period (used by the bulk mirror).

Methods:

tiles_for

tiles_for(
    roi: BaseGeometry, return_period: int
) -> list[HazardTile]

Return the tiles whose extent intersects roi for return_period.

all_tiles

all_tiles(return_period: int) -> list[HazardTile]

Return every tile for return_period (used by the bulk mirror).

Network & IO

ScraperService

ScraperService(settings: Settings | None = None)

Handles navigation and parsing of the remote JRC FTP-like website.

This service connects to the base URL defined in settings, recursively navigates year directories, and parses HTML links to identify valid flood map TIF files based on a regex pattern.

Attributes:

Name Type Description
base_url str

The root URL to scrape.

session Session

Persistent HTTP session for efficiency.

file_pattern Pattern

Regex pattern to match valid filenames.

Initialize the ScraperService.

Parameters:

Name Type Description Default
settings Settings | None

Optional configuration. Defaults to get_settings.

None

Methods:

Name Description
fetch_all_records

Crawls all year directories and returns raw record dictionaries.

Methods:

fetch_all_records

fetch_all_records() -> list[dict[str, Any]]

Crawls all year directories and returns raw record dictionaries.

Iterates through every year folder found by _get_year_links, scrapes the TIF files within, and extracts metadata using the regex pattern.

Returns:

Type Description
list[dict[str, Any]]

List[Dict[str, Any]]: List of dictionaries compatible with the FloodRecord schema. Each dict contains keys: global_id, filename, year, start_date, cluster_id, download_url.

Note

Failures in specific year folders are logged as errors but do not stop the scraping of other years.

DownloadService

DownloadService(
    download_dir: Path | None = None,
    settings: Settings | None = None,
)

Manages file downloads with caching and retry logic.

This service ensures files are downloaded reliably to the local cache. It checks if files already exist to avoid redundant downloads and uses atomic file writing (download to .tmp -> rename) to prevent partial files.

Attributes:

Name Type Description
download_dir Path

The directory where downloaded files are stored.

Initialize the DownloadService.

Parameters:

Name Type Description Default
download_dir Optional[Path]

Target directory for downloads. Defaults to settings.cache_dir / 'downloads'.

None
settings Settings | None

Optional configuration. Defaults to get_settings.

None

Methods:

Name Description
download_file

Download a file if it doesn't exist locally (or is the wrong size).

Methods:

download_file

download_file(
    url: str,
    filename: str,
    expected_size: int | None = None,
    *,
    on_bytes: Callable[[int], None] | None = None
) -> Path | None

Download a file if it doesn't exist locally (or is the wrong size).

Checks the cache first. If the file is missing, empty, or — when expected_size is given — a different size than expected, it is (re-)downloaded. A size mismatch means a stale/corrupt cache file (e.g. from an older interrupted run), so it is re-fetched.

Parameters:

Name Type Description Default
url str

Remote URL.

required
filename str

Target filename to save as in the download directory.

required
expected_size Optional[int]

Expected file size in bytes. When set, a cached file whose size differs is treated as corrupt and re-downloaded.

None
on_bytes Callable[[int], None] | None

Optional per-chunk progress callback (n_bytes) -> None. Intended for single, large, foreground downloads (a cache hit never calls it).

None

Returns:

Type Description
Path | None

Optional[Path]: Path to the downloaded file if successful, or None if failed.

Data processing (producer)

RasterProcessor

RasterProcessor(settings: Settings | None = None)

Converts raw Flood TIFs into standardized sparse point data (Parquet).

The processor performs the following steps: 1. Reads the raster TIF in full-width row stripes (bounded memory). 2. Filters for valid data (wet pixels > 0). 3. Transforms pixel coordinates to WGS84 Lat/Lon. 4. Maps these Lat/Lon coordinates to the GlobalGrid indices (col, row). 5. Saves the result as a Parquet file for efficient aggregation later.

Initialize the RasterProcessor.

Parameters:

Name Type Description Default
settings Settings | None

Optional configuration. Defaults to get_settings. Stored on the instance so the configuration travels with the processor when it is pickled to ProcessPoolExecutor workers.

None

Methods:

Name Description
process

Process a single TIF file.

Methods:

process

process(tif_path: Path, global_id: int, year: str) -> int

Process a single TIF file.

Parameters:

Name Type Description Default
tif_path Path

Path to source TIF file.

required
global_id int

Unique ID assigned to this flood event (from inventory).

required
year str

Year string for directory organization.

required

Returns:

Name Type Description
int int

Number of points stored in the parquet file. Returns 0 if the file was empty, skipped, or had no valid pixels.

Raises:

Type Description
ProcessingError

If an error occurs during raster reading or processing.

InventoryRepository

InventoryRepository(settings: Settings | None = None)

Handles loading, saving, and querying the flood map inventory.

The inventory is stored as a CSV file defined in settings.inventory_filename. It uses Pydantic for validation before saving to ensure data integrity.

Attributes:

Name Type Description
csv_path Path

Path to the inventory CSV file.

Initiate Inventory Repository.

Parameters:

Name Type Description Default
settings Settings | None

Optional configuration. Defaults to get_settings.

None

Methods:

Name Description
save

Validate and save records to CSV.

load

Load the inventory into a pandas DataFrame.

exists

Check if inventory file exists.

Methods:

save

save(records: list[dict[str, Any]]) -> None

Validate and save records to CSV.

Converts raw dictionaries into FloodRecord Pydantic models to validate them, then serializes them to a CSV file.

Parameters:

Name Type Description Default
records List[Dict[str, Any]]

List of raw dictionaries obtained from the scraper.

required
Note

If records is empty, no file is written and a warning is logged.

load

load() -> DataFrame

Load the inventory into a pandas DataFrame.

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the inventory. Returns an empty DataFrame if the file does not exist.

exists

exists() -> bool

Check if inventory file exists.

Returns:

Name Type Description
bool bool

True if the inventory CSV exists, False otherwise.

Statistics

depth_raster_stats

depth_raster_stats(
    path: str | Path,
    *,
    scale: float = 0.01,
    max_depth: float | None = None
) -> dict[str, float]

Summarize one downloaded depth GeoTIFF.

Parameters:

Name Type Description Default
path str | Path

A downloaded depth GeoTIFF.

required
scale float

Multiplier applied to raster values (EFAS depth is centimetres, so the default 0.01 yields metres; pass 1.0 for a metres raster).

0.01
max_depth float | None

Optional upper bound (in raster units) above which pixels are treated as suspect and excluded.

None

Returns:

Type Description
dict[str, float]

A dict with wet_pixels, max_depth_m, mean_depth_m,

dict[str, float]

p95_depth_m, flooded_area_km2, volume_m3 and volume_Mm3

dict[str, float]

(million m³). An all-dry raster yields zeros.

catalogue_stats

catalogue_stats(
    frame: Any, *, scale: float | None = None
) -> DataFrame

Per-event depth statistics for a catalogue's downloaded rasters.

One row per downloaded event, carrying the identifying columns (event_id / date for historic, return_period for hazard) alongside the depth_raster_stats fields. Reads only already-downloaded rasters.

Parameters:

Name Type Description Default
frame Any

A catalogue (a downloaded FloodFrame or equivalent) with a path column.

required
scale float | None

Raster-value->metre multiplier. None (default) picks it from the catalogue kind via depth_scale_for (0.01 for EFAS cm, 1.0 for GLOFAS m).

None

Raises:

Type Description
ProcessingError

If nothing has been downloaded.

catalogue_summary

catalogue_summary(
    frame: Any, *, scale: float | None = None
) -> dict[str, float]

Aggregate (envelope) depth statistics across a catalogue's downloaded rasters.

Combines the rasters into a per-pixel MAX composite and summarizes that, so overlapping events are counted once: the returned flooded_area_km2 is the union footprint and volume_m3 the envelope volume. Adds n_events (how many rasters went in). Reads only already-downloaded rasters.

Parameters:

Name Type Description Default
frame Any

A catalogue (a downloaded FloodFrame or equivalent) with a path column.

required
scale float | None

Raster-value->metre multiplier. None (default) picks it from the catalogue kind via depth_scale_for.

None

Raises:

Type Description
ProcessingError

If nothing has been downloaded.