weatherdb 1.1.1__tar.gz → 1.2.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. {weatherdb-1.1.1 → weatherdb-1.2.0}/.gitlab-ci.yml +19 -3
  2. {weatherdb-1.1.1 → weatherdb-1.2.0}/CHANGES.md +13 -2
  3. {weatherdb-1.1.1 → weatherdb-1.2.0}/PKG-INFO +1 -1
  4. {weatherdb-1.1.1 → weatherdb-1.2.0}/docker/docker-compose.yaml +5 -1
  5. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/Methode.md +5 -1
  6. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/setup/Quickstart.md +2 -1
  7. weatherdb-1.2.0/tests/test-data/DEM/COP-DEM_GLO-30-DGED__2023_1_clipped.tif +0 -0
  8. weatherdb-1.2.0/tests/test-data/DEM/README.md +3 -0
  9. weatherdb-1.2.0/tests/test-data/DEM/eula_F.pdf +0 -0
  10. weatherdb-1.2.0/tests/test-data/regionalisation/DWD-grid_ma_1991_2020_DGM25_clipped.tif +0 -0
  11. weatherdb-1.2.0/tests/test-data/regionalisation/HYRAS_ma_1991_2020_DGM25_clipped.tif +0 -0
  12. weatherdb-1.2.0/tests/test-data/regionalisation/README.md +3 -0
  13. weatherdb-1.2.0/tests/test-data/test-data-config.ini +28 -0
  14. weatherdb-1.2.0/weatherdb/_version.py +1 -0
  15. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/cli.py +18 -3
  16. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/config/config_default.ini +14 -1
  17. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/GroupStation.py +3 -1
  18. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/StationBases.py +77 -10
  19. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/utils/get_data.py +208 -40
  20. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb.egg-info/PKG-INFO +1 -1
  21. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb.egg-info/SOURCES.txt +7 -6
  22. weatherdb-1.1.1/weatherDB.egg-info/PKG-INFO +0 -761
  23. weatherdb-1.1.1/weatherDB.egg-info/SOURCES.txt +0 -84
  24. weatherdb-1.1.1/weatherDB.egg-info/entry_points.txt +0 -2
  25. weatherdb-1.1.1/weatherDB.egg-info/requires.txt +0 -21
  26. weatherdb-1.1.1/weatherDB.egg-info/top_level.txt +0 -5
  27. weatherdb-1.1.1/weatherdb/_version.py +0 -1
  28. weatherdb-1.1.1/weatherdb.egg-info/dependency_links.txt +0 -1
  29. {weatherdb-1.1.1 → weatherdb-1.2.0}/.dockerignore +0 -0
  30. {weatherdb-1.1.1 → weatherdb-1.2.0}/.github/workflows/cleanup-cache.yml +0 -0
  31. {weatherdb-1.1.1 → weatherdb-1.2.0}/.github/workflows/python-publish.yml +0 -0
  32. {weatherdb-1.1.1 → weatherdb-1.2.0}/.github/workflows/python-test.yml +0 -0
  33. {weatherdb-1.1.1 → weatherdb-1.2.0}/.gitignore +0 -0
  34. {weatherdb-1.1.1 → weatherdb-1.2.0}/.gitlab/merge_request_templates/new_version.md +0 -0
  35. {weatherdb-1.1.1 → weatherdb-1.2.0}/.readthedocs.yaml +0 -0
  36. {weatherdb-1.1.1 → weatherdb-1.2.0}/LICENSE +0 -0
  37. {weatherdb-1.1.1 → weatherdb-1.2.0}/MANIFEST.in +0 -0
  38. {weatherdb-1.1.1 → weatherdb-1.2.0}/README.md +0 -0
  39. {weatherdb-1.1.1 → weatherdb-1.2.0}/docker/Dockerfile +0 -0
  40. {weatherdb-1.1.1 → weatherdb-1.2.0}/docker/docker-compose_test.yaml +0 -0
  41. {weatherdb-1.1.1 → weatherdb-1.2.0}/docker/start-docker-test.sh +0 -0
  42. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/requirements.txt +0 -0
  43. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/Changelog.md +0 -0
  44. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/License.rst +0 -0
  45. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/_static/custom.css +0 -0
  46. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/_static/favicon.ico +0 -0
  47. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/_static/logo.png +0 -0
  48. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/api.rst +0 -0
  49. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/cli.rst +0 -0
  50. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherDB.broker.rst +0 -0
  51. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherdb.config.rst +0 -0
  52. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherdb.db.rst +0 -0
  53. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherdb.rst +0 -0
  54. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherdb.station.rst +0 -0
  55. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherdb.stations.rst +0 -0
  56. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/api/weatherdb.utils.rst +0 -0
  57. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/conf.py +0 -0
  58. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/index.rst +0 -0
  59. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/setup/Configuration.md +0 -0
  60. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/setup/Hosting.md +0 -0
  61. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/setup/Install.md +0 -0
  62. {weatherdb-1.1.1 → weatherdb-1.2.0}/docs/source/setup/setup.rst +0 -0
  63. {weatherdb-1.1.1 → weatherdb-1.2.0}/pyproject.toml +0 -0
  64. {weatherdb-1.1.1 → weatherdb-1.2.0}/setup.cfg +0 -0
  65. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/__init__.py +0 -0
  66. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/README.md +0 -0
  67. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/alembic.ini +0 -0
  68. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/config.py +0 -0
  69. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/env.py +0 -0
  70. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/script.py.mako +0 -0
  71. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/versions/V1.0.0_initial_database_creation.py +0 -0
  72. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/versions/V1.0.2_more_charachters_for_settings+term_station_ma_raster.py +0 -0
  73. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/versions/V1.0.5_fix-ma-raster-values.py +0 -0
  74. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/alembic/versions/V1.0.6_update-views.py +0 -0
  75. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/broker.py +0 -0
  76. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/config/ConfigParser.py +0 -0
  77. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/config/__init__.py +0 -0
  78. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/db/__init__.py +0 -0
  79. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/db/connections.py +0 -0
  80. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/db/fixtures/RichterParameters.json +0 -0
  81. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/db/models.py +0 -0
  82. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/db/queries/get_quotient.py +0 -0
  83. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/db/views.py +0 -0
  84. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/StationET.py +0 -0
  85. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/StationP.py +0 -0
  86. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/StationPD.py +0 -0
  87. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/StationT.py +0 -0
  88. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/__init__.py +0 -0
  89. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/station/constants.py +0 -0
  90. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/GroupStations.py +0 -0
  91. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/StationsBase.py +0 -0
  92. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/StationsBaseTET.py +0 -0
  93. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/StationsET.py +0 -0
  94. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/StationsP.py +0 -0
  95. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/StationsPD.py +0 -0
  96. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/StationsT.py +0 -0
  97. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/stations/__init__.py +0 -0
  98. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/utils/TimestampPeriod.py +0 -0
  99. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/utils/__init__.py +0 -0
  100. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/utils/dwd.py +0 -0
  101. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/utils/geometry.py +0 -0
  102. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb/utils/logging.py +0 -0
  103. {weatherdb-1.1.1/weatherDB.egg-info → weatherdb-1.2.0/weatherdb.egg-info}/dependency_links.txt +0 -0
  104. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb.egg-info/entry_points.txt +0 -0
  105. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb.egg-info/requires.txt +0 -0
  106. {weatherdb-1.1.1 → weatherdb-1.2.0}/weatherdb.egg-info/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  stages:
2
2
  - test_single
3
3
  - test_matrix
4
+ - test_manual
4
5
  - build
5
6
  - deploy
6
7
 
@@ -21,10 +22,10 @@ test_single:
21
22
  before_script:
22
23
  - python3 -m pip install --upgrade pip --root-user-action=ignore
23
24
  - python3 -m pip install unittest-xml-reporting --root-user-action=ignore
24
- script:
25
25
  - python3 -m pip install .[optionals] --root-user-action=ignore
26
26
  - mkdir -p $WEATHERDB_DATA_BASE_DIR
27
27
  - mkdir -p test-reports
28
+ script:
28
29
  - python3 -m xmlrunner discover -s tests -p testSuite.py -o test-reports
29
30
  cache:
30
31
  - key: weatherdb-data-cache-$CI_COMMIT_REF_SLUG
@@ -61,10 +62,13 @@ test_single:
61
62
  WEATHERDB_LOGGING_HANDLER: console
62
63
  WEATHERDB_LOGGING_LEVEL: DEBUG
63
64
  WEATHERDB_HORIZON_RADIUS: 40000
65
+ WEATHERDB_OPENTOPO_API_KEY: $WEATHERDB_OPENTOPO_API_KEY
64
66
  WEATHERDB_TEST_ARTIFACT_DIR: $CI_PROJECT_DIR/artifacts
65
67
  WEATHERDB_TEST_ARTIFACT_DB_DUMP: ON_FAILURE
66
68
  WEATHERDB_TEST_ARTIFACT_LIST_DATA_FILES: ON_FAILURE
67
69
  WEATHERDB_TEST_ARTIFACT_COPY_USER_CONFIG: ON_FAILURE
70
+ WEATHERDB_TEST_DR_COPY_RASTERS: True
71
+ WEATHERDB_TEST_DR_UPDATE_USER_CONFIG: True
68
72
  DOCKER_ENV: test
69
73
  tags:
70
74
  - docker
@@ -72,14 +76,26 @@ test_single:
72
76
  test_matrix:
73
77
  stage: test_matrix
74
78
  extends: test_single
75
- dependencies:
76
- - test_single
77
79
  parallel:
78
80
  matrix:
79
81
  - PY_VERSION: ["3.9","3.10","3.11","3.12","3.13"]
80
82
  variables:
81
83
  PYTHON_VERSION: $PY_VERSION
82
84
 
85
+ test_download_rasters:
86
+ extends: test_single
87
+ stage: test_manual
88
+ rules:
89
+ - when: manual
90
+ allow_failure: true
91
+ script:
92
+ - python3 -m xmlrunner discover -s tests -p test_downloadRasters.py --complete -o test-reports
93
+ variables:
94
+ PYTHON_VERSION: $PY_VERSION
95
+ parallel:
96
+ matrix:
97
+ - PY_VERSION: ["3.9","3.10","3.11","3.12","3.13"]
98
+
83
99
  build:
84
100
  rules:
85
101
  - if: $CI_COMMIT_TAG =~ /^[Vv]\d+\.\d+\.\d+$/
@@ -1,8 +1,19 @@
1
1
  # Change-log
2
2
 
3
- ## Version 1.0.1
3
+ ## Version 1.2.0
4
+
5
+ - tests: add test rasters and make downloading the rasters a manual job on GitLab as there were many problems
6
+ - fillup: add method to linearly interpolate residual missing values
7
+
8
+ ## Version 1.1.2
9
+
10
+ - add method to download DEM data from OpenTopography-API
11
+ - update StationBase._expand_timeserie_to_period to remove entries that are older than the min_date config to remove unnessecary database entries
12
+ - fix GroupStation.create_ts: don't throw period changed warning if the change was only due to the expand_to_timestamp output
13
+
14
+ ## Version 1.1.1
4
15
 
5
- - minor fixes to fix previous version
16
+ - minor fixes to fix previous version
6
17
 
7
18
  ## Version 1.1.0
8
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: weatherdb
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: This is a package to work with and to create the Weather Database which handles, checks, fills and corrects DWD Weather Station data.
5
5
  Author-email: Max Schmit <max.schmit@hydrology.uni-freiburg.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -14,12 +14,14 @@ services:
14
14
  WEATHERDB_USER_CONFIG_FILE: /home/config_user.yaml
15
15
  WEATHERDB_DATA_BASE_DIR: /home/data
16
16
  WEATHERDB_HANDLE_NON_EXISTING_CONFIG: create
17
+ WEATHERDB_OPENTOPO_API_KEY: /run/secrets/opentopo_api_key
17
18
  DOCKER_ENV: main
18
19
  volumes:
19
20
  - weatherdb_home:/home
20
21
  command: ["sleep", "infinity"] # to keep awake
21
22
  secrets:
22
23
  - db_password
24
+ - opentopo_api_key
23
25
  develop:
24
26
  watch:
25
27
  - action: rebuild
@@ -53,6 +55,8 @@ volumes:
53
55
  secrets:
54
56
  db_password:
55
57
  file: ./db_password.docker-secret
58
+ opentopo_api_key:
59
+ file: ./opentopo_api_key.docker-secret
56
60
 
57
61
  # start from parent folder with `docker compose -f docker\\docker-compose.yaml up --build`
58
- # To connect to ther weatherdb service use `docker-compose exec weatherdb bash`
62
+ # To connect to the weatherdb service use `docker-compose exec weatherdb bash`
@@ -117,7 +117,11 @@ N_{neighbor} * \dfrac{N_{station,ma,winter}}{N_{neighbor,ma,winter}} \space if\s
117
117
  N_{neighbor} * \dfrac{N_{station,ma,summer}}{N_{neighbor,ma,summer}} \space if\space month\notin[4:9]
118
118
  \end{cases}$
119
119
 
120
- For the precipitation stations only stations within a 100 km radius are taken to fill missing values. For the potential Evapotranspiration and the temperature this radius is 150km. For the temperature stations the median of the regionalised values from the 5 closest stations (but not more than 150 km away) is taken to fill missing values.
120
+ For the precipitation stations only stations within a 110 km radius are taken to fill missing values. For the potential Evapotranspiration and the temperature this radius is 150km. For the temperature stations the median of the regionalised values from the 5 closest stations (but not more than 150 km away) is taken to fill missing values. Those limits can get configured in the user configurations.
121
+
122
+ ### linear interpolation
123
+
124
+ After missing values got filled with regionalised values from the neighboring stations there can still be missing values left. To fill those residual missing values, they get interpolated linearly. Those linear interpolations have configurable limits for how big the interval of missing values can be. The default values are 1h for precipitation and 2 days for temperature and evapotranspiration.
121
125
 
122
126
  ### adjusting precipitation to daily station measurements
123
127
 
@@ -46,7 +46,8 @@ So there is:
46
46
  - "raw" : the raw measurements as on the DWD server
47
47
  - "qc" : The quality checked data
48
48
  - "filled" : The filled timeseries
49
- - "filled_by" : The station ID of the station from which the data was taken to fill the measurements
49
+ - "filled_by" : The station ID of the station from which the data was taken to fill the measurements.
50
+ If the value was filled by linear intrepolation "filled_by" is `-1`
50
51
  - "corr" : The Richter corrected timeserie.
51
52
 
52
53
  If you want more than just one kind of timeseries, e.g. the filled timeseries, together with the id from which station the respective field got filled with use:
@@ -0,0 +1,3 @@
1
+ # Readme
2
+
3
+ The DEM file in this folder originates from Copernicus and got clipped to the region of analysis in the test environment + 40km
@@ -0,0 +1,3 @@
1
+ # Readme
2
+
3
+ The files in this folder originate from https://zenodo.org/records/10066045 and got clipped to the region of analysis in the test environment
@@ -0,0 +1,28 @@
1
+ ; Those are configs that should get loaded, when the test files are loaded
2
+ [data:rasters]
3
+ DEMS = ${data:BASE_DIR}/DEM/COP-DEM_GLO-30-DGED__2023_1_clipped.tif
4
+
5
+ [data:rasters:hyras]
6
+ FILE = ${data:BASE_DIR}/regionalisation/HYRAS_ma_1991_2020_DGM25_clipped.tif
7
+ BAND_P_WIHY = n_hyras_wihj
8
+ BAND_P_SUHY = n_hyras_sohj
9
+ BAND_P_YEAR = n_hyras_year
10
+ SRID = 3035
11
+ FACTOR_P_WIHY = 1
12
+ FACTOR_P_SUHY = 1
13
+ FACTOR_P_YEAR = 1
14
+
15
+ [data:rasters:dwd]
16
+ FILE = ${data:BASE_DIR}/regionalisation/DWD-grid_ma_1991_2020_DGM25_clipped.tif
17
+ BAND_P_WIHY = n_wihj
18
+ BAND_P_SUHY = n_sohj
19
+ BAND_P_YEAR = n_year
20
+ BAND_T_YEAR = t_year
21
+ BAND_ET_YEAR = et_year
22
+ SRID = 3035
23
+ FACTOR_P_WIHY = 1
24
+ FACTOR_P_SUHY = 1
25
+ FACTOR_P_YEAR = 1
26
+ FACTOR_T_YEAR = 0.1
27
+ FACTOR_ET_YEAR = 1
28
+
@@ -0,0 +1 @@
1
+ __version__ = "1.2.0"
@@ -103,10 +103,16 @@ def download_ma_rasters(which, overwrite, update_user_config):
103
103
  """
104
104
  click.echo("starting downloading multi annual raster data")
105
105
  from weatherdb.utils.get_data import download_ma_rasters
106
- download_ma_rasters(overwrite=overwrite)
106
+ download_ma_rasters(
107
+ which=which,
108
+ overwrite=overwrite,
109
+ update_user_config=update_user_config)
107
110
 
108
111
 
109
112
  @cli.command(short_help="Download the needed digital elevation model raster data from Copernicus to the data folder.")
113
+ @click.option('--out-dir', '-d',
114
+ type=click.Path(), default=None, show_default=False,
115
+ help="The directory to save the downloaded DEM data to.")
110
116
  @click.option('--overwrite/--no-overwrite', '-o/-no-o',
111
117
  type=bool, is_flag=True, default=None, show_default=False,
112
118
  help="Should the digital elevation model raster be downloaded even if it already exists?")
@@ -116,7 +122,11 @@ def download_ma_rasters(which, overwrite, update_user_config):
116
122
  @click.option("--update-user-config", "-u",
117
123
  type=bool, default=False, show_default=True, is_flag=True,
118
124
  help="Should the user configuration be updated with the path to the downloaded DEM?")
119
- def download_dem(overwrite, extent):
125
+ @click.option("--service", "-s",
126
+ type=str, default=["prism", "openTopography"], show_default=True, multiple=True,
127
+ help="The service to use to download the DEM. Options are 'prism' or 'openTopography'. " +\
128
+ "You can use this option muultiple times to test both in the given order until the file could be downloaded.")
129
+ def download_dem(out_dir, overwrite, extent, update_user_config, service="prism"):
120
130
  """Download the newest DEM data from the Copernicus Sentinel dataset.
121
131
 
122
132
  Only the GLO-30 DEM, wich has a 30m resolution, is downloaded as it is freely available.
@@ -131,7 +141,12 @@ def download_dem(overwrite, extent):
131
141
  """
132
142
  click.echo("Starting downloading digital elevation model from Copernicus")
133
143
  from weatherdb.utils.get_data import download_dem
134
- download_dem(overwrite=overwrite, extent=extent)
144
+ download_dem(
145
+ out_dir=out_dir,
146
+ overwrite=overwrite,
147
+ extent=extent,
148
+ service=service,
149
+ update_user_config=update_user_config)
135
150
 
136
151
 
137
152
  # cli statements to update the database
@@ -143,12 +143,25 @@ RASTER_BUFFER_CRS = ${weatherdb:HORIZON_CRS}
143
143
  ; The default is 1999-01-01
144
144
  MIN_DATE = 1999-01-01
145
145
 
146
+ ; When there are still NAs after filling with neighboring stations, the module can use linear interpolation to fill the gaps
147
+ ; The limit defines the maximum interval that is allowed to fill with linear interpolation
148
+ ; The value is given with a unit, e.g. 3 days, 30 minutes, 1 hour
149
+ ; For further explanation about the format see https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT
150
+ ; for precipitation, where the timeseries resolution is 10 minutes, 30 minutes means 3 missing values are interpolated, but not more.
151
+ ; If there shouldn't be any linear interpolation, set the limit to 0
152
+ ; For precipitation, the default is 1 hour
153
+ LINEAR_INTERPOLATION_LIMIT_P = 1 hour
154
+ ; For temperature, the default is 2 days
155
+ LINEAR_INTERPOLATION_LIMIT_T = 2 days
156
+ ; For Evapotranspiration, the default is 2 days
157
+ LINEAR_INTERPOLATION_LIMIT_ET = 2 days
158
+
146
159
 
147
160
  [weatherdb:max_fillup_distance]
148
161
  ; The maximum distance in meters to use for the filling of the station data
149
162
  ; For each parameter (P, T, ET) the module uses a different distance
150
163
  ; Precipitation (P)
151
- P = 110000
164
+ P = 130000
152
165
  ; Temperature (T)
153
166
  T = 150000
154
167
  ; Evapotranspiration (ET)
@@ -526,7 +526,9 @@ class GroupStation(object):
526
526
  period_new = period_filled.union(
527
527
  period,
528
528
  how="inner")
529
- if period_new != period:
529
+ if period_new != period and not (
530
+ (period.start == period_new.start) and
531
+ ((period.end - period_new.end) <= pd.Timedelta(days=1))):
530
532
  warnings.warn(
531
533
  f"The Period for Station {self.id} got changed from {str(period)} to {str(period_new)}.")
532
534
  period = period_new
@@ -504,10 +504,12 @@ class StationBase:
504
504
  '{interval}'::INTERVAL)::{tstp_dtype} AS timestamp)
505
505
  INSERT INTO timeseries."{stid}_{para}"(timestamp)
506
506
  (SELECT wts.timestamp
507
- FROM whole_ts wts
508
- LEFT JOIN timeseries."{stid}_{para}" ts
507
+ FROM whole_ts wts
508
+ LEFT JOIN timeseries."{stid}_{para}" ts
509
509
  ON ts.timestamp=wts.timestamp
510
- WHERE ts.timestamp IS NULL);
510
+ WHERE ts.timestamp IS NULL);
511
+ DELETE FROM timeseries."{stid}_{para}"
512
+ WHERE timestamp < '{min_date} 00:00'::{tstp_dtype};
511
513
  """.format(
512
514
  stid=self.id,
513
515
  para=self._para,
@@ -1489,6 +1491,70 @@ class StationBase:
1489
1491
  extra_fillup_where=sql_format_dict["extra_fillup_where"] +\
1490
1492
  ' OR ts."filled_by" IS DISTINCT FROM new."filled_by"'))
1491
1493
 
1494
+ # linear interpolation for the last missing values
1495
+ lr_limit = config.get(
1496
+ "weatherdb",
1497
+ "LINEAR_INTERPOLATION_LIMIT_{self._para_base}",
1498
+ fallback="0")
1499
+ if lr_limit != "0":
1500
+ sql_format_dict.update(dict(
1501
+ sql_linear_interpolation=f"""
1502
+ DO
1503
+ $do$
1504
+ DECLARE reg_borders record;
1505
+ BEGIN
1506
+ FOR reg_borders IN
1507
+ WITH empty_periods AS (
1508
+ SELECT *
1509
+ FROM ( SELECT
1510
+ CASE WHEN dist_next>'{self._interval}'::interval
1511
+ THEN timestamp ELSE NULL
1512
+ END AS start,
1513
+ CASE WHEN dist_next>'{self._interval}'::interval
1514
+ THEN LEAD(timestamp) OVER (ORDER BY timestamp)
1515
+ ELSE NULL
1516
+ END AS end
1517
+ FROM (
1518
+ SELECT *,
1519
+ timestamp - lag(timestamp, 1) OVER ( ORDER BY timestamp) AS dist_prev,
1520
+ lead(timestamp, 1) OVER ( ORDER BY timestamp) - timestamp AS dist_next
1521
+ FROM new_filled_{self.id}_{self._para}
1522
+ WHERE filled IS NOT NULL) t
1523
+ WHERE t.dist_prev > '{self._interval}'::interval
1524
+ OR t.dist_next > '{self._interval}'::interval
1525
+ ) p
1526
+ WHERE p.start IS NOT NULL AND p.end IS NOT NULL)
1527
+ SELECT
1528
+ ep.start AS timestamp_start,
1529
+ ttss.filled AS value_start,
1530
+ ep.end AS timestamp_end,
1531
+ ttse.filled AS value_end,
1532
+ (ttse.filled - ttss.filled)::numeric/(EXTRACT(EPOCH FROM (ep.end - ep.start))/EXTRACT(EPOCH FROM '{self._interval}'::interval))::numeric as slope
1533
+ FROM empty_periods ep
1534
+ LEFT JOIN new_filled_{self.id}_{self._para} ttss ON ep.start = ttss.timestamp
1535
+ LEFT JOIN new_filled_{self.id}_{self._para} ttse ON ep.end = ttse.timestamp
1536
+ where (ep.end - ep.start - '{self._interval}'::interval) <= '{lr_limit}'::interval
1537
+ loop
1538
+ execute FORMAT(
1539
+ $$
1540
+ UPDATE new_filled_{self.id}_{self._para} ts
1541
+ SET filled=%2$L + (EXTRACT(EPOCH FROM ts.timestamp - %1$L)::numeric/(EXTRACT(EPOCH FROM '{self._interval}'::interval))::numeric * %5$L),
1542
+ filled_by=-1
1543
+ WHERE ts.timestamp>%1$L and ts.timestamp<%3$L;
1544
+ $$,
1545
+ reg_borders.timestamp_start,
1546
+ reg_borders.value_start,
1547
+ reg_borders.timestamp_end,
1548
+ reg_borders.value_end,
1549
+ reg_borders.slope
1550
+ );
1551
+ END loop;
1552
+ END
1553
+ $do$;"""))
1554
+ else:
1555
+ sql_format_dict.update(dict(sql_linear_interpolation=""))
1556
+
1557
+
1492
1558
  # Make SQL statement to fill the missing values with values from nearby stations
1493
1559
  sql = """
1494
1560
  CREATE TEMP TABLE new_filled_{stid}_{para}
@@ -1561,15 +1627,16 @@ class StationBase:
1561
1627
  FROM new_filled_{stid}_{para}
1562
1628
  WHERE "filled" IS NULL {extra_unfilled_period_where};
1563
1629
  END LOOP;
1564
- {sql_extra_after_loop}
1565
- UPDATE timeseries."{stid}_{para}" ts
1566
- SET filled = new.filled, {extra_cols_fillup}
1567
- filled_by = new.filled_by
1568
- FROM new_filled_{stid}_{para} new
1569
- WHERE ts.timestamp = new.timestamp
1570
- AND (ts."filled" IS DISTINCT FROM new."filled" {extra_fillup_where}) ;
1571
1630
  END
1572
1631
  $do$;
1632
+ {sql_linear_interpolation}
1633
+ {sql_extra_after_loop}
1634
+ UPDATE timeseries."{stid}_{para}" ts
1635
+ SET filled = new.filled, {extra_cols_fillup}
1636
+ filled_by = new.filled_by
1637
+ FROM new_filled_{stid}_{para} new
1638
+ WHERE ts.timestamp = new.timestamp
1639
+ AND (ts."filled" IS DISTINCT FROM new."filled" {extra_fillup_where}) ;
1573
1640
  """.format(**sql_format_dict)
1574
1641
 
1575
1642
  # execute