weatherdb 1.1.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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"]
|