Source code for stilt.config.geometry
"""
Geometry settings for footprints.
A footprint's ``geometry`` names the polygons it will be aggregated to: a
vector file, H3 hexagons, or windows around points. Each spec's ``build``
method returns a :class:`stilt.Mesh`. When ``grid`` is not given, the grid
is derived from the geometry with :meth:`stilt.Grid.from_geometry`.
.. code-block:: yaml
geometry: # the default footprint's geometry
kind: file
path: counties.shp
ids: NAME
variants:
hrrr: {}
hrrr-hexes: # a second footprint from the same particles
from: hrrr
geometry: {kind: h3, resolution: 8, bounds: {xmin: -112.3, xmax: -111.6, ymin: 40.4, ymax: 41.0}}
cells_per_target: 4
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Annotated, Literal
from pydantic import BaseModel, ConfigDict, Field
from .spatial import Bounds
if TYPE_CHECKING:
from stilt.geometry import Mesh
[docs]
class FileGeometrySpec(BaseModel):
"""Polygons read from a vector file such as a shapefile or GeoPackage."""
model_config = ConfigDict(frozen=True)
kind: Literal["file"] = "file"
path: str = Field(..., description="Path to the vector file.")
ids: str | None = Field(
None,
description="Attribute column holding the cell ids. Unset uses the row number.",
)
layer: str | None = Field(
None, description="Layer to read from a multi-layer file."
)
where: str | None = Field(
None, description="Attribute filter as an OGR SQL WHERE clause."
)
[docs]
def build(self) -> Mesh:
"""Read the file into a :class:`stilt.Mesh`. Requires geopandas."""
from stilt.geometry import Mesh
kwargs = {}
if self.layer is not None:
kwargs["layer"] = self.layer
if self.where is not None:
kwargs["where"] = self.where
return Mesh.from_file(self.path, ids=self.ids, **kwargs)
[docs]
class H3GeometrySpec(BaseModel):
"""H3 hexagons of one resolution covering a longitude/latitude box."""
model_config = ConfigDict(frozen=True)
kind: Literal["h3"] = "h3"
resolution: int = Field(..., description="H3 resolution (0-15).", ge=0, le=15)
bounds: Bounds = Field(..., description="Longitude/latitude box to cover.")
[docs]
def build(self) -> Mesh:
"""Build the hexagons as a :class:`stilt.Mesh`. Requires ``h3``."""
from stilt.geometry import Mesh
return Mesh.from_h3(self.resolution, self.bounds)
[docs]
class WindowsGeometrySpec(BaseModel):
"""Rectangular windows centered on points, such as known point sources."""
model_config = ConfigDict(frozen=True)
kind: Literal["windows"] = "windows"
coords: list[tuple[float, float]] = Field(
..., description="Window centers as (x, y) pairs in ``crs`` units."
)
size: float | tuple[float, float] = Field(
..., description="Window width, or (width, height), in ``crs`` units."
)
ids: list[str] | None = Field(None, description="Label for each window.")
crs: str = Field(
"+proj=longlat", description="Coordinate reference system of ``coords``."
)
[docs]
def build(self) -> Mesh:
"""Build the windows as a :class:`stilt.Mesh`."""
from stilt.geometry import Mesh
return Mesh.from_windows(self.coords, self.size, ids=self.ids, crs=self.crs)
GeometrySpec = Annotated[
FileGeometrySpec | H3GeometrySpec | WindowsGeometrySpec,
Field(discriminator="kind"),
]
"""Any geometry spec accepted by ``FootprintConfig.geometry``."""
__all__ = [
"FileGeometrySpec",
"GeometrySpec",
"H3GeometrySpec",
"WindowsGeometrySpec",
]