# 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.

View Source

     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: View Source

     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) View Source

     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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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: View Source

    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: View Source

     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>
bitMapFlag: grib2io.templates.Grib2Metadata = 255
indicatorSection: <built-in function array>

GRIB2 Section 0, Indicator Section

discipline: grib2io.templates.Grib2Metadata

Discipline From Table 0.0

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)

masterTableInfo: grib2io.templates.Grib2Metadata

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

localTableInfo: grib2io.templates.Grib2Metadata

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

productionStatus: grib2io.templates.Grib2Metadata

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

typeOfData: grib2io.templates.Grib2Metadata

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

shapeOfEarth: grib2io.templates.Grib2Metadata
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
typeOfValues: grib2io.templates.Grib2Metadata
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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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): View Source

    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(): View Source

    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): View Source

    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.ndarray`s 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): View Source

    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.ndarray`s 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: View Source

    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): View Source

    1543    @classmethod
    1544    def from_section3(cls, section3):
    1545        return cls(section3[4],section3[5:])

nx
ny
npoints
shape