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: object

Client 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.Session or None) – Optional pre-configured session.

  • allow_partial (bool) – If False (default), a failure to read FMI’s public station table (the only source of elevations) raises FMIError; if True it is logged and stations get elevation_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:
  • active_only (bool) – Keep stations with an activity period open to now.

  • bbox (tuple, optional) – (min_lon, min_lat, max_lon, max_lat).

Returns:

list[dict] – station_id (fmisid), name, latitude, longitude (ETRS89), elevation_m (FMI’s station table; None when absent), status, begin_date, end_date (None while active), wmo_id, region, network_code (AWS/CLIM/WS/PRCP, see SNOW_NETWORKS), network_ids, networks (Finnish names, every network the station belongs to), station_groups, operator and station_url (None: FMI has no per-station page).

Raises:

FMIError – On an API failure, or when the station table cannot be read and allow_partial is False.

get_metadata(station_id: str) → dict[source]#

Station metadata plus data_variables.

The service has no per-series inventory, so data_variables lists what FMI’s snow-depth networks report, with the station’s period of activity as begin_date / end_date; n_obs is None. Whether a series holds values is only known by fetching it (closed stations may be unknown to the observation service — see screen_station_ids()).

Parameters:

station_id (str) – fmisid.

Returns:

dict – The station dict plus data_variables: daily snow, tday, tmin, tmax and rrday for every station, and the hourly variables for automatic weather stations.

Raises:

FMIError – If the station is not in a snow-depth network.

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.

Parameters:

station_ids (list[str] or str) – fmisids.

Returns:

tuple[list[str], list[str]] – (known, unknown), each in input order.

Raises:

FMIError – On an API failure other than an unknown id.

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 bbox query 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 than screen_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:
  • day (str or date) – YYYY-MM-DD.

  • variable (str) – A daily FMI parameter (default "snow").

  • bbox (tuple) – (min_lon, min_lat, max_lon, max_lat); default FINLAND_BBOX.

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] or str or None) – fmisid(s). Required unless bbox is given. Stations the fmisid parameter rejects are fetched with a box of ±0.25° around their listed position, keeping only their own series.

  • variables (list[str] or str or None) – FMI parameter names ("snow", "tday", "snow_aws", …) or standardized types ("snwd", "temp", …). None = snow depth (snow daily, snow_aws sub-daily).

  • bbox (tuple, optional) – (min_lon, min_lat, max_lon, max_lat), used when station_ids is 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 (str or date, optional) – Inclusive YYYY-MM-DD bounds. With station_ids and no dates, the request covers the stations’ joint activity period: from the earliest begin_date to the latest end_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 (str or date, optional) – Inclusive YYYY-MM-DD bounds. With station_ids and no dates, the request covers the stations’ joint activity period: from the earliest begin_date to the latest end_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) – Add flag: "snow_free" / "no_precipitation" where the source sent its -1 code, else None.

Returns:

list[dict] – {station_id, date, variable, type, value, units, interval} (+ datetime for sub-daily, flag), one per non-missing value. A station no request reaches is logged.

Raises:
  • ValueError – If neither station_ids nor bbox is 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'

Examples using easysnowdata.stations.clients.FMIClient#

Finnish daily snow depth (FMI)

Finnish daily snow depth (FMI)