meteodata-lab 0.3.0__tar.gz
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.
- meteodata_lab-0.3.0/AUTHORS.md +13 -0
- meteodata_lab-0.3.0/LICENSE +19 -0
- meteodata_lab-0.3.0/PKG-INFO +88 -0
- meteodata_lab-0.3.0/README.md +53 -0
- meteodata_lab-0.3.0/pyproject.toml +202 -0
- meteodata_lab-0.3.0/src/meteodatalab/__init__.py +8 -0
- meteodata_lab-0.3.0/src/meteodatalab/cli.py +151 -0
- meteodata_lab-0.3.0/src/meteodatalab/config.py +61 -0
- meteodata_lab-0.3.0/src/meteodatalab/data/fdb_config_balfrin.yaml +8 -0
- meteodata_lab-0.3.0/src/meteodatalab/data/fdb_config_tsa.yaml +8 -0
- meteodata_lab-0.3.0/src/meteodatalab/data/field_mappings.yml +190 -0
- meteodata_lab-0.3.0/src/meteodatalab/data_cache.py +90 -0
- meteodata_lab-0.3.0/src/meteodatalab/data_source.py +174 -0
- meteodata_lab-0.3.0/src/meteodatalab/grib_decoder.py +531 -0
- meteodata_lab-0.3.0/src/meteodatalab/icon_grid.py +94 -0
- meteodata_lab-0.3.0/src/meteodatalab/mars.py +216 -0
- meteodata_lab-0.3.0/src/meteodatalab/mch_model_data.py +141 -0
- meteodata_lab-0.3.0/src/meteodatalab/metadata.py +276 -0
- meteodata_lab-0.3.0/src/meteodatalab/ogd_api.py +334 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/__init__.py +1 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/atmo.py +103 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/brn.py +74 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/crop.py +80 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/curl.py +98 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/destagger.py +173 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/diff.py +120 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/flexpart.py +40 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/gis.py +273 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/hzerocl.py +79 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/lateral_operators.py +203 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/omega_slope.py +80 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/pot_vortic.py +84 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/radiation.py +52 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/regrid.py +565 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/relhum.py +61 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/rho.py +53 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/support_operators.py +123 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/theta.py +33 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/thetav.py +35 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/time_operators.py +184 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/total_diff.py +51 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/vertical_interpolation.py +399 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/vertical_reduction.py +300 -0
- meteodata_lab-0.3.0/src/meteodatalab/operators/wind.py +91 -0
- meteodata_lab-0.3.0/src/meteodatalab/physical_constants.py +30 -0
- meteodata_lab-0.3.0/src/meteodatalab/product.py +120 -0
- meteodata_lab-0.3.0/src/meteodatalab/products/ninjo_k2th.py +130 -0
- meteodata_lab-0.3.0/src/meteodatalab/py.typed +0 -0
- meteodata_lab-0.3.0/src/meteodatalab/tasking.py +23 -0
- meteodata_lab-0.3.0/src/meteodatalab/util.py +35 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Credits
|
|
2
|
+
|
|
3
|
+
## Development Lead
|
|
4
|
+
|
|
5
|
+
Carlos Osuna <carlos.osuna@meteoswiss.ch>
|
|
6
|
+
|
|
7
|
+
## Contributors
|
|
8
|
+
|
|
9
|
+
- Petra Baumann <petra.baumann@meteoswiss.ch>
|
|
10
|
+
- Nina Burgdorfer <nina.burgdorfer@meteoswiss.ch>
|
|
11
|
+
- Victoria Cherkas <victoria.cherkas@meteoswiss.ch>
|
|
12
|
+
- Christian Kanesan <christian.kanesan@meteoswiss.ch>
|
|
13
|
+
- Tobias Wicky <tobias.wicky@meteoswiss.ch>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2023 Federal Office of Meteorlogy and Climatology MeteoSwiss
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: meteodata-lab
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A data post-processing framework on the basis of xarray.
|
|
5
|
+
Keywords: Icon,Data Processing
|
|
6
|
+
Author: Carlos Osuna
|
|
7
|
+
Author-email: carlos.osuna@meteoswiss.ch
|
|
8
|
+
Requires-Python: >=3.10,<3.13
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Provides-Extra: polytope
|
|
17
|
+
Provides-Extra: regrid
|
|
18
|
+
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
19
|
+
Requires-Dist: earthkit-data[covjsonkit] (>=0.11,<1)
|
|
20
|
+
Requires-Dist: eccodes (>=2.38,<2.39)
|
|
21
|
+
Requires-Dist: eccodes-cosmo-resources-python (>=2.38,<2.39)
|
|
22
|
+
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
|
23
|
+
Requires-Dist: polytope-client (>=0.7.4,<0.8.0) ; extra == "polytope"
|
|
24
|
+
Requires-Dist: pydantic
|
|
25
|
+
Requires-Dist: pyproj (>=3.6.1,<4.0.0) ; extra == "regrid"
|
|
26
|
+
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
27
|
+
Requires-Dist: rasterio (>=1.3.10,<2.0.0) ; extra == "regrid"
|
|
28
|
+
Requires-Dist: scipy (>=1.13,<2.0) ; extra == "regrid"
|
|
29
|
+
Requires-Dist: setuptools
|
|
30
|
+
Requires-Dist: xarray (>=2024)
|
|
31
|
+
Project-URL: documentation, https://meteoswiss.github.io/meteodata-lab/
|
|
32
|
+
Project-URL: source, https://github.com/MeteoSwiss/meteodata-lab
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<picture>
|
|
37
|
+
<img src="meteodata_lab_logo_with_name-optimized.gif" height="160" alt="meteodata-lab animated logo showing a cloud partially hiding the sun in a shape of a gear">
|
|
38
|
+
</picture>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<a href="https://pypi.org/project/meteodata-lab/">
|
|
43
|
+
<img src="https://img.shields.io/pypi/v/meteodata-lab.svg?color=ff69b4" alt="PyPI version">
|
|
44
|
+
</a>
|
|
45
|
+
<a href="https://github.com/meteoswiss/meteodata-lab/releases">
|
|
46
|
+
<img src="https://img.shields.io/github/v/release/meteoswiss/meteodata-lab?color=purple&label=Release" alt="Latest Release">
|
|
47
|
+
</a>
|
|
48
|
+
<a href="https://opensource.org/licenses/mit">
|
|
49
|
+
<img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence">
|
|
50
|
+
</a>
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<a href="#installation">Installation</a> •
|
|
55
|
+
<a href="https://meteoswiss.github.io/meteodata-lab/">Documentation</a>
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
> [!WARNING]
|
|
59
|
+
> This project is in BETA and under active development. Interfaces and functionality are subject to change.
|
|
60
|
+
|
|
61
|
+
**Meteodata-lab** is a NumPy/Xarray-based Python library for processing and analyzing gridded meteorological data. It supports GRIB (read/write) and is tailored to common workflows that require data interpolation, regridding to custom grids (e.g., Swiss grid or rotated lat/lon), and the computation of advanced meteorological fields. One of the key features of meteodata-lab is its use of operators that ensure the integrity of GRIB metadata is maintained throughout processing, allowing for consistent writing back to GRIB format.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### For Users
|
|
67
|
+
|
|
68
|
+
To install the latest release from PyPI:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install meteodata-lab
|
|
72
|
+
```
|
|
73
|
+
#### Optional Extras
|
|
74
|
+
To install optional extras:
|
|
75
|
+
```bash
|
|
76
|
+
pip install "meteodata-lab[polytope,regrid]"
|
|
77
|
+
```
|
|
78
|
+
**Note**: The `fdb` extra is currently disabled because its dependency `pyfdb` is not available on PyPI. As an alternative the development setup can be used.
|
|
79
|
+
|
|
80
|
+
### For Development
|
|
81
|
+
To set up the project for local development, clone the repository and use the provided Poetry setup script:
|
|
82
|
+
```bash
|
|
83
|
+
git clone git@github.com:MeteoSwiss/meteodata-lab.git
|
|
84
|
+
cd meteodata-lab
|
|
85
|
+
./scripts/setup_poetry.sh
|
|
86
|
+
```
|
|
87
|
+
This will install Poetry (if not already available), set up the virtual environment, and install all dependencies with extras.
|
|
88
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<img src="meteodata_lab_logo_with_name-optimized.gif" height="160" alt="meteodata-lab animated logo showing a cloud partially hiding the sun in a shape of a gear">
|
|
4
|
+
</picture>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://pypi.org/project/meteodata-lab/">
|
|
9
|
+
<img src="https://img.shields.io/pypi/v/meteodata-lab.svg?color=ff69b4" alt="PyPI version">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/meteoswiss/meteodata-lab/releases">
|
|
12
|
+
<img src="https://img.shields.io/github/v/release/meteoswiss/meteodata-lab?color=purple&label=Release" alt="Latest Release">
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://opensource.org/licenses/mit">
|
|
15
|
+
<img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence">
|
|
16
|
+
</a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="#installation">Installation</a> •
|
|
21
|
+
<a href="https://meteoswiss.github.io/meteodata-lab/">Documentation</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
> [!WARNING]
|
|
25
|
+
> This project is in BETA and under active development. Interfaces and functionality are subject to change.
|
|
26
|
+
|
|
27
|
+
**Meteodata-lab** is a NumPy/Xarray-based Python library for processing and analyzing gridded meteorological data. It supports GRIB (read/write) and is tailored to common workflows that require data interpolation, regridding to custom grids (e.g., Swiss grid or rotated lat/lon), and the computation of advanced meteorological fields. One of the key features of meteodata-lab is its use of operators that ensure the integrity of GRIB metadata is maintained throughout processing, allowing for consistent writing back to GRIB format.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### For Users
|
|
33
|
+
|
|
34
|
+
To install the latest release from PyPI:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install meteodata-lab
|
|
38
|
+
```
|
|
39
|
+
#### Optional Extras
|
|
40
|
+
To install optional extras:
|
|
41
|
+
```bash
|
|
42
|
+
pip install "meteodata-lab[polytope,regrid]"
|
|
43
|
+
```
|
|
44
|
+
**Note**: The `fdb` extra is currently disabled because its dependency `pyfdb` is not available on PyPI. As an alternative the development setup can be used.
|
|
45
|
+
|
|
46
|
+
### For Development
|
|
47
|
+
To set up the project for local development, clone the repository and use the provided Poetry setup script:
|
|
48
|
+
```bash
|
|
49
|
+
git clone git@github.com:MeteoSwiss/meteodata-lab.git
|
|
50
|
+
cd meteodata-lab
|
|
51
|
+
./scripts/setup_poetry.sh
|
|
52
|
+
```
|
|
53
|
+
This will install Poetry (if not already available), set up the virtual environment, and install all dependencies with extras.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=1.0.0"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "meteodata-lab"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "A data post-processing framework on the basis of xarray."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = ["Icon", "Data Processing"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Natural Language :: English",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
]
|
|
18
|
+
authors = [
|
|
19
|
+
"Carlos Osuna <carlos.osuna@meteoswiss.ch>",
|
|
20
|
+
"Petra Baumann <petra.baumanna@meteoswiss.ch>",
|
|
21
|
+
"Nina Burgdorfer <nina.burgdorfer@meteoswiss.ch>",
|
|
22
|
+
"Victoria Cherkas <victoria.cherkas@meteoswiss.ch>",
|
|
23
|
+
"Christian Kanesan <christian.kanesan@meteoswiss.ch>",
|
|
24
|
+
"Tobias Wicky <tobias.wicky@meteoswiss.ch>",
|
|
25
|
+
]
|
|
26
|
+
packages = [
|
|
27
|
+
{ include = "meteodatalab", from = "src" },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.poetry.dependencies]
|
|
31
|
+
python = ">=3.10,<3.13"
|
|
32
|
+
click = "^8.1.7"
|
|
33
|
+
earthkit-data = { version = ">=0.11,<1", extras = ["covjsonkit"] }
|
|
34
|
+
eccodes = "~2.38"
|
|
35
|
+
eccodes-cosmo-resources-python = "~2.38"
|
|
36
|
+
numpy = "^1.26.4"
|
|
37
|
+
polytope-client = { version = "^0.7.4", optional = true }
|
|
38
|
+
pydantic = "*"
|
|
39
|
+
pyproj = { version = "^3.6.1", optional = true }
|
|
40
|
+
pyyaml = "^6.0.1"
|
|
41
|
+
rasterio = { version = "^1.3.10", optional = true }
|
|
42
|
+
scipy = { version = "^1.13", optional = true }
|
|
43
|
+
setuptools = "*"
|
|
44
|
+
xarray = ">=2024"
|
|
45
|
+
|
|
46
|
+
[tool.poetry.extras]
|
|
47
|
+
# fdb = ["pyfdb"]
|
|
48
|
+
polytope = ["polytope-client"]
|
|
49
|
+
regrid = ["pyproj", "rasterio", "scipy"]
|
|
50
|
+
|
|
51
|
+
[tool.poetry.group.dev.dependencies]
|
|
52
|
+
black = "*"
|
|
53
|
+
codespell = "*"
|
|
54
|
+
flake8 = "*"
|
|
55
|
+
Flake8-pyproject = "*"
|
|
56
|
+
ipykernel = "*"
|
|
57
|
+
isort = "*"
|
|
58
|
+
mypy = "*"
|
|
59
|
+
pandas-stubs = "*"
|
|
60
|
+
pre-commit = "*"
|
|
61
|
+
pydocstyle = "*"
|
|
62
|
+
pyfdb = { url = "https://github.com/ecmwf/pyfdb/archive/refs/tags/0.0.3.zip" }
|
|
63
|
+
pytest = "*"
|
|
64
|
+
rstcheck = { version = "*", extras = ["sphinx"] }
|
|
65
|
+
types-requests = "*"
|
|
66
|
+
types-PyYAML = "*"
|
|
67
|
+
|
|
68
|
+
[tool.poetry.urls]
|
|
69
|
+
source = "https://github.com/MeteoSwiss/meteodata-lab"
|
|
70
|
+
documentation = "https://meteoswiss.github.io/meteodata-lab/"
|
|
71
|
+
|
|
72
|
+
[tool.poetry.scripts]
|
|
73
|
+
# Format: <command> = "<package>.<module>:<function>"
|
|
74
|
+
meteodata-lab = "meteodatalab.cli:main"
|
|
75
|
+
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
testpaths = ["tests"]
|
|
78
|
+
addopts = [
|
|
79
|
+
"-ra",
|
|
80
|
+
"--pdbcls=IPython.terminal.debugger:TerminalPdb",
|
|
81
|
+
"--tb=short",
|
|
82
|
+
# "--cov=meteodatalab",
|
|
83
|
+
# "--mypy",
|
|
84
|
+
]
|
|
85
|
+
markers = [
|
|
86
|
+
"ifs", # marks tests that use IFS grib definitions
|
|
87
|
+
"cosmo", # marks tests that use COSMO grib definitions
|
|
88
|
+
"data", # marks tests that depend on specific test data
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[tool.black]
|
|
92
|
+
|
|
93
|
+
[tool.isort]
|
|
94
|
+
default_section = "THIRDPARTY"
|
|
95
|
+
profile = "black"
|
|
96
|
+
# Headings
|
|
97
|
+
import_heading_stdlib = "Standard library"
|
|
98
|
+
import_heading_thirdparty = "Third-party"
|
|
99
|
+
import_heading_firstparty = "First-party"
|
|
100
|
+
import_heading_localfolder = "Local"
|
|
101
|
+
# Known modules to avoid misclassification
|
|
102
|
+
known_standard_library = [
|
|
103
|
+
# Add standard library modules that may be misclassified by isort
|
|
104
|
+
]
|
|
105
|
+
known_third_party = [
|
|
106
|
+
# Add third-party modules that may be misclassified by isort
|
|
107
|
+
]
|
|
108
|
+
known_first_party = [
|
|
109
|
+
# Add first-party modules that may be misclassified by isort
|
|
110
|
+
"meteodatalab",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
[tool.mypy]
|
|
114
|
+
plugins = ["pydantic.mypy"]
|
|
115
|
+
|
|
116
|
+
[[tool.mypy.overrides]]
|
|
117
|
+
# Note: Only globally ignore missing imports as a matter of last resort!
|
|
118
|
+
# See https://blog.wolt.com/engineering/2021/09/30/professional-grade-mypy-configuration/
|
|
119
|
+
module = [
|
|
120
|
+
# Add external modules w/o type hints here
|
|
121
|
+
"rasterio",
|
|
122
|
+
"rasterio.crs",
|
|
123
|
+
]
|
|
124
|
+
ignore_missing_imports = true
|
|
125
|
+
|
|
126
|
+
[tool.flake8]
|
|
127
|
+
max-line-length = 88
|
|
128
|
+
# max-complexity = 10
|
|
129
|
+
ignore = [
|
|
130
|
+
"E203", # Allow whitespace before ':' (https://github.com/PyCQA/pycodestyle/issues/373)
|
|
131
|
+
"F811", # Allow redefinition of unused name (necessary for typing.overload)
|
|
132
|
+
"I002", # Don't check for isort configuration
|
|
133
|
+
"W503", # Allow line break before binary operator (PEP 8-compatible)
|
|
134
|
+
"E704", # Allow multiple statements on one line (def)
|
|
135
|
+
]
|
|
136
|
+
per-file-ignores = [
|
|
137
|
+
"__init__.py: F401", # Allow unused imports
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
[tool.pylint]
|
|
141
|
+
recursive = true
|
|
142
|
+
ignore-imports = true
|
|
143
|
+
max-line-length = 88
|
|
144
|
+
# Tweak valid name formats
|
|
145
|
+
# Defaults (http://pylint-messages.wikidot.com/messages:c0103):
|
|
146
|
+
# argument-rgx = "^[a-z_][a-z0-9_]{2,30}$"
|
|
147
|
+
# attr-rgx = "^[a-z_][a-z0-9_]{2,30}$"
|
|
148
|
+
# function-rgx = "^[a-z_][a-z0-9_]{2,30}$"
|
|
149
|
+
# method-rgx = "^[a-z_][a-z0-9_]{2,30}$"
|
|
150
|
+
# variable-rgx = "^[a-z_][a-z0-9_]{2,30}$"
|
|
151
|
+
# class-rgx = "^[A-Z_][a-zA-Z0-9]+$"
|
|
152
|
+
# const-rgx = "^(([A-Z_][A-Z0-9_]*)|(__.*__))$"
|
|
153
|
+
# module-rgx = "^(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$"
|
|
154
|
+
argument-rgx = "^[a-z_][a-z0-9_]{,40}$"
|
|
155
|
+
attr-rgx = "^[a-z_][a-z0-9_]{,40}$"
|
|
156
|
+
function-rgx = "^[a-z_][a-z0-9_]{,40}$"
|
|
157
|
+
method-rgx = "^[a-z_][a-z0-9_]{,40}$"
|
|
158
|
+
variable-rgx = "^[a-z_][a-z0-9_]{,40}$"
|
|
159
|
+
class-rgx = "^[A-Z_][a-zA-Z0-9]+$"
|
|
160
|
+
const-rgx = "^(([a-z_][a-z0-9_]*)|([A-Z_][A-Z0-9_]*)|(__[a-zA-Z0-9]+__))$"
|
|
161
|
+
module-rgx = "^(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$"
|
|
162
|
+
inlinevar-rgx = "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
163
|
+
# Disable selected warnings, errors etc. that conflict with style guide etc.
|
|
164
|
+
# Note: To locally ignore certain errors, use "pylint: disable=XXXX" comments instead!
|
|
165
|
+
disable = [
|
|
166
|
+
"C0115", # Missing class docstring
|
|
167
|
+
"C0116", # Missing function or method docstring
|
|
168
|
+
"R0903", # Too few public methods (*/2) (too-few-public-methods)
|
|
169
|
+
# "R0801", # Similar lines in 2 files (duplicate-code)
|
|
170
|
+
# -> see https://github.com/PyCQA/pylint/issues/214
|
|
171
|
+
"R1705", # Unnecessary "elif" after "return" (no-else-return)
|
|
172
|
+
"R1720", # Unnecessary "elif" after "raise" (no-else-raise)
|
|
173
|
+
"R1724", # Unnecessary "elif" after "continue" (no-else-continue)
|
|
174
|
+
"W1116", # Second argument of isinstance is not a type (isinstance-second-argument-not-valid-type)
|
|
175
|
+
]
|
|
176
|
+
# Ignore (sub-)modules that trigger errors like E1101 (no-member) or E0611 (no-name-in-module)
|
|
177
|
+
ignored-modules = []
|
|
178
|
+
|
|
179
|
+
[tool.pydocstyle]
|
|
180
|
+
# All codes: http://www.pydocstyle.org/en/stable/error_codes.html
|
|
181
|
+
ignore = [
|
|
182
|
+
# "D100", # Missing docstring in public module
|
|
183
|
+
"D101", # Missing docstring in public class
|
|
184
|
+
"D102", # Missing docstring in public method
|
|
185
|
+
"D103", # Missing docstring in public function
|
|
186
|
+
# "D104", # Missing docstring in public package
|
|
187
|
+
"D105", # Missing docstring in magic method
|
|
188
|
+
# "D105", # Missing docstring in public nested class
|
|
189
|
+
"D107", # Missing docstring in __init__
|
|
190
|
+
"D203", # Blank line required before class docstring
|
|
191
|
+
"D213", # Multi-line docstring summary should start at the second line
|
|
192
|
+
# "D405", # Section name should be properly capitalized
|
|
193
|
+
"D406", # Section name should end with a newline
|
|
194
|
+
"D407", # Missing dashed underline after section
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
[tool.rstcheck]
|
|
198
|
+
ignore_directives = ["automodule", "mdinclude", "autosummary"]
|
|
199
|
+
|
|
200
|
+
[tool.codespell]
|
|
201
|
+
ignore-words-list = "aare,inout,THIRDPARTY"
|
|
202
|
+
skip = "poetry.lock"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Command line interface of meteodata-lab."""
|
|
2
|
+
|
|
3
|
+
# Standard library
|
|
4
|
+
from importlib.resources import files
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Third-party
|
|
8
|
+
import click
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
# Local
|
|
12
|
+
from . import __version__, grib_decoder
|
|
13
|
+
from .metadata import is_staggered_horizontal
|
|
14
|
+
from .operators import destagger, gis, regrid
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def print_version(ctx, _, value: bool) -> None:
|
|
18
|
+
"""Print the version number and exit."""
|
|
19
|
+
if value:
|
|
20
|
+
click.echo(__version__)
|
|
21
|
+
ctx.exit(0)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group()
|
|
25
|
+
@click.option(
|
|
26
|
+
"--version",
|
|
27
|
+
"-V",
|
|
28
|
+
help="Print version and exit.",
|
|
29
|
+
is_flag=True,
|
|
30
|
+
expose_value=False,
|
|
31
|
+
callback=print_version,
|
|
32
|
+
)
|
|
33
|
+
def main() -> None:
|
|
34
|
+
"""Console script for test_cli_project."""
|
|
35
|
+
print("CLI for meteodatalab")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
RESAMPLING = {
|
|
39
|
+
"nearest": regrid.Resampling.nearest,
|
|
40
|
+
"bilinear": regrid.Resampling.bilinear,
|
|
41
|
+
"cubic": regrid.Resampling.cubic,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _load_mapping():
|
|
46
|
+
mapping_path = files("meteodatalab.data").joinpath("field_mappings.yml")
|
|
47
|
+
return yaml.safe_load(mapping_path.open())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def handle_vector_fields(ds):
|
|
51
|
+
mapping = _load_mapping()
|
|
52
|
+
names = set(ds)
|
|
53
|
+
pairs = []
|
|
54
|
+
while names:
|
|
55
|
+
name = names.pop()
|
|
56
|
+
item = mapping[name]["cosmo"]
|
|
57
|
+
if u := item.get("uComponent"):
|
|
58
|
+
if u not in names:
|
|
59
|
+
msg = f"The u-component {u} must be part of PARAMS"
|
|
60
|
+
raise click.UsageError(msg)
|
|
61
|
+
names.remove(u)
|
|
62
|
+
pairs.append((u, name))
|
|
63
|
+
elif v := item.get("vComponent"):
|
|
64
|
+
if v not in names:
|
|
65
|
+
msg = f"The v-component {v} must be part of PARAMS"
|
|
66
|
+
raise click.UsageError(msg)
|
|
67
|
+
names.remove(v)
|
|
68
|
+
pairs.append((name, v))
|
|
69
|
+
|
|
70
|
+
for u_name, v_name in pairs:
|
|
71
|
+
click.echo(f"Rotating vector field components {u_name}, {v_name} to geolatlon")
|
|
72
|
+
u, v = ds[u_name], ds[v_name]
|
|
73
|
+
if is_staggered_horizontal(u):
|
|
74
|
+
u = destagger.destagger(u, "x")
|
|
75
|
+
if is_staggered_horizontal(v):
|
|
76
|
+
v = destagger.destagger(v, "y")
|
|
77
|
+
if u.vref == "native" and v.vref == "native":
|
|
78
|
+
u_g, v_g = gis.vref_rot2geolatlon(u, v)
|
|
79
|
+
ds[u_name] = u_g
|
|
80
|
+
ds[v_name] = v_g
|
|
81
|
+
elif u.vref == "geo" and v.vref == "geo":
|
|
82
|
+
continue
|
|
83
|
+
else:
|
|
84
|
+
msg = "Differing vector references."
|
|
85
|
+
raise RuntimeError(msg)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@main.command("regrid")
|
|
89
|
+
@click.option(
|
|
90
|
+
"--crs",
|
|
91
|
+
type=click.Choice(["geolatlon"]),
|
|
92
|
+
default="geolatlon",
|
|
93
|
+
help="Coordinate reference system",
|
|
94
|
+
)
|
|
95
|
+
@click.option(
|
|
96
|
+
"--resampling",
|
|
97
|
+
type=click.Choice(list(RESAMPLING.keys())),
|
|
98
|
+
default="nearest",
|
|
99
|
+
help="Resampling method",
|
|
100
|
+
)
|
|
101
|
+
@click.option(
|
|
102
|
+
"--ref-param",
|
|
103
|
+
default="HHL",
|
|
104
|
+
help="Reference parameter with respect to staggering, default: HHL",
|
|
105
|
+
)
|
|
106
|
+
@click.argument(
|
|
107
|
+
"infile",
|
|
108
|
+
nargs=-1,
|
|
109
|
+
type=click.Path(exists=True, path_type=Path),
|
|
110
|
+
)
|
|
111
|
+
@click.argument(
|
|
112
|
+
"outfile",
|
|
113
|
+
type=click.Path(writable=True, path_type=Path),
|
|
114
|
+
)
|
|
115
|
+
@click.argument("params")
|
|
116
|
+
def regrid_cmd(
|
|
117
|
+
crs: str,
|
|
118
|
+
resampling: str,
|
|
119
|
+
ref_param: str,
|
|
120
|
+
infile: tuple[Path, ...],
|
|
121
|
+
outfile: Path,
|
|
122
|
+
params: str,
|
|
123
|
+
):
|
|
124
|
+
"""Regrid the given PARAMS found in INFILE and write to OUTFILE.
|
|
125
|
+
|
|
126
|
+
PARAMS is a comma separated list of short names following the cosmo convention.
|
|
127
|
+
"""
|
|
128
|
+
resampling_arg = RESAMPLING[resampling]
|
|
129
|
+
crs_str = regrid.CRS_ALIASES.get(crs, crs)
|
|
130
|
+
|
|
131
|
+
if not infile:
|
|
132
|
+
raise click.UsageError("No input files")
|
|
133
|
+
|
|
134
|
+
if outfile.exists():
|
|
135
|
+
click.confirm(f"OUTFILE {outfile} exists. Overwrite?")
|
|
136
|
+
|
|
137
|
+
reader = grib_decoder.GribReader.from_files(list(infile), ref_param=ref_param)
|
|
138
|
+
ds = reader.load_fieldnames(params.split(","))
|
|
139
|
+
|
|
140
|
+
handle_vector_fields(ds)
|
|
141
|
+
|
|
142
|
+
with outfile.open("wb") as fout:
|
|
143
|
+
for name, field in ds.items():
|
|
144
|
+
src = regrid.RegularGrid.from_field(field)
|
|
145
|
+
dst = src.to_crs(crs_str)
|
|
146
|
+
|
|
147
|
+
click.echo(f"Regridding field {name} to {dst}")
|
|
148
|
+
field_out = regrid.regrid(field, dst, resampling_arg)
|
|
149
|
+
|
|
150
|
+
click.echo(f"Writing GRIB fields to {outfile}")
|
|
151
|
+
grib_decoder.save(field_out, fout)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""global configuration for meteodatalab."""
|
|
2
|
+
|
|
3
|
+
# Standard library
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
config: dict = {}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@contextmanager
|
|
11
|
+
def set_values(config: dict = config, **kwargs):
|
|
12
|
+
"""Temporarily set configuration values within a context manager.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
config : dict, optional
|
|
17
|
+
dictionary object use to hold the configuration.
|
|
18
|
+
Default will use the config object in this module
|
|
19
|
+
**kwargs :
|
|
20
|
+
the configuration key-value pairs to set.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
record: list[tuple[Literal["insert", "replace"], str, Any]] = []
|
|
24
|
+
|
|
25
|
+
for key, value in kwargs.items():
|
|
26
|
+
if key in config:
|
|
27
|
+
record.append(("replace", key, config[key]))
|
|
28
|
+
else:
|
|
29
|
+
record.append(("insert", key, None))
|
|
30
|
+
|
|
31
|
+
config[key] = value
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
yield config
|
|
35
|
+
finally:
|
|
36
|
+
for op, key, value in reversed(record):
|
|
37
|
+
d = config
|
|
38
|
+
if op == "replace":
|
|
39
|
+
d[key] = value
|
|
40
|
+
else: # insert
|
|
41
|
+
d.pop(key, None)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get(
|
|
45
|
+
key: str,
|
|
46
|
+
default: Any = None,
|
|
47
|
+
config: dict = config,
|
|
48
|
+
) -> Any:
|
|
49
|
+
"""Get values from global config.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
key: str
|
|
54
|
+
specifies the name of the key for which the value is requested
|
|
55
|
+
default: Any
|
|
56
|
+
default value to be returned in case the key does not exist in config
|
|
57
|
+
config: dict, optional
|
|
58
|
+
config object holding the mapping. Default value is the global config
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
return config.get(key, default)
|