forked from ecmwf/eccodes-python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gribapi.py
2565 lines (2008 loc) · 76.7 KB
/
gribapi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
# (C) Copyright 2017- ECMWF.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
#
# In applying this licence, ECMWF does not waive the privileges and immunities
# granted to it by virtue of its status as an intergovernmental organisation nor
# does it submit to any jurisdiction.
#
"""
@package gribapi
@brief This package is the \b Python 3 interface to ecCodes.
It offers almost one to one bindings to the C API functions.
The Python 3 interface to ecCodes uses the <a href="http://numpy.scipy.org/"><b>NumPy</b></a> package
as the container of choice for the possible arrays of values that can be encoded/decoded in and from a message.
Numpy is a package used for scientific computing in Python and an efficient container for generic data.
@em Requirements:
- Python 3.7 or higher
- NumPy
"""
import os
import sys
from functools import wraps
import numpy as np
from gribapi.errors import GribInternalError
from . import errors
from .bindings import ENC
from .bindings import __version__ as bindings_version # noqa
from .bindings import ffi, lib, library_path
try:
type(file)
except NameError:
import io
file = io.IOBase
long = int
KEYTYPES = {
1: int,
2: float,
3: str,
}
CODES_PRODUCT_ANY = 0
""" Generic product kind """
CODES_PRODUCT_GRIB = 1
""" GRIB product kind """
CODES_PRODUCT_BUFR = 2
""" BUFR product kind """
CODES_PRODUCT_METAR = 3
""" METAR product kind """
CODES_PRODUCT_GTS = 4
""" GTS product kind """
CODES_PRODUCT_TAF = 5
""" TAF product kind """
# Constants for 'missing'
GRIB_MISSING_DOUBLE = -1e100
GRIB_MISSING_LONG = 2147483647
# Constants for GRIB nearest neighbour
GRIB_NEAREST_SAME_GRID = 1 << 0
GRIB_NEAREST_SAME_DATA = 1 << 1
GRIB_NEAREST_SAME_POINT = 1 << 2
# ECC-1029: Disable function-arguments type-checking unless
# environment variable is defined and equal to 1
enable_type_checks = os.environ.get("ECCODES_PYTHON_ENABLE_TYPE_CHECKS") == "1"
# Function-arguments type-checking decorator
# inspired from http://code.activestate.com/recipes/454322-type-checking-decorator/
# modified to support multiple allowed types and all types in the same decorator call
# This returns a decorator. _params_ is the dict with the type specs
def require(**_params_):
"""
The actual decorator. Receives the target function in _func_
"""
def check_types(_func_, _params_=_params_):
if not enable_type_checks:
return _func_
@wraps(_func_)
# The wrapper function. Replaces the target function and receives its args
def modified(*args, **kw):
arg_names = _func_.__code__.co_varnames
# argnames, varargs, kwargs, defaults = inspect.getargspec(_func_)
kw.update(zip(arg_names, args))
for name, allowed_types in _params_.items():
param = kw[name]
if isinstance(allowed_types, type):
allowed_types = (allowed_types,)
assert any(
[isinstance(param, type1) for type1 in allowed_types]
), "Parameter '%s' should be of type %s (instead of %s)" % (
name,
" or ".join([t.__name__ for t in allowed_types]),
type(param).__name__,
)
return _func_(**kw)
return modified
return check_types
# @cond
class Bunch(dict):
"""
The collector of a bunch of named stuff :).
"""
def __init__(self, **kw):
dict.__init__(self, kw)
self.__dict__.update(kw)
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.__dict__[key] = value
def __setattr__(self, key, value):
dict.__setitem__(self, key, value)
self.__dict__[key] = value
def __delitem__(self, key):
dict.__delitem__(self, key)
del self.__dict__[key]
def __delattr__(self, key):
dict.__delitem__(self, key)
del self.__dict__[key]
def __str__(self):
state = [
"%s=%r" % (attribute, value) for (attribute, value) in self.__dict__.items()
]
return "\n".join(state)
# @endcond
def err_last(func):
@wraps(func)
def wrapper(*args):
err = ffi.new("int *")
args += (err,)
retval = func(*args)
return err[0], retval
return wrapper
def get_handle(msgid):
h = ffi.cast("grib_handle*", msgid)
if h == ffi.NULL:
raise errors.InvalidGribError(f"get_handle: Bad message ID {msgid}")
return h
def put_handle(handle):
if handle == ffi.NULL:
raise errors.InvalidGribError(f"put_handle: Bad message ID {handle}")
return int(ffi.cast("size_t", handle))
def get_multi_handle(msgid):
return ffi.cast("grib_multi_handle*", msgid)
def put_multi_handle(handle):
return int(ffi.cast("size_t", handle))
def get_index(indexid):
return ffi.cast("grib_index*", indexid)
def put_index(indexh):
return int(ffi.cast("size_t", indexh))
def get_iterator(iterid):
return ffi.cast("grib_iterator*", iterid)
def put_iterator(iterh):
return int(ffi.cast("size_t", iterh))
def get_grib_keys_iterator(iterid):
return ffi.cast("grib_keys_iterator*", iterid)
def put_grib_keys_iterator(iterh):
return int(ffi.cast("size_t", iterh))
def get_bufr_keys_iterator(iterid):
return ffi.cast("bufr_keys_iterator*", iterid)
def put_bufr_keys_iterator(iterh):
return int(ffi.cast("size_t", iterh))
# @cond
@require(errid=int)
def GRIB_CHECK(errid):
"""
Utility function checking the ecCodes error code and raising
an error if that was set.
@param errid the C interface error id to check
@exception CodesInternalError
"""
if errid:
errors.raise_grib_error(errid)
# @endcond
@require(fileobj=file)
def gts_new_from_file(fileobj, headers_only=False):
"""
@brief Load in memory a GTS message from a file.
The message can be accessed through its id and will be available\n
until @ref codes_release is called.\n
@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the GTS loaded in memory or None
@exception CodesInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(
ffi.NULL, fileobj, CODES_PRODUCT_GTS
)
if err:
if err == lib.GRIB_END_OF_FILE:
return None
else:
GRIB_CHECK(err)
return None
if h == ffi.NULL:
return None
else:
return put_handle(h)
@require(fileobj=file)
def metar_new_from_file(fileobj, headers_only=False):
"""
@brief Load in memory a METAR message from a file.
The message can be accessed through its id and will be available\n
until @ref codes_release is called.\n
@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the METAR loaded in memory or None
@exception CodesInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(
ffi.NULL, fileobj, CODES_PRODUCT_METAR
)
if err:
if err == lib.GRIB_END_OF_FILE:
return None
else:
GRIB_CHECK(err)
return None
if h == ffi.NULL:
return None
else:
return put_handle(h)
@require(fileobj=file, product_kind=int)
def codes_new_from_file(fileobj, product_kind, headers_only=False):
"""
@brief Load in memory a message from a file for a given product.
The message can be accessed through its id and will be available\n
until @ref codes_release is called.\n
\b Examples: \ref get_product_kind.py "get_product_kind.py"
@param fileobj python file object
@param product_kind one of CODES_PRODUCT_GRIB, CODES_PRODUCT_BUFR, CODES_PRODUCT_METAR or CODES_PRODUCT_GTS
@param headers_only whether or not to load the message with the headers only
@return id of the message loaded in memory or None
@exception CodesInternalError
"""
if product_kind == CODES_PRODUCT_GRIB:
return grib_new_from_file(fileobj, headers_only)
if product_kind == CODES_PRODUCT_BUFR:
return bufr_new_from_file(fileobj, headers_only)
if product_kind == CODES_PRODUCT_METAR:
return metar_new_from_file(fileobj, headers_only)
if product_kind == CODES_PRODUCT_GTS:
return gts_new_from_file(fileobj, headers_only)
if product_kind == CODES_PRODUCT_ANY:
return any_new_from_file(fileobj, headers_only)
raise ValueError("Invalid product kind %d" % product_kind)
@require(fileobj=file)
def any_new_from_file(fileobj, headers_only=False):
"""
@brief Load in memory a message from a file.
The message can be accessed through its id and will be available\n
until @ref codes_release is called.\n
\b Examples: \ref grib_get_keys.py "grib_get_keys.py"
@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the message loaded in memory or None
@exception CodesInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(
ffi.NULL, fileobj, CODES_PRODUCT_ANY
)
if err:
if err == lib.GRIB_END_OF_FILE:
return None
else:
GRIB_CHECK(err)
return None
if h == ffi.NULL:
return None
else:
return put_handle(h)
@require(fileobj=file)
def bufr_new_from_file(fileobj, headers_only=False):
"""
@brief Load in memory a BUFR message from a file.
The message can be accessed through its id and will be available\n
until @ref codes_release is called.\n
\b Examples: \ref bufr_get_keys.py "bufr_get_keys.py"
@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the BUFR loaded in memory or None
@exception CodesInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(
ffi.NULL, fileobj, CODES_PRODUCT_BUFR
)
if err:
if err == lib.GRIB_END_OF_FILE:
return None
else:
GRIB_CHECK(err)
return None
if h == ffi.NULL:
return None
else:
return put_handle(h)
@require(fileobj=file)
def grib_new_from_file(fileobj, headers_only=False):
"""
@brief Load in memory a GRIB message from a file.
The message can be accessed through its gribid and will be available\n
until @ref codes_release is called.\n
The message can be loaded headers only by using the headers_only argument.
Default is to have the headers only option set to off (False). If set to on (True),
data values will be skipped. This will result in a significant performance gain
if one is only interested in browsing through messages to retrieve metadata.
Any attempt to retrieve data values keys when in the headers only mode will
result in a key not found error.
\b Examples: \ref grib_get_keys.py "grib_get_keys.py"
@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the grib loaded in memory or None
@exception CodesInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(
ffi.NULL, fileobj, CODES_PRODUCT_GRIB
)
if err:
if err == lib.GRIB_END_OF_FILE:
return None
else:
GRIB_CHECK(err)
return None
if h == ffi.NULL:
return None
else:
return put_handle(h)
@require(fileobj=file)
def grib_count_in_file(fileobj):
"""
@brief Count the messages in a file.
\b Examples: \ref count_messages.py "count_messages.py"
@param fileobj python file object
@return number of messages in the file
@exception CodesInternalError
"""
num_p = ffi.new("int*")
err = lib.grib_count_in_file(ffi.NULL, fileobj, num_p)
GRIB_CHECK(err)
return num_p[0]
def grib_multi_support_on():
"""
@brief Turn on the support for multiple fields in a single GRIB message.
@exception CodesInternalError
"""
lib.grib_multi_support_on(ffi.NULL)
def grib_multi_support_off():
"""
@brief Turn off the support for multiple fields in a single GRIB message.
@exception CodesInternalError
"""
lib.grib_multi_support_off(ffi.NULL)
@require(fileobj=file)
def grib_multi_support_reset_file(fileobj):
"""
@brief Reset file handle in multiple field support mode
"""
context = lib.grib_context_get_default()
lib.grib_multi_support_reset_file(context, fileobj)
@require(msgid=int)
def grib_release(msgid):
"""
@brief Free the memory for the message referred to by msgid.
\b Examples: \ref grib_get_keys.py "grib_get_keys.py"
@param msgid id of the message loaded in memory
@exception CodesInternalError
"""
h = get_handle(msgid)
GRIB_CHECK(lib.grib_handle_delete(h))
@require(msgid=int, key=str)
def grib_get_string(msgid, key):
"""
@brief Get the string value of a key from a message.
@param msgid id of the message loaded in memory
@param key key name
@return string value of key
@exception CodesInternalError
"""
length = grib_get_string_length(msgid, key)
h = get_handle(msgid)
values = ffi.new("char[]", length)
length_p = ffi.new("size_t *", length)
err = lib.grib_get_string(h, key.encode(ENC), values, length_p)
GRIB_CHECK(err)
return _decode_bytes(values, length_p[0])
@require(msgid=int, key=str, value=str)
def grib_set_string(msgid, key, value):
"""
@brief Set the value for a string key in a message.
@param msgid id of the message loaded in memory
@param key key name
@param value string value
@exception CodesInternalError
"""
h = get_handle(msgid)
bvalue = value.encode(ENC)
length_p = ffi.new("size_t *", len(bvalue))
GRIB_CHECK(lib.grib_set_string(h, key.encode(ENC), bvalue, length_p))
def grib_gribex_mode_on():
"""
@brief Turn on the compatibility mode with GRIBEX.
@exception CodesInternalError
"""
lib.grib_gribex_mode_on(ffi.NULL)
def grib_gribex_mode_off():
"""
@brief Turn off the compatibility mode with GRIBEX.
@exception CodesInternalError
"""
lib.grib_gribex_mode_off(ffi.NULL)
@require(msgid=int, fileobj=file)
def grib_write(msgid, fileobj):
"""
@brief Write a message to a file.
\b Examples: \ref grib_set_keys.py "grib_set_keys.py"
@param msgid id of the message loaded in memory
@param fileobj python file object
@exception CodesInternalError
"""
msg_bytes = grib_get_message(msgid)
fileobj.write(msg_bytes)
fileobj.flush()
@require(multigribid=int, fileobj=file)
def grib_multi_write(multigribid, fileobj):
"""
@brief Write a multi-field GRIB message to a file.
\b Examples: \ref grib_multi_write.py "grib_multi_write.py"
@param multigribid id of the multi-field grib loaded in memory
@param fileobj python file object
@exception CodesInternalError
"""
mh = get_multi_handle(multigribid)
GRIB_CHECK(lib.grib_multi_handle_write(mh, fileobj))
@require(ingribid=int, startsection=int, multigribid=int)
def grib_multi_append(ingribid, startsection, multigribid):
"""
@brief Append a single-field GRIB message to a multi-field GRIB message.
Only the sections with section number greather or equal "startsection"
are copied from the input single message to the multi-field output grib.
\b Examples: \ref grib_multi_write.py "grib_multi_write.py"
@param ingribid id of the input single-field GRIB
@param startsection starting from startsection (included) all the sections are copied
from the input single grib to the output multi-field grib
@param multigribid id of the output multi-field GRIB
@exception CodesInternalError
"""
h = get_handle(ingribid)
mh = get_multi_handle(multigribid)
GRIB_CHECK(lib.grib_multi_handle_append(h, startsection, mh))
@require(msgid=int, key=str)
def grib_get_size(msgid, key):
"""
@brief Get the size of an array key.
\b Examples: \ref grib_get_keys.py "grib_get_keys.py",\ref count_messages.py "count_messages.py"
@param msgid id of the message loaded in memory
@param key name of the key
@exception CodesInternalError
"""
h = get_handle(msgid)
size_p = ffi.new("size_t*")
err = lib.grib_get_size(h, key.encode(ENC), size_p)
GRIB_CHECK(err)
return size_p[0]
@require(msgid=int, key=str)
def grib_get_string_length(msgid, key):
"""
@brief Get the length of the string version of a key.
@param msgid id of the message loaded in memory
@param key name of the key
@exception CodesInternalError
"""
h = get_handle(msgid)
size = ffi.new("size_t *")
err = lib.grib_get_length(h, key.encode(ENC), size)
GRIB_CHECK(err)
return size[0]
@require(iterid=int)
def grib_skip_computed(iterid):
"""
@brief Skip the computed keys in a keys iterator.
The computed keys are not coded in the message, they are computed
from other keys.
@see grib_keys_iterator_new,grib_keys_iterator_next,grib_keys_iterator_delete
@param iterid keys iterator id
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_set_flags(gki, lib.GRIB_KEYS_ITERATOR_SKIP_COMPUTED)
@require(iterid=int)
def grib_skip_coded(iterid):
"""
@brief Skip the coded keys in a keys iterator.
The coded keys are actually coded in the message.
@see grib_keys_iterator_new,grib_keys_iterator_next,grib_keys_iterator_delete
@param iterid keys iterator id
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_set_flags(gki, lib.GRIB_KEYS_ITERATOR_SKIP_CODED)
@require(iterid=int)
def grib_skip_edition_specific(iterid):
"""
@brief Skip the edition specific keys in a keys iterator.
@see grib_keys_iterator_new,grib_keys_iterator_next,grib_keys_iterator_delete
@param iterid keys iterator id
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_set_flags(gki, lib.GRIB_KEYS_ITERATOR_SKIP_EDITION_SPECIFIC)
@require(iterid=int)
def grib_skip_duplicates(iterid):
"""
@brief Skip the duplicate keys in a keys iterator.
@see grib_keys_iterator_new,grib_keys_iterator_next,grib_keys_iterator_delete
@param iterid keys iterator id
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_set_flags(gki, lib.GRIB_KEYS_ITERATOR_SKIP_DUPLICATES)
@require(iterid=int)
def grib_skip_read_only(iterid):
"""
@brief Skip the read_only keys in a keys iterator.
Read only keys cannot be set.
@see grib_keys_iterator_new,grib_keys_iterator_next,grib_keys_iterator_delete
@param iterid keys iterator id
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_set_flags(gki, lib.GRIB_KEYS_ITERATOR_SKIP_READ_ONLY)
@require(iterid=int)
def grib_skip_function(iterid):
"""
@brief Skip the function keys in a keys iterator.
@see grib_keys_iterator_new,grib_keys_iterator_next,grib_keys_iterator_delete
@param iterid keys iterator id
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_set_flags(gki, lib.GRIB_KEYS_ITERATOR_SKIP_FUNCTION)
@require(gribid=int, mode=int)
def grib_iterator_new(gribid, mode):
"""
@brief Create a new geoiterator for the given GRIB message, using its geometry and values.
The geoiterator can be used to go through all the geopoints in a GRIB message and
retrieve the values corresponding to those geopoints.
\b Examples: \ref grib_iterator.py "grib_iterator.py"
@param gribid id of the GRIB loaded in memory
@param mode flags for future use
@return geoiterator id
"""
h = get_handle(gribid)
err, iterid = err_last(lib.grib_iterator_new)(h, mode)
GRIB_CHECK(err)
return put_iterator(iterid)
@require(iterid=int)
def grib_iterator_delete(iterid):
"""
@brief Delete a geoiterator and free memory.
\b Examples: \ref grib_iterator.py "grib_iterator.py"
@param iterid geoiterator id
@exception CodesInternalError
"""
ih = get_iterator(iterid)
GRIB_CHECK(lib.grib_iterator_delete(ih))
@require(iterid=int)
def grib_iterator_next(iterid):
"""
@brief Retrieve the next value from a geoiterator.
\b Examples: \ref grib_iterator.py "grib_iterator.py"
@param iterid geoiterator id
@return tuple with the latitude, longitude and value
@exception CodesInternalError
"""
iterh = get_iterator(iterid)
lat_p = ffi.new("double*")
lon_p = ffi.new("double*")
value_p = ffi.new("double*")
err = lib.grib_iterator_next(iterh, lat_p, lon_p, value_p)
if err == 0:
return []
elif err < 0:
GRIB_CHECK(err)
return None
else:
return (lat_p[0], lon_p[0], value_p[0])
@require(msgid=int)
def grib_keys_iterator_new(msgid, namespace=None):
"""
@brief Create a new iterator on the keys.
The keys iterator can be navigated to give all the key names which
can then be used to get or set the key values with \ref grib_get or
\ref grib_set.
The set of keys returned can be controlled with the input variable
namespace or using the functions
\ref grib_skip_read_only, \ref grib_skip_duplicates,
\ref grib_skip_coded,\ref grib_skip_computed.
If namespace is a non empty string only the keys belonging to
that namespace are returned. Example namespaces are "ls" (to get the same
default keys as the grib_ls) and "mars" to get the keys used by mars.
\b Examples: \ref grib_iterator.py "grib_iterator.py"
@param msgid id of the message loaded in memory
@param namespace the namespace of the keys to search for (all the keys if None)
@return keys iterator id to be used in the keys iterator functions
@exception CodesInternalError
"""
h = get_handle(msgid)
bnamespace = ffi.NULL if namespace is None else namespace.encode(ENC)
iterid = lib.grib_keys_iterator_new(h, 0, bnamespace)
return put_grib_keys_iterator(iterid)
@require(iterid=int)
def grib_keys_iterator_next(iterid):
"""
@brief Advance to the next keys iterator value.
\b Examples: \ref grib_keys_iterator.py "grib_keys_iterator.py"
@param iterid keys iterator id created with @ref grib_keys_iterator_new
@exception CodesInternalError
"""
kih = get_grib_keys_iterator(iterid)
res = lib.grib_keys_iterator_next(kih)
if res < 0:
GRIB_CHECK(res)
return res
@require(iterid=int)
def grib_keys_iterator_delete(iterid):
"""
@brief Delete a keys iterator and free memory.
\b Examples: \ref grib_keys_iterator.py "grib_keys_iterator.py"
@param iterid keys iterator id created with @ref grib_keys_iterator_new
@exception CodesInternalError
"""
kih = get_grib_keys_iterator(iterid)
lib.grib_keys_iterator_delete(kih)
@require(iterid=int)
def grib_keys_iterator_get_name(iterid):
"""
@brief Get the name of a key from a keys iterator.
\b Examples: \ref grib_keys_iterator.py "grib_keys_iterator.py"
@param iterid keys iterator id created with @ref grib_keys_iterator_new
@return key name to be retrieved
@exception CodesInternalError
"""
kih = get_grib_keys_iterator(iterid)
name = lib.grib_keys_iterator_get_name(kih)
return ffi.string(name).decode(ENC)
@require(iterid=int)
def grib_keys_iterator_rewind(iterid):
"""
@brief Rewind a keys iterator.
@param iterid keys iterator id created with @ref grib_keys_iterator_new
@exception CodesInternalError
"""
gki = get_grib_keys_iterator(iterid)
GRIB_CHECK(lib.grib_keys_iterator_rewind(gki))
# BUFR keys iterator
@require(bufrid=int)
def codes_bufr_keys_iterator_new(bufrid):
"""
@brief Create a new iterator on the BUFR keys.
The keys iterator can be navigated to give all the key names which
can then be used to get or set the key values with \ref codes_get or
\ref codes_set.
\b Examples: \ref bufr_keys_iterator.py "bufr_keys_iterator.py"
@param bufrid id of the BUFR message loaded in memory
@return keys iterator id to be used in the bufr keys iterator functions
@exception CodesInternalError
"""
h = get_handle(bufrid)
bki = lib.codes_bufr_keys_iterator_new(h, 0)
if bki == ffi.NULL:
raise errors.InvalidKeysIteratorError(
f"BUFR keys iterator failed bufrid={bufrid}"
)
return put_bufr_keys_iterator(bki)
@require(iterid=int)
def codes_bufr_keys_iterator_next(iterid):
"""
@brief Advance to the next BUFR keys iterator value.
\b Examples: \ref bufr_keys_iterator.py "bufr_keys_iterator.py"
@param iterid keys iterator id created with @ref codes_bufr_keys_iterator_new
@exception CodesInternalError
"""
bki = get_bufr_keys_iterator(iterid)
res = lib.codes_bufr_keys_iterator_next(bki)
if res < 0:
GRIB_CHECK(res)
return res
@require(iterid=int)
def codes_bufr_keys_iterator_delete(iterid):
"""
@brief Delete a BUFR keys iterator and free memory.
\b Examples: \ref bufr_keys_iterator.py "bufr_keys_iterator.py"
@param iterid keys iterator id created with @ref codes_bufr_keys_iterator_new
@exception CodesInternalError
"""
bki = get_bufr_keys_iterator(iterid)
GRIB_CHECK(lib.codes_bufr_keys_iterator_delete(bki))
@require(iterid=int)
def codes_bufr_keys_iterator_get_name(iterid):
"""
@brief Get the name of a key from a BUFR keys iterator.
\b Examples: \ref bufr_keys_iterator.py "bufr_keys_iterator.py"
@param iterid keys iterator id created with @ref codes_bufr_keys_iterator_new
@return key name to be retrieved
@exception CodesInternalError
"""
bki = get_bufr_keys_iterator(iterid)
name = lib.codes_bufr_keys_iterator_get_name(bki)
return ffi.string(name).decode(ENC)
@require(iterid=int)
def codes_bufr_keys_iterator_rewind(iterid):
"""
@brief Rewind a BUFR keys iterator.
@param iterid keys iterator id created with @ref codes_bufr_keys_iterator_new
@exception CodesInternalError
"""
bki = get_bufr_keys_iterator(iterid)
GRIB_CHECK(lib.codes_bufr_keys_iterator_rewind(bki))
@require(msgid=int, key=str)
def grib_get_long(msgid, key):
"""
@brief Get the value of a key in a message as an integer.
@param msgid id of the message loaded in memory
@param key key name
@return value of key as int
@exception CodesInternalError
"""
h = get_handle(msgid)
value_p = ffi.new("long*")
err = lib.grib_get_long(h, key.encode(ENC), value_p)
GRIB_CHECK(err)
return value_p[0]
@require(msgid=int, key=str)
def grib_get_double(msgid, key):
"""
@brief Get the value of a key in a message as a float.
@param msgid id of the message loaded in memory
@param key key name
@return value of key as float
@exception CodesInternalError
"""
h = get_handle(msgid)
value_p = ffi.new("double*")
err = lib.grib_get_double(h, key.encode(ENC), value_p)
GRIB_CHECK(err)
return value_p[0]
@require(msgid=int, key=str, value=(int, float, np.float16, np.float32, np.int64, str))
def grib_set_long(msgid, key, value):
"""
@brief Set the integer value for a key in a message.
A TypeError exception will be thrown if value cannot be represented
as an integer.
@param msgid id of the message loaded in memory
@param key key name
@param value value to set
@exception CodesInternalError,TypeError
"""
try:
value = int(value)
except (ValueError, TypeError):
raise TypeError("Invalid type")
if value > sys.maxsize:
raise ValueError("Value too large")
h = get_handle(msgid)
GRIB_CHECK(lib.grib_set_long(h, key.encode(ENC), value))
@require(msgid=int, key=str, value=(int, float, np.float16, np.float32, str))