weatherdb 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docker/Dockerfile +30 -0
- docker/docker-compose.yaml +58 -0
- docker/docker-compose_test.yaml +24 -0
- docker/start-docker-test.sh +6 -0
- docs/requirements.txt +10 -0
- docs/source/Changelog.md +2 -0
- docs/source/License.rst +7 -0
- docs/source/Methode.md +161 -0
- docs/source/_static/custom.css +8 -0
- docs/source/_static/favicon.ico +0 -0
- docs/source/_static/logo.png +0 -0
- docs/source/api/api.rst +15 -0
- docs/source/api/cli.rst +8 -0
- docs/source/api/weatherDB.broker.rst +10 -0
- docs/source/api/weatherDB.config.rst +7 -0
- docs/source/api/weatherDB.db.rst +23 -0
- docs/source/api/weatherDB.rst +22 -0
- docs/source/api/weatherDB.station.rst +56 -0
- docs/source/api/weatherDB.stations.rst +46 -0
- docs/source/api/weatherDB.utils.rst +22 -0
- docs/source/conf.py +137 -0
- docs/source/index.rst +33 -0
- docs/source/setup/Configuration.md +127 -0
- docs/source/setup/Hosting.md +9 -0
- docs/source/setup/Install.md +49 -0
- docs/source/setup/Quickstart.md +183 -0
- docs/source/setup/setup.rst +12 -0
- weatherdb/__init__.py +24 -0
- weatherdb/_version.py +1 -0
- weatherdb/alembic/README.md +8 -0
- weatherdb/alembic/alembic.ini +80 -0
- weatherdb/alembic/config.py +9 -0
- weatherdb/alembic/env.py +100 -0
- weatherdb/alembic/script.py.mako +26 -0
- weatherdb/alembic/versions/V1.0.0_initial_database_creation.py +898 -0
- weatherdb/alembic/versions/V1.0.2_more_charachters_for_settings+term_station_ma_raster.py +88 -0
- weatherdb/alembic/versions/V1.0.5_fix-ma-raster-values.py +152 -0
- weatherdb/alembic/versions/V1.0.6_update-views.py +22 -0
- weatherdb/broker.py +667 -0
- weatherdb/cli.py +214 -0
- weatherdb/config/ConfigParser.py +663 -0
- weatherdb/config/__init__.py +5 -0
- weatherdb/config/config_default.ini +162 -0
- weatherdb/db/__init__.py +3 -0
- weatherdb/db/connections.py +374 -0
- weatherdb/db/fixtures/RichterParameters.json +34 -0
- weatherdb/db/models.py +402 -0
- weatherdb/db/queries/get_quotient.py +155 -0
- weatherdb/db/views.py +165 -0
- weatherdb/station/GroupStation.py +710 -0
- weatherdb/station/StationBases.py +3108 -0
- weatherdb/station/StationET.py +111 -0
- weatherdb/station/StationP.py +807 -0
- weatherdb/station/StationPD.py +98 -0
- weatherdb/station/StationT.py +164 -0
- weatherdb/station/__init__.py +13 -0
- weatherdb/station/constants.py +21 -0
- weatherdb/stations/GroupStations.py +519 -0
- weatherdb/stations/StationsBase.py +1021 -0
- weatherdb/stations/StationsBaseTET.py +30 -0
- weatherdb/stations/StationsET.py +17 -0
- weatherdb/stations/StationsP.py +128 -0
- weatherdb/stations/StationsPD.py +24 -0
- weatherdb/stations/StationsT.py +21 -0
- weatherdb/stations/__init__.py +11 -0
- weatherdb/utils/TimestampPeriod.py +369 -0
- weatherdb/utils/__init__.py +3 -0
- weatherdb/utils/dwd.py +350 -0
- weatherdb/utils/geometry.py +69 -0
- weatherdb/utils/get_data.py +285 -0
- weatherdb/utils/logging.py +126 -0
- weatherdb-1.1.0.dist-info/LICENSE +674 -0
- weatherdb-1.1.0.dist-info/METADATA +765 -0
- weatherdb-1.1.0.dist-info/RECORD +77 -0
- weatherdb-1.1.0.dist-info/WHEEL +5 -0
- weatherdb-1.1.0.dist-info/entry_points.txt +2 -0
- weatherdb-1.1.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
import sqlalchemy as sa
|
4
|
+
from sqlalchemy import text as sqltxt
|
5
|
+
from functools import cached_property
|
6
|
+
|
7
|
+
from ..db.connections import db_engine
|
8
|
+
from ..utils.dwd import dwd_id_to_str
|
9
|
+
from ..db.models import MetaPD
|
10
|
+
from .StationBases import StationPBase, StationCanVirtualBase
|
11
|
+
|
12
|
+
# set settings
|
13
|
+
# ############
|
14
|
+
__all__ = ["StationPD"]
|
15
|
+
log = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
# class definition
|
18
|
+
##################
|
19
|
+
class StationPD(StationPBase, StationCanVirtualBase):
|
20
|
+
"""A class to work with and download daily precipitation data for one station.
|
21
|
+
|
22
|
+
Those station data are only downloaded to do some quality checks on the 10 minute data.
|
23
|
+
Therefor there is no special quality check and richter correction done on this data.
|
24
|
+
If you want daily precipitation data, better use the 10 minutes station(StationP) and aggregate to daily values."""
|
25
|
+
|
26
|
+
# common settings
|
27
|
+
_MetaModel = MetaPD
|
28
|
+
_para = "p_d"
|
29
|
+
_para_base = "p"
|
30
|
+
_para_long = "daily Precipitation"
|
31
|
+
_unit = "mm/day"
|
32
|
+
_valid_kinds = {"raw", "filled", "filled_by"}
|
33
|
+
_best_kind = "filled"
|
34
|
+
|
35
|
+
# cdc dwd parameters
|
36
|
+
_ftp_folder_base = [
|
37
|
+
"climate_environment/CDC/observations_germany/climate/daily/kl/",
|
38
|
+
"climate_environment/CDC/observations_germany/climate/daily/more_precip/"]
|
39
|
+
_cdc_col_names_imp = ["RSK"]
|
40
|
+
_db_col_names_imp = ["raw"]
|
41
|
+
|
42
|
+
# timestamp configurations
|
43
|
+
_tstp_format_db = "%Y%m%d"
|
44
|
+
_tstp_format_human = "%Y-%m-%d"
|
45
|
+
_tstp_dtype = "date"
|
46
|
+
_interval = "1 day"
|
47
|
+
|
48
|
+
# aggregation
|
49
|
+
_min_agg_to = "day"
|
50
|
+
|
51
|
+
# methods from the base class that should not be active for this class
|
52
|
+
quality_check = property(doc='(!) Disallowed inherited')
|
53
|
+
last_imp_quality_check = property(doc='(!) Disallowed inherited')
|
54
|
+
get_corr = property(doc='(!) Disallowed inherited')
|
55
|
+
get_adj = property(doc='(!) Disallowed inherited')
|
56
|
+
get_qc = property(doc='(!) Disallowed inherited')
|
57
|
+
|
58
|
+
def __init__(self, id, **kwargs):
|
59
|
+
super().__init__(id, **kwargs)
|
60
|
+
self.id_str = dwd_id_to_str(id)
|
61
|
+
|
62
|
+
def _download_raw(self, zipfiles):
|
63
|
+
df_all, max_hist_tstp = super()._download_raw(zipfiles)
|
64
|
+
|
65
|
+
# fill RSK with values from RS if not given
|
66
|
+
if "RS" in df_all.columns and "RSK" in df_all.columns:
|
67
|
+
mask = df_all["RSK"].isna()
|
68
|
+
df_all.loc[mask, "RSK"] = df_all.loc[mask, "RS"]
|
69
|
+
elif "RS" in df_all.columns:
|
70
|
+
df_all["RSK"] = df_all["RS"]
|
71
|
+
|
72
|
+
return df_all, max_hist_tstp
|
73
|
+
|
74
|
+
@cached_property
|
75
|
+
def _table(self):
|
76
|
+
return sa.table(
|
77
|
+
f"{self.id}_{self._para}",
|
78
|
+
sa.column("timestamp", sa.Date),
|
79
|
+
sa.column("raw", sa.Integer),
|
80
|
+
sa.column("filled", sa.Integer),
|
81
|
+
sa.column("filled_by", sa.SmallInteger),
|
82
|
+
schema="timeseries")
|
83
|
+
|
84
|
+
@db_engine.deco_create_privilege
|
85
|
+
def _create_timeseries_table(self):
|
86
|
+
"""Create the timeseries table in the DB if it is not yet existing."""
|
87
|
+
sql_add_table = '''
|
88
|
+
CREATE TABLE IF NOT EXISTS timeseries."{stid}_{para}" (
|
89
|
+
timestamp date PRIMARY KEY,
|
90
|
+
raw int4,
|
91
|
+
filled int4,
|
92
|
+
filled_by int2
|
93
|
+
);
|
94
|
+
'''.format(stid=self.id, para=self._para)
|
95
|
+
with db_engine.connect() as con:
|
96
|
+
con.execute(sqltxt(sql_add_table))
|
97
|
+
con.commit()
|
98
|
+
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# libraries
|
2
|
+
import logging
|
3
|
+
import sqlalchemy as sa
|
4
|
+
from sqlalchemy import text as sqltxt
|
5
|
+
from functools import cached_property
|
6
|
+
|
7
|
+
from ..db.connections import db_engine
|
8
|
+
from ..utils.dwd import dwd_id_to_str
|
9
|
+
from ..db.models import MetaT
|
10
|
+
from .StationBases import StationTETBase
|
11
|
+
|
12
|
+
# set settings
|
13
|
+
# ############
|
14
|
+
__all__ = ["StationT"]
|
15
|
+
log = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
# class definition
|
18
|
+
##################
|
19
|
+
class StationT(StationTETBase):
|
20
|
+
"""A class to work with and download temperaure data for one station."""
|
21
|
+
|
22
|
+
# common settings
|
23
|
+
_MetaModel = MetaT
|
24
|
+
_para = "t"
|
25
|
+
_para_base = _para
|
26
|
+
_para_long = "Temperature"
|
27
|
+
_unit = "°C"
|
28
|
+
_decimals = 10
|
29
|
+
_valid_kinds = {"raw", "raw_min", "raw_max", "qc",
|
30
|
+
"filled", "filled_min", "filled_max", "filled_by"}
|
31
|
+
|
32
|
+
# cdc dwd parameters
|
33
|
+
_ftp_folder_base = [
|
34
|
+
"climate_environment/CDC/observations_germany/climate/daily/kl/"]
|
35
|
+
_cdc_date_col = "MESS_DATUM"
|
36
|
+
_cdc_col_names_imp = ["TMK", "TNK", "TXK"]
|
37
|
+
_db_col_names_imp = ["raw", "raw_min", "raw_max"]
|
38
|
+
|
39
|
+
# aggregation
|
40
|
+
_agg_fun = "avg"
|
41
|
+
|
42
|
+
# for regionalistaion
|
43
|
+
_ma_terms = ["year"]
|
44
|
+
_coef_sign = ["-", "+"]
|
45
|
+
|
46
|
+
# # for the fillup
|
47
|
+
_filled_by_n = 5
|
48
|
+
_fillup_max_dist = 100e3
|
49
|
+
|
50
|
+
|
51
|
+
def __init__(self, id, **kwargs):
|
52
|
+
super().__init__(id, **kwargs)
|
53
|
+
self.id_str = dwd_id_to_str(id)
|
54
|
+
|
55
|
+
@cached_property
|
56
|
+
def _table(self):
|
57
|
+
return sa.table(
|
58
|
+
f"{self.id}_{self._para}",
|
59
|
+
sa.column("timestamp", sa.Date),
|
60
|
+
sa.column("raw", sa.Integer),
|
61
|
+
sa.column("raw_min", sa.Integer),
|
62
|
+
sa.column("raw_max", sa.Integer),
|
63
|
+
sa.column("qc", sa.Integer),
|
64
|
+
sa.column("filled", sa.Integer),
|
65
|
+
sa.column("filled_min", sa.Integer),
|
66
|
+
sa.column("filled_max", sa.Integer),
|
67
|
+
sa.column("filled_by", sa.SmallInteger),
|
68
|
+
schema="timeseries")
|
69
|
+
|
70
|
+
def _create_timeseries_table(self):
|
71
|
+
"""Create the timeseries table in the DB if it is not yet existing."""
|
72
|
+
sql_add_table = f'''
|
73
|
+
CREATE TABLE IF NOT EXISTS timeseries."{self.id}_{self._para}" (
|
74
|
+
timestamp date PRIMARY KEY,
|
75
|
+
raw integer NULL DEFAULT NULL,
|
76
|
+
raw_min integer NULL DEFAULT NULL,
|
77
|
+
raw_max integer NULL DEFAULT NULL,
|
78
|
+
qc integer NULL DEFAULT NULL,
|
79
|
+
filled integer NULL DEFAULT NULL,
|
80
|
+
filled_min integer NULL DEFAULT NULL,
|
81
|
+
filled_max integer NULL DEFAULT NULL,
|
82
|
+
filled_by smallint[{self._filled_by_n}] NULL DEFAULT NULL
|
83
|
+
);
|
84
|
+
'''
|
85
|
+
with db_engine.connect() as con:
|
86
|
+
con.execute(sqltxt(sql_add_table))
|
87
|
+
con.commit()
|
88
|
+
|
89
|
+
def _get_sql_new_qc(self, period):
|
90
|
+
# inversion possible?
|
91
|
+
do_invers = self.get_meta(infos=["stationshoehe"])>800
|
92
|
+
|
93
|
+
sql_nears = self._get_sql_near_median(
|
94
|
+
period=period, only_real=False, add_is_winter=do_invers,
|
95
|
+
extra_cols="raw-nbs_median AS diff")
|
96
|
+
|
97
|
+
if do_invers:
|
98
|
+
# without inversion
|
99
|
+
sql_null_case = "CASE WHEN (winter) THEN "+\
|
100
|
+
f"diff < {-5 * self._decimals} ELSE "+\
|
101
|
+
f"ABS(diff) > {5 * self._decimals} END "+\
|
102
|
+
f"OR raw < {-50 * self._decimals} OR raw > {50 * self._decimals}"
|
103
|
+
else:
|
104
|
+
# with inversion
|
105
|
+
sql_null_case = f"ABS(diff) > {5 * self._decimals}"
|
106
|
+
|
107
|
+
# create sql for new qc
|
108
|
+
sql_new_qc = f"""
|
109
|
+
WITH nears AS ({sql_nears})
|
110
|
+
SELECT
|
111
|
+
timestamp,
|
112
|
+
(CASE WHEN ({sql_null_case})
|
113
|
+
THEN NULL
|
114
|
+
ELSE nears."raw"
|
115
|
+
END) as qc
|
116
|
+
FROM nears
|
117
|
+
"""
|
118
|
+
|
119
|
+
return sql_new_qc
|
120
|
+
|
121
|
+
@db_engine.deco_update_privilege
|
122
|
+
def _sql_fillup_extra_dict(self, **kwargs):
|
123
|
+
# additional parts to calculate the filling of min and max
|
124
|
+
fillup_extra_dict = super()._sql_fillup_extra_dict(**kwargs)
|
125
|
+
sql_array_init = "ARRAY[{0}]".format(
|
126
|
+
", ".join(["NULL::smallint"] * self._filled_by_n))
|
127
|
+
fillup_extra_dict.update({
|
128
|
+
"extra_new_temp_cols": "raw_min AS filled_min, raw_max AS filled_max," +
|
129
|
+
f"{sql_array_init} AS nb_min, {sql_array_init} AS nb_max,",
|
130
|
+
"extra_cols_fillup_calc": "filled_min=round(nb.raw_min + %3$s, 0)::int, " +
|
131
|
+
"filled_max=round(nb.raw_max + %3$s, 0)::int, ",
|
132
|
+
"extra_cols_fillup": "filled_min = new.filled_min, " +
|
133
|
+
"filled_max = new.filled_max, ",
|
134
|
+
"extra_fillup_where": ' OR ts."filled_min" IS DISTINCT FROM new."filled_min"' +
|
135
|
+
' OR ts."filled_max" IS DISTINCT FROM new."filled_max"',
|
136
|
+
"extra_exec_cols": "nb_max[{i}]=round(nb.raw_max + %3$s, 0)::int,"+
|
137
|
+
"nb_min[{i}]=round(nb.raw_min + %3$s, 0)::int,",
|
138
|
+
"extra_after_loop_extra_col": """,
|
139
|
+
filled_min=(SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY v)
|
140
|
+
FROM unnest(nb_min) as T(v)),
|
141
|
+
filled_max=(SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY v)
|
142
|
+
FROM unnest(nb_max) as T(v))"""})
|
143
|
+
return fillup_extra_dict
|
144
|
+
|
145
|
+
def get_multi_annual_raster(self):
|
146
|
+
mas = super().get_multi_annual_raster()
|
147
|
+
if mas is not None:
|
148
|
+
return [ma / 10 for ma in mas]
|
149
|
+
else:
|
150
|
+
return None
|
151
|
+
|
152
|
+
def get_adj(self, **kwargs):
|
153
|
+
main_df, adj_df, ma, main_df_tr = super().get_adj(**kwargs)
|
154
|
+
|
155
|
+
# calculate the yearly
|
156
|
+
main_df_y = main_df.groupby(main_df_tr.index.year)\
|
157
|
+
.mean().mean()
|
158
|
+
|
159
|
+
adj_df["adj"] = (main_df + (ma[0] - main_df_y)).round(1)
|
160
|
+
|
161
|
+
return adj_df
|
162
|
+
|
163
|
+
def get_quotient(self, **kwargs):
|
164
|
+
raise NotImplementedError("The quotient is not yet implemented for temperature.")
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""
|
2
|
+
This module has a class for every type of station. E.g. StationP (or StationP).
|
3
|
+
One object represents one Station with one parameter.
|
4
|
+
This object can get used to get the corresponding timeserie.
|
5
|
+
There is also a StationGroup class that groups the three parameters precipitation, temperature and evapotranspiration together for one station.
|
6
|
+
"""
|
7
|
+
from .StationET import StationET
|
8
|
+
from .StationP import StationP
|
9
|
+
from .StationPD import StationPD
|
10
|
+
from .StationT import StationT
|
11
|
+
from .GroupStation import GroupStation
|
12
|
+
|
13
|
+
__all__ = ["StationET", "StationP", "StationPD", "StationT", "GroupStation"]
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Variables
|
2
|
+
# ---------
|
3
|
+
# possible aggregation periods from small to big
|
4
|
+
AGG_TO = {
|
5
|
+
None: {
|
6
|
+
"split":{"p": 5, "t":3, "et": 3}},
|
7
|
+
"10 min": {
|
8
|
+
"split":{"p": 5, "t":3, "et": 3}},
|
9
|
+
"hour": {
|
10
|
+
"split":{"p": 4, "t":3, "et": 3}},
|
11
|
+
"day": {
|
12
|
+
"split":{"p": 3, "t":3, "et": 3}},
|
13
|
+
"month": {
|
14
|
+
"split":{"p": 2, "t":2, "et": 2}},
|
15
|
+
"year": {
|
16
|
+
"split":{"p": 1, "t":1, "et": 1}},
|
17
|
+
"decade": {
|
18
|
+
"split":{"p": 1, "t":1, "et": 1}}
|
19
|
+
}
|
20
|
+
|
21
|
+
__all__ = ["AGG_TO"]
|