Source code for sgu_client.models.modeled
"""Pydantic models for modeled groundwater data from SGU API."""
from typing import ClassVar
from pydantic import Field, field_validator
from sgu_client.models.base import SGUBaseModel
from sgu_client.models.dates import ApiDatetime
from sgu_client.models.features import Feature, FeatureCollection, TimeSeriesCollection
# Modeled area properties (from omraden collection)
[docs]
class ModeledAreaProperties(SGUBaseModel):
"""Properties for a modeled groundwater area."""
model_config: ClassVar = {"validate_by_name": True, "validate_by_alias": True}
area_id: int = Field(..., alias="omrade_id", description="Area ID")
time_series_url: str | None = Field(
None, alias="url_tidsserie", description="Time series URL for this area"
)
[docs]
class ModeledArea(Feature[ModeledAreaProperties]):
"""A modeled groundwater area (GeoJSON Feature)."""
# Modeled groundwater level properties (from grundvattennivaer-tidigare collection)
[docs]
class ModeledGroundwaterLevelProperties(SGUBaseModel):
"""Properties for a modeled groundwater level."""
model_config: ClassVar = {"validate_by_name": True, "validate_by_alias": True}
# Date and area identification
date: ApiDatetime = Field(None, alias="datum", description="Date (midnight UTC)")
area_id: int = Field(..., alias="omrade_id", description="Area ID")
# Deviation percentiles (0-100)
deviation_small_resources: int | None = Field(
None,
alias="grundvattensituation_sma",
description="Deviation for small resources (percentile 0-100)",
)
deviation_large_resources: int | None = Field(
None,
alias="grundvattensituation_stora",
description="Deviation for large resources (percentile 0-100)",
)
# Relative level percentiles (0-100)
relative_level_small_resources: int | None = Field(
None,
alias="fyllnadsgrad_sma",
description="Relative level for small resources (percentile 0-100)",
)
relative_level_large_resources: int | None = Field(
None,
alias="fyllnadsgrad_stora",
description="Relative level for large resources (percentile 0-100)",
)
# Object identifier
object_id: int = Field(..., alias="objectid", description="Object ID")
@field_validator(
"deviation_large_resources", "relative_level_large_resources", mode="before"
)
@classmethod
def transform_missing_values(cls, v):
"""Transform -1 values to None for large resource fields."""
return None if v == -1 else v
[docs]
class ModeledGroundwaterLevel(Feature[ModeledGroundwaterLevelProperties]):
"""A modeled groundwater level (GeoJSON Feature)."""
# Collection response models
[docs]
class ModeledAreaCollection(FeatureCollection[ModeledArea]):
"""Collection of modeled groundwater areas (GeoJSON FeatureCollection).
Areas are grid cells, so the DataFrame has ``centroid_longitude`` and
``centroid_latitude`` columns: the centre of the cell. SGU sends only the
cell's outline; the centre is computed by sgu-client.
Examples:
>>> from sgu_client import SGUClient
>>> with SGUClient() as client:
... areas = client.levels.modeled.get_areas(limit=10)
... df = areas.to_dataframe()
... print(df[['area_id', 'centroid_longitude', 'centroid_latitude']].head())
"""
_geometry = "centroid"
[docs]
class ModeledGroundwaterLevelCollection(TimeSeriesCollection[ModeledGroundwaterLevel]):
"""Collection of modeled groundwater levels (GeoJSON FeatureCollection).
Its DataFrame has no geometry columns. Sorted by ``date``; ``to_series()``
defaults to ``relative_level_small_resources``.
Examples:
>>> from sgu_client import SGUClient
>>> with SGUClient() as client:
... levels = client.levels.modeled.get_levels(limit=100)
... df = levels.to_dataframe()
... print(df[['date', 'area_id', 'relative_level_small_resources']].head())
... series = levels.to_series(data="relative_level_large_resources")
"""
_date_column = "date"
_series_column = "relative_level_small_resources"