eccodes 2.44.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 (130) hide show
  1. {eccodes-2.44.0 → eccodes-2.45.0}/CHANGELOG.rst +8 -0
  2. {eccodes-2.44.0 → eccodes-2.45.0}/MANIFEST.in +4 -0
  3. {eccodes-2.44.0 → eccodes-2.45.0}/PKG-INFO +9 -1
  4. {eccodes-2.44.0 → eccodes-2.45.0}/ci/requirements-docs.txt +1 -1
  5. {eccodes-2.44.0 → eccodes-2.45.0}/docs/conf.py +1 -1
  6. eccodes-2.45.0/eccodes/highlevel/_bufr/BUFR_Snippets.md +463 -0
  7. eccodes-2.45.0/eccodes/highlevel/_bufr/BUFR_Tour.md +377 -0
  8. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes.egg-info/PKG-INFO +9 -1
  9. eccodes-2.45.0/eccodes.egg-info/SOURCES.txt +128 -0
  10. {eccodes-2.44.0 → eccodes-2.45.0}/gribapi/bindings.py +1 -1
  11. {eccodes-2.44.0 → eccodes-2.45.0}/setup.py +8 -1
  12. eccodes-2.45.0/tests/sample-data/acars.attributes +291 -0
  13. eccodes-2.45.0/tests/sample-data/acars.bufr +0 -0
  14. eccodes-2.45.0/tests/sample-data/acars.items +82 -0
  15. eccodes-2.45.0/tests/sample-data/ahi-himawari-8.attributes +181 -0
  16. eccodes-2.45.0/tests/sample-data/ahi-himawari-8.bufr +0 -0
  17. eccodes-2.45.0/tests/sample-data/ahi-himawari-8.items +614 -0
  18. eccodes-2.45.0/tests/sample-data/amsu-a-noaa-19.attributes +241 -0
  19. eccodes-2.45.0/tests/sample-data/amsu-a-noaa-19.bufr +0 -0
  20. eccodes-2.45.0/tests/sample-data/amsu-a-noaa-19.items +459 -0
  21. eccodes-2.45.0/tests/sample-data/amv-goes-9.attributes +331 -0
  22. eccodes-2.45.0/tests/sample-data/amv-goes-9.bufr +0 -0
  23. eccodes-2.45.0/tests/sample-data/amv-goes-9.items +763 -0
  24. eccodes-2.45.0/tests/sample-data/amv-insat-3d.attributes +241 -0
  25. eccodes-2.45.0/tests/sample-data/amv-insat-3d.bufr +0 -0
  26. eccodes-2.45.0/tests/sample-data/amv-insat-3d.items +906 -0
  27. eccodes-2.45.0/tests/sample-data/amv-meteosat-9.attributes +331 -0
  28. eccodes-2.45.0/tests/sample-data/amv-meteosat-9.bufr +0 -0
  29. eccodes-2.45.0/tests/sample-data/amv-meteosat-9.items +1232 -0
  30. eccodes-2.45.0/tests/sample-data/amv-noaa-20.attributes +326 -0
  31. eccodes-2.45.0/tests/sample-data/amv-noaa-20.bufr +0 -0
  32. eccodes-2.45.0/tests/sample-data/amv-noaa-20.items +1008 -0
  33. eccodes-2.45.0/tests/sample-data/aura-omi-ak.attributes +191 -0
  34. eccodes-2.45.0/tests/sample-data/aura-omi-ak.bufr +0 -0
  35. eccodes-2.45.0/tests/sample-data/aura-omi-ak.items +231 -0
  36. eccodes-2.45.0/tests/sample-data/buoy-drifting.attributes +427 -0
  37. eccodes-2.45.0/tests/sample-data/buoy-drifting.bufr +0 -0
  38. eccodes-2.45.0/tests/sample-data/buoy-drifting.items +158 -0
  39. eccodes-2.45.0/tests/sample-data/geos-abi-goes-16.attributes +296 -0
  40. eccodes-2.45.0/tests/sample-data/geos-abi-goes-16.bufr +0 -0
  41. eccodes-2.45.0/tests/sample-data/geos-abi-goes-16.items +520 -0
  42. eccodes-2.45.0/tests/sample-data/geos-mviri-meteosat-7.attributes +236 -0
  43. eccodes-2.45.0/tests/sample-data/geos-mviri-meteosat-7.bufr +0 -0
  44. eccodes-2.45.0/tests/sample-data/geos-mviri-meteosat-7.items +205 -0
  45. eccodes-2.45.0/tests/sample-data/hdob.attributes +416 -0
  46. eccodes-2.45.0/tests/sample-data/hdob.bufr +0 -0
  47. eccodes-2.45.0/tests/sample-data/hdob.items +150 -0
  48. eccodes-2.45.0/tests/sample-data/rwp.attributes +151 -0
  49. eccodes-2.45.0/tests/sample-data/rwp.bufr +0 -0
  50. eccodes-2.45.0/tests/sample-data/rwp.items +90 -0
  51. eccodes-2.45.0/tests/sample-data/rwp_jma.attributes +96 -0
  52. eccodes-2.45.0/tests/sample-data/rwp_jma.bufr +0 -0
  53. eccodes-2.45.0/tests/sample-data/rwp_jma.items +233 -0
  54. eccodes-2.45.0/tests/sample-data/saral-altika.attributes +371 -0
  55. eccodes-2.45.0/tests/sample-data/saral-altika.bufr +0 -0
  56. eccodes-2.45.0/tests/sample-data/saral-altika.items +905 -0
  57. eccodes-2.45.0/tests/sample-data/sral_sentinel_3a.attributes +536 -0
  58. eccodes-2.45.0/tests/sample-data/sral_sentinel_3a.bufr +0 -0
  59. eccodes-2.45.0/tests/sample-data/sral_sentinel_3a.items +929 -0
  60. eccodes-2.45.0/tests/sample-data/synop.attributes +963 -0
  61. eccodes-2.45.0/tests/sample-data/synop.bufr +0 -0
  62. eccodes-2.45.0/tests/sample-data/synop.items +348 -0
  63. eccodes-2.45.0/tests/sample-data/synop_multi_subset.attributes +266 -0
  64. eccodes-2.45.0/tests/sample-data/synop_multi_subset.bufr +0 -0
  65. eccodes-2.45.0/tests/sample-data/synop_multi_subset.items +671 -0
  66. eccodes-2.45.0/tests/sample-data/temp.attributes +743 -0
  67. eccodes-2.45.0/tests/sample-data/temp.bufr +0 -0
  68. eccodes-2.45.0/tests/sample-data/temp.items +361 -0
  69. eccodes-2.45.0/tests/sample-data/terra-modis-aerosol.attributes +156 -0
  70. eccodes-2.45.0/tests/sample-data/terra-modis-aerosol.bufr +0 -0
  71. eccodes-2.45.0/tests/sample-data/terra-modis-aerosol.items +161 -0
  72. eccodes-2.45.0/tests/sample-data/wave.attributes +201 -0
  73. eccodes-2.45.0/tests/sample-data/wave.bufr +0 -0
  74. eccodes-2.45.0/tests/sample-data/wave.items +128 -0
  75. eccodes-2.44.0/eccodes.egg-info/SOURCES.txt +0 -63
  76. {eccodes-2.44.0 → eccodes-2.45.0}/.dockerignore +0 -0
  77. {eccodes-2.44.0 → eccodes-2.45.0}/CONTRIBUTING.rst +0 -0
  78. {eccodes-2.44.0 → eccodes-2.45.0}/Dockerfile +0 -0
  79. {eccodes-2.44.0 → eccodes-2.45.0}/LICENSE +0 -0
  80. {eccodes-2.44.0 → eccodes-2.45.0}/Makefile +0 -0
  81. {eccodes-2.44.0 → eccodes-2.45.0}/README.rst +0 -0
  82. {eccodes-2.44.0 → eccodes-2.45.0}/builder.py +0 -0
  83. {eccodes-2.44.0 → eccodes-2.45.0}/ci/requirements-dev.txt +0 -0
  84. {eccodes-2.44.0 → eccodes-2.45.0}/ci/requirements-docs.in +0 -0
  85. {eccodes-2.44.0 → eccodes-2.45.0}/ci/requirements-tests.in +0 -0
  86. {eccodes-2.44.0 → eccodes-2.45.0}/ci/requirements-tests.txt +0 -0
  87. {eccodes-2.44.0 → eccodes-2.45.0}/docs/_static/.gitkeep +0 -0
  88. {eccodes-2.44.0 → eccodes-2.45.0}/docs/index.rst +0 -0
  89. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/__init__.py +0 -0
  90. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/__main__.py +0 -0
  91. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/eccodes.py +0 -0
  92. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/__init__.py +0 -0
  93. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/__init__.py +0 -0
  94. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/coder.py +0 -0
  95. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/common.py +0 -0
  96. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/data.py +0 -0
  97. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/header.py +0 -0
  98. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/helpers.py +0 -0
  99. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/message.py +0 -0
  100. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/tables.py +0 -0
  101. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/tree.py +0 -0
  102. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/_bufr/view.py +0 -0
  103. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/message.py +0 -0
  104. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes/highlevel/reader.py +0 -0
  105. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes.egg-info/dependency_links.txt +0 -0
  106. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes.egg-info/requires.txt +0 -0
  107. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes.egg-info/top_level.txt +0 -0
  108. {eccodes-2.44.0 → eccodes-2.45.0}/eccodes.egg-info/zip-safe +0 -0
  109. {eccodes-2.44.0 → eccodes-2.45.0}/gribapi/__init__.py +0 -0
  110. {eccodes-2.44.0 → eccodes-2.45.0}/gribapi/eccodes.h +0 -0
  111. {eccodes-2.44.0 → eccodes-2.45.0}/gribapi/errors.py +0 -0
  112. {eccodes-2.44.0 → eccodes-2.45.0}/gribapi/grib_api.h +0 -0
  113. {eccodes-2.44.0 → eccodes-2.45.0}/gribapi/gribapi.py +0 -0
  114. {eccodes-2.44.0 → eccodes-2.45.0}/setup.cfg +0 -0
  115. {eccodes-2.44.0 → eccodes-2.45.0}/tests/examples/__init__.py +0 -0
  116. {eccodes-2.44.0 → eccodes-2.45.0}/tests/examples/attributes.py +0 -0
  117. {eccodes-2.44.0 → eccodes-2.45.0}/tests/examples/items.py +0 -0
  118. {eccodes-2.44.0 → eccodes-2.45.0}/tests/sample-data/era5-levels-members.grib +0 -0
  119. {eccodes-2.44.0 → eccodes-2.45.0}/tests/sample-data/tiggelam_cnmc_sfc.grib2 +0 -0
  120. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_20_main.py +0 -0
  121. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_20_messages.py +0 -0
  122. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_bufr_data.py +0 -0
  123. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_bufr_examples.py +0 -0
  124. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_bufr_header.py +0 -0
  125. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_bufr_helpers.py +0 -0
  126. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_bufr_message.py +0 -0
  127. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_bufr_workarounds.py +0 -0
  128. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_eccodes.py +0 -0
  129. {eccodes-2.44.0 → eccodes-2.45.0}/tests/test_highlevel.py +0 -0
  130. {eccodes-2.44.0 → eccodes-2.45.0}/tox.ini +0 -0
@@ -2,6 +2,14 @@
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
+
5
13
  2.44.0 (2025-10-03)
6
14
  --------------------
7
15
 
@@ -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.44.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)
@@ -242,6 +242,14 @@ does it submit to any jurisdiction.
242
242
  Changelog for eccodes-python
243
243
  ============================
244
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
+
245
253
  2.44.0 (2025-10-03)
246
254
  --------------------
247
255
 
@@ -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
 
@@ -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.