easysnowdata.stations.clients.FMIClient#
- class easysnowdata.stations.clients.FMIClient(base_url: str = 'https://opendata.fmi.fi/wfs', timeout: int = 120, max_retries: int = 3, backoff: int = 4, min_interval: float = 0.5, session: Session | None = None, allow_partial: bool = False)[source]#
Bases:
objectClient for FMI’s open-data WFS (daily snow depth and weather).
No credentials are needed.
- Parameters:
base_url (
str) – WFS endpoint.timeout (
int) – HTTP settings for the shared retry loop.max_retries (
int) – HTTP settings for the shared retry loop.backoff (
int) – HTTP settings for the shared retry loop.min_interval (
float) – Minimum seconds between requests (FMI allows 600 per 5 minutes).session (
requests.SessionorNone) – Optional pre-configured session.allow_partial (
bool) – If False (default), a failure to read FMI’s public station table (the only source of elevations) raisesFMIError; if True it is logged and stations getelevation_m = None.
- get_all_stations(active_only: bool = False, bbox: tuple[float, float, float, float] | None = None) list[dict][source]#
All stations of FMI’s snow-depth networks, open and closed.
- Parameters:
- Returns:
list[dict]–station_id(fmisid),name,latitude,longitude(ETRS89),elevation_m(FMI’s station table;Nonewhen absent),status,begin_date,end_date(Nonewhile active),wmo_id,region,network_code(AWS/CLIM/WS/PRCP, seeSNOW_NETWORKS),network_ids,networks(Finnish names, every network the station belongs to),station_groups,operatorandstation_url(None: FMI has no per-station page).- Raises:
FMIError – On an API failure, or when the station table cannot be read and
allow_partialis False.
- get_metadata(station_id: str) dict[source]#
Station metadata plus
data_variables.The service has no per-series inventory, so
data_variableslists what FMI’s snow-depth networks report, with the station’s period of activity asbegin_date/end_date;n_obsisNone. Whether a series holds values is only known by fetching it (closed stations may be unknown to the observation service — seescreen_station_ids()).
- screen_station_ids(station_ids: list[str] | str) tuple[list[str], list[str]][source]#
Split fmisids into those the observation service knows and those it does not.
The service rejects a whole request when any one fmisid is unknown, so ids are tried in batches with a one-day window and a failing batch is halved until the unknown ids are isolated. Results are cached on the client.
- reporting_stations(day: str | date, variable: str = 'snow', bbox: tuple[float, float, float, float] = (19.0, 59.0, 33.0, 71.0)) dict[str, dict][source]#
Stations with a daily value of variable on day, from one request.
A
bboxquery returns only stations that have data, so sampling a few days a year is a cheap way to find every station with a daily record — far cheaper thanscreen_station_ids()over thousands of closed stations, most of which the observation service does not know. One day for all of Finland is about 1 MB. The response also gives each station’s exact position, which the public station table rounds to 0.1°.- Parameters:
- Returns:
dict[str,dict]–{fmisid: {"name", "latitude", "longitude"}}for the stations with a non-missing value that day.- Raises:
FMIError – On a variable not served daily, or an API failure.
- reporting_station_ids(day: str | date, variable: str = 'snow', bbox: tuple[float, float, float, float] = (19.0, 59.0, 33.0, 71.0)) set[str][source]#
The fmisids of
reporting_stations().
- get_data(station_ids: list[str] | str | None = None, variables: list[str] | str | None = None, bbox: tuple[float, float, float, float] | None = None, begin_date: str | date | None = None, end_date: str | date | None = None, interval: str = 'daily', include_flags: bool = False) list[dict][source]#
Observations as flat records.
- Parameters:
station_ids (
list[str]orstrorNone) – fmisid(s). Required unlessbboxis given. Stations thefmisidparameter rejects are fetched with a box of ±0.25° around their listed position, keeping only their own series.variables (
list[str]orstrorNone) – FMI parameter names ("snow","tday","snow_aws", …) or standardized types ("snwd","temp", …).None= snow depth (snowdaily,snow_awssub-daily).bbox (
tuple, optional) –(min_lon, min_lat, max_lon, max_lat), used whenstation_idsis None: the service’s own bbox query, which returns every station with data in the box (the cheapest way to fetch many stations: all of Finland for a year is one request of ~20–60 MB). Without dates it covers 1840 to today.begin_date (
strordate, optional) – InclusiveYYYY-MM-DDbounds. Withstation_idsand no dates, the request covers the stations’ joint activity period: from the earliestbegin_dateto the latestend_date, or to today when any station is active (the whole record). Ranges are split into 366-day (daily) or 7-day (sub-daily) requests.end_date (
strordate, optional) – InclusiveYYYY-MM-DDbounds. Withstation_idsand no dates, the request covers the stations’ joint activity period: from the earliestbegin_dateto the latestend_date, or to today when any station is active (the whole record). Ranges are split into 366-day (daily) or 7-day (sub-daily) requests.interval (
str) –"daily"(default),"hourly"or"sub_hourly"(10-minute values).include_flags (
bool) – Addflag:"snow_free"/"no_precipitation"where the source sent its-1code, elseNone.
- Returns:
list[dict]–{station_id, date, variable, type, value, units, interval}(+datetimefor sub-daily,flag), one per non-missing value. A station no request reaches is logged.- Raises:
ValueError – If neither
station_idsnorbboxis given.FMIError – On an unknown variable, unsupported interval, a variable not served at the interval, a non-numeric station id, or an API failure.
Example
>>> client = FMIClient() >>> records = client.get_data( ... station_ids="101932", # Sodankylä Tähtelä ... begin_date="2024-01-01", ... end_date="2024-04-30", ... ) >>> records[0]["units"] 'cm'