Meteo Data#

Weather data is essential for energy data analysis — outdoor temperature drives heating demand, solar radiation affects PV production, and humidity influences cooling loads. In Switzerland, two main open data sources provide free weather station data:

Provider

Description

Data Access

Resolution

MeteoSwiss

Federal Office of Meteorology and Climatology, official network

Station lookup + data download (open data)

10 min, hourly, daily

Agrometeo

Agricultural weather network

Station lookup + data download

Hourly

The pyedautils package provides a unified interface to both providers. This chapter shows how to:

  1. Find the nearest weather station to any Swiss location

  2. Download hourly weather data

  3. Compare both station networks

Note

Both providers require an internet connection. All API calls go to public endpoints — no API key needed.

import pandas as pd

Station Lookup#

Both providers let you find the nearest weather station to a given location. The typical workflow is:

  1. Convert a postal code (PLZ) to GPS coordinates

  2. Find the nearest station that has the sensor you need

Helper: PLZ to Coordinates#

The geopy module converts any Swiss postal code to latitude/longitude and altitude.

from pyedautils.geopy import get_coordindates_ch_plz, get_altitude_lat_long

plz = 6048  # Horw
coord = get_coordindates_ch_plz(plz)
altitude = get_altitude_lat_long(coord[0], coord[1])

print(f"PLZ {plz}: lat={coord[0]:.4f}, lon={coord[1]:.4f}, altitude={altitude:.0f} m")
PLZ 6048: lat=47.0108, lon=8.3039, altitude=455 m

MeteoSwiss#

MeteoSwiss operates Switzerland’s official automatic weather stations. The find_nearest_station() function returns the station abbreviation (e.g. "LUZ" for Luzern) of the closest station that has the requested sensor and is within ±150 m altitude.

from pyedautils.weather.meteo_swiss import find_nearest_station as find_meteoswiss

station_ms = find_meteoswiss(coord[0], coord[1], altitude, sensor="temp")
print(f"Nearest MeteoSwiss station for temperature: {station_ms}")
Nearest MeteoSwiss station for temperature: LUZ

You can search for different sensor types: "temp", "globrad" (global radiation), "relhum" (relative humidity), "rain".

for sensor in ["temp", "globrad", "relhum", "rain"]:
    station = find_meteoswiss(coord[0], coord[1], altitude, sensor=sensor)
    print(f"  {sensor:8s} → {station}")
  temp     → LUZ
  globrad  → LUZ
  relhum   → LUZ
  rain     → LUZ

Agrometeo#

Agrometeo is a Swiss agricultural weather network with a public API for station lookup and data download.

from pyedautils.weather.agroweather import find_nearest_station as find_agro

station_agro = find_agro(coord[0], coord[1], sensor="temp")
print(f"Nearest Agrometeo station for temperature: {station_agro}")
Nearest Agrometeo station for temperature: 239

Station Networks#

Both providers expose a function to list all available stations. This is useful for understanding coverage and available sensors.

from pyedautils.weather.meteo_swiss import get_current_station_data

stations_ms = get_current_station_data()
print(f"MeteoSwiss: {len(stations_ms)} stations")
stations_ms[["Abk.", "Station", "Stationshöhe m ü. M."]].head()
MeteoSwiss: 300 stations
Abk. Station Stationshöhe m ü. M.
0 TAE Aadorf / Tänikon 539.0
1 ABE Aarberg 444.0
2 COM Acquarossa / Comprovasco 575.0
3 ABO Adelboden 1321.0
4 AIE Affoltern i. E. 755.0
from pyedautils.weather.agroweather import get_station_data

stations_agro = get_station_data()
print(f"Agrometeo: {len(stations_agro)} stations")
stations_agro[["id", "name", "lat", "lon", "altitude"]].head()
Agrometeo: 218 stations
id name lat lon altitude
0 189 AESCH-BL 47.466863 7.575196 382
1 360 AGNO-Progetto ViSo-KS 45.997000 8.893944 364
2 94 AIGLE 46.314537 6.979451 442
3 78 AMBURNEX 46.536925 6.215849 1321
4 79 AMBURNEX-COMBE 46.535419 6.218099 1285

Downloading Weather Data#

Both providers offer hourly weather data through public interfaces. MeteoSwiss publishes its measurements as open data; download_data() takes the station abbreviation and returns the data in UTC (or the time zone given with tz).

The examples use fixed stations and a fixed past week, so the results stay the same even if a new station opens closer to Horw. Here: MeteoSwiss station Luzern (LUZ) and Agrometeo station Meggen (ID 239).

from pyedautils.weather import meteo_swiss

df_luz = meteo_swiss.download_data("LUZ", "2026-06-01", "2026-06-07", tz="Europe/Zurich")
df_luz.head()
temp globrad relhum rain
datetime
2026-06-01 00:00:00+02:00 16.1 0.0 91.4 0.0
2026-06-01 01:00:00+02:00 15.6 0.0 94.6 0.0
2026-06-01 02:00:00+02:00 15.2 0.0 95.8 0.0
2026-06-01 03:00:00+02:00 15.2 0.0 95.8 0.0
2026-06-01 04:00:00+02:00 15.5 0.0 93.7 0.0

Agrometeo can be queried in two ways:

  1. By station ID — if you already know the station

  2. By postal code (PLZ) — automatically finds the nearest station(s)

Download by Station ID#

Use download_data() when you know the station ID, e.g. from find_nearest_station() above or from get_station_data().

from pyedautils.weather.agroweather import download_data

df_agro = download_data(
    station_id=239,  # Meggen, nearest Agrometeo station to Horw
    start_date="2026-06-01",
    end_date="2026-06-07",
    sensors=["temp", "globrad", "relhum", "rain"]
)

print(f"Shape: {df_agro.shape}")
print(f"Period: {df_agro.index.min()} to {df_agro.index.max()}")
df_agro.head()
Shape: (168, 4)
Period: 2026-06-01 00:00:00 to 2026-06-07 23:00:00
temp globrad relhum rain
datetime
2026-06-01 00:00:00 17.1 0.1 84.0 0.1
2026-06-01 01:00:00 16.9 0.4 82.0 0.0
2026-06-01 02:00:00 16.9 NaN 83.0 0.0
2026-06-01 03:00:00 16.9 NaN 84.0 0.0
2026-06-01 04:00:00 17.2 NaN 83.0 0.0

Download by Postal Code#

The convenience function download_data_by_plz() handles the full workflow: PLZ → coordinates → nearest station(s) → download. If different sensors are closest to different stations, it automatically merges the results. Because the nearest station can change over time, use it for quick looks and pin the station ID for reproducible analyses:

from pyedautils.weather.agroweather import download_data_by_plz

df_plz = download_data_by_plz(plz=6048, start_date="2026-06-01", end_date="2026-06-07")
df_agro.describe()
temp globrad relhum rain
count 168.000000 125.000000 168.000000 168.000000
mean 17.729167 273.253600 67.273810 0.244643
std 3.573344 272.144148 12.144688 0.779664
min 11.400000 0.000000 44.000000 0.000000
25% 15.100000 25.500000 56.000000 0.000000
50% 17.250000 189.600000 69.000000 0.000000
75% 20.375000 474.700000 77.250000 0.000000
max 25.100000 931.600000 85.000000 4.600000

Quick Visualization#

Let’s plot the downloaded data to verify it looks reasonable.

from pyedautils.plots import plot_timeseries

fig = plot_timeseries(
    df_agro,
    columns=["temp"],
    title="Outdoor Temperature — Meggen (Agrometeo 239)",
    ylab="Temperature [°C]",
)
fig.show()
fig = plot_timeseries(
    df_agro,
    columns=["globrad"],
    title="Global Radiation — Meggen (Agrometeo 239)",
    ylab="Radiation [W/m²]",
)
fig.show()

Available Sensors#

Both providers support the same four sensor types:

Sensor key

Description

Unit

"temp"

Air temperature

°C

"globrad"

Global radiation

W/m²

"relhum"

Relative humidity

%

"rain"

Precipitation

mm

Summary#

Task

MeteoSwiss

Agrometeo

Find nearest station

weather.meteo_swiss.find_nearest_station(lat, lon, alt, sensor)

weather.agroweather.find_nearest_station(lat, lon, sensor)

List all stations

weather.meteo_swiss.get_current_station_data()

weather.agroweather.get_station_data()

Download data by station

weather.meteo_swiss.download_data(station, start, end)

weather.agroweather.download_data(station_id, start, end)

Download data by PLZ

weather.meteo_swiss.download_data_by_plz(plz, start, end)

weather.agroweather.download_data_by_plz(plz, start, end)

PLZ → coordinates

geopy.get_coordindates_ch_plz(plz)

(built into download_data_by_plz)

Get altitude

geopy.get_altitude_lat_long(lat, lon)

—