Smhi-data python-klient publicerad

/ 2023-03-29 / Projekt

IFKs pythonbibliotek för att läsa SMHI-data, ifk-smhi har i dagarna officiellt släppts i version 0.1.

Som tidigare rapporterats om har föreningen, främst genom dr. Mladen Gibanicas försorg, arbetat med att ta fram ett pythonbibliotek för att på ett enkelt sätt läsa data från SMHIs API. Söndagen den 27 mars släpptes den första officiella versionen, och nu finns version 0.1.1 tillgänglig via paketinstallationsplattformen pypi.

"Vi läser fortfarande de fyra mest kritiska API:na", förklarar dr. Gibanica, "det vill säga MetObs - som läser historisk data från Meteorologiska Observationer, Mesan - som interpolerar fram meterologisk data även där mätdata saknas, MetFcts - som genererar meteorologiska förutsägelser, och Strång - som modellerar solens strålningsintensitet. Det som utvecklats till denna release är framför allt att vi arbetat för att få till enhetliga och lättlästa tabeller som utdata. Dessutom har vi lagt ner mycket möda på att säkerställa kodkvalitén."

Källkoden är tillgänglig under MIT-licens (fritt användande) på Ingenjörsarbete För Klimatets GitHub. Öppna ett ärende där om någonting saknas eller fungerar dåligt så lovar utvecklingsteamet att de ska ta sig an det å det snaraste!

Nedan följer några exempel på hur de olika modulerna kan användas - flera av dessa har modifierats från originalartikeln på mladen.gibanica.net.

Exempel på användning av Metobs

Metobs är som tidigare nämnts ett verktyg för att läsa ut data från metereologiska observationer från de väderstationer som SMHI underhåller i landet. För att ta reda på vilka parametrar som finns att utläsa kan vi använda följande kod:

from smhi.metobs import Parameters, Stations, Periods, Data
parameter = Parameters()
parameter.show

som kommer ge svar:

('1', 'Lufttemperatur', 'momentanvärde, 1 gång/tim')
('2', 'Lufttemperatur', 'medelvärde 1 dygn, 1 gång/dygn, kl 00')
('3', 'Vindriktning', 'medelvärde 10 min, 1 gång/tim')
...
('11', 'Global Irradians (svenska stationer)', 'medelvärde 1 timme, 1 gång/tim')
...

Om vi nu vill veta vilka stationer som har mätvärden för parameter 1 så kör vi:

stations = Stations(parameter, 1)
stations.show

Som kommer att leverera en lista på 968 stationer:

(1, 'Akalla')
(2, 'Högdalen')
(3, 'Sigtuna')
(4, 'Jönköping')
(7, 'Gävle')
...
(71420, 'Göteborg A')
...
(72630, 'Göteborg')
...

Vi tittar på stationen "Göteborg A". Vilka perioder finns data tillgängligt för?

periods = Periods(stations, station_name="Göteborg A")
periods.show

listar de tillgängliga perioderna:

corrected-archive
latest-day
latest-hour
latest-months

För att få datan frågar vi slutligen efter den genom:

data = Data(periods)
data.data

detta ger "corrected-archive"-perioden i en Pandas DataFrame:

                     Lufttemperatur
1961-01-01 06:00:00             0.8
1961-01-01 12:00:00             1.0
1961-01-01 18:00:00             1.4
1961-01-02 06:00:00             1.8
1961-01-02 12:00:00             2.2
...                             ...
2022-12-01 02:00:00             2.5
2022-12-01 03:00:00             2.4
2022-12-01 04:00:00             2.3
2022-12-01 05:00:00             2.1
2022-12-01 06:00:00             1.9
[303469 rows x 1 columns]

Observera att de tre senaste månaderna vanligtvis saknas från "corrected-archive". (De finns istället tillgängliga i "latest-months"). Denna data presenteras med fördel i en scatter plot. Här används bara daglig medeltemperatur istället för temperatur per timme för att begränsa datamängden i denna artikel, se "Plot code details".

Plot code
import plotly.graph_objects as go
data_agg_day = data.data.resample("D").mean()
fig = go.Figure()
fig.add_trace(
    go.Scattergl(
        x=data_agg_day.index,
        y=data_agg_day["Lufttemperatur"],
        mode="markers",
        name="Göteborg A station"
    )
)
fig.update_layout(
    title="Lufttemperatur Göteborg A",
    xaxis_title="År",
    yaxis_title="Lufttemperatur [°C]",
    legend={"orientation": "h"},
    margin={"l": 0, "r": 0, "b": 80, "t": 100},
    paper_bgcolor="rgba(250, 250, 250, 1)",
)
fig.show()

Exempel på användning av Strang

Tänk om vi istället är intresserade av global irradians över Sverige? Hur många stationer har vi där? Det handlar om parameter 11 från Metobs enligt listan här ovanför:

stations = Stations(parameter, 11)
stations.show

vilket ger oss:

(53445, 'Lund Sol')
(64565, 'Växjö Sol')
(68545, 'Hoburg Sol')
(71415, 'Göteborg Sol')
(77215, 'Ölands norra udde Sol')
(78645, 'Visby Sol')
(81525, 'Nordkoster Sol')
(86655, 'Norrköping Sol')
(93235, 'Karlstad Sol')
(98735, 'Stockholm Sol')
(99275, 'Svenska Högarna Sol')
(105285, 'Borlänge Sol')
(132165, 'Storlien-Visjövalen Sol')
(134615, 'Östersund Sol')
(140615, 'Umeå Sol')
(147655, 'Gunnarn Sol')
(162015, 'Luleå Sol')
(178985, 'Tarfala Sol')
(180025, 'Kiruna Sol')

Det innebär alltså att det bara finns 19 stationer som mäter global irradians. För att bättre kunna täcka in hela landet kan vi istället använda STRÅNG-API:t, som använder observationer tillsammans med simuleringar för att interpolera fram strålningsdata. Vi kan använda det som:

from smhi.strang import Strang
strang = Strang()
parameters = strang.parameters

vilket listar tillgängliga parametrar:

parameter: 116, info: CIE UV irradiance [mW/m²]
parameter: 117, info: Global irradiance [W/m²]
parameter: 118, info: Direct normal irradiance [W/m²]
parameter: 120, info: PAR [W/m²]
parameter: 121, info: Direct horizontal irradiance [W/m²]
parameter: 122, info: Diffuse irradiance [W/m²]

I detta exempel letar vi alltså efter parameter 117. Låt oss börja med att undersöka vilket som är det första datum för vilket data är tillgängligt:

strang.available_parameters[117].time_from

som ger:

datetime.datetime(1999, 1, 1, 0, 0, tzinfo=tzutc())

Nu kan vi generera ett multi-point-svar från APIt för den dagen aggregerat daily:

data = strang.get_multipoint(117, "1999-01-01", "daily")
data

vilket återigen ger en Pandas DataFrame:

lat        lon  Global irradiance [W/m²] 1999-01-01T00:00:00+00:00 daily
0      74.945244   1.212198                                                0.0
1      74.910484   1.942287                                                0.0
2      74.873570   2.669121                                                0.0
3      74.834520   3.392533                                                0.0
4      74.793340   4.112356                                                0.0
...          ...        ...                                                ...
11827  46.503345  23.264704                                             1561.9
11828  46.408020  23.516861                                             1655.8
11829  46.312065  23.768070                                             1717.9
11830  46.215477  24.018330                                             1701.7
11831  46.118263  24.267643                                             1673.6
[11832 rows x 3 columns]

Det är lämpligare att titta på även denna data i figur:

Scatter plot code
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(
    go.Scattergl(
        x=data["lon"],
        y=data["lat"],
        mode="markers",
        name="Global irradians 1999-01-01",
        marker={
            "color": data["Global irradiance [W/m²] 1999-01-01T00:00:00+00:00 daily"],
            "colorbar": {"title": "[W/m²]"},
        },
    )
)
fig.update_layout(
    title="Global irradians 1999-01-01",
    xaxis_title="Longitud",
    yaxis_title="Latitud",
    legend={"orientation": "h"},
    margin={"l": 0, "r": 0, "b": 80, "t": 100},
    paper_bgcolor="rgba(250, 250, 250, 1)",
)
fig.update_yaxes(
    scaleanchor="x",
    scaleratio=1,
)
fig.show()

Exempel på användning av Mesan

Interpolation mellan stationer med hjälp av metereologiska modeller är som vi sett i exemplet med solstrålningsmätningar ett väldigt bra sätt att få goda lokala prediktioner av parametrar som endast finns tillgängliga från ett fåtal stationer i landet. Denna princip finns tillgänglig även för andra parametrar i Mesan-paketet, där vi på samma sätt som från Strång kan få data antingen över ett område mha get_multipoint eller för en specifik position mha get_point.

from smhi.mesan import Mesan
client = Mesan()
data = client.get_point(57.632973, 11.605914)

Ger oss en pandas dataframe med modelldata för Vinga ute i Göteborgs skärgård för det senaste dygnet, vid denna artikels skrivande sista mars och första april. Parameterbeskrivningar mm finns tillgängligt på SMHIs API-sida. En intressant parameter ute i skärgården är förstås vindhastighet - för att läsa ut byvindhastigheter timme för timme tar vi då:

data["gust"]

Vilket ger:

unit                        m/s
validTime
2023-03-31 09:00:00+00:00  13.4
2023-03-31 10:00:00+00:00  13.8
2023-03-31 11:00:00+00:00  13.8
2023-03-31 12:00:00+00:00  14.4
2023-03-31 13:00:00+00:00  14.8
2023-03-31 14:00:00+00:00  14.6
2023-03-31 15:00:00+00:00  15.0
2023-03-31 16:00:00+00:00  13.8
2023-03-31 17:00:00+00:00  13.6
2023-03-31 18:00:00+00:00  14.1
2023-03-31 19:00:00+00:00  13.5
2023-03-31 20:00:00+00:00  13.9
2023-03-31 21:00:00+00:00  13.9
2023-03-31 22:00:00+00:00  13.6
2023-03-31 23:00:00+00:00  13.2
2023-04-01 00:00:00+00:00  12.4
2023-04-01 01:00:00+00:00  12.4
2023-04-01 02:00:00+00:00  14.1
2023-04-01 03:00:00+00:00  13.8
2023-04-01 04:00:00+00:00  13.4
2023-04-01 05:00:00+00:00  14.0
2023-04-01 06:00:00+00:00  12.1
2023-04-01 07:00:00+00:00  11.8
2023-04-01 08:00:00+00:00  15.1

Det blåser alltså ute på öarna idag!

Exempel på användning av Metfcts

Hur kommer det då att se ut imorgon? SMHI tillhandahåller även metereologiska prediktioner från sina modeller, vilka finns tillgängliga i en parallell anropsstruktur i paketet smhi.metfcts. För att få en tiodygnsprognos av byvinden ute vid Vinga skriver vi helt enkelt:

from smhi.metfcts import Metfcts
client = Metfcts()
data = client.get_point(57.632973,11.605914)
data["gust"]

Vilket i skrivande stund ger oss:

unit                        m/s
validTime
2023-04-01 10:00:00+00:00  13.2
2023-04-01 11:00:00+00:00  12.8
2023-04-01 12:00:00+00:00  12.0
2023-04-01 13:00:00+00:00  11.4
2023-04-01 14:00:00+00:00  11.3
...                         ...
2023-04-09 00:00:00+00:00   8.5
2023-04-09 12:00:00+00:00  10.2
2023-04-10 00:00:00+00:00  10.3
2023-04-10 12:00:00+00:00  11.0
2023-04-11 00:00:00+00:00  10.0

Och med hjälp av numpy kan vi sluta oss till att den förväntade medelhastigheten på byvindarna under de närmsta tio dagarna är 6.3m/s:

import numpy as np
np.mean(data.gust)

(c) Ingenjörsarbete för Klimatet. För återpublicering kontakta ansvarig utgivare Anders Nord anders.nord@ingenjorsarbeteforklimatet.se.