grib2io

Introduction

grib2io is a Python package that provides an interface to the NCEP GRIB2 C (g2c) library for the purpose of reading and writing WMO GRIdded Binary, Edition 2 (GRIB2) messages. A physical file can contain one or more GRIB2 messages.

GRIB2 file IO is performed directly in Python. The unpacking/packing of GRIB2 integer, coded metadata and data sections is performed by the g2c library functions via the g2clib Cython wrapper module. The decoding/encoding of GRIB2 metadata is translated into more descriptive, plain language metadata by looking up the integer code values against the appropriate GRIB2 code tables. These code tables are a part of the grib2io module.

For example usage of grib2io, please see the Jupyter Notebook.

 1from ._grib2io import *
 2from ._grib2io import __doc__
 3from ._grib2io import _Grib2Message
 4
 5try:
 6    from . import __config__
 7    __version__ = __config__.grib2io_version
 8except(ImportError):
 9    pass
10
11__all__ = ['open','Grib2Message','_Grib2Message','show_config','interpolate',
12           'interpolate_to_stations','tables','templates','utils','Grib2GridDef']
13
14def show_config():
15    """
16    Print grib2io build configuration information.
17    """
18    from g2clib import __version__ as g2clib_version
19    from g2clib import _has_png as have_png
20    from g2clib import _has_jpeg as have_jpeg
21    print("grib2io version %s Configuration:\n"%(__version__))
22    print("\tg2c library version:".expandtabs(4),g2clib_version)
23    print("\tJPEG compression support:".expandtabs(4),bool(have_jpeg))
24    print("\tPNG compression support:".expandtabs(4),bool(have_png))
class open:
 50class open():
 51    """
 52    GRIB2 File Object.  A physical file can contain one or more GRIB2 messages.  When instantiated,
 53    class `grib2io.open`, the file named `filename` is opened for reading (`mode = 'r'`) and is
 54    automatically indexed.  The indexing procedure reads some of the GRIB2 metadata for all GRIB2 Messages.
 55
 56    A GRIB2 Message may contain submessages whereby Section 2-7 can be repeated.  grib2io accommodates
 57    for this by flattening any GRIB2 submessages into multiple individual messages.
 58
 59    Attributes
 60    ----------
 61
 62    **`mode`** File IO mode of opening the file.
 63
 64    **`name`** Full path name of the GRIB2 file.
 65
 66    **`messages`** Count of GRIB2 Messages contained in the file.
 67
 68    **`current_message`** Current position of the file in units of GRIB2 Messages.
 69
 70    **`size`** Size of the file in units of bytes.
 71
 72    **`closed`** `True` is file handle is close; `False` otherwise.
 73
 74    **`variables`** Tuple containing a unique list of variable short names (i.e. GRIB2 abbreviation names).
 75
 76    **`levels`** Tuple containing a unique list of wgrib2-formatted level/layer strings.
 77    """
 78    __slots__ = ('_filehandle','_hasindex','_index','mode','name','messages',
 79                 'current_message','size','closed','variables','levels','_pos')
 80    def __init__(self, filename, mode='r', **kwargs):
 81        """
 82        `open` Constructor
 83
 84        Parameters
 85        ----------
 86
 87        **`filename : str`**
 88
 89        File name containing GRIB2 messages.
 90
 91        **`mode : str, optional`**
 92
 93        File access mode where `r` opens the files for reading only; `w` opens the file for writing.
 94        """
 95        if mode in {'a','r','w'}:
 96            mode = mode+'b'
 97            if 'w' in mode: mode += '+'
 98        self._filehandle = builtins.open(filename,mode=mode,buffering=ONE_MB)
 99        self._hasindex = False
100        self._index = {}
101        self.mode = mode
102        self.name = os.path.abspath(filename)
103        self.messages = 0
104        self.current_message = 0
105        self.size = os.path.getsize(self.name)
106        self.closed = self._filehandle.closed
107        self.levels = None
108        self.variables = None
109        if 'r' in self.mode:
110            try:
111                self._build_index(no_data=kwargs['_xarray_backend'])
112            except(KeyError):
113                self._build_index()
114        # FIX: Cannot perform reads on mode='a'
115        #if 'a' in self.mode and self.size > 0: self._build_index()
116
117
118    def __delete__(self, instance):
119        """
120        """
121        self.close()
122        del self._index
123
124
125    def __enter__(self):
126        """
127        """
128        return self
129
130
131    def __exit__(self, atype, value, traceback):
132        """
133        """
134        self.close()
135
136
137    def __iter__(self):
138        """
139        """
140        yield from self._index['msg']
141
142
143    def __len__(self):
144        """
145        """
146        return self.messages
147
148
149    def __repr__(self):
150        """
151        """
152        strings = []
153        for k in self.__slots__:
154            if k.startswith('_'): continue
155            strings.append('%s = %s\n'%(k,eval('self.'+k)))
156        return ''.join(strings)
157
158
159    def __getitem__(self, key):
160        """
161        """
162        if isinstance(key,int):
163            if abs(key) >= len(self._index['msg']):
164                raise IndexError("index out of range")
165            else:
166                return self._index['msg'][key]
167        elif isinstance(key,str):
168            return self.select(shortName=key)
169        elif isinstance(key,slice):
170            return self._index['msg'][key]
171        else:
172            raise KeyError('Key must be an integer, slice, or GRIB2 variable shortName.')
173
174
175    def _build_index(self, no_data=False):
176        """
177        Perform indexing of GRIB2 Messages.
178        """
179        # Initialize index dictionary
180        if not self._hasindex:
181            self._index['offset'] = []
182            self._index['bitmap_offset'] = []
183            self._index['data_offset'] = []
184            self._index['size'] = []
185            self._index['data_size'] = []
186            self._index['submessageOffset'] = []
187            self._index['submessageBeginSection'] = []
188            self._index['isSubmessage'] = []
189            self._index['messageNumber'] = []
190            self._index['msg'] = []
191            self._hasindex = True
192
193        # Iterate
194        while True:
195            try:
196                # Read first 4 bytes and decode...looking for "GRIB"
197                pos = self._filehandle.tell()
198                header = struct.unpack('>i',self._filehandle.read(4))[0]
199
200                # Test header. Then get information from GRIB2 Section 0: the discipline
201                # number, edition number (should always be 2), and GRIB2 message size.
202                # Then iterate to check for submessages.
203                if header.to_bytes(4,'big') == b'GRIB':
204
205                    _issubmessage = False
206                    _submsgoffset = 0
207                    _submsgbegin = 0
208                    _bmapflag = None
209
210                    # Read the rest of Section 0 using struct.
211                    section0 = np.concatenate(([header],list(struct.unpack('>HBBQ',self._filehandle.read(12)))),dtype=np.int64)
212                    assert section0[3] == 2
213
214                    # Read and unpack Section 1
215                    secsize = struct.unpack('>i',self._filehandle.read(4))[0]
216                    secnum = struct.unpack('>B',self._filehandle.read(1))[0]
217                    assert secnum == 1
218                    self._filehandle.seek(self._filehandle.tell()-5)
219                    _grbmsg = self._filehandle.read(secsize)
220                    _grbpos = 0
221                    section1,_grbpos = g2clib.unpack1(_grbmsg,_grbpos,np.empty)
222                    secrange = range(2,8)
223                    while 1:
224                        section2 = b''
225                        for num in secrange:
226                            secsize = struct.unpack('>i',self._filehandle.read(4))[0]
227                            secnum = struct.unpack('>B',self._filehandle.read(1))[0]
228                            if secnum == num:
229                                if secnum == 2:
230                                    if secsize > 0:
231                                        section2 = self._filehandle.read(secsize-5)
232                                elif secnum == 3:
233                                    self._filehandle.seek(self._filehandle.tell()-5)
234                                    _grbmsg = self._filehandle.read(secsize)
235                                    _grbpos = 0
236                                    # Unpack Section 3
237                                    _gds,_gdt,_deflist,_grbpos = g2clib.unpack3(_grbmsg,_grbpos,np.empty)
238                                    _gds = _gds.tolist()
239                                    _gdt = _gdt.tolist()
240                                    section3 = np.concatenate((_gds,_gdt))
241                                    section3 = np.where(section3==4294967295,-1,section3)
242                                elif secnum == 4:
243                                    self._filehandle.seek(self._filehandle.tell()-5)
244                                    _grbmsg = self._filehandle.read(secsize)
245                                    _grbpos = 0
246                                    # Unpack Section 4
247                                    _numcoord,_pdt,_pdtnum,_coordlist,_grbpos = g2clib.unpack4(_grbmsg,_grbpos,np.empty)
248                                    _pdt = _pdt.tolist()
249                                    section4 = np.concatenate((np.array((_numcoord,_pdtnum)),_pdt))
250                                elif secnum == 5:
251                                    self._filehandle.seek(self._filehandle.tell()-5)
252                                    _grbmsg = self._filehandle.read(secsize)
253                                    _grbpos = 0
254                                    # Unpack Section 5
255                                    _drt,_drtn,_npts,self._pos = g2clib.unpack5(_grbmsg,_grbpos,np.empty)
256                                    section5 = np.concatenate((np.array((_npts,_drtn)),_drt))
257                                    section5 = np.where(section5==4294967295,-1,section5)
258                                elif secnum == 6:
259                                    # Unpack Section 6. Not really...just get the flag value.
260                                    _bmapflag = struct.unpack('>B',self._filehandle.read(1))[0]
261                                    if _bmapflag == 0:
262                                        _bmappos = self._filehandle.tell()-6
263                                    elif _bmapflag == 254:
264                                        pass # Do this to keep the previous position value
265                                    else:
266                                        _bmappos = None
267                                    self._filehandle.seek(self._filehandle.tell()+secsize-6)
268                                elif secnum == 7:
269                                    # Unpack Section 7. No need to read it, just index the position in file.
270                                    _datapos = self._filehandle.tell()-5
271                                    _datasize = secsize
272                                    self._filehandle.seek(self._filehandle.tell()+secsize-5)
273                                else:
274                                    self._filehandle.seek(self._filehandle.tell()+secsize-5)
275                            else:
276                                if num == 2 and secnum == 3:
277                                    pass # Allow this.  Just means no Local Use Section.
278                                else:
279                                    _issubmessage = True
280                                    _submsgoffset = (self._filehandle.tell()-5)-(self._index['offset'][-1])
281                                    _submsgbegin = secnum
282                                self._filehandle.seek(self._filehandle.tell()-5)
283                                continue
284                        trailer = struct.unpack('>4s',self._filehandle.read(4))[0]
285                        if trailer == b'7777':
286                            self.messages += 1
287                            self._index['offset'].append(pos)
288                            self._index['bitmap_offset'].append(_bmappos)
289                            self._index['data_offset'].append(_datapos)
290                            self._index['size'].append(section0[-1])
291                            self._index['data_size'].append(_datasize)
292                            self._index['messageNumber'].append(self.messages)
293                            self._index['isSubmessage'].append(_issubmessage)
294                            if _issubmessage:
295                                self._index['submessageOffset'].append(_submsgoffset)
296                                self._index['submessageBeginSection'].append(_submsgbegin)
297                            else:
298                                self._index['submessageOffset'].append(0)
299                                self._index['submessageBeginSection'].append(_submsgbegin)
300
301                            # Create Grib2Message with data.
302                            msg = Grib2Message(section0,section1,section2,section3,section4,section5,_bmapflag)
303                            msg._msgnum = self.messages-1
304                            msg._deflist = _deflist
305                            msg._coordlist = _coordlist
306                            if not no_data:
307                                shape = (msg.ny,msg.nx)
308                                ndim = 2
309                                if msg.typeOfValues == 0:
310                                    dtype = 'float32'
311                                elif msg.typeOfValues == 1:
312                                    dtype = 'int32'
313                                msg._data = Grib2MessageOnDiskArray(shape, ndim, dtype, self._filehandle,
314                                                                    msg, pos, _bmappos, _datapos)
315                            self._index['msg'].append(msg)
316
317                            break
318                        else:
319                            self._filehandle.seek(self._filehandle.tell()-4)
320                            self.messages += 1
321                            self._index['offset'].append(pos)
322                            self._index['bitmap_offset'].append(_bmappos)
323                            self._index['data_offset'].append(_datapos)
324                            self._index['size'].append(section0[-1])
325                            self._index['data_size'].append(_datasize)
326                            self._index['messageNumber'].append(self.messages)
327                            self._index['isSubmessage'].append(_issubmessage)
328                            self._index['submessageOffset'].append(_submsgoffset)
329                            self._index['submessageBeginSection'].append(_submsgbegin)
330
331                            # Create Grib2Message with data.
332                            msg = Grib2Message(section0,section1,section2,section3,section4,section5,_bmapflag)
333                            msg._msgnum = self.messages-1
334                            msg._deflist = _deflist
335                            msg._coordlist = _coordlist
336                            if not no_data:
337                                shape = (msg.ny,msg.nx)
338                                ndim = 2
339                                if msg.typeOfValues == 0:
340                                    dtype = 'float32'
341                                elif msg.typeOfValues == 1:
342                                    dtype = 'int32'
343                                msg._data = Grib2MessageOnDiskArray(shape, ndim, dtype, self._filehandle,
344                                                                    msg, pos, _bmappos, _datapos)
345                            self._index['msg'].append(msg)
346
347                            continue
348
349            except(struct.error):
350                if 'r' in self.mode:
351                    self._filehandle.seek(0)
352                break
353
354        # Index at end of _build_index()
355        if self._hasindex and not no_data:
356             self.variables = tuple(sorted(set([msg.shortName for msg in self._index['msg']])))
357             self.levels = tuple(sorted(set([msg.level for msg in self._index['msg']])))
358
359
360    def close(self):
361        """
362        Close the file handle
363        """
364        if not self._filehandle.closed:
365            self.messages = 0
366            self.current_message = 0
367            self._filehandle.close()
368            self.closed = self._filehandle.closed
369
370
371    def read(self, size=None):
372        """
373        Read size amount of GRIB2 messages from the current position. If no argument is
374        given, then size is None and all messages are returned from the current position
375        in the file. This read method follows the behavior of Python's builtin open()
376        function, but whereas that operates on units of bytes, we operate on units of
377        GRIB2 messages.
378
379        Parameters
380        ----------
381
382        **`size : int, optional`**
383
384        The number of GRIB2 messages to read from the current position. If no argument is
385        give, the default value is `None` and remainder of the file is read.
386
387        Returns
388        -------
389
390        `Grib2Message` object when size = 1 or a `list` of Grib2Messages when
391        size > 1.
392        """
393        if size is not None and size < 0:
394            size = None
395        if size is None or size > 1:
396            start = self.tell()
397            stop = self.messages if size is None else start+size
398            if size is None:
399                self.current_message = self.messages-1
400            else:
401                self.current_message += size
402            return self._index['msg'][slice(start,stop,1)]
403        elif size == 1:
404            self.current_message += 1
405            return self._index['msg'][self.current_message]
406        else:
407            None
408
409
410    def seek(self, pos):
411        """
412        Set the position within the file in units of GRIB2 messages.
413
414        Parameters
415        ----------
416
417        **`pos : int`**
418
419        The GRIB2 Message number to set the file pointer to.
420        """
421        if self._hasindex:
422            self._filehandle.seek(self._index['offset'][pos])
423            self.current_message = pos
424
425
426    def tell(self):
427        """
428        Returns the position of the file in units of GRIB2 Messages.
429        """
430        return self.current_message
431
432
433    def select(self,**kwargs):
434        """
435        Select GRIB2 messages by `Grib2Message` attributes.
436        """
437        # TODO: Added ability to process multiple values for each keyword (attribute)
438        idxs = []
439        nkeys = len(kwargs.keys())
440        for k,v in kwargs.items():
441            for m in self._index['msg']:
442                if hasattr(m,k) and getattr(m,k) == v: idxs.append(m._msgnum)
443        idxs = np.array(idxs,dtype=np.int32)
444        return [self._index['msg'][i] for i in [ii[0] for ii in collections.Counter(idxs).most_common() if ii[1] == nkeys]]
445
446
447    def write(self, msg):
448        """
449        Writes GRIB2 message object to file.
450
451        Parameters
452        ----------
453
454        **`msg : Grib2Message or sequence of Grib2Messages`**
455
456        GRIB2 message objects to write to file.
457        """
458        if isinstance(msg,list):
459            for m in msg:
460                self.write(m)
461            return
462
463        if issubclass(msg.__class__,_Grib2Message):
464            if hasattr(msg,'_msg'):
465                self._filehandle.write(msg._msg)
466            else:
467                if msg._signature != msg._generate_signature():
468                    msg.pack()
469                    self._filehandle.write(msg._msg)
470                else:
471                    if hasattr(msg._data,'filehandle'):
472                        msg._data.filehandle.seek(msg._data.offset)
473                        self._filehandle.write(msg._data.filehandle.read(msg.section0[-1]))
474                    else:
475                        msg.pack()
476                        self._filehandle.write(msg._msg)
477            self.flush()
478            self.size = os.path.getsize(self.name)
479            self._filehandle.seek(self.size-msg.section0[-1])
480            self._build_index()
481        else:
482            raise TypeError("msg must be a Grib2Message object.")
483        return
484
485
486    def flush(self):
487        """
488        Flush the file object buffer.
489        """
490        self._filehandle.flush()
491
492
493    def levels_by_var(self,name):
494        """
495        Return a list of level strings given a variable shortName.
496
497        Parameters
498        ----------
499
500        **`name : str`**
501
502        Grib2Message variable shortName
503
504        Returns
505        -------
506
507        A list of strings of unique level strings.
508        """
509        return list(sorted(set([msg.level for msg in self.select(shortName=name)])))
510
511
512    def vars_by_level(self,level):
513        """
514        Return a list of variable shortName strings given a level.
515
516        Parameters
517        ----------
518
519        **`level : str`**
520
521        Grib2Message variable level
522
523        Returns
524        -------
525
526        A list of strings of variable shortName strings.
527        """
528        return list(sorted(set([msg.shortName for msg in self.select(level=level)])))

GRIB2 File Object. A physical file can contain one or more GRIB2 messages. When instantiated, class grib2io.open, the file named filename is opened for reading (mode = 'r') and is automatically indexed. The indexing procedure reads some of the GRIB2 metadata for all GRIB2 Messages.

A GRIB2 Message may contain submessages whereby Section 2-7 can be repeated. grib2io accommodates for this by flattening any GRIB2 submessages into multiple individual messages.

Attributes

mode File IO mode of opening the file.

name Full path name of the GRIB2 file.

messages Count of GRIB2 Messages contained in the file.

current_message Current position of the file in units of GRIB2 Messages.

size Size of the file in units of bytes.

closed True is file handle is close; False otherwise.

variables Tuple containing a unique list of variable short names (i.e. GRIB2 abbreviation names).

levels Tuple containing a unique list of wgrib2-formatted level/layer strings.

open(filename, mode='r', **kwargs)
 80    def __init__(self, filename, mode='r', **kwargs):
 81        """
 82        `open` Constructor
 83
 84        Parameters
 85        ----------
 86
 87        **`filename : str`**
 88
 89        File name containing GRIB2 messages.
 90
 91        **`mode : str, optional`**
 92
 93        File access mode where `r` opens the files for reading only; `w` opens the file for writing.
 94        """
 95        if mode in {'a','r','w'}:
 96            mode = mode+'b'
 97            if 'w' in mode: mode += '+'
 98        self._filehandle = builtins.open(filename,mode=mode,buffering=ONE_MB)
 99        self._hasindex = False
100        self._index = {}
101        self.mode = mode
102        self.name = os.path.abspath(filename)
103        self.messages = 0
104        self.current_message = 0
105        self.size = os.path.getsize(self.name)
106        self.closed = self._filehandle.closed
107        self.levels = None
108        self.variables = None
109        if 'r' in self.mode:
110            try:
111                self._build_index(no_data=kwargs['_xarray_backend'])
112            except(KeyError):
113                self._build_index()
114        # FIX: Cannot perform reads on mode='a'
115        #if 'a' in self.mode and self.size > 0: self._build_index()

open Constructor

Parameters

filename : str

File name containing GRIB2 messages.

mode : str, optional

File access mode where r opens the files for reading only; w opens the file for writing.

mode
name
messages
current_message
size
closed
levels
variables
def close(self):
360    def close(self):
361        """
362        Close the file handle
363        """
364        if not self._filehandle.closed:
365            self.messages = 0
366            self.current_message = 0
367            self._filehandle.close()
368            self.closed = self._filehandle.closed

Close the file handle

def read(self, size=None):
371    def read(self, size=None):
372        """
373        Read size amount of GRIB2 messages from the current position. If no argument is
374        given, then size is None and all messages are returned from the current position
375        in the file. This read method follows the behavior of Python's builtin open()
376        function, but whereas that operates on units of bytes, we operate on units of
377        GRIB2 messages.
378
379        Parameters
380        ----------
381
382        **`size : int, optional`**
383
384        The number of GRIB2 messages to read from the current position. If no argument is
385        give, the default value is `None` and remainder of the file is read.
386
387        Returns
388        -------
389
390        `Grib2Message` object when size = 1 or a `list` of Grib2Messages when
391        size > 1.
392        """
393        if size is not None and size < 0:
394            size = None
395        if size is None or size > 1:
396            start = self.tell()
397            stop = self.messages if size is None else start+size
398            if size is None:
399                self.current_message = self.messages-1
400            else:
401                self.current_message += size
402            return self._index['msg'][slice(start,stop,1)]
403        elif size == 1:
404            self.current_message += 1
405            return self._index['msg'][self.current_message]
406        else:
407            None

Read size amount of GRIB2 messages from the current position. If no argument is given, then size is None and all messages are returned from the current position in the file. This read method follows the behavior of Python's builtin open() function, but whereas that operates on units of bytes, we operate on units of GRIB2 messages.

Parameters

size : int, optional

The number of GRIB2 messages to read from the current position. If no argument is give, the default value is None and remainder of the file is read.

Returns

Grib2Message object when size = 1 or a list of Grib2Messages when size > 1.

def seek(self, pos):
410    def seek(self, pos):
411        """
412        Set the position within the file in units of GRIB2 messages.
413
414        Parameters
415        ----------
416
417        **`pos : int`**
418
419        The GRIB2 Message number to set the file pointer to.
420        """
421        if self._hasindex:
422            self._filehandle.seek(self._index['offset'][pos])
423            self.current_message = pos

Set the position within the file in units of GRIB2 messages.

Parameters

pos : int

The GRIB2 Message number to set the file pointer to.

def tell(self):
426    def tell(self):
427        """
428        Returns the position of the file in units of GRIB2 Messages.
429        """
430        return self.current_message

Returns the position of the file in units of GRIB2 Messages.

def select(self, **kwargs):
433    def select(self,**kwargs):
434        """
435        Select GRIB2 messages by `Grib2Message` attributes.
436        """
437        # TODO: Added ability to process multiple values for each keyword (attribute)
438        idxs = []
439        nkeys = len(kwargs.keys())
440        for k,v in kwargs.items():
441            for m in self._index['msg']:
442                if hasattr(m,k) and getattr(m,k) == v: idxs.append(m._msgnum)
443        idxs = np.array(idxs,dtype=np.int32)
444        return [self._index['msg'][i] for i in [ii[0] for ii in collections.Counter(idxs).most_common() if ii[1] == nkeys]]

Select GRIB2 messages by Grib2Message attributes.

def write(self, msg):
447    def write(self, msg):
448        """
449        Writes GRIB2 message object to file.
450
451        Parameters
452        ----------
453
454        **`msg : Grib2Message or sequence of Grib2Messages`**
455
456        GRIB2 message objects to write to file.
457        """
458        if isinstance(msg,list):
459            for m in msg:
460                self.write(m)
461            return
462
463        if issubclass(msg.__class__,_Grib2Message):
464            if hasattr(msg,'_msg'):
465                self._filehandle.write(msg._msg)
466            else:
467                if msg._signature != msg._generate_signature():
468                    msg.pack()
469                    self._filehandle.write(msg._msg)
470                else:
471                    if hasattr(msg._data,'filehandle'):
472                        msg._data.filehandle.seek(msg._data.offset)
473                        self._filehandle.write(msg._data.filehandle.read(msg.section0[-1]))
474                    else:
475                        msg.pack()
476                        self._filehandle.write(msg._msg)
477            self.flush()
478            self.size = os.path.getsize(self.name)
479            self._filehandle.seek(self.size-msg.section0[-1])
480            self._build_index()
481        else:
482            raise TypeError("msg must be a Grib2Message object.")
483        return

Writes GRIB2 message object to file.

Parameters

msg : Grib2Message or sequence of Grib2Messages

GRIB2 message objects to write to file.

def flush(self):
486    def flush(self):
487        """
488        Flush the file object buffer.
489        """
490        self._filehandle.flush()

Flush the file object buffer.

def levels_by_var(self, name):
493    def levels_by_var(self,name):
494        """
495        Return a list of level strings given a variable shortName.
496
497        Parameters
498        ----------
499
500        **`name : str`**
501
502        Grib2Message variable shortName
503
504        Returns
505        -------
506
507        A list of strings of unique level strings.
508        """
509        return list(sorted(set([msg.level for msg in self.select(shortName=name)])))

Return a list of level strings given a variable shortName.

Parameters

name : str

Grib2Message variable shortName

Returns

A list of strings of unique level strings.

def vars_by_level(self, level):
512    def vars_by_level(self,level):
513        """
514        Return a list of variable shortName strings given a level.
515
516        Parameters
517        ----------
518
519        **`level : str`**
520
521        Grib2Message variable level
522
523        Returns
524        -------
525
526        A list of strings of variable shortName strings.
527        """
528        return list(sorted(set([msg.shortName for msg in self.select(level=level)])))

Return a list of variable shortName strings given a level.

Parameters

level : str

Grib2Message variable level

Returns

A list of strings of variable shortName strings.

class Grib2Message:
531class Grib2Message:
532    """
533    Creation class for a GRIB2 message.
534    """
535    def __new__(self, section0: np.array = np.array([struct.unpack('>I',b'GRIB')[0],0,0,2,0]),
536                      section1: np.array = np.zeros((13),dtype=np.int64),
537                      section2: bytes = None,
538                      section3: np.array = None,
539                      section4: np.array = None,
540                      section5: np.array = None, *args, **kwargs):
541
542        if np.all(section1==0):
543            section1[5:11] = datetime.datetime.utcfromtimestamp(0).timetuple()[:6]
544
545        bases = list()
546        if section3 is None:
547            if 'gdtn' in kwargs.keys():
548                gdtn = kwargs['gdtn']
549                Gdt = templates.gdt_class_by_gdtn(gdtn)
550                bases.append(Gdt)
551                section3 = np.zeros((Gdt._len+5),dtype=np.int64)
552            else:
553                raise ValueError("Must provide GRIB2 Grid Definition Template Number or section 3 array")
554        else:
555            gdtn = section3[4]
556            Gdt = templates.gdt_class_by_gdtn(gdtn)
557            bases.append(Gdt)
558
559        if section4 is None:
560            if 'pdtn' in kwargs.keys():
561                pdtn = kwargs['pdtn']
562                Pdt = templates.pdt_class_by_pdtn(pdtn)
563                bases.append(Pdt)
564                section4 = np.zeros((Pdt._len+2),dtype=np.int64)
565            else:
566                raise ValueError("Must provide GRIB2 Production Definition Template Number or section 4 array")
567        else:
568            pdtn = section4[1]
569            Pdt = templates.pdt_class_by_pdtn(pdtn)
570            bases.append(Pdt)
571
572        if section5 is None:
573            if 'drtn' in kwargs.keys():
574                drtn = kwargs['drtn']
575                Drt = templates.drt_class_by_drtn(drtn)
576                bases.append(Drt)
577                section5 = np.zeros((Drt._len+2),dtype=np.int64)
578            else:
579                raise ValueError("Must provide GRIB2 Data Representation Template Number or section 5 array")
580        else:
581            drtn = section5[1]
582            Drt = templates.drt_class_by_drtn(drtn)
583            bases.append(Drt)
584
585        # attempt to use existing Msg class if it has already been made with gdtn,pdtn,drtn combo
586        try:
587            Msg = _msg_class_store[f"{gdtn}:{pdtn}:{drtn}"]
588        except KeyError:
589            @dataclass(init=False, repr=False)
590            class Msg(_Grib2Message, *bases):
591                pass
592            _msg_class_store[f"{gdtn}:{pdtn}:{drtn}"] = Msg
593
594        return Msg(section0, section1, section2, section3, section4, section5, *args)

Creation class for a GRIB2 message.

@dataclass
class _Grib2Message:
 597@dataclass
 598class _Grib2Message:
 599    """GRIB2 Message base class"""
 600    # GRIB2 Sections
 601    section0: np.array = field(init=True,repr=False)
 602    section1: np.array = field(init=True,repr=False)
 603    section2: bytes = field(init=True,repr=False)
 604    section3: np.array = field(init=True,repr=False)
 605    section4: np.array = field(init=True,repr=False)
 606    section5: np.array = field(init=True,repr=False)
 607    bitMapFlag: templates.Grib2Metadata = field(init=True,repr=False,default=255)
 608
 609    # Section 0 looked up attributes
 610    indicatorSection: np.array = field(init=False,repr=False,default=templates.IndicatorSection())
 611    discipline: templates.Grib2Metadata = field(init=False,repr=False,default=templates.Discipline())
 612
 613    # Section 1 looked up attributes
 614    identificationSection: np.array = field(init=False,repr=False,default=templates.IdentificationSection())
 615    originatingCenter: templates.Grib2Metadata = field(init=False,repr=False,default=templates.OriginatingCenter())
 616    originatingSubCenter: templates.Grib2Metadata = field(init=False,repr=False,default=templates.OriginatingSubCenter())
 617    masterTableInfo: templates.Grib2Metadata = field(init=False,repr=False,default=templates.MasterTableInfo())
 618    localTableInfo: templates.Grib2Metadata = field(init=False,repr=False,default=templates.LocalTableInfo())
 619    significanceOfReferenceTime: templates.Grib2Metadata = field(init=False,repr=False,default=templates.SignificanceOfReferenceTime())
 620    year: int = field(init=False,repr=False,default=templates.Year())
 621    month: int = field(init=False,repr=False,default=templates.Month())
 622    day: int = field(init=False,repr=False,default=templates.Day())
 623    hour: int = field(init=False,repr=False,default=templates.Hour())
 624    minute: int = field(init=False,repr=False,default=templates.Minute())
 625    second: int = field(init=False,repr=False,default=templates.Second())
 626    refDate: datetime.datetime = field(init=False,repr=False,default=templates.RefDate())
 627    productionStatus: templates.Grib2Metadata = field(init=False,repr=False,default=templates.ProductionStatus())
 628    typeOfData: templates.Grib2Metadata = field(init=False,repr=False,default=templates.TypeOfData())
 629
 630    @property
 631    def _isNDFD(self):
 632        """Check if GRIB2 message is from NWS NDFD"""
 633        return np.all(self.section1[0:2]==[8,65535])
 634
 635    # Section 3 looked up common attributes.  Other looked up attributes are available according
 636    # to the Grid Definition Template.
 637    gridDefinitionSection: np.array = field(init=False,repr=False,default=templates.GridDefinitionSection())
 638    sourceOfGridDefinition: int = field(init=False,repr=False,default=templates.SourceOfGridDefinition())
 639    numberOfDataPoints: int = field(init=False,repr=False,default=templates.NumberOfDataPoints())
 640    interpretationOfListOfNumbers: templates.Grib2Metadata = field(init=False,repr=False,default=templates.InterpretationOfListOfNumbers())
 641    gridDefinitionTemplateNumber: templates.Grib2Metadata = field(init=False,repr=False,default=templates.GridDefinitionTemplateNumber())
 642    gridDefinitionTemplate: list = field(init=False,repr=False,default=templates.GridDefinitionTemplate())
 643    _earthparams: dict = field(init=False,repr=False,default=templates.EarthParams())
 644    _dxsign: float = field(init=False,repr=False,default=templates.DxSign())
 645    _dysign: float = field(init=False,repr=False,default=templates.DySign())
 646    _llscalefactor: float = field(init=False,repr=False,default=templates.LLScaleFactor())
 647    _lldivisor: float = field(init=False,repr=False,default=templates.LLDivisor())
 648    _xydivisor: float = field(init=False,repr=False,default=templates.XYDivisor())
 649    shapeOfEarth: templates.Grib2Metadata = field(init=False,repr=False,default=templates.ShapeOfEarth())
 650    earthRadius: float = field(init=False,repr=False,default=templates.EarthRadius())
 651    earthMajorAxis: float = field(init=False,repr=False,default=templates.EarthMajorAxis())
 652    earthMinorAxis: float = field(init=False,repr=False,default=templates.EarthMinorAxis())
 653    resolutionAndComponentFlags: list = field(init=False,repr=False,default=templates.ResolutionAndComponentFlags())
 654    ny: int = field(init=False,repr=False,default=templates.Ny())
 655    nx: int = field(init=False,repr=False,default=templates.Nx())
 656    scanModeFlags: list = field(init=False,repr=False,default=templates.ScanModeFlags())
 657    projParameters: dict = field(init=False,repr=False,default=templates.ProjParameters())
 658
 659    # Section 4 attributes. Listed here are "extra" or "helper" attrs that use metadata from
 660    # the given PDT, but not a formal part of the PDT.
 661    productDefinitionTemplateNumber: templates.Grib2Metadata = field(init=False,repr=False,default=templates.ProductDefinitionTemplateNumber())
 662    productDefinitionTemplate: np.array = field(init=False,repr=False,default=templates.ProductDefinitionTemplate())
 663    _varinfo: list = field(init=False, repr=False, default=templates.VarInfo())
 664    _fixedsfc1info: list = field(init=False, repr=False, default=templates.FixedSfc1Info())
 665    _fixedsfc2info: list = field(init=False, repr=False, default=templates.FixedSfc2Info())
 666    fullName: str = field(init=False, repr=False, default=templates.FullName())
 667    units: str = field(init=False, repr=False, default=templates.Units())
 668    shortName: str = field(init=False, repr=False, default=templates.ShortName())
 669    leadTime: datetime.timedelta = field(init=False,repr=False,default=templates.LeadTime())
 670    unitOfFirstFixedSurface: str = field(init=False,repr=False,default=templates.UnitOfFirstFixedSurface())
 671    valueOfFirstFixedSurface: int = field(init=False,repr=False,default=templates.ValueOfFirstFixedSurface())
 672    unitOfSecondFixedSurface: str = field(init=False,repr=False,default=templates.UnitOfSecondFixedSurface())
 673    valueOfSecondFixedSurface: int = field(init=False,repr=False,default=templates.ValueOfSecondFixedSurface())
 674    level: str = field(init=False, repr=False, default=templates.Level())
 675    duration: datetime.timedelta = field(init=False,repr=False,default=templates.Duration())
 676    validDate: datetime.datetime = field(init=False,repr=False,default=templates.ValidDate())
 677
 678    # Section 5 looked up common attributes.  Other looked up attributes are available according
 679    # to the Data Representation Template.
 680    numberOfPackedValues: int = field(init=False,repr=False,default=templates.NumberOfPackedValues())
 681    dataRepresentationTemplateNumber: templates.Grib2Metadata = field(init=False,repr=False,default=templates.DataRepresentationTemplateNumber())
 682    dataRepresentationTemplate: list = field(init=False,repr=False,default=templates.DataRepresentationTemplate())
 683    typeOfValues: templates.Grib2Metadata = field(init=False,repr=False,default=templates.TypeOfValues())
 684
 685
 686    def __post_init__(self):
 687        """Set some attributes after init"""
 688        self._msgnum = -1
 689        self._deflist = None
 690        self._coordlist = None
 691        self._signature = self._generate_signature()
 692        try:
 693            self._sha1_section3 = hashlib.sha1(self.section3).hexdigest()
 694        except(TypeError):
 695            pass
 696        self.bitMapFlag = templates.Grib2Metadata(self.bitMapFlag,table='6.0')
 697
 698
 699    @property
 700    def gdtn(self):
 701        """Return Grid Definition Template Number"""
 702        return self.section3[4]
 703
 704
 705    @property
 706    def pdtn(self):
 707        """Return Product Definition Template Number"""
 708        return self.section4[1]
 709
 710
 711    @property
 712    def drtn(self):
 713        """Return Data Representation Template Number"""
 714        return self.section5[1]
 715
 716
 717    @property
 718    def pdy(self):
 719        """Return the PDY ('YYYYMMDD')"""
 720        return ''.join([str(i) for i in self.section1[5:8]])
 721
 722
 723    @property
 724    def griddef(self):
 725        """Return a Grib2GridDef instance for a GRIB2 message"""
 726        return Grib2GridDef.from_section3(self.section3)
 727
 728
 729    def __repr__(self):
 730        info = ''
 731        for sect in [0,1,3,4,5,6]:
 732            for k,v in self.attrs_by_section(sect,values=True).items():
 733                info += f'Section {sect}: {k} = {v}\n'
 734        return info
 735
 736
 737    def __str__(self):
 738        return (f'{self._msgnum}:d={self.refDate}:{self.shortName}:'
 739                f'{self.fullName} ({self.units}):{self.level}:'
 740                f'{self.leadTime}')
 741
 742
 743    def _generate_signature(self):
 744        """Generature SHA-1 hash string from GRIB2 integer sections"""
 745        return hashlib.sha1(np.concatenate((self.section0,self.section1,
 746                                            self.section3,self.section4,
 747                                            self.section5))).hexdigest()
 748
 749
 750    def attrs_by_section(self, sect, values=False):
 751        """
 752        Provide a tuple of attribute names for the given GRIB2 section.
 753
 754        Parameters
 755        ----------
 756
 757        **`sect : int`**
 758
 759        The GRIB2 section number.
 760
 761        **`values : bool, optional`**
 762
 763        Optional (default is `False`) arugment to return attributes values.
 764
 765        Returns
 766        -------
 767
 768        A List attribute names or Dict if `values = True`.
 769        """
 770        if sect in {0,1,6}:
 771            attrs = templates._section_attrs[sect]
 772        elif sect in {3,4,5}:
 773            def _find_class_index(n):
 774                _key = {3:'Grid', 4:'Product', 5:'Data'}
 775                for i,c in enumerate(self.__class__.__mro__):
 776                    if _key[n] in c.__name__:
 777                        return i
 778                else:
 779                    return []
 780            if sys.version_info.minor <= 8:
 781                attrs = templates._section_attrs[sect]+\
 782                        [a for a in dir(self.__class__.__mro__[_find_class_index(sect)]) if not a.startswith('_')]
 783            else:
 784                attrs = templates._section_attrs[sect]+\
 785                        self.__class__.__mro__[_find_class_index(sect)]._attrs
 786        else:
 787            attrs = []
 788        if values:
 789            return {k:getattr(self,k) for k in attrs}
 790        else:
 791            return attrs
 792
 793
 794    def pack(self):
 795        """
 796        Packs GRIB2 section data into a binary message.  It is the user's responsibility
 797        to populate the GRIB2 section information with appropriate metadata.
 798        """
 799        # Create beginning of packed binary message with section 0 and 1 data.
 800        self._sections = []
 801        self._msg,self._pos = g2clib.grib2_create(self.indicatorSection[2:4],self.identificationSection)
 802        self._sections += [0,1]
 803
 804        # Add section 2 if present.
 805        if isinstance(self.section2,bytes) and len(self.section2) > 0:
 806            self._msg,self._pos = g2clib.grib2_addlocal(self._msg,self.section2)
 807            self._sections.append(2)
 808
 809        # Add section 3.
 810        self._msg,self._pos = g2clib.grib2_addgrid(self._msg,self.gridDefinitionSection,
 811                                                   self.gridDefinitionTemplate,
 812                                                   self._deflist)
 813        self._sections.append(3)
 814
 815        # Prepare data.
 816        field = np.copy(self.data)
 817        if self.scanModeFlags is not None:
 818            if self.scanModeFlags[3]:
 819                fieldsave = field.astype('f') # Casting makes a copy
 820                field[1::2,:] = fieldsave[1::2,::-1]
 821        fld = field.astype('f')
 822
 823        # Prepare bitmap, if necessary
 824        bitmapflag = self.bitMapFlag.value
 825        if bitmapflag == 0:
 826            bmap = np.ravel(np.where(np.isnan(fld),0,1)).astype(DEFAULT_NUMPY_INT)
 827        else:
 828            bmap = None
 829
 830        # Prepare optional coordinate list
 831        if self._coordlist is not None:
 832            crdlist = np.array(self._coordlist,'f')
 833        else:
 834            crdlist = None
 835
 836        # Prepare data for packing if nans are present
 837        fld = np.ravel(fld)
 838        if np.isnan(fld).any() and hasattr(self,'_missvalmap'):
 839            fld = np.where(self._missvalmap==1,self.priMissingValue,fld)
 840            fld = np.where(self._missvalmap==2,self.secMissingValue,fld)
 841
 842        # Add sections 4, 5, 6 (if present), and 7.
 843        self._msg,self._pos = g2clib.grib2_addfield(self._msg,self.pdtn,
 844                                                    self.productDefinitionTemplate,
 845                                                    crdlist,
 846                                                    self.drtn,
 847                                                    self.dataRepresentationTemplate,
 848                                                    fld,
 849                                                    bitmapflag,
 850                                                    bmap)
 851        self._sections.append(4)
 852        self._sections.append(5)
 853        if bmap is not None: self._sections.append(6)
 854        self._sections.append(7)
 855
 856        # Finalize GRIB2 message with section 8.
 857        self._msg, self._pos = g2clib.grib2_end(self._msg)
 858        self._sections.append(8)
 859        self.section0[-1] = len(self._msg)
 860
 861
 862    @property
 863    def data(self) -> np.array:
 864        """
 865        Accessing the data attribute loads data into memmory
 866        """
 867        if not hasattr(self,'_auto_nans'): self._auto_nans = _AUTO_NANS
 868        if hasattr(self,'_data'):
 869            if self._auto_nans != _AUTO_NANS:
 870                self._data = self._ondiskarray
 871            if isinstance(self._data, Grib2MessageOnDiskArray):
 872                self._ondiskarray = self._data
 873                self._data = np.asarray(self._data)
 874            return self._data
 875        raise ValueError
 876
 877    @data.setter
 878    def data(self, data):
 879        if not isinstance(data, np.ndarray):
 880            raise ValueError('Grib2Message data only supports numpy arrays')
 881        self._data = data
 882
 883
 884    def __getitem__(self, item):
 885        return self.data[item]
 886
 887
 888    def __setitem__(self, item):
 889        raise NotImplementedError('assignment of data not supported via setitem')
 890
 891
 892    def latlons(self, *args, **kwrgs):
 893        """Alias for `grib2io.Grib2Message.grid` method"""
 894        return self.grid(*args, **kwrgs)
 895
 896
 897    def grid(self, unrotate=True):
 898        """
 899        Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon,
 900        global Gaussian, mercator, stereographic, lambert conformal, albers equal-area,
 901        space-view and azimuthal equidistant grids.
 902
 903        Parameters
 904        ----------
 905
 906        **`unrotate : bool`**
 907
 908        If `True` [DEFAULT], and grid is rotated lat/lon, then unrotate the grid,
 909        otherwise `False`, do not.
 910
 911        Returns
 912        -------
 913
 914        **`lats, lons : numpy.ndarray`**
 915
 916        Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and
 917        longitudes in units of degrees.
 918        """
 919        if self._sha1_section3 in _latlon_datastore.keys():
 920            return _latlon_datastore[self._sha1_section3]
 921        gdtn = self.gridDefinitionTemplateNumber.value
 922        gdtmpl = self.gridDefinitionTemplate
 923        reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid
 924        if gdtn == 0:
 925            # Regular lat/lon grid
 926            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
 927            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
 928            dlon = self.gridlengthXDirection
 929            dlat = self.gridlengthYDirection
 930            if lon2 < lon1 and dlon < 0: lon1 = -lon1
 931            lats = np.linspace(lat1,lat2,self.ny)
 932            if reggrid:
 933                lons = np.linspace(lon1,lon2,self.nx)
 934            else:
 935                lons = np.linspace(lon1,lon2,self.ny*2)
 936            lons,lats = np.meshgrid(lons,lats) # Make 2-d arrays.
 937        elif gdtn == 1: # Rotated Lat/Lon grid
 938            pj = pyproj.Proj(self.projParameters)
 939            lat1,lon1 = self.latitudeFirstGridpoint,self.longitudeFirstGridpoint
 940            lat2,lon2 = self.latitudeLastGridpoint,self.longitudeLastGridpoint
 941            if lon1 > 180.0: lon1 -= 360.0
 942            if lon2 > 180.0: lon2 -= 360.0
 943            lats = np.linspace(lat1,lat2,self.ny)
 944            lons = np.linspace(lon1,lon2,self.nx)
 945            lons,lats = np.meshgrid(lons,lats) # Make 2-d arrays.
 946            if unrotate:
 947                from grib2io.utils import rotated_grid
 948                lats,lons = rotated_grid.unrotate(lats,lons,self.anglePoleRotation,
 949                                                  self.latitudeSouthernPole,
 950                                                  self.longitudeSouthernPole)
 951        elif gdtn == 40: # Gaussian grid (only works for global!)
 952            from utils.gauss_grids import gaussian_latitudes
 953            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
 954            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
 955            nlats = self.ny
 956            if not reggrid: # Reduced Gaussian grid.
 957                nlons = 2*nlats
 958                dlon = 360./nlons
 959            else:
 960                nlons = self.nx
 961                dlon = self.gridlengthXDirection
 962            lons = np.arange(lon1,lon2+dlon,dlon)
 963            # Compute Gaussian lats (north to south)
 964            lats = gaussian_latitudes(nlats)
 965            if lat1 < lat2:  # reverse them if necessary
 966                lats = lats[::-1]
 967            lons,lats = np.meshgrid(lons,lats)
 968        elif gdtn in {10,20,30,31,110}:
 969            # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant
 970            dx,dy = self.gridlengthXDirection, self.gridlengthYDirection
 971            lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
 972            pj = pyproj.Proj(self.projParameters)
 973            llcrnrx, llcrnry = pj(lon1,lat1)
 974            x = llcrnrx+dx*np.arange(self.nx)
 975            y = llcrnry+dy*np.arange(self.ny)
 976            x,y = np.meshgrid(x, y)
 977            lons,lats = pj(x, y, inverse=True)
 978        elif gdtn == 90:
 979            # Satellite Projection
 980            dx = self.gridlengthXDirection
 981            dy = self.gridlengthYDirection
 982            pj = pyproj.Proj(self.projParameters)
 983            x = dx*np.indices((self.ny,self.nx),'f')[1,:,:]
 984            x -= 0.5*x.max()
 985            y = dy*np.indices((self.ny,self.nx),'f')[0,:,:]
 986            y -= 0.5*y.max()
 987            lons,lats = pj(x,y,inverse=True)
 988            # Set lons,lats to 1.e30 where undefined
 989            abslons = np.fabs(lons)
 990            abslats = np.fabs(lats)
 991            lons = np.where(abslons < 1.e20, lons, 1.e30)
 992            lats = np.where(abslats < 1.e20, lats, 1.e30)
 993        else:
 994            raise ValueError('Unsupported grid')
 995
 996        _latlon_datastore[self._sha1_section3] = (lats,lons)
 997
 998        return lats, lons
 999
1000
1001    def map_keys(self):
1002        """
1003        Returns an unpacked data grid where integer grid values are replaced with
1004        a string in which the numeric value is a representation of. These types
1005        of fields are cateogrical or classifications where data values do not
1006        represent an observable or predictable physical quantity.
1007
1008        An example of such a field field would be [Dominant Precipitation Type -
1009        DPTYPE](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-201.shtml)
1010
1011        Returns
1012        -------
1013
1014        **`numpy.ndarray`** of string values per element.
1015        """
1016        hold_auto_nans = _AUTO_NANS
1017        set_auto_nans(False)
1018        if (np.all(self.section1[0:2]==[7,14]) and self.shortName == 'PWTHER') or \
1019        (np.all(self.section1[0:2]==[8,65535]) and self.shortName == 'WX'):
1020            keys = utils.decode_wx_strings(self.section2)
1021            if hasattr(self,'priMissingValue') and self.priMissingValue not in [None,0]:
1022                keys[int(self.priMissingValue)] = 'Missing'
1023            if hasattr(self,'secMissingValue') and self.secMissingValue not in [None,0]:
1024                keys[int(self.secMissingValue)] = 'Missing'
1025            u,inv = np.unique(self.data,return_inverse=True)
1026            fld = np.array([keys[x] for x in u])[inv].reshape(self.data.shape)
1027        else:
1028            # For data whose units are defined in a code table (i.e. classification or mask)
1029            tblname = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0]
1030            fld = self.data.astype(np.int32).astype(str)
1031            tbl = tables.get_table(tblname,expand=True)
1032            for val in np.unique(fld):
1033                fld = np.where(fld==val,tbl[val],fld)
1034        set_auto_nans(hold_auto_nans)
1035        return fld
1036
1037
1038    def to_bytes(self, validate=True):
1039        """
1040        Return packed GRIB2 message in bytes format. This will be Useful for
1041        exporting data in non-file formats.  For example, can be used to
1042        output grib data directly to S3 using the boto3 client without the
1043        need to write a temporary file to upload first.
1044
1045        Parameters
1046        ----------
1047
1048        **`validate : bool, optional`**
1049
1050        If `True` (DEFAULT), validates first/last four bytes for proper
1051        formatting, else returns None. If `False`, message is output as is.
1052
1053        Returns
1054        -------
1055
1056        Returns GRIB2 formatted message as bytes.
1057        """
1058        if validate:
1059            if self._msg[0:4]+self._msg[-4:] == b'GRIB7777':
1060                return self._msg
1061            else:
1062                return None
1063        else:
1064            return self._msg
1065
1066
1067    def interpolate(self, method, grid_def_out, method_options=None):
1068        """
1069        Perform grid spatial interpolation via the [NCEPLIBS-ip library](https://github.com/NOAA-EMC/NCEPLIBS-ip).
1070
1071        **IMPORTANT:**  This interpolate method only supports scalar interpolation. If you
1072        need to perform vector interpolation, use the module-level `grib2io.interpolate` function.
1073
1074        Parameters
1075        ----------
1076
1077        **`method : int or str`**
1078
1079        Interpolate method to use. This can either be an integer or string using
1080        the following mapping:
1081
1082        | Interpolate Scheme | Integer Value |
1083        | :---:              | :---:         |
1084        | 'bilinear'         | 0             |
1085        | 'bicubic'          | 1             |
1086        | 'neighbor'         | 2             |
1087        | 'budget'           | 3             |
1088        | 'spectral'         | 4             |
1089        | 'neighbor-budget'  | 6             |
1090
1091        **`grid_def_out : grib2io.Grib2GridDef`**
1092
1093        Grib2GridDef object of the output grid.
1094
1095        **`method_options : list of ints, optional`**
1096
1097        Interpolation options. See the NCEPLIBS-ip doucmentation for
1098        more information on how these are used.
1099
1100        Returns
1101        -------
1102
1103        If interpolating to a grid, a new Grib2Message object is returned.  The GRIB2 metadata of
1104        the new Grib2Message object is indentical to the input except where required to be different
1105        because of the new grid specs.
1106
1107        If interpolating to station points, the interpolated data values are returned as a numpy.ndarray.
1108        """
1109        section0 = self.section0
1110        section0[-1] = 0
1111        gds = [0, grid_def_out.npoints, 0, 255, grid_def_out.gdtn]
1112        section3 = np.concatenate((gds,grid_def_out.gdt))
1113
1114        msg = Grib2Message(section0,self.section1,self.section2,section3,
1115                           self.section4,self.section5,self.bitMapFlag.value)
1116
1117        msg._msgnum = -1
1118        msg._deflist = self._deflist
1119        msg._coordlist = self._coordlist
1120        shape = (msg.ny,msg.nx)
1121        ndim = 2
1122        if msg.typeOfValues == 0:
1123            dtype = 'float32'
1124        elif msg.typeOfValues == 1:
1125            dtype = 'int32'
1126        msg._data = interpolate(self.data,method,Grib2GridDef.from_section3(self.section3),grid_def_out,
1127                                method_options=method_options).reshape(msg.ny,msg.nx)
1128        return msg

GRIB2 Message base class

_Grib2Message( section0: <built-in function array>, section1: <built-in function array>, section2: bytes, section3: <built-in function array>, section4: <built-in function array>, section5: <built-in function array>, bitMapFlag: grib2io.templates.Grib2Metadata = 255)
section0: <built-in function array>
section1: <built-in function array>
section2: bytes
section3: <built-in function array>
section4: <built-in function array>
section5: <built-in function array>
indicatorSection: <built-in function array>

GRIB2 Section 0, Indicator Section

identificationSection: <built-in function array>

GRIB2 Section 1, Identification Section

originatingCenter: grib2io.templates.Grib2Metadata

Identification of originating/generating center (See Table 0)

originatingSubCenter: grib2io.templates.Grib2Metadata

Identification of originating/generating subcenter (See Table C)

GRIB master tables version number (currently 2) (See Table 1.0)

Version number of GRIB local tables used to augment Master Tables (See Table 1.1)

significanceOfReferenceTime: grib2io.templates.Grib2Metadata

Significance of reference time (See Table 1.2)

year: int

Year of reference time

month: int

Month of reference time

day: int

Day of reference time

hour: int

Hour of reference time

minute: int

Minute of reference time

second: int

Second of reference time

refDate: datetime.datetime

Reference date as a datetime.datetime object

Production Status of Processed data in the GRIB message (See Table 1.3)

Type of processed data in this GRIB message (See Table 1.4)

gridDefinitionSection: <built-in function array>

GRIB2 Section 3, Grid Definition Section

sourceOfGridDefinition: int

Source of grid definition [(See Table 3.0)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-0.shtml

numberOfDataPoints: int

Number of Data Points

interpretationOfListOfNumbers: grib2io.templates.Grib2Metadata

Interpretation of List of Numbers

gridDefinitionTemplateNumber: grib2io.templates.Grib2Metadata

Grid definition template number [(See Table 3.1)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml

gridDefinitionTemplate: list

Grid definition template

earthRadius: float
earthMajorAxis: float
earthMinorAxis: float
resolutionAndComponentFlags: list
ny: int
nx: int
scanModeFlags: list
projParameters: dict
productDefinitionTemplateNumber: grib2io.templates.Grib2Metadata
productDefinitionTemplate: <built-in function array>
fullName: str
units: str
shortName: str
leadTime: datetime.timedelta
unitOfFirstFixedSurface: str
valueOfFirstFixedSurface: int
unitOfSecondFixedSurface: str
valueOfSecondFixedSurface: int
level: str
duration: datetime.timedelta
validDate: datetime.datetime
numberOfPackedValues: int
dataRepresentationTemplateNumber: grib2io.templates.Grib2Metadata
dataRepresentationTemplate: list
gdtn

Return Grid Definition Template Number

pdtn

Return Product Definition Template Number

drtn

Return Data Representation Template Number

pdy

Return the PDY ('YYYYMMDD')

griddef

Return a Grib2GridDef instance for a GRIB2 message

def attrs_by_section(self, sect, values=False):
750    def attrs_by_section(self, sect, values=False):
751        """
752        Provide a tuple of attribute names for the given GRIB2 section.
753
754        Parameters
755        ----------
756
757        **`sect : int`**
758
759        The GRIB2 section number.
760
761        **`values : bool, optional`**
762
763        Optional (default is `False`) arugment to return attributes values.
764
765        Returns
766        -------
767
768        A List attribute names or Dict if `values = True`.
769        """
770        if sect in {0,1,6}:
771            attrs = templates._section_attrs[sect]
772        elif sect in {3,4,5}:
773            def _find_class_index(n):
774                _key = {3:'Grid', 4:'Product', 5:'Data'}
775                for i,c in enumerate(self.__class__.__mro__):
776                    if _key[n] in c.__name__:
777                        return i
778                else:
779                    return []
780            if sys.version_info.minor <= 8:
781                attrs = templates._section_attrs[sect]+\
782                        [a for a in dir(self.__class__.__mro__[_find_class_index(sect)]) if not a.startswith('_')]
783            else:
784                attrs = templates._section_attrs[sect]+\
785                        self.__class__.__mro__[_find_class_index(sect)]._attrs
786        else:
787            attrs = []
788        if values:
789            return {k:getattr(self,k) for k in attrs}
790        else:
791            return attrs

Provide a tuple of attribute names for the given GRIB2 section.

Parameters

sect : int

The GRIB2 section number.

values : bool, optional

Optional (default is False) arugment to return attributes values.

Returns

A List attribute names or Dict if values = True.

def pack(self):
794    def pack(self):
795        """
796        Packs GRIB2 section data into a binary message.  It is the user's responsibility
797        to populate the GRIB2 section information with appropriate metadata.
798        """
799        # Create beginning of packed binary message with section 0 and 1 data.
800        self._sections = []
801        self._msg,self._pos = g2clib.grib2_create(self.indicatorSection[2:4],self.identificationSection)
802        self._sections += [0,1]
803
804        # Add section 2 if present.
805        if isinstance(self.section2,bytes) and len(self.section2) > 0:
806            self._msg,self._pos = g2clib.grib2_addlocal(self._msg,self.section2)
807            self._sections.append(2)
808
809        # Add section 3.
810        self._msg,self._pos = g2clib.grib2_addgrid(self._msg,self.gridDefinitionSection,
811                                                   self.gridDefinitionTemplate,
812                                                   self._deflist)
813        self._sections.append(3)
814
815        # Prepare data.
816        field = np.copy(self.data)
817        if self.scanModeFlags is not None:
818            if self.scanModeFlags[3]:
819                fieldsave = field.astype('f') # Casting makes a copy
820                field[1::2,:] = fieldsave[1::2,::-1]
821        fld = field.astype('f')
822
823        # Prepare bitmap, if necessary
824        bitmapflag = self.bitMapFlag.value
825        if bitmapflag == 0:
826            bmap = np.ravel(np.where(np.isnan(fld),0,1)).astype(DEFAULT_NUMPY_INT)
827        else:
828            bmap = None
829
830        # Prepare optional coordinate list
831        if self._coordlist is not None:
832            crdlist = np.array(self._coordlist,'f')
833        else:
834            crdlist = None
835
836        # Prepare data for packing if nans are present
837        fld = np.ravel(fld)
838        if np.isnan(fld).any() and hasattr(self,'_missvalmap'):
839            fld = np.where(self._missvalmap==1,self.priMissingValue,fld)
840            fld = np.where(self._missvalmap==2,self.secMissingValue,fld)
841
842        # Add sections 4, 5, 6 (if present), and 7.
843        self._msg,self._pos = g2clib.grib2_addfield(self._msg,self.pdtn,
844                                                    self.productDefinitionTemplate,
845                                                    crdlist,
846                                                    self.drtn,
847                                                    self.dataRepresentationTemplate,
848                                                    fld,
849                                                    bitmapflag,
850                                                    bmap)
851        self._sections.append(4)
852        self._sections.append(5)
853        if bmap is not None: self._sections.append(6)
854        self._sections.append(7)
855
856        # Finalize GRIB2 message with section 8.
857        self._msg, self._pos = g2clib.grib2_end(self._msg)
858        self._sections.append(8)
859        self.section0[-1] = len(self._msg)

Packs GRIB2 section data into a binary message. It is the user's responsibility to populate the GRIB2 section information with appropriate metadata.

data: <built-in function array>

Accessing the data attribute loads data into memmory

def latlons(self, *args, **kwrgs):
892    def latlons(self, *args, **kwrgs):
893        """Alias for `grib2io.Grib2Message.grid` method"""
894        return self.grid(*args, **kwrgs)

Alias for grib2io.Grib2Message.grid method

def grid(self, unrotate=True):
897    def grid(self, unrotate=True):
898        """
899        Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon,
900        global Gaussian, mercator, stereographic, lambert conformal, albers equal-area,
901        space-view and azimuthal equidistant grids.
902
903        Parameters
904        ----------
905
906        **`unrotate : bool`**
907
908        If `True` [DEFAULT], and grid is rotated lat/lon, then unrotate the grid,
909        otherwise `False`, do not.
910
911        Returns
912        -------
913
914        **`lats, lons : numpy.ndarray`**
915
916        Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and
917        longitudes in units of degrees.
918        """
919        if self._sha1_section3 in _latlon_datastore.keys():
920            return _latlon_datastore[self._sha1_section3]
921        gdtn = self.gridDefinitionTemplateNumber.value
922        gdtmpl = self.gridDefinitionTemplate
923        reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid
924        if gdtn == 0:
925            # Regular lat/lon grid
926            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
927            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
928            dlon = self.gridlengthXDirection
929            dlat = self.gridlengthYDirection
930            if lon2 < lon1 and dlon < 0: lon1 = -lon1
931            lats = np.linspace(lat1,lat2,self.ny)
932            if reggrid:
933                lons = np.linspace(lon1,lon2,self.nx)
934            else:
935                lons = np.linspace(lon1,lon2,self.ny*2)
936            lons,lats = np.meshgrid(lons,lats) # Make 2-d arrays.
937        elif gdtn == 1: # Rotated Lat/Lon grid
938            pj = pyproj.Proj(self.projParameters)
939            lat1,lon1 = self.latitudeFirstGridpoint,self.longitudeFirstGridpoint
940            lat2,lon2 = self.latitudeLastGridpoint,self.longitudeLastGridpoint
941            if lon1 > 180.0: lon1 -= 360.0
942            if lon2 > 180.0: lon2 -= 360.0
943            lats = np.linspace(lat1,lat2,self.ny)
944            lons = np.linspace(lon1,lon2,self.nx)
945            lons,lats = np.meshgrid(lons,lats) # Make 2-d arrays.
946            if unrotate:
947                from grib2io.utils import rotated_grid
948                lats,lons = rotated_grid.unrotate(lats,lons,self.anglePoleRotation,
949                                                  self.latitudeSouthernPole,
950                                                  self.longitudeSouthernPole)
951        elif gdtn == 40: # Gaussian grid (only works for global!)
952            from utils.gauss_grids import gaussian_latitudes
953            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
954            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
955            nlats = self.ny
956            if not reggrid: # Reduced Gaussian grid.
957                nlons = 2*nlats
958                dlon = 360./nlons
959            else:
960                nlons = self.nx
961                dlon = self.gridlengthXDirection
962            lons = np.arange(lon1,lon2+dlon,dlon)
963            # Compute Gaussian lats (north to south)
964            lats = gaussian_latitudes(nlats)
965            if lat1 < lat2:  # reverse them if necessary
966                lats = lats[::-1]
967            lons,lats = np.meshgrid(lons,lats)
968        elif gdtn in {10,20,30,31,110}:
969            # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant
970            dx,dy = self.gridlengthXDirection, self.gridlengthYDirection
971            lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
972            pj = pyproj.Proj(self.projParameters)
973            llcrnrx, llcrnry = pj(lon1,lat1)
974            x = llcrnrx+dx*np.arange(self.nx)
975            y = llcrnry+dy*np.arange(self.ny)
976            x,y = np.meshgrid(x, y)
977            lons,lats = pj(x, y, inverse=True)
978        elif gdtn == 90:
979            # Satellite Projection
980            dx = self.gridlengthXDirection
981            dy = self.gridlengthYDirection
982            pj = pyproj.Proj(self.projParameters)
983            x = dx*np.indices((self.ny,self.nx),'f')[1,:,:]
984            x -= 0.5*x.max()
985            y = dy*np.indices((self.ny,self.nx),'f')[0,:,:]
986            y -= 0.5*y.max()
987            lons,lats = pj(x,y,inverse=True)
988            # Set lons,lats to 1.e30 where undefined
989            abslons = np.fabs(lons)
990            abslats = np.fabs(lats)
991            lons = np.where(abslons < 1.e20, lons, 1.e30)
992            lats = np.where(abslats < 1.e20, lats, 1.e30)
993        else:
994            raise ValueError('Unsupported grid')
995
996        _latlon_datastore[self._sha1_section3] = (lats,lons)
997
998        return lats, lons

Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon, global Gaussian, mercator, stereographic, lambert conformal, albers equal-area, space-view and azimuthal equidistant grids.

Parameters

unrotate : bool

If True [DEFAULT], and grid is rotated lat/lon, then unrotate the grid, otherwise False, do not.

Returns

lats, lons : numpy.ndarray

Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and longitudes in units of degrees.

def map_keys(self):
1001    def map_keys(self):
1002        """
1003        Returns an unpacked data grid where integer grid values are replaced with
1004        a string in which the numeric value is a representation of. These types
1005        of fields are cateogrical or classifications where data values do not
1006        represent an observable or predictable physical quantity.
1007
1008        An example of such a field field would be [Dominant Precipitation Type -
1009        DPTYPE](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-201.shtml)
1010
1011        Returns
1012        -------
1013
1014        **`numpy.ndarray`** of string values per element.
1015        """
1016        hold_auto_nans = _AUTO_NANS
1017        set_auto_nans(False)
1018        if (np.all(self.section1[0:2]==[7,14]) and self.shortName == 'PWTHER') or \
1019        (np.all(self.section1[0:2]==[8,65535]) and self.shortName == 'WX'):
1020            keys = utils.decode_wx_strings(self.section2)
1021            if hasattr(self,'priMissingValue') and self.priMissingValue not in [None,0]:
1022                keys[int(self.priMissingValue)] = 'Missing'
1023            if hasattr(self,'secMissingValue') and self.secMissingValue not in [None,0]:
1024                keys[int(self.secMissingValue)] = 'Missing'
1025            u,inv = np.unique(self.data,return_inverse=True)
1026            fld = np.array([keys[x] for x in u])[inv].reshape(self.data.shape)
1027        else:
1028            # For data whose units are defined in a code table (i.e. classification or mask)
1029            tblname = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0]
1030            fld = self.data.astype(np.int32).astype(str)
1031            tbl = tables.get_table(tblname,expand=True)
1032            for val in np.unique(fld):
1033                fld = np.where(fld==val,tbl[val],fld)
1034        set_auto_nans(hold_auto_nans)
1035        return fld

Returns an unpacked data grid where integer grid values are replaced with a string in which the numeric value is a representation of. These types of fields are cateogrical or classifications where data values do not represent an observable or predictable physical quantity.

An example of such a field field would be Dominant Precipitation Type - DPTYPE

Returns

numpy.ndarray of string values per element.

def to_bytes(self, validate=True):
1038    def to_bytes(self, validate=True):
1039        """
1040        Return packed GRIB2 message in bytes format. This will be Useful for
1041        exporting data in non-file formats.  For example, can be used to
1042        output grib data directly to S3 using the boto3 client without the
1043        need to write a temporary file to upload first.
1044
1045        Parameters
1046        ----------
1047
1048        **`validate : bool, optional`**
1049
1050        If `True` (DEFAULT), validates first/last four bytes for proper
1051        formatting, else returns None. If `False`, message is output as is.
1052
1053        Returns
1054        -------
1055
1056        Returns GRIB2 formatted message as bytes.
1057        """
1058        if validate:
1059            if self._msg[0:4]+self._msg[-4:] == b'GRIB7777':
1060                return self._msg
1061            else:
1062                return None
1063        else:
1064            return self._msg

Return packed GRIB2 message in bytes format. This will be Useful for exporting data in non-file formats. For example, can be used to output grib data directly to S3 using the boto3 client without the need to write a temporary file to upload first.

Parameters

validate : bool, optional

If True (DEFAULT), validates first/last four bytes for proper formatting, else returns None. If False, message is output as is.

Returns

Returns GRIB2 formatted message as bytes.

def interpolate(self, method, grid_def_out, method_options=None):
1067    def interpolate(self, method, grid_def_out, method_options=None):
1068        """
1069        Perform grid spatial interpolation via the [NCEPLIBS-ip library](https://github.com/NOAA-EMC/NCEPLIBS-ip).
1070
1071        **IMPORTANT:**  This interpolate method only supports scalar interpolation. If you
1072        need to perform vector interpolation, use the module-level `grib2io.interpolate` function.
1073
1074        Parameters
1075        ----------
1076
1077        **`method : int or str`**
1078
1079        Interpolate method to use. This can either be an integer or string using
1080        the following mapping:
1081
1082        | Interpolate Scheme | Integer Value |
1083        | :---:              | :---:         |
1084        | 'bilinear'         | 0             |
1085        | 'bicubic'          | 1             |
1086        | 'neighbor'         | 2             |
1087        | 'budget'           | 3             |
1088        | 'spectral'         | 4             |
1089        | 'neighbor-budget'  | 6             |
1090
1091        **`grid_def_out : grib2io.Grib2GridDef`**
1092
1093        Grib2GridDef object of the output grid.
1094
1095        **`method_options : list of ints, optional`**
1096
1097        Interpolation options. See the NCEPLIBS-ip doucmentation for
1098        more information on how these are used.
1099
1100        Returns
1101        -------
1102
1103        If interpolating to a grid, a new Grib2Message object is returned.  The GRIB2 metadata of
1104        the new Grib2Message object is indentical to the input except where required to be different
1105        because of the new grid specs.
1106
1107        If interpolating to station points, the interpolated data values are returned as a numpy.ndarray.
1108        """
1109        section0 = self.section0
1110        section0[-1] = 0
1111        gds = [0, grid_def_out.npoints, 0, 255, grid_def_out.gdtn]
1112        section3 = np.concatenate((gds,grid_def_out.gdt))
1113
1114        msg = Grib2Message(section0,self.section1,self.section2,section3,
1115                           self.section4,self.section5,self.bitMapFlag.value)
1116
1117        msg._msgnum = -1
1118        msg._deflist = self._deflist
1119        msg._coordlist = self._coordlist
1120        shape = (msg.ny,msg.nx)
1121        ndim = 2
1122        if msg.typeOfValues == 0:
1123            dtype = 'float32'
1124        elif msg.typeOfValues == 1:
1125            dtype = 'int32'
1126        msg._data = interpolate(self.data,method,Grib2GridDef.from_section3(self.section3),grid_def_out,
1127                                method_options=method_options).reshape(msg.ny,msg.nx)
1128        return msg

Perform grid spatial interpolation via the NCEPLIBS-ip library.

IMPORTANT: This interpolate method only supports scalar interpolation. If you need to perform vector interpolation, use the module-level grib2io.interpolate function.

Parameters

method : int or str

Interpolate method to use. This can either be an integer or string using the following mapping:

Interpolate Scheme Integer Value
'bilinear' 0
'bicubic' 1
'neighbor' 2
'budget' 3
'spectral' 4
'neighbor-budget' 6

grid_def_out : grib2io.Grib2GridDef

Grib2GridDef object of the output grid.

method_options : list of ints, optional

Interpolation options. See the NCEPLIBS-ip doucmentation for more information on how these are used.

Returns

If interpolating to a grid, a new Grib2Message object is returned. The GRIB2 metadata of the new Grib2Message object is indentical to the input except where required to be different because of the new grid specs.

If interpolating to station points, the interpolated data values are returned as a numpy.ndarray.

def show_config():
15def show_config():
16    """
17    Print grib2io build configuration information.
18    """
19    from g2clib import __version__ as g2clib_version
20    from g2clib import _has_png as have_png
21    from g2clib import _has_jpeg as have_jpeg
22    print("grib2io version %s Configuration:\n"%(__version__))
23    print("\tg2c library version:".expandtabs(4),g2clib_version)
24    print("\tJPEG compression support:".expandtabs(4),bool(have_jpeg))
25    print("\tPNG compression support:".expandtabs(4),bool(have_png))

Print grib2io build configuration information.

def interpolate(a, method, grid_def_in, grid_def_out, method_options=None):
1267def interpolate(a, method, grid_def_in, grid_def_out, method_options=None):
1268    """
1269    This is the module-level interpolation function that interfaces with the grib2io_interp
1270    component pakcage that interfaces to the [NCEPLIBS-ip library](https://github.com/NOAA-EMC/NCEPLIBS-ip).
1271    It supports scalar and vector interpolation according to the type of object a.  It also
1272    supports scalar and vector interpolation to station points when grid_def_out is set up
1273    properly for station interpolation.
1274
1275    Parameters
1276    ----------
1277
1278    **`a : numpy.ndarray or tuple`**
1279
1280    Input data.  If `a` is a `numpy.ndarray`, scalar interpolation will be
1281    performed.  If `a` is a `tuple`, then vector interpolation will be performed
1282    with the assumption that u = a[0] and v = a[1] and are both `numpy.ndarray`.
1283
1284    These data are expected to be in 2-dimensional form with shape (ny, nx) or
1285    3-dimensional (:, ny, nx) where the 1st dimension represents another spatial,
1286    temporal, or classification (i.e. ensemble members) dimension. The function will
1287    properly flatten the (ny,nx) dimensions into (nx * ny) acceptable for input into
1288    the interpolation subroutines.
1289
1290    **`method : int or str`**
1291
1292    Interpolate method to use. This can either be an integer or string using
1293    the following mapping:
1294
1295    | Interpolate Scheme | Integer Value |
1296    | :---:              | :---:         |
1297    | 'bilinear'         | 0             |
1298    | 'bicubic'          | 1             |
1299    | 'neighbor'         | 2             |
1300    | 'budget'           | 3             |
1301    | 'spectral'         | 4             |
1302    | 'neighbor-budget'  | 6             |
1303
1304    **`grid_def_in : grib2io.Grib2GridDef`**
1305
1306    Grib2GridDef object for the input grid.
1307
1308    **`grid_def_out : grib2io.Grib2GridDef`**
1309
1310    Grib2GridDef object for the output grid or station points.
1311
1312    **`method_options : list of ints, optional`**
1313
1314    Interpolation options. See the NCEPLIBS-ip doucmentation for
1315    more information on how these are used.
1316
1317    Returns
1318    -------
1319
1320    Returns a `numpy.ndarray` when scalar interpolation is performed or
1321    a `tuple` of `numpy.ndarray`s when vector interpolation is performed
1322    with the assumptions that 0-index is the interpolated u and 1-index
1323    is the interpolated v.
1324    """
1325    from grib2io_interp import interpolate
1326
1327    interp_schemes = {'bilinear':0, 'bicubic':1, 'neighbor':2,
1328                      'budget':3, 'spectral':4, 'neighbor-budget':6}
1329
1330    if isinstance(method,int) and method not in interp_schemes.values():
1331        raise ValueError('Invalid interpolation method.')
1332    elif isinstance(method,str):
1333        if method in interp_schemes.keys():
1334            method = interp_schemes[method]
1335        else:
1336            raise ValueError('Invalid interpolation method.')
1337
1338    if method_options is None:
1339        method_options = np.zeros((20),dtype=np.int32)
1340        if method in {3,6}:
1341            method_options[0:2] = -1
1342
1343    ni = grid_def_in.npoints
1344    no = grid_def_out.npoints
1345
1346    # Adjust shape of input array(s)
1347    a,newshp = _adjust_array_shape_for_interp(a,grid_def_in,grid_def_out)
1348
1349    # Set lats and lons if stations, else create array for grids.
1350    if grid_def_out.gdtn == -1:
1351        rlat = np.array(grid_def_out.lats,dtype=np.float32)
1352        rlon = np.array(grid_def_out.lons,dtype=np.float32)
1353    else:
1354        rlat = np.zeros((no),dtype=np.float32)
1355        rlon = np.zeros((no),dtype=np.float32)
1356
1357    # Call interpolation subroutines according to type of a.
1358    if isinstance(a,np.ndarray):
1359        # Scalar
1360        ibi = np.zeros((a.shape[0]),dtype=np.int32)
1361        li = np.zeros(a.shape,dtype=np.int32)
1362        go = np.zeros((a.shape[0],no),dtype=np.float32)
1363        no,ibo,lo,iret = interpolate.interpolate_scalar(method,method_options,
1364                                                 grid_def_in.gdtn,grid_def_in.gdt,
1365                                                 grid_def_out.gdtn,grid_def_out.gdt,
1366                                                 ibi,li.T,a.T,go.T,rlat,rlon)
1367        out = go.reshape(newshp)
1368    elif isinstance(a,tuple):
1369        # Vector
1370        ibi = np.zeros((a[0].shape[0]),dtype=np.int32)
1371        li = np.zeros(a[0].shape,dtype=np.int32)
1372        uo = np.zeros((a[0].shape[0],no),dtype=np.float32)
1373        vo = np.zeros((a[1].shape[0],no),dtype=np.float32)
1374        crot = np.ones((no),dtype=np.float32)
1375        srot = np.zeros((no),dtype=np.float32)
1376        no,ibo,lo,iret = interpolate.interpolate_vector(method,method_options,
1377                                                 grid_def_in.gdtn,grid_def_in.gdt,
1378                                                 grid_def_out.gdtn,grid_def_out.gdt,
1379                                                 ibi,li.T,a[0].T,a[1].T,uo.T,vo.T,
1380                                                 rlat,rlon,crot,srot)
1381        del crot
1382        del srot
1383        out = (uo.reshape(newshp),vo.reshape(newshp))
1384
1385    del rlat
1386    del rlon
1387    return out

This is the module-level interpolation function that interfaces with the grib2io_interp component pakcage that interfaces to the NCEPLIBS-ip library. It supports scalar and vector interpolation according to the type of object a. It also supports scalar and vector interpolation to station points when grid_def_out is set up properly for station interpolation.

Parameters

a : numpy.ndarray or tuple

Input data. If a is a numpy.ndarray, scalar interpolation will be performed. If a is a tuple, then vector interpolation will be performed with the assumption that u = a[0] and v = a[1] and are both numpy.ndarray.

These data are expected to be in 2-dimensional form with shape (ny, nx) or 3-dimensional (:, ny, nx) where the 1st dimension represents another spatial, temporal, or classification (i.e. ensemble members) dimension. The function will properly flatten the (ny,nx) dimensions into (nx * ny) acceptable for input into the interpolation subroutines.

method : int or str

Interpolate method to use. This can either be an integer or string using the following mapping:

Interpolate Scheme Integer Value
'bilinear' 0
'bicubic' 1
'neighbor' 2
'budget' 3
'spectral' 4
'neighbor-budget' 6

grid_def_in : grib2io.Grib2GridDef

Grib2GridDef object for the input grid.

grid_def_out : grib2io.Grib2GridDef

Grib2GridDef object for the output grid or station points.

method_options : list of ints, optional

Interpolation options. See the NCEPLIBS-ip doucmentation for more information on how these are used.

Returns

Returns a numpy.ndarray when scalar interpolation is performed or a tuple of numpy.ndarrays when vector interpolation is performed with the assumptions that 0-index is the interpolated u and 1-index is the interpolated v.

def interpolate_to_stations(a, method, grid_def_in, lats, lons, method_options=None):
1390def interpolate_to_stations(a, method, grid_def_in, lats, lons, method_options=None):
1391    """
1392    This is the module-level interpolation function **for interpolation to stations**
1393    that interfaces with the grib2io_interp component pakcage that interfaces to
1394    the [NCEPLIBS-ip library](https://github.com/NOAA-EMC/NCEPLIBS-ip). It supports
1395    scalar and vector interpolation according to the type of object a.
1396
1397    Parameters
1398    ----------
1399
1400    **`a : numpy.ndarray or tuple`**
1401
1402    Input data.  If `a` is a `numpy.ndarray`, scalar interpolation will be
1403    performed.  If `a` is a `tuple`, then vector interpolation will be performed
1404    with the assumption that u = a[0] and v = a[1] and are both `numpy.ndarray`.
1405
1406    These data are expected to be in 2-dimensional form with shape (ny, nx) or
1407    3-dimensional (:, ny, nx) where the 1st dimension represents another spatial,
1408    temporal, or classification (i.e. ensemble members) dimension. The function will
1409    properly flatten the (ny,nx) dimensions into (nx * ny) acceptable for input into
1410    the interpolation subroutines.
1411
1412    **`method : int or str`**
1413
1414    Interpolate method to use. This can either be an integer or string using
1415    the following mapping:
1416
1417    | Interpolate Scheme | Integer Value |
1418    | :---:              | :---:         |
1419    | 'bilinear'         | 0             |
1420    | 'bicubic'          | 1             |
1421    | 'neighbor'         | 2             |
1422    | 'budget'           | 3             |
1423    | 'spectral'         | 4             |
1424    | 'neighbor-budget'  | 6             |
1425
1426    **`grid_def_in : grib2io.Grib2GridDef`**
1427
1428    Grib2GridDef object for the input grid.
1429
1430    **`lats : numpy.ndarray or list`**
1431
1432    Latitudes for station points
1433
1434    **`lons : numpy.ndarray or list`**
1435
1436    Longitudes for station points
1437
1438    **`method_options : list of ints, optional`**
1439
1440    Interpolation options. See the NCEPLIBS-ip doucmentation for
1441    more information on how these are used.
1442
1443    Returns
1444    -------
1445
1446    Returns a `numpy.ndarray` when scalar interpolation is performed or
1447    a `tuple` of `numpy.ndarray`s when vector interpolation is performed
1448    with the assumptions that 0-index is the interpolated u and 1-index
1449    is the interpolated v.
1450    """
1451    from grib2io_interp import interpolate
1452
1453    interp_schemes = {'bilinear':0, 'bicubic':1, 'neighbor':2,
1454                      'budget':3, 'spectral':4, 'neighbor-budget':6}
1455
1456    if isinstance(method,int) and method not in interp_schemes.values():
1457        raise ValueError('Invalid interpolation method.')
1458    elif isinstance(method,str):
1459        if method in interp_schemes.keys():
1460            method = interp_schemes[method]
1461        else:
1462            raise ValueError('Invalid interpolation method.')
1463
1464    if method_options is None:
1465        method_options = np.zeros((20),dtype=np.int32)
1466        if method in {3,6}:
1467            method_options[0:2] = -1
1468
1469    # Check lats and lons
1470    if isinstance(lats,list):
1471        nlats = len(lats)
1472    elif isinstance(lats,np.ndarray) and len(lats.shape) == 1:
1473        nlats = lats.shape[0]
1474    else:
1475        raise ValueError('Station latitudes must be a list or 1-D NumPy array.')
1476    if isinstance(lons,list):
1477        nlons = len(lons)
1478    elif isinstance(lons,np.ndarray) and len(lons.shape) == 1:
1479        nlons = lons.shape[0]
1480    else:
1481        raise ValueError('Station longitudes must be a list or 1-D NumPy array.')
1482    if nlats != nlons:
1483        raise ValueError('Station lats and lons must be same size.')
1484
1485    ni = grid_def_in.npoints
1486    no = nlats
1487
1488    # Adjust shape of input array(s)
1489    a,newshp = _adjust_array_shape_for_interp_stations(a,grid_def_in,no)
1490
1491    # Set lats and lons if stations
1492    rlat = np.array(lats,dtype=np.float32)
1493    rlon = np.array(lons,dtype=np.float32)
1494
1495    # Use gdtn = -1 for stations and an empty template array
1496    gdtn = -1
1497    gdt = np.zeros((200),dtype=np.int32)
1498
1499    # Call interpolation subroutines according to type of a.
1500    if isinstance(a,np.ndarray):
1501        # Scalar
1502        ibi = np.zeros((a.shape[0]),dtype=np.int32)
1503        li = np.zeros(a.shape,dtype=np.int32)
1504        go = np.zeros((a.shape[0],no),dtype=np.float32)
1505        no,ibo,lo,iret = interpolate.interpolate_scalar(method,method_options,
1506                                                 grid_def_in.gdtn,grid_def_in.gdt,
1507                                                 gdtn,gdt,
1508                                                 ibi,li.T,a.T,go.T,rlat,rlon)
1509        out = go.reshape(newshp)
1510    elif isinstance(a,tuple):
1511        # Vector
1512        ibi = np.zeros((a[0].shape[0]),dtype=np.int32)
1513        li = np.zeros(a[0].shape,dtype=np.int32)
1514        uo = np.zeros((a[0].shape[0],no),dtype=np.float32)
1515        vo = np.zeros((a[1].shape[0],no),dtype=np.float32)
1516        crot = np.ones((no),dtype=np.float32)
1517        srot = np.zeros((no),dtype=np.float32)
1518        no,ibo,lo,iret = interpolate.interpolate_vector(method,method_options,
1519                                                 grid_def_in.gdtn,grid_def_in.gdt,
1520                                                 gdtn,gdt,
1521                                                 ibi,li.T,a[0].T,a[1].T,uo.T,vo.T,
1522                                                 rlat,rlon,crot,srot)
1523        del crot
1524        del srot
1525        out = (uo.reshape(newshp),vo.reshape(newshp))
1526
1527    del rlat
1528    del rlon
1529    return out

This is the module-level interpolation function for interpolation to stations that interfaces with the grib2io_interp component pakcage that interfaces to the NCEPLIBS-ip library. It supports scalar and vector interpolation according to the type of object a.

Parameters

a : numpy.ndarray or tuple

Input data. If a is a numpy.ndarray, scalar interpolation will be performed. If a is a tuple, then vector interpolation will be performed with the assumption that u = a[0] and v = a[1] and are both numpy.ndarray.

These data are expected to be in 2-dimensional form with shape (ny, nx) or 3-dimensional (:, ny, nx) where the 1st dimension represents another spatial, temporal, or classification (i.e. ensemble members) dimension. The function will properly flatten the (ny,nx) dimensions into (nx * ny) acceptable for input into the interpolation subroutines.

method : int or str

Interpolate method to use. This can either be an integer or string using the following mapping:

Interpolate Scheme Integer Value
'bilinear' 0
'bicubic' 1
'neighbor' 2
'budget' 3
'spectral' 4
'neighbor-budget' 6

grid_def_in : grib2io.Grib2GridDef

Grib2GridDef object for the input grid.

lats : numpy.ndarray or list

Latitudes for station points

lons : numpy.ndarray or list

Longitudes for station points

method_options : list of ints, optional

Interpolation options. See the NCEPLIBS-ip doucmentation for more information on how these are used.

Returns

Returns a numpy.ndarray when scalar interpolation is performed or a tuple of numpy.ndarrays when vector interpolation is performed with the assumptions that 0-index is the interpolated u and 1-index is the interpolated v.

@dataclass
class Grib2GridDef:
1532@dataclass
1533class Grib2GridDef:
1534    """
1535    Class to hold GRIB2 Grid Definition Template Number and Template as
1536    class attributes. This allows for cleaner looking code when passing these
1537    metadata around.  For example, the `grib2io._Grib2Message.interpolate`
1538    method and `grib2io.interpolate` function accepts these objects.
1539    """
1540    gdtn: int
1541    gdt: np.array
1542
1543    @classmethod
1544    def from_section3(cls, section3):
1545        return cls(section3[4],section3[5:])
1546
1547    @property
1548    def nx(self):
1549        return self.gdt[7]
1550
1551    @property
1552    def ny(self):
1553        return self.gdt[8]
1554
1555    @property
1556    def npoints(self):
1557        return self.gdt[7] * self.gdt[8]
1558
1559    @property
1560    def shape(self):
1561        return (self.ny, self.nx)

Class to hold GRIB2 Grid Definition Template Number and Template as class attributes. This allows for cleaner looking code when passing these metadata around. For example, the grib2io._Grib2Message.interpolate method and grib2io.interpolate function accepts these objects.

Grib2GridDef(gdtn: int, gdt: <built-in function array>)
gdtn: int
gdt: <built-in function array>
@classmethod
def from_section3(cls, section3):
1543    @classmethod
1544    def from_section3(cls, section3):
1545        return cls(section3[4],section3[5:])
nx
ny
npoints
shape