Skip to content

Commit d8e6098

Browse files
committed
Add streamlit app for DWD climate stations
1 parent a00f295 commit d8e6098

File tree

7 files changed

+693
-339
lines changed

7 files changed

+693
-339
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Development
55
***********
66

77
- Make parameters TEMPERATURE_AIR_MAX_200 and TEMPERATURE_AIR_MIN_200 summarizable/interpolatable
8+
- Add streamlit app for DWD climate stations
89

910
0.60.0 (16.09.2023)
1011
*******************

README.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,14 @@ Features
210210
- Define general settings in Settings context
211211
- Command line interface
212212
- Web-API via FastAPI
213+
- Rich UI features like wetterdienst explorer and `streamlit_app`_
213214
- Run SQL queries on the results
214215
- Export results to databases and other data sinks
215216
- Public Docker image
216217
- Interpolation and Summary of station values
217218

219+
.. _streamlit_app: https://wetterdienst.streamlit.app
220+
218221
Setup
219222
*****
220223

poetry.lock

Lines changed: 595 additions & 338 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ classifiers = [
9999
"Releases" = "https://github.com/earthobservations/wetterdienst/releases"
100100

101101
[tool.poetry.dependencies]
102-
python = "^3.9,<3.12"
102+
python = "^3.9,!=3.9.7,<3.12"
103103

104104
aenum = "^3.0"
105105
aiohttp = "^3.8.1"
@@ -155,6 +155,7 @@ psycopg2-binary = { version = "^2.8", optional = true } # Expor
155155
scipy = { version = "^1.9", optional = true } # Interpolation feature.
156156
shapely = { version = "^2.0", optional = true } # Interpolation feature.
157157
sqlalchemy = { version = "^2.0", optional = true } # Export feature.
158+
streamlit = { version = "^1.27.0", optional = true } # Streamlit app
158159
utm = { version = "^0.7", optional = true } # Interpolation feature.
159160
uvicorn = { version = "^0.14", optional = true } # HTTP REST API feature.
160161
wradlib = { version = "^1.19", optional = true } # Radar feature.
@@ -228,6 +229,7 @@ radar = ["h5py"]
228229
radarplus = ["pybufrkit", "wradlib", "xradar"]
229230
restapi = ["fastapi", "httpx", "uvicorn"]
230231
sql = ["duckdb"]
232+
streamlit = ["streamlit"]
231233

232234
[tool.poetry.scripts]
233235
wetterdienst = 'wetterdienst.ui.cli:cli'
@@ -310,6 +312,8 @@ coverage-parallel = "pytest --cov=wetterdienst --numprocesses=auto -m 'not (expl
310312
update = "poetry update"
311313
citation = "python -m tools.citation"
312314

315+
stlit = "streamlit run ./wetterdienst/ui/stlit.py"
316+
313317
[tool.pytest.ini_options]
314318
addopts = "-rsfEX -p pytester --strict-markers --verbosity=3 --webdriver=Firefox --headless"
315319
#log_cli = true

wetterdienst/ui/streamlit/__init__.py

Whitespace-only changes.

wetterdienst/ui/streamlit/app.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (C) 2018-2023, earthobservations developers.
3+
# Distributed under the MIT License. See LICENSE for more info.
4+
import plotly.express as px
5+
import polars as pl
6+
import streamlit
7+
import streamlit as st
8+
9+
from wetterdienst.provider.dwd.observation import DwdObservationRequest
10+
11+
request = DwdObservationRequest("climate_summary", "daily")
12+
13+
14+
@streamlit.cache_data
15+
def get_dwd_observation_stations():
16+
return request.all().df
17+
18+
19+
@streamlit.cache_data
20+
def get_dwd_observation_station(station_id):
21+
return request.filter_by_station_id(station_id)
22+
23+
24+
@streamlit.cache_data
25+
def get_dwd_observation_station_values(station_id):
26+
return get_dwd_observation_station(station_id).values.all()
27+
28+
29+
def main():
30+
"""Small streamlit app for accessing German climate stations by DWD"""
31+
st.title("Wetterdienst - Data Tool")
32+
33+
st.subheader("Introduction")
34+
st.markdown(
35+
"""
36+
This is a streamlit app based on the [wetterdienst](https://github.com/earthobservations/wetterdienst)
37+
library that allows analysis of German climate stations (internally phrased "climate summary") by
38+
the [German Weather Service (DWD)](https://www.dwd.de/). There are over 1_500 climate stations in Germany and
39+
all of the data can be accessed freely thanks to the open data initiative. The app enables you to select any
40+
of the stations (by station id or name), download its data (as CSV) and get visualizations of it.
41+
"""
42+
)
43+
st.markdown("Here's a map of all stations:")
44+
st.map(get_dwd_observation_stations(), latitude="latitude", longitude="longitude")
45+
46+
st.subheader("Select")
47+
station = st.selectbox(
48+
"Select climate station",
49+
options=get_dwd_observation_stations().sort("name").rows(named=True),
50+
format_func=lambda s: f"{s['name']} [{s['station_id']}]",
51+
)
52+
if station:
53+
st.map(get_dwd_observation_station(station["station_id"]).df)
54+
55+
st.subheader("DataFrame")
56+
df = pl.DataFrame()
57+
if station:
58+
df = get_dwd_observation_station_values(station["station_id"]).df
59+
st.dataframe(df, hide_index=True, use_container_width=True)
60+
st.download_button("Download CSV", df.write_csv(), "data.csv", "text/csv")
61+
62+
st.subheader("Plot")
63+
parameters = st.multiselect("Select parameters", options=df.get_column("parameter").unique().sort().to_list())
64+
if parameters:
65+
fig = px.scatter(
66+
df.filter(pl.col("parameter").is_in(parameters)),
67+
x="date",
68+
y="value",
69+
color="parameter",
70+
facet_row="parameter",
71+
)
72+
fig.update_layout(
73+
showlegend=False, # Hide the legend
74+
height=400 * len(parameters), # plot height times parameters
75+
)
76+
fig.update_yaxes(matches=None)
77+
# Update y-axis titles to use facet labels and remove subplot titles
78+
for i, annotation in enumerate(fig.layout.annotations):
79+
axis_name = f"yaxis{i + 1}"
80+
if axis_name in fig.layout:
81+
fig.layout[axis_name].title.text = annotation.text
82+
annotation.text = ""
83+
st.plotly_chart(fig)
84+
85+
86+
if __name__ == "__main__":
87+
main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
wetterdienst
2+
plotly

0 commit comments

Comments
 (0)