eccodes 2.43.0__tar.gz → 2.45.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.
Files changed (131) hide show
  1. {eccodes-2.43.0 → eccodes-2.45.0}/CHANGELOG.rst +16 -0
  2. {eccodes-2.43.0 → eccodes-2.45.0}/MANIFEST.in +4 -0
  3. {eccodes-2.43.0 → eccodes-2.45.0}/PKG-INFO +21 -2
  4. {eccodes-2.43.0 → eccodes-2.45.0}/README.rst +4 -1
  5. {eccodes-2.43.0 → eccodes-2.45.0}/ci/requirements-docs.txt +1 -1
  6. {eccodes-2.43.0 → eccodes-2.45.0}/docs/conf.py +1 -1
  7. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes/eccodes.py +4 -0
  8. eccodes-2.45.0/eccodes/highlevel/__init__.py +3 -0
  9. eccodes-2.45.0/eccodes/highlevel/_bufr/BUFR_Snippets.md +463 -0
  10. eccodes-2.45.0/eccodes/highlevel/_bufr/BUFR_Tour.md +377 -0
  11. eccodes-2.45.0/eccodes/highlevel/_bufr/__init__.py +15 -0
  12. eccodes-2.45.0/eccodes/highlevel/_bufr/coder.py +751 -0
  13. eccodes-2.45.0/eccodes/highlevel/_bufr/common.py +288 -0
  14. eccodes-2.45.0/eccodes/highlevel/_bufr/data.py +618 -0
  15. eccodes-2.45.0/eccodes/highlevel/_bufr/header.py +313 -0
  16. eccodes-2.45.0/eccodes/highlevel/_bufr/helpers.py +537 -0
  17. eccodes-2.45.0/eccodes/highlevel/_bufr/message.py +427 -0
  18. eccodes-2.45.0/eccodes/highlevel/_bufr/tables.py +267 -0
  19. eccodes-2.45.0/eccodes/highlevel/_bufr/tree.py +604 -0
  20. eccodes-2.45.0/eccodes/highlevel/_bufr/view.py +118 -0
  21. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes/highlevel/message.py +2 -18
  22. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes/highlevel/reader.py +11 -2
  23. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes.egg-info/PKG-INFO +21 -2
  24. eccodes-2.45.0/eccodes.egg-info/SOURCES.txt +128 -0
  25. {eccodes-2.43.0 → eccodes-2.45.0}/gribapi/__init__.py +2 -2
  26. {eccodes-2.43.0 → eccodes-2.45.0}/gribapi/bindings.py +1 -1
  27. {eccodes-2.43.0 → eccodes-2.45.0}/gribapi/errors.py +5 -0
  28. {eccodes-2.43.0 → eccodes-2.45.0}/gribapi/grib_api.h +4 -0
  29. {eccodes-2.43.0 → eccodes-2.45.0}/gribapi/gribapi.py +22 -0
  30. {eccodes-2.43.0 → eccodes-2.45.0}/setup.py +8 -1
  31. eccodes-2.45.0/tests/examples/__init__.py +0 -0
  32. eccodes-2.45.0/tests/examples/attributes.py +45 -0
  33. eccodes-2.45.0/tests/examples/items.py +49 -0
  34. eccodes-2.45.0/tests/sample-data/acars.attributes +291 -0
  35. eccodes-2.45.0/tests/sample-data/acars.bufr +0 -0
  36. eccodes-2.45.0/tests/sample-data/acars.items +82 -0
  37. eccodes-2.45.0/tests/sample-data/ahi-himawari-8.attributes +181 -0
  38. eccodes-2.45.0/tests/sample-data/ahi-himawari-8.bufr +0 -0
  39. eccodes-2.45.0/tests/sample-data/ahi-himawari-8.items +614 -0
  40. eccodes-2.45.0/tests/sample-data/amsu-a-noaa-19.attributes +241 -0
  41. eccodes-2.45.0/tests/sample-data/amsu-a-noaa-19.bufr +0 -0
  42. eccodes-2.45.0/tests/sample-data/amsu-a-noaa-19.items +459 -0
  43. eccodes-2.45.0/tests/sample-data/amv-goes-9.attributes +331 -0
  44. eccodes-2.45.0/tests/sample-data/amv-goes-9.bufr +0 -0
  45. eccodes-2.45.0/tests/sample-data/amv-goes-9.items +763 -0
  46. eccodes-2.45.0/tests/sample-data/amv-insat-3d.attributes +241 -0
  47. eccodes-2.45.0/tests/sample-data/amv-insat-3d.bufr +0 -0
  48. eccodes-2.45.0/tests/sample-data/amv-insat-3d.items +906 -0
  49. eccodes-2.45.0/tests/sample-data/amv-meteosat-9.attributes +331 -0
  50. eccodes-2.45.0/tests/sample-data/amv-meteosat-9.bufr +0 -0
  51. eccodes-2.45.0/tests/sample-data/amv-meteosat-9.items +1232 -0
  52. eccodes-2.45.0/tests/sample-data/amv-noaa-20.attributes +326 -0
  53. eccodes-2.45.0/tests/sample-data/amv-noaa-20.bufr +0 -0
  54. eccodes-2.45.0/tests/sample-data/amv-noaa-20.items +1008 -0
  55. eccodes-2.45.0/tests/sample-data/aura-omi-ak.attributes +191 -0
  56. eccodes-2.45.0/tests/sample-data/aura-omi-ak.bufr +0 -0
  57. eccodes-2.45.0/tests/sample-data/aura-omi-ak.items +231 -0
  58. eccodes-2.45.0/tests/sample-data/buoy-drifting.attributes +427 -0
  59. eccodes-2.45.0/tests/sample-data/buoy-drifting.bufr +0 -0
  60. eccodes-2.45.0/tests/sample-data/buoy-drifting.items +158 -0
  61. eccodes-2.45.0/tests/sample-data/geos-abi-goes-16.attributes +296 -0
  62. eccodes-2.45.0/tests/sample-data/geos-abi-goes-16.bufr +0 -0
  63. eccodes-2.45.0/tests/sample-data/geos-abi-goes-16.items +520 -0
  64. eccodes-2.45.0/tests/sample-data/geos-mviri-meteosat-7.attributes +236 -0
  65. eccodes-2.45.0/tests/sample-data/geos-mviri-meteosat-7.bufr +0 -0
  66. eccodes-2.45.0/tests/sample-data/geos-mviri-meteosat-7.items +205 -0
  67. eccodes-2.45.0/tests/sample-data/hdob.attributes +416 -0
  68. eccodes-2.45.0/tests/sample-data/hdob.bufr +0 -0
  69. eccodes-2.45.0/tests/sample-data/hdob.items +150 -0
  70. eccodes-2.45.0/tests/sample-data/rwp.attributes +151 -0
  71. eccodes-2.45.0/tests/sample-data/rwp.bufr +0 -0
  72. eccodes-2.45.0/tests/sample-data/rwp.items +90 -0
  73. eccodes-2.45.0/tests/sample-data/rwp_jma.attributes +96 -0
  74. eccodes-2.45.0/tests/sample-data/rwp_jma.bufr +0 -0
  75. eccodes-2.45.0/tests/sample-data/rwp_jma.items +233 -0
  76. eccodes-2.45.0/tests/sample-data/saral-altika.attributes +371 -0
  77. eccodes-2.45.0/tests/sample-data/saral-altika.bufr +0 -0
  78. eccodes-2.45.0/tests/sample-data/saral-altika.items +905 -0
  79. eccodes-2.45.0/tests/sample-data/sral_sentinel_3a.attributes +536 -0
  80. eccodes-2.45.0/tests/sample-data/sral_sentinel_3a.bufr +0 -0
  81. eccodes-2.45.0/tests/sample-data/sral_sentinel_3a.items +929 -0
  82. eccodes-2.45.0/tests/sample-data/synop.attributes +963 -0
  83. eccodes-2.45.0/tests/sample-data/synop.bufr +0 -0
  84. eccodes-2.45.0/tests/sample-data/synop.items +348 -0
  85. eccodes-2.45.0/tests/sample-data/synop_multi_subset.attributes +266 -0
  86. eccodes-2.45.0/tests/sample-data/synop_multi_subset.bufr +0 -0
  87. eccodes-2.45.0/tests/sample-data/synop_multi_subset.items +671 -0
  88. eccodes-2.45.0/tests/sample-data/temp.attributes +743 -0
  89. eccodes-2.45.0/tests/sample-data/temp.bufr +0 -0
  90. eccodes-2.45.0/tests/sample-data/temp.items +361 -0
  91. eccodes-2.45.0/tests/sample-data/terra-modis-aerosol.attributes +156 -0
  92. eccodes-2.45.0/tests/sample-data/terra-modis-aerosol.bufr +0 -0
  93. eccodes-2.45.0/tests/sample-data/terra-modis-aerosol.items +161 -0
  94. eccodes-2.45.0/tests/sample-data/wave.attributes +201 -0
  95. eccodes-2.45.0/tests/sample-data/wave.bufr +0 -0
  96. eccodes-2.45.0/tests/sample-data/wave.items +128 -0
  97. eccodes-2.45.0/tests/test_bufr_data.py +334 -0
  98. eccodes-2.45.0/tests/test_bufr_examples.py +85 -0
  99. eccodes-2.45.0/tests/test_bufr_header.py +104 -0
  100. eccodes-2.45.0/tests/test_bufr_helpers.py +232 -0
  101. eccodes-2.45.0/tests/test_bufr_message.py +455 -0
  102. eccodes-2.45.0/tests/test_bufr_workarounds.py +117 -0
  103. {eccodes-2.43.0 → eccodes-2.45.0}/tests/test_eccodes.py +62 -6
  104. {eccodes-2.43.0 → eccodes-2.45.0}/tests/test_highlevel.py +64 -0
  105. {eccodes-2.43.0 → eccodes-2.45.0}/tox.ini +1 -0
  106. eccodes-2.43.0/eccodes/highlevel/__init__.py +0 -2
  107. eccodes-2.43.0/eccodes.egg-info/SOURCES.txt +0 -44
  108. {eccodes-2.43.0 → eccodes-2.45.0}/.dockerignore +0 -0
  109. {eccodes-2.43.0 → eccodes-2.45.0}/CONTRIBUTING.rst +0 -0
  110. {eccodes-2.43.0 → eccodes-2.45.0}/Dockerfile +0 -0
  111. {eccodes-2.43.0 → eccodes-2.45.0}/LICENSE +0 -0
  112. {eccodes-2.43.0 → eccodes-2.45.0}/Makefile +0 -0
  113. {eccodes-2.43.0 → eccodes-2.45.0}/builder.py +0 -0
  114. {eccodes-2.43.0 → eccodes-2.45.0}/ci/requirements-dev.txt +0 -0
  115. {eccodes-2.43.0 → eccodes-2.45.0}/ci/requirements-docs.in +0 -0
  116. {eccodes-2.43.0 → eccodes-2.45.0}/ci/requirements-tests.in +0 -0
  117. {eccodes-2.43.0 → eccodes-2.45.0}/ci/requirements-tests.txt +0 -0
  118. {eccodes-2.43.0 → eccodes-2.45.0}/docs/_static/.gitkeep +0 -0
  119. {eccodes-2.43.0 → eccodes-2.45.0}/docs/index.rst +0 -0
  120. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes/__init__.py +0 -0
  121. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes/__main__.py +0 -0
  122. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes.egg-info/dependency_links.txt +0 -0
  123. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes.egg-info/requires.txt +0 -0
  124. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes.egg-info/top_level.txt +0 -0
  125. {eccodes-2.43.0 → eccodes-2.45.0}/eccodes.egg-info/zip-safe +0 -0
  126. {eccodes-2.43.0 → eccodes-2.45.0}/gribapi/eccodes.h +0 -0
  127. {eccodes-2.43.0 → eccodes-2.45.0}/setup.cfg +0 -0
  128. {eccodes-2.43.0 → eccodes-2.45.0}/tests/sample-data/era5-levels-members.grib +0 -0
  129. {eccodes-2.43.0 → eccodes-2.45.0}/tests/sample-data/tiggelam_cnmc_sfc.grib2 +0 -0
  130. {eccodes-2.43.0 → eccodes-2.45.0}/tests/test_20_main.py +0 -0
  131. {eccodes-2.43.0 → eccodes-2.45.0}/tests/test_20_messages.py +0 -0
@@ -2,6 +2,22 @@
2
2
  Changelog for eccodes-python
3
3
  ============================
4
4
 
5
+ 2.45.0 (2025-01-16)
6
+ --------------------
7
+
8
+ - GitHub issue 372: Option "--no-binary" does not work
9
+ See: `#372 <https://github.com/ecmwf/eccodes/issues/372>`_.
10
+ - GitHub issue 145: test run fails
11
+ See: `#145 <https://github.com/ecmwf/eccodes/issues/145>`_.
12
+
13
+ 2.44.0 (2025-10-03)
14
+ --------------------
15
+
16
+ - ECC-2102: Improved high-level interface for BUFR
17
+ - ECC-2108: Implement a way of switching off error output to stderr
18
+ - GitHub pull request #129: Update MemoryReader to read more than one field
19
+
20
+
5
21
  2.43.0 (2025-07-24)
6
22
  --------------------
7
23
 
@@ -1,5 +1,6 @@
1
1
  include .dockerignore
2
2
  include *.rst
3
+ recursive-include eccodes *.md
3
4
  include *.yml
4
5
  include Dockerfile
5
6
  include LICENSE
@@ -16,5 +17,8 @@ recursive-include docs *.rst
16
17
  recursive-include gribapi *.h
17
18
  recursive-include tests *.grib2
18
19
  recursive-include tests *.grib
20
+ recursive-include tests *.bufr
21
+ recursive-include tests *.items
22
+ recursive-include tests *.attributes
19
23
  recursive-include tests *.ipynb
20
24
  recursive-include tests *.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eccodes
3
- Version: 2.43.0
3
+ Version: 2.45.0
4
4
  Summary: Python interface to the ecCodes GRIB and BUFR decoder/encoder
5
5
  Home-page: https://github.com/ecmwf/eccodes-python
6
6
  Author: European Centre for Medium-Range Weather Forecasts (ECMWF)
@@ -148,7 +148,7 @@ You may run a simple selfcheck command to ensure that your system is set
148
148
  up correctly::
149
149
 
150
150
  $ python -m eccodes selfcheck
151
- Found: ecCodes v2.39.0.
151
+ Found: ecCodes v2.42.0.
152
152
  Your system is ready.
153
153
 
154
154
 
@@ -164,6 +164,9 @@ the following environment variable before importing eccodes::
164
164
  Usage
165
165
  -----
166
166
 
167
+ See examples of how to use the high-level BUFR interface: `BUFR_Tour.md <eccodes/highlevel/_bufr/BUFR_Tour.md>`_
168
+ and `BUFR_Snippets.md <eccodes/highlevel/_bufr/BUFR_Snippets.md>`_.
169
+
167
170
  Refer to the *ecCodes* `documentation pages <https://confluence.ecmwf.int/display/ECC/Documentation>`_
168
171
  for usage.
169
172
 
@@ -239,6 +242,22 @@ does it submit to any jurisdiction.
239
242
  Changelog for eccodes-python
240
243
  ============================
241
244
 
245
+ 2.45.0 (2025-01-16)
246
+ --------------------
247
+
248
+ - GitHub issue 372: Option "--no-binary" does not work
249
+ See: `#372 <https://github.com/ecmwf/eccodes/issues/372>`_.
250
+ - GitHub issue 145: test run fails
251
+ See: `#145 <https://github.com/ecmwf/eccodes/issues/145>`_.
252
+
253
+ 2.44.0 (2025-10-03)
254
+ --------------------
255
+
256
+ - ECC-2102: Improved high-level interface for BUFR
257
+ - ECC-2108: Implement a way of switching off error output to stderr
258
+ - GitHub pull request #129: Update MemoryReader to read more than one field
259
+
260
+
242
261
  2.43.0 (2025-07-24)
243
262
  --------------------
244
263
 
@@ -111,7 +111,7 @@ You may run a simple selfcheck command to ensure that your system is set
111
111
  up correctly::
112
112
 
113
113
  $ python -m eccodes selfcheck
114
- Found: ecCodes v2.39.0.
114
+ Found: ecCodes v2.42.0.
115
115
  Your system is ready.
116
116
 
117
117
 
@@ -127,6 +127,9 @@ the following environment variable before importing eccodes::
127
127
  Usage
128
128
  -----
129
129
 
130
+ See examples of how to use the high-level BUFR interface: `BUFR_Tour.md <eccodes/highlevel/_bufr/BUFR_Tour.md>`_
131
+ and `BUFR_Snippets.md <eccodes/highlevel/_bufr/BUFR_Snippets.md>`_.
132
+
130
133
  Refer to the *ecCodes* `documentation pages <https://confluence.ecmwf.int/display/ECC/Documentation>`_
131
134
  for usage.
132
135
 
@@ -44,5 +44,5 @@ sphinx==1.8.5
44
44
  # via -r requirements-docs.in
45
45
  sphinxcontrib-websupport==1.1.0
46
46
  # via sphinx
47
- urllib3==2.4.0
47
+ urllib3==2.6.3
48
48
  # via requests
@@ -40,7 +40,7 @@ copyright = "2017-, European Centre for Medium-Range Weather Forecasts (ECMWF)."
40
40
  # the built documents.
41
41
  #
42
42
  # The full version, including alpha/beta/rc tags.
43
- release = pkg_resources.get_distribution("eccodes-python").version
43
+ release = pkg_resources.get_distribution("eccodes").version
44
44
  # The short X.Y version.
45
45
  version = ".".join(release.split(".")[:2])
46
46
 
@@ -61,6 +61,7 @@ from gribapi import (
61
61
  )
62
62
  from gribapi import grib_clone as codes_clone
63
63
  from gribapi import grib_context_delete as codes_context_delete
64
+ from gribapi import grib_context_set_logging as codes_context_set_logging
64
65
  from gribapi import grib_copy_namespace as codes_copy_namespace
65
66
  from gribapi import grib_count_in_file as codes_count_in_file
66
67
  from gribapi import grib_find_nearest as codes_grib_find_nearest
@@ -211,6 +212,7 @@ from gribapi.errors import (
211
212
  OutOfRangeError,
212
213
  PrematureEndOfFileError,
213
214
  ReadOnlyError,
215
+ RuntimeError,
214
216
  StringTooSmallError,
215
217
  SwitchNoMatchError,
216
218
  TooManyAttributesError,
@@ -371,6 +373,7 @@ __all__ = [
371
373
  "codes_skip_read_only",
372
374
  "codes_write",
373
375
  "codes_context_delete",
376
+ "codes_context_set_logging",
374
377
  "CodesInternalError",
375
378
  "ConceptNoMatchError",
376
379
  "ConstantFieldError",
@@ -420,6 +423,7 @@ __all__ = [
420
423
  "OutOfRangeError",
421
424
  "PrematureEndOfFileError",
422
425
  "ReadOnlyError",
426
+ "RuntimeError",
423
427
  "StringTooSmallError",
424
428
  "SwitchNoMatchError",
425
429
  "TooManyAttributesError",
@@ -0,0 +1,3 @@
1
+ from ._bufr import BUFRMessage # noqa
2
+ from .message import GRIBMessage, Message # noqa
3
+ from .reader import FileReader, MemoryReader, StreamReader # noqa
@@ -0,0 +1,463 @@
1
+ # BUFR Snippets
2
+
3
+ This document seeks to compile a collection of code snippets demonstrating common
4
+ (but also less common) BUFR-handling tasks, utilising both the low-level and the
5
+ high-level interface.
6
+
7
+ ## Reading from file
8
+
9
+ ### The low-level way
10
+
11
+ Read a single message:
12
+
13
+ ```python
14
+ bufr = codes_bufr_new_from_file(file)
15
+ ```
16
+
17
+ Iterate over all messages in the file:
18
+
19
+ ```python
20
+ while bufr := codes_bufr_new_from_file(file):
21
+ ...
22
+ codes_release(bufr)
23
+ ```
24
+
25
+ ### The high-level way
26
+
27
+ Read a single message:
28
+
29
+ ```python
30
+ bufr = BUFRMessage(file)
31
+ ```
32
+
33
+ Iterate over all messages in the file:
34
+
35
+ ```python
36
+ for bufr in FileReader(path, eccodes.CODES_PRODUCT_BUFR):
37
+ ...
38
+ ```
39
+
40
+ We can also use Python's `with` statement (aka context manager) to ensure
41
+ that `BUFRMessage` resources are properly released after its use:
42
+
43
+ ```python
44
+ for bufr in FileReader(path, eccodes.CODES_PRODUCT_BUFR):
45
+ with bufr:
46
+ ...
47
+ ```
48
+
49
+ ## Accessing items
50
+
51
+ ### The low-level way
52
+
53
+ Get value of a header key:
54
+
55
+ ```python
56
+ value = codes_get(bufr, 'numberOfSubsets')
57
+ ```
58
+
59
+ Get value of a data key:
60
+
61
+ ```python
62
+ codes_set(bufr, 'unpack', 1)
63
+ value = codes_get_array(bufr, 'latitude')
64
+ ```
65
+
66
+ ### The high-level way
67
+
68
+ Get value of a header key:
69
+
70
+ ```python
71
+ value = bufr['numberOfSubsets']
72
+ ```
73
+
74
+ Get value of a data key:
75
+
76
+ ```python
77
+ value = bufr['latitude']
78
+ ```
79
+
80
+ Note that we didn't need to do explicit unpacking in order to access data section keys.
81
+ This was done for us automatically.
82
+ However, had we had a needed for a more precise control over when and where the message
83
+ gets unpacked, we could do that manually by calling the `unpack()` method:
84
+
85
+ ```python
86
+ bufr.unpack()
87
+ # guaranteed to be in the unpacked state henceforth
88
+ ```
89
+
90
+ After the first invocation, subsequent calls to `unpack()` will have no effect.
91
+
92
+ ## Iterating over items
93
+
94
+ ### The low-level way
95
+
96
+ ```python
97
+ codes_set(bufr, 'unpack', 1)
98
+ it = codes_bufr_keys_iterator_new(bufr)
99
+ while codes_bufr_keys_iterator_next(it):
100
+ key = codes_bufr_keys_iterator_get_name(it)
101
+ try:
102
+ value = codes_get(bufr, key)
103
+ except ArrayTooSmallError:
104
+ value = codes_get_array(bufr, key)
105
+ ...
106
+ codes_bufr_keys_iterator_release(it)
107
+ ```
108
+
109
+ ### The high-level way
110
+
111
+ ```python
112
+ for key, value in bufr.items():
113
+ ...
114
+ ```
115
+
116
+ ## Iterating over data items only
117
+
118
+ ### The low-level way
119
+
120
+ ```python
121
+ codes_set(bufr, 'unpack', 1)
122
+ it = codes_bufr_keys_iterator_new(bufr)
123
+ while codes_bufr_keys_iterator_next(it):
124
+ key = codes_bufr_keys_iterator_get_name(it)
125
+ if codes_bufr_key_is_header(bufr, key):
126
+ continue
127
+ try:
128
+ value = codes_get(bufr, key)
129
+ except ArrayTooSmallError:
130
+ value = codes_get_array(bufr, key)
131
+ ...
132
+ codes_bufr_keys_iterator_release(it)
133
+ ```
134
+
135
+ ### The high-level way
136
+
137
+ ```python
138
+ for key, value in bufr.data.items():
139
+ ...
140
+ ```
141
+
142
+ ## Creating a new message from samples
143
+
144
+ ### The low-level way
145
+
146
+ Note that 'numberOfSubsets', 'compressedData' and 'input...ReplicationFactor' must
147
+ be set _before_ the 'unexpandedDescriptor' key!
148
+
149
+ ```python
150
+ bufr = codes_bufr_new_from_samples('BUFR4_local')
151
+ codes_set('masterTablesVersionNumber', 18)
152
+ codes_set('numberOfSubsets', 5)
153
+ codes_set('compressedData', 1)
154
+ codes_set_array('inputShortDelayedDescriptorReplicationFactor', [1, 1, 1, 1, 1, 1])
155
+ codes_set_array('unexpandedDescriptors', [311010])
156
+ ```
157
+
158
+ ### The high-level way
159
+
160
+ The same restrictions apply in the high-level case:
161
+
162
+ ```python
163
+ bufr = BUFRMessage('BUFR4_local')
164
+ bufr['masterTablesVersionNumber'] = 18
165
+ bufr['numberOfSubsets'] = 5
166
+ bufr['compressedData'] = 1
167
+ bufr['inputShortDelayedDescriptorReplicationFactor'] = [1, 1, 1, 1, 1, 1]
168
+ bufr['unexpandedDescriptors'] = [311010]
169
+ ```
170
+
171
+ (In the future version of eccodes-python we are planning to relax some of these restrictions.
172
+ Specifically, the need to set 'input...ReplicationFactor' upfront.)
173
+
174
+ ## Creating a message copy with ECMWF's local section 2
175
+
176
+ ### The low-level way
177
+
178
+ ```python
179
+ old = codes_bufr_new_from_file(foreign_file)
180
+ new = codes_bufr_new_from_samples('BUFR4_local_satellite')
181
+ # Copy header keys (section 1 only)
182
+ it = codes_bufr_keys_iterator_new(old)
183
+ codes_skip_read_only(it)
184
+ while codes_bufr_keys_iterator_next(it):
185
+ key = codes_bufr_keys_iterator_get_name(it)
186
+ if key == 'unexpandedDescriptors':
187
+ break
188
+ value = codes_get(old, key)
189
+ codes_set(new, value)
190
+ # Copy template-related keys
191
+ codes_set(old, 'unpack', 1)
192
+ value = codes_get_array(old, 'dataPresentIndicator')
193
+ codes_set_array(new, 'inputDataPresentIndicator', value)
194
+ value = codes_get_array(old, 'delayedDescriptorReplicationFactor')
195
+ codes_set_array(new, 'inputDelayedDescriptorReplicationFactor', value)
196
+ value = codes_get_array(old, 'unexpandedDescriptors')
197
+ codes_set_array(new, 'unexpandedDescriptors', value)
198
+ # Copy data keys
199
+ codes_copy_data(old, new)
200
+ # Set section 2 header keys
201
+ ...
202
+ value = codes_get_array(old, 'latitude')'
203
+ codes_set(new, 'localLatitude1', min(value))
204
+ codes_set(new, 'localLatitude2', max(value))
205
+ ...
206
+ codes_set(new, 'satelliteID', 123)
207
+ ```
208
+
209
+ ### The high-level way
210
+
211
+ ```python
212
+ old = BUFRMessage(foreign_file)
213
+ new = BUFRMessage('BUFR4_local_satellite')
214
+ old.copy_to(new)
215
+ new['satelliteID'] = 123
216
+ ```
217
+ Note that unlike in the low-level example, we don't need to set every section 2 key
218
+ explicitly. The only exception is the 'satelliteID' (or, in the case of uncompressed
219
+ messages, the 'ident' key).
220
+ All other section 2 keys are set automatically on message packing.
221
+
222
+ ## Iterating over subsets of uncompressed multi-subset messages
223
+
224
+ ### The low-level way
225
+
226
+ ```python
227
+ subset_count = codes_get(bufr, 'numberOfSubsets')
228
+ codes_set(bufr, 'unpack', 1)
229
+ for n in range(1, subset_count + 1):
230
+ print(f'Working on subset {n}')
231
+ block_number = codes_get(bufr, f'/subsetNumber={n}/blockNumber')
232
+ ...
233
+ # BUT THIS IS CURRENTLY NOT ALLOWED!!!
234
+ # pressure1 = codes_get(bufr, f'/subsetNumber={n}/#1#pressure')
235
+ ...
236
+ ```
237
+
238
+ ### The high-level way
239
+
240
+ ```python
241
+ for n, subset in enumerate(bufr.data, start=1):
242
+ print(f'Working on subset {n}')
243
+ block_number = subset['blockNumber']
244
+ ...
245
+ pressure1 = subset['#1#pressure']
246
+ ...
247
+ ```
248
+
249
+ ## Working with uncompressed multi-subset messages with nested replications
250
+
251
+ The following is an example of a wind profiler BUFR message (see tests/sample-data/rwp\_jma.bufr).
252
+ Each subset in this uncompressed BUFR message represents a unique station.
253
+ Within each subset there is one static block at the top which contains some
254
+ common metadata, followed by a doubly-nested delayed replication block with
255
+ the actual profile data.
256
+ The outer replication corresponds to profiles measured at different times, and
257
+ the inner replication contains the individual level data for each of the profiles.
258
+ The tricky part about this message is that the number of profiles and the number
259
+ of levels varies across stations, which means we can't navigate the structure by doing a simple
260
+ rank arithmetic.
261
+ We have to employ more involved calculations in order to navigate the structure correctly.
262
+
263
+ ### The low-level way
264
+
265
+ ```python
266
+ subset_count = codes_get(bufr, 'numberOfSubsets')
267
+ codes_set(bufr, 'unpack', 1)
268
+ for n in range(1, subset_count + 1):
269
+ codes_set(bufr, 'extractSubset', n)
270
+ codes_set(bufr, 'doExtractSubsets', 1)
271
+ sbufr = codes_clone(bufr)
272
+ codes_set(sbufr, 'unpack', 1)
273
+ # Station info
274
+ ...
275
+ latitude = codes_get(sbufr, 'latitude')
276
+ ...
277
+ factors = codes_get_array(sbufr, 'delayedDescriptorReplicationFactor')
278
+ station_counts = factors[0]
279
+ level_counts = factors[1:]
280
+ assert len(level_counts) == station_counts
281
+ level_offsets = [0] + list(itertools.accumulate(level_counts[:-1]))
282
+ us = codes_get_array(sbufr, 'u')
283
+ vs = codes_get_array(sbufr, 'u')
284
+ ...
285
+ for prof, (off, len) in enumerate(zip(level_offsets, level_counts)):
286
+ # Profile info
287
+ ...
288
+ hour = codes_get_array(sbufr, 'hour')[prof]
289
+ ...
290
+ # Profile data
291
+ ...
292
+ u = np.ma.masked_equal(us[off:off+len], CODES_MISSING_DOUBLE)
293
+ v = np.ma.masked_equal(vs[off:off+len], CODES_MISSING_DOUBLE)
294
+ ...
295
+ codes_release(sbufr)
296
+ ```
297
+
298
+ ### The high-level way
299
+
300
+ With the high-level interface, navigating the hierarchical structure is much simpler:
301
+
302
+ ```python
303
+ for subset in bufr.data:
304
+ station_info, station_profiles = subset
305
+ # Station info
306
+ ...
307
+ latitude = station_info['latitude']
308
+ ...
309
+ for (profile_info, profile_data) in station_profiles:
310
+ # Profile info
311
+ ...
312
+ hour = profile_info['hour']
313
+ ...
314
+ # Profile data
315
+ ...
316
+ u = profile_data['u']
317
+ ...
318
+ ```
319
+
320
+ ## Extracting subsets by index
321
+
322
+ ### The low-level way
323
+
324
+ ```python
325
+ codes_set(bufr, 'unpack', 1)
326
+ codes_set(bufr, 'extractSubsetList', [2, 4, 8])
327
+ codes_set(bufr, 'doExtractSubsets', 1)
328
+ new = codes_clone(bufr)
329
+ ```
330
+
331
+ ### The high-level way
332
+
333
+ ```python
334
+ new = bufr.copy(subsets=[1, 3, 7])
335
+ ```
336
+ Note that unlike in the low-level example where we used 1-based subset
337
+ numbers, here we use subset indices which are 0-based!
338
+
339
+ ## Extracting subsets by range / slice
340
+
341
+ ### The low-level way
342
+
343
+ ```python
344
+ codes_set(bufr, 'unpack', 1)
345
+ codes_set(bufr, 'extractSubsetIntervalStart', 2)
346
+ codes_set(bufr, 'extractSubsetIntervalEnd', 8)
347
+ codes_set(bufr, 'doExtractSubsets', 1)
348
+ new = codes_clone(bufr)
349
+ ```
350
+
351
+ ### The high-level way
352
+
353
+ ```python
354
+ new = bufr.copy(subsets=range(1, 9))
355
+ ```
356
+ or
357
+ ```python
358
+ new = bufr.copy(subsets=slice(1, 9))
359
+ ```
360
+ Note that the bounds in the high-level example follow standard Python
361
+ convention, whereas in the low-level example they are 1-based and inclusive!
362
+
363
+ ## Extracting subsets by date & time
364
+
365
+ ### The low-level way
366
+
367
+ ```python
368
+ codes_set(bufr, 'unpack', 1)
369
+ start = datetime(2000, 2, 4, 8, 16)
370
+ end = start + timedelta(seconds=32)
371
+ for suffix, value in zip(('Start', 'End'), (start, end)):
372
+ codes_set(bufr, 'extractDateTimeYear' + suffix, value.year)
373
+ codes_set(bufr, 'extractDateTimeMonth' + suffix, value.month)
374
+ codes_set(bufr, 'extractDateTimeDay' + suffix, value.day)
375
+ codes_set(bufr, 'extractDateTimeHour' + suffix, value.hour)
376
+ codes_set(bufr, 'extractDateTimeMinute' + suffix, value.minute)
377
+ codes_set(bufr, 'extractDateTimeSecond' + suffix, value.second)
378
+ codes_set(bufr, 'doExtractDateTime', 1)
379
+ new = codes_clone(bufr)
380
+ ```
381
+
382
+ ### The high-level way
383
+
384
+ ```python
385
+ start = datetime(2000, 2, 4, 8, 16)
386
+ end = start + timedelta(seconds=32)
387
+ new = bufr.copy(subsets=slice(start, end))
388
+ ```
389
+ Note that the bounds of datetime slices are inclusive on both sides!
390
+
391
+ ## Extracting subsets within a lat-lon area
392
+
393
+ ### The low-level way
394
+
395
+ ```python
396
+ codes_set(bufr, 'unpack', 1)
397
+ codes_set(bufr, 'extractAreaNorthLatitude', 64.0)
398
+ codes_set(bufr, 'extractAreaSouthLatitude', 32.0)
399
+ codes_set(bufr, 'extractAreaEastLongitude', 16.0)
400
+ codes_set(bufr, 'extractAreaWestLongitude', 8.0)
401
+ codes_set(bufr, 'doExtractArea', 1)
402
+ new = codes_clone(bufr)
403
+ ```
404
+
405
+ ### The high-level way
406
+
407
+ Unlike in the previous examples, there is no built-in option to extract subsets by area.
408
+ However, we can pass an arbitrary boolean mask argument to the `copy()`
409
+ method, which provides unlimited flexibility:
410
+
411
+ ```python
412
+ lat = bufr['latitude']
413
+ lon = bufr['longitude']
414
+ lat_mask = np.logical_and(lat <= 64.0, lat >= 32.0)
415
+ lon_mask = np.logical_and(lon <= 16.0, lon >= 8.0)
416
+ mask = np.logical_and(lat_mask, lon_mask)
417
+ new = bufr.copy(subsets=mask)
418
+ ```
419
+
420
+ ## Thinning subsets
421
+
422
+ ### The low-level way
423
+
424
+ ```python
425
+ codes_set(bufr, 'unpack', 1)
426
+ codes_set(bufr, 'simpleThinningSkip', 4)
427
+ codes_set(bufr, 'doSimpleThinning', 1)
428
+ new = codes_clone(bufr)
429
+ ```
430
+ This will extract subset numbers 1, 6, 11, etc.
431
+
432
+ ### The high-level way
433
+
434
+ Although the high-level interface doesn't provide bespoke option for
435
+ the thinning, we can achieve the same by using slices:
436
+
437
+ ```python
438
+ new = bufr.copy(subsets=slice(None, None, 5))
439
+ ```
440
+ Note that the slice step must be 1 larger than the thinning step to get the
441
+ same result.
442
+
443
+ ## Writing to file
444
+
445
+ ### The low-level way
446
+
447
+ ```python
448
+ file = open('output.bufr', 'wb')
449
+ codes_set(bufr, 'pack', 1)
450
+ codes_write(bufr, file)
451
+ ```
452
+
453
+ ### The high-level way
454
+
455
+ ```python
456
+ file = open('output.bufr', 'wb')
457
+ bufr.write_to(file)
458
+ ```
459
+ Notice that in contrast to the low-level example, we didn't need to
460
+ pack the message explicitly before writing.
461
+ This was done for us automatically.
462
+ But, if needed, packing can also be triggered manually at any time by calling
463
+ the `pack()` method.