grib2io API documentation

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

View Source

     1from ._grib2io import *
     2from ._grib2io import __doc__
     3
     4try:
     5    from . import __config__
     6    __version__ = __config__.grib2io_version
     7except(ImportError):
     8    pass
     9
    10__all__ = ['open','Grib2Message','Grib2Metadata','show_config','tables','utils']
    11
    12def show_config():
    13    """
    14    Print grib2io build configuration information.
    15    """
    16    from g2clib import __version__ as g2clib_version
    17    have_jpeg = True if 'jasper' in __config__.libraries or 'openjp2' in __config__.libraries else False
    18    have_png = True if 'png' in __config__.libraries else False
    19    jpeglib = 'OpenJPEG' if 'openjp2' in __config__.libraries else ('Jasper' if 'jasper' in __config__.libraries else None)
    20    pnglib = 'libpng' if 'png' in __config__.libraries else None
    21    print("grib2io version %s Configuration:\n"%(__version__))
    22    print("\tg2c library version:".expandtabs(4),g2clib_version)
    23    print("\tJPEG compression support:".expandtabs(4),have_jpeg)
    24    if have_jpeg: print("\t\tLibrary:".expandtabs(4),jpeglib)
    25    print("\tPNG compression support:".expandtabs(4),have_png)
    26    if have_png: print("\t\tLibrary:".expandtabs(4),pnglib)

class open: View Source

     41class open():
     42    """
     43    GRIB2 File Object.  A physical file can contain one or more GRIB2 messages.  When instantiated,
     44    class `grib2io.open`, the file named `filename` is opened for reading (`mode = 'r'`) and is
     45    automatically indexed.  The indexing procedure reads some of the GRIB2 metadata for all GRIB2 Messages.
     46
     47    A GRIB2 Message may contain submessages whereby Section 2-7 can be repeated.  grib2io accommodates
     48    for this by flattening any GRIB2 submessages into multiple individual messages.
     49
     50    Attributes
     51    ----------
     52
     53    **`mode`**: File IO mode of opening the file.
     54
     55    **`name`**: Full path name of the GRIB2 file.
     56
     57    **`messages`**: Count of GRIB2 Messages contained in the file.
     58
     59    **`current_message`**: Current position of the file in units of GRIB2 Messages.
     60
     61    **`size`**: Size of the file in units of bytes.
     62
     63    **`closed`** `True` is file handle is close; `False` otherwise.
     64
     65    **`decode `**: If `True` [DEFAULT] automatically decode metadata from unpacked
     66    section data for Grib2Messages.
     67
     68    **`variables`**: Tuple containing a unique list of variable short names (i.e. GRIB2 abbreviation names).
     69
     70    **`levels`**: Tuple containing a unique list of wgrib2-formatted level/layer strings.
     71    """
     72    def __init__(self, filename, mode='r', decode=True):
     73        """
     74        `open` Constructor
     75
     76        Parameters
     77        ----------
     78
     79        **`filename`**: File name containing GRIB2 messages.
     80
     81        **`mode `**: File access mode where `r` opens the files for reading only;
     82        `w` opens the file for writing.
     83
     84        **`decode `**: If `True` [DEFAULT] automatically decode metadata from
     85        unpacked section data for Grib2Messages.
     86        """
     87        if mode in ['a','r','w']:
     88            mode = mode+'b'
     89        self._filehandle = builtins.open(filename,mode=mode,buffering=ONE_MB)
     90        self._hasindex = False
     91        self._index = {}
     92        self.mode = mode
     93        self.name = os.path.abspath(filename)
     94        self.messages = 0
     95        self.current_message = 0
     96        self.size = os.path.getsize(self.name)
     97        self.closed = self._filehandle.closed
     98        self.decode = decode
     99        if 'r' in self.mode: self._build_index()
    100        # FIX: Cannot perform reads on mode='a'
    101        #if 'a' in self.mode and self.size > 0: self._build_index()
    102        if self._hasindex:
    103            self.variables = tuple(sorted(set(filter(None,self._index['shortName']))))
    104            self.levels = tuple(sorted(set(filter(None,self._index['levelString']))))
    105
    106
    107    def __delete__(self, instance):
    108        """
    109        """
    110        self.close()
    111        del self._index
    112
    113
    114    def __enter__(self):
    115        """
    116        """
    117        return self
    118
    119
    120    def __exit__(self, atype, value, traceback):
    121        """
    122        """
    123        self.close()
    124
    125
    126    def __iter__(self):
    127        """
    128        """
    129        return self
    130
    131
    132    def __next__(self):
    133        """
    134        """
    135        if self.current_message < self.messages:
    136            return self.read()[0]
    137        else:
    138            self.seek(0)
    139            raise StopIteration
    140
    141
    142    def __repr__(self):
    143        """
    144        """
    145        strings = []
    146        keys = self.__dict__.keys()
    147        for k in keys:
    148            if not k.startswith('_'):
    149                strings.append('%s = %s\n'%(k,self.__dict__[k]))
    150        return ''.join(strings)
    151
    152
    153    def __getitem__(self, key):
    154        """
    155        """
    156        if isinstance(key,slice):
    157            if key.start is None and key.stop is None and key.step is None:
    158                beg = 1
    159                end = self.messages+1
    160                inc = 1
    161            else:
    162                beg, end, inc = key.indices(self.messages)
    163            return [self[i][0] for i in range(beg,end,inc)]
    164        elif isinstance(key,int):
    165            if key == 0: return None
    166            self._filehandle.seek(self._index['offset'][key])
    167            return [Grib2Message(msg=self._filehandle.read(self._index['size'][key]),
    168                                 source=self,
    169                                 num=self._index['messageNumber'][key],
    170                                 decode=self.decode)]
    171        elif isinstance(key,str):
    172            return self.select(shortName=key)
    173        else:
    174            raise KeyError('Key must be an integer, slice, or GRIB2 variable shortName.')
    175
    176
    177    def _build_index(self):
    178        """
    179        Perform indexing of GRIB2 Messages.
    180        """
    181        # Initialize index dictionary
    182        self._index['offset'] = [None]
    183        self._index['discipline'] = [None]
    184        self._index['edition'] = [None]
    185        self._index['size'] = [None]
    186        self._index['submessageOffset'] = [None]
    187        self._index['submessageBeginSection'] = [None]
    188        self._index['isSubmessage'] = [None]
    189        self._index['messageNumber'] = [None]
    190        self._index['identificationSection'] = [None]
    191        self._index['refDate'] = [None]
    192        self._index['productDefinitionTemplateNumber'] = [None]
    193        self._index['productDefinitionTemplate'] = [None]
    194        self._index['leadTime'] = [None]
    195        self._index['duration'] = [None]
    196        self._index['shortName'] = [None]
    197        self._index['bitMap'] = [None]
    198        self._index['levelString'] = [None]
    199        self._index['probString'] = [None]
    200
    201        # Iterate
    202        while True:
    203            try:
    204                # Read first 4 bytes and decode...looking for "GRIB"
    205                pos = self._filehandle.tell()
    206                header = struct.unpack('>4s',self._filehandle.read(4))[0].decode()
    207
    208                # Test header. Then get information from GRIB2 Section 0: the discipline
    209                # number, edition number (should always be 2), and GRIB2 message size.
    210                # Then iterate to check for submessages.
    211                if header == 'GRIB':
    212                    _issubmessage = False
    213                    _submsgoffset = 0
    214                    _submsgbegin = 0
    215
    216                    # Read and unpack Section 0. Note that this is not done through
    217                    # the g2clib.
    218                    self._filehandle.seek(self._filehandle.tell()+2)
    219                    discipline = int(struct.unpack('>B',self._filehandle.read(1))[0])
    220                    edition = int(struct.unpack('>B',self._filehandle.read(1))[0])
    221                    assert edition == 2
    222                    size = struct.unpack('>Q',self._filehandle.read(8))[0]
    223
    224                    # Read and unpack Section 1
    225                    secsize = struct.unpack('>i',self._filehandle.read(4))[0]
    226                    secnum = struct.unpack('>B',self._filehandle.read(1))[0]
    227                    assert secnum == 1
    228                    self._filehandle.seek(self._filehandle.tell()-5)
    229                    _grbmsg = self._filehandle.read(secsize)
    230                    _grbpos = 0
    231                    _grbsec1,_grbpos = g2clib.unpack1(_grbmsg,_grbpos,np.empty)
    232                    _grbsec1 = _grbsec1.tolist()
    233                    _refdate = utils.getdate(_grbsec1[5],_grbsec1[6],_grbsec1[7],_grbsec1[8])
    234                    secrange = range(2,8)
    235                    while 1:
    236                        for num in secrange:
    237                            secsize = struct.unpack('>i',self._filehandle.read(4))[0]
    238                            secnum = struct.unpack('>B',self._filehandle.read(1))[0]
    239                            if secnum == num:
    240                                if secnum == 3:
    241                                    self._filehandle.seek(self._filehandle.tell()-5)
    242                                    _grbmsg = self._filehandle.read(secsize)
    243                                    _grbpos = 0
    244                                    # Unpack Section 3
    245                                    _gds,_gdtn,_deflist,_grbpos = g2clib.unpack3(_grbmsg,_grbpos,np.empty)
    246                                elif secnum == 4:
    247                                    self._filehandle.seek(self._filehandle.tell()-5)
    248                                    _grbmsg = self._filehandle.read(secsize)
    249                                    _grbpos = 0
    250                                    # Unpack Section 4
    251                                    _pdt,_pdtnum,_coordlist,_grbpos = g2clib.unpack4(_grbmsg,_grbpos,np.empty)
    252                                    _pdt = _pdt.tolist()
    253                                    _varinfo = tables.get_varinfo_from_table(discipline,_pdt[0],_pdt[1])
    254                                elif secnum == 6:
    255                                    self._filehandle.seek(self._filehandle.tell()-5)
    256                                    _grbmsg = self._filehandle.read(secsize)
    257                                    _grbpos = 0
    258                                    # Unpack Section 6. Save bitmap
    259                                    _bmap,_bmapflag = g2clib.unpack6(_grbmsg,_gds[1],_grbpos,np.empty)
    260                                    if _bmapflag == 0:
    261                                        _bmap_save = copy.deepcopy(_bmap)
    262                                    elif _bmapflag == 254:
    263                                        _bmap = copy.deepcopy(_bmap_save)
    264                                else:
    265                                    self._filehandle.seek(self._filehandle.tell()+secsize-5)
    266                            else:
    267                                if num == 2 and secnum == 3:
    268                                    pass # Allow this.  Just means no Local Use Section.
    269                                else:
    270                                    _issubmessage = True
    271                                    _submsgoffset = (self._filehandle.tell()-5)-(self._index['offset'][self.messages])
    272                                    _submsgbegin = secnum
    273                                self._filehandle.seek(self._filehandle.tell()-5)
    274                                continue
    275                        trailer = struct.unpack('>4s',self._filehandle.read(4))[0].decode()
    276                        if trailer == '7777':
    277                            self.messages += 1
    278                            self._index['offset'].append(pos)
    279                            self._index['discipline'].append(discipline)
    280                            self._index['edition'].append(edition)
    281                            self._index['size'].append(size)
    282                            self._index['messageNumber'].append(self.messages)
    283                            self._index['isSubmessage'].append(_issubmessage)
    284                            self._index['identificationSection'].append(_grbsec1)
    285                            self._index['refDate'].append(_refdate)
    286                            self._index['productDefinitionTemplateNumber'].append(_pdtnum)
    287                            self._index['productDefinitionTemplate'].append(_pdt)
    288                            self._index['leadTime'].append(utils.getleadtime(_grbsec1,_pdtnum,_pdt))
    289                            self._index['duration'].append(utils.getduration(_pdtnum,_pdt))
    290                            self._index['shortName'].append(_varinfo[2])
    291                            self._index['bitMap'].append(_bmap)
    292                            self._index['levelString'].append(tables.get_wgrib2_level_string(*_pdt[9:15]))
    293                            if _pdtnum in [5,9]:
    294                                self._index['probString'].append(utils.get_wgrib2_prob_string(*_pdt[17:22]))
    295                            else:
    296                                self._index['probString'].append('')
    297                            if _issubmessage:
    298                                self._index['submessageOffset'].append(_submsgoffset)
    299                                self._index['submessageBeginSection'].append(_submsgbegin)
    300                            else:
    301                                self._index['submessageOffset'].append(0)
    302                                self._index['submessageBeginSection'].append(_submsgbegin)
    303                            break
    304                        else:
    305                            self._filehandle.seek(self._filehandle.tell()-4)
    306                            self.messages += 1
    307                            self._index['offset'].append(pos)
    308                            self._index['discipline'].append(discipline)
    309                            self._index['edition'].append(edition)
    310                            self._index['size'].append(size)
    311                            self._index['messageNumber'].append(self.messages)
    312                            self._index['isSubmessage'].append(_issubmessage)
    313                            self._index['identificationSection'].append(_grbsec1)
    314                            self._index['refDate'].append(_refdate)
    315                            self._index['productDefinitionTemplateNumber'].append(_pdtnum)
    316                            self._index['productDefinitionTemplate'].append(_pdt)
    317                            self._index['leadTime'].append(utils.getleadtime(_grbsec1,_pdtnum,_pdt))
    318                            self._index['duration'].append(utils.getduration(_pdtnum,_pdt))
    319                            self._index['shortName'].append(_varinfo[2])
    320                            self._index['bitMap'].append(_bmap)
    321                            self._index['levelString'].append(tables.get_wgrib2_level_string(*_pdt[9:15]))
    322                            if _pdtnum in [5,9]:
    323                                self._index['probString'].append(utils.get_wgrib2_prob_string(*_pdt[17:22]))
    324                            else:
    325                                self._index['probString'].append('')
    326                            self._index['submessageOffset'].append(_submsgoffset)
    327                            self._index['submessageBeginSection'].append(_submsgbegin)
    328                            continue
    329
    330            except(struct.error):
    331                self._filehandle.seek(0)
    332                break
    333
    334        self._hasindex = True
    335
    336
    337    def close(self):
    338        """
    339        Close the file handle
    340        """
    341        if not self._filehandle.closed:
    342            self._filehandle.close()
    343            self.closed = self._filehandle.closed
    344
    345
    346    def read(self, num=1):
    347        """
    348        Read num GRIB2 messages from the current position
    349
    350        Parameters
    351        ----------
    352
    353        **`num`**: integer number of GRIB2 Message to read.
    354
    355        Returns
    356        -------
    357
    358        **`list`**: list of `grib2io.Grib2Message` instances.
    359        """
    360        msgs = []
    361        if self.tell() >= self.messages: return msgs
    362        if num > 0:
    363            if num == 1:
    364                msgrange = [self.tell()+1]
    365            else:
    366                beg = self.tell()+1
    367                end = self.tell()+1+num if self.tell()+1+num <= self.messages else self.messages
    368                msgrange = range(beg,end+1)
    369            for n in msgrange:
    370                self._filehandle.seek(self._index['offset'][n])
    371                msgs.append(Grib2Message(msg=self._filehandle.read(self._index['size'][n]),
    372                                         source=self,
    373                                         num=self._index['messageNumber'][n],
    374                                         decode=self.decode))
    375                self.current_message += 1
    376        return msgs
    377
    378
    379    def rewind(self):
    380        """
    381        Set the position of the file to zero in units of GRIB2 messages.
    382        """
    383        self.seek(0)
    384
    385
    386    def seek(self, pos):
    387        """
    388        Set the position within the file in units of GRIB2 messages.
    389
    390        Parameters
    391        ----------
    392
    393        **`pos`**: GRIB2 Message number to set the read pointer to.
    394        """
    395        if self._hasindex:
    396            if pos == 0:
    397                self._filehandle.seek(pos)
    398                self.current_message = pos
    399            elif pos > 0:
    400                self._filehandle.seek(self._index['offset'][pos-1])
    401                self.current_message = pos
    402
    403
    404    def tell(self):
    405        """
    406        Returns the position of the file in units of GRIB2 Messages.
    407        """
    408        return self.current_message
    409
    410
    411    def select(self,**kwargs):
    412        """
    413        Returns a list of `grib2io.Grib2Message` instances based on the selection **`**kwargs`**.
    414
    415        The following keywords are currently supported:
    416
    417        **`duration : int`** specifiying the time duration (in unit of hours) of a GRIB2 Message that is
    418        determined from a period of time.
    419
    420        **`leadTime : int`** specifying ending lead time (in units of hours) of a GRIB2 Message.
    421
    422        **`level : str`** wgrib2-formatted layer/level string.
    423
    424        **`percentile : int`** specify the percentile value.
    425
    426        **`refDate : int`** specifying the reference date in `YYYYMMDDHH[MMSS]` format.
    427
    428        **`shortName : str`** the GRIB2 `shortName`.  This is the abbreviation name found in the NCEP GRIB2 tables.
    429
    430        **`threshold : str`** wgrib2-formatted probability threshold string.
    431        """
    432        kwargs_allowed = ['duration','leadTime','level','percentile','refDate','shortName','threshold']
    433        idxs = {}
    434        for k,v in kwargs.items():
    435            if k not in kwargs_allowed: continue
    436            if k == 'duration':
    437                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['duration']])==v)[0]
    438            elif k == 'leadTime':
    439                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['leadTime']])==v)[0]
    440            elif k == 'level':
    441                idxs[k] = np.where(np.array(self._index['levelString'])==v)[0]
    442            elif k == 'percentile':
    443                tmp1 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==6)[0]
    444                tmp2 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==10)[0]
    445                idxs[k] = [i for i in np.concatenate((tmp1,tmp2)) if self._index["productDefinitionTemplate"][i][15]==v]
    446                del tmp1,tmp2
    447            elif k == 'refDate':
    448                idxs[k] = np.where(np.asarray(self._index['refDate'])==v)[0]
    449            elif k == 'shortName':
    450                idxs[k] = np.where(np.array(self._index['shortName'])==v)[0]
    451            elif k == 'threshold':
    452                idxs[k] = np.where(np.array(self._index['probString'])==v)[0]
    453        idxsarr = np.concatenate(tuple(idxs.values()))
    454        nidxs = len(idxs.keys())
    455        if nidxs == 1:
    456            return [self[int(i)][0] for i in idxsarr]
    457        elif nidxs > 1:
    458            return [self[int(i)][0] for i in [ii[0] for ii in collections.Counter(idxsarr).most_common() if ii[1] == nidxs]]
    459
    460
    461    def write(self, msg):
    462        """
    463        Writes a packed GRIB2 message to file.
    464
    465        Parameters
    466        ----------
    467
    468        **`msg`**: instance of `Grib2Message`.
    469        """
    470        if isinstance(msg,Grib2Message):
    471            self._filehandle.write(msg._msg)
    472            self.size = os.path.getsize(self.name)
    473            self.messages += 1
    474            self.current_message += 1
    475        else:
    476            raise TypeError("msg must be a Grib2Message object.")

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.

`decode`: If `True` [DEFAULT] automatically decode metadata from unpacked section data for Grib2Messages.

`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', decode=True) View Source

     72    def __init__(self, filename, mode='r', decode=True):
     73        """
     74        `open` Constructor
     75
     76        Parameters
     77        ----------
     78
     79        **`filename`**: File name containing GRIB2 messages.
     80
     81        **`mode `**: File access mode where `r` opens the files for reading only;
     82        `w` opens the file for writing.
     83
     84        **`decode `**: If `True` [DEFAULT] automatically decode metadata from
     85        unpacked section data for Grib2Messages.
     86        """
     87        if mode in ['a','r','w']:
     88            mode = mode+'b'
     89        self._filehandle = builtins.open(filename,mode=mode,buffering=ONE_MB)
     90        self._hasindex = False
     91        self._index = {}
     92        self.mode = mode
     93        self.name = os.path.abspath(filename)
     94        self.messages = 0
     95        self.current_message = 0
     96        self.size = os.path.getsize(self.name)
     97        self.closed = self._filehandle.closed
     98        self.decode = decode
     99        if 'r' in self.mode: self._build_index()
    100        # FIX: Cannot perform reads on mode='a'
    101        #if 'a' in self.mode and self.size > 0: self._build_index()
    102        if self._hasindex:
    103            self.variables = tuple(sorted(set(filter(None,self._index['shortName']))))
    104            self.levels = tuple(sorted(set(filter(None,self._index['levelString']))))

`open` Constructor

## Parameters

`filename`: File name containing GRIB2 messages.

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

`decode`: If `True` [DEFAULT] automatically decode metadata from unpacked section data for Grib2Messages.

def close(self) View Source

    337    def close(self):
    338        """
    339        Close the file handle
    340        """
    341        if not self._filehandle.closed:
    342            self._filehandle.close()
    343            self.closed = self._filehandle.closed

Close the file handle

def read(self, num=1) View Source

    346    def read(self, num=1):
    347        """
    348        Read num GRIB2 messages from the current position
    349
    350        Parameters
    351        ----------
    352
    353        **`num`**: integer number of GRIB2 Message to read.
    354
    355        Returns
    356        -------
    357
    358        **`list`**: list of `grib2io.Grib2Message` instances.
    359        """
    360        msgs = []
    361        if self.tell() >= self.messages: return msgs
    362        if num > 0:
    363            if num == 1:
    364                msgrange = [self.tell()+1]
    365            else:
    366                beg = self.tell()+1
    367                end = self.tell()+1+num if self.tell()+1+num <= self.messages else self.messages
    368                msgrange = range(beg,end+1)
    369            for n in msgrange:
    370                self._filehandle.seek(self._index['offset'][n])
    371                msgs.append(Grib2Message(msg=self._filehandle.read(self._index['size'][n]),
    372                                         source=self,
    373                                         num=self._index['messageNumber'][n],
    374                                         decode=self.decode))
    375                self.current_message += 1
    376        return msgs

Read num GRIB2 messages from the current position

## Parameters

`num`: integer number of GRIB2 Message to read.

## Returns

`list`: list of `grib2io.Grib2Message` instances.

def rewind(self) View Source

    379    def rewind(self):
    380        """
    381        Set the position of the file to zero in units of GRIB2 messages.
    382        """
    383        self.seek(0)

Set the position of the file to zero in units of GRIB2 messages.

def seek(self, pos) View Source

    386    def seek(self, pos):
    387        """
    388        Set the position within the file in units of GRIB2 messages.
    389
    390        Parameters
    391        ----------
    392
    393        **`pos`**: GRIB2 Message number to set the read pointer to.
    394        """
    395        if self._hasindex:
    396            if pos == 0:
    397                self._filehandle.seek(pos)
    398                self.current_message = pos
    399            elif pos > 0:
    400                self._filehandle.seek(self._index['offset'][pos-1])
    401                self.current_message = pos

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

## Parameters

`pos`: GRIB2 Message number to set the read pointer to.

def tell(self) View Source

    404    def tell(self):
    405        """
    406        Returns the position of the file in units of GRIB2 Messages.
    407        """
    408        return self.current_message

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

def select(self, **kwargs) View Source

    411    def select(self,**kwargs):
    412        """
    413        Returns a list of `grib2io.Grib2Message` instances based on the selection **`**kwargs`**.
    414
    415        The following keywords are currently supported:
    416
    417        **`duration : int`** specifiying the time duration (in unit of hours) of a GRIB2 Message that is
    418        determined from a period of time.
    419
    420        **`leadTime : int`** specifying ending lead time (in units of hours) of a GRIB2 Message.
    421
    422        **`level : str`** wgrib2-formatted layer/level string.
    423
    424        **`percentile : int`** specify the percentile value.
    425
    426        **`refDate : int`** specifying the reference date in `YYYYMMDDHH[MMSS]` format.
    427
    428        **`shortName : str`** the GRIB2 `shortName`.  This is the abbreviation name found in the NCEP GRIB2 tables.
    429
    430        **`threshold : str`** wgrib2-formatted probability threshold string.
    431        """
    432        kwargs_allowed = ['duration','leadTime','level','percentile','refDate','shortName','threshold']
    433        idxs = {}
    434        for k,v in kwargs.items():
    435            if k not in kwargs_allowed: continue
    436            if k == 'duration':
    437                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['duration']])==v)[0]
    438            elif k == 'leadTime':
    439                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['leadTime']])==v)[0]
    440            elif k == 'level':
    441                idxs[k] = np.where(np.array(self._index['levelString'])==v)[0]
    442            elif k == 'percentile':
    443                tmp1 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==6)[0]
    444                tmp2 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==10)[0]
    445                idxs[k] = [i for i in np.concatenate((tmp1,tmp2)) if self._index["productDefinitionTemplate"][i][15]==v]
    446                del tmp1,tmp2
    447            elif k == 'refDate':
    448                idxs[k] = np.where(np.asarray(self._index['refDate'])==v)[0]
    449            elif k == 'shortName':
    450                idxs[k] = np.where(np.array(self._index['shortName'])==v)[0]
    451            elif k == 'threshold':
    452                idxs[k] = np.where(np.array(self._index['probString'])==v)[0]
    453        idxsarr = np.concatenate(tuple(idxs.values()))
    454        nidxs = len(idxs.keys())
    455        if nidxs == 1:
    456            return [self[int(i)][0] for i in idxsarr]
    457        elif nidxs > 1:
    458            return [self[int(i)][0] for i in [ii[0] for ii in collections.Counter(idxsarr).most_common() if ii[1] == nidxs]]

Returns a list of `grib2io.Grib2Message` instances based on the selection `**kwargs`.

The following keywords are currently supported:

`duration : int` specifiying the time duration (in unit of hours) of a GRIB2 Message that is determined from a period of time.

`leadTime : int` specifying ending lead time (in units of hours) of a GRIB2 Message.

`level : str` wgrib2-formatted layer/level string.

`percentile : int` specify the percentile value.

`refDate : int` specifying the reference date in `YYYYMMDDHH[MMSS]` format.

`shortName : str` the GRIB2 `shortName`. This is the abbreviation name found in the NCEP GRIB2 tables.

`threshold : str` wgrib2-formatted probability threshold string.

def write(self, msg) View Source

    461    def write(self, msg):
    462        """
    463        Writes a packed GRIB2 message to file.
    464
    465        Parameters
    466        ----------
    467
    468        **`msg`**: instance of `Grib2Message`.
    469        """
    470        if isinstance(msg,Grib2Message):
    471            self._filehandle.write(msg._msg)
    472            self.size = os.path.getsize(self.name)
    473            self.messages += 1
    474            self.current_message += 1
    475        else:
    476            raise TypeError("msg must be a Grib2Message object.")

Writes a packed GRIB2 message to file.

## Parameters

`msg`: instance of `Grib2Message`.

class Grib2Message: View Source

     479class Grib2Message:
     480    def __init__(self, msg=None, source=None, num=-1, decode=True, discipline=None, idsect=None):
     481        """
     482        Class Constructor. Instantiation of this class can handle a GRIB2 message from an existing
     483        file or the creation of new GRIB2 message.  To create a new GRIB2 message, provide the
     484        appropriate values to the arguments `discipline` and `idsect`.  When these 2 arguments
     485        are not `None`, then a new GRIB2 message is created. NOTE: All other keyword arguments
     486        are ignored when a new message is created.
     487
     488        ...
     489
     490        Parameters
     491        ----------
     492
     493        **`msg`**: Binary string representing the GRIB2 Message read from file.
     494
     495        **`source`**: Source of where where this GRIB2 message originated
     496        from (i.e. the input file). This allow for interaction with the
     497        instance of `grib2io.open`. Default is None.
     498
     499        **`num`**: integer GRIB2 Message number from `grib2io.open`. Default value is -1.
     500
     501        **`decode`**: If True [DEFAULT], decode GRIB2 section lists into metadata
     502        instance variables.
     503
     504        **`discipline`**: integer GRIB2 Discipline [GRIB2 Table 0.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table0-0.shtml)
     505
     506        **`idsect`**: Sequence containing GRIB1 Identification Section values (Section 1).
     507
     508        | Index | Description |
     509        | :---: | :---        |
     510        | idsect[0] | Id of orginating centre - [ON388 - Table 0](https://www.nco.ncep.noaa.gov/pmb/docs/on388/table0.html)|
     511        | idsect[1] | Id of orginating sub-centre - [ON388 - Table C](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablec.html)|
     512        | idsect[2] | GRIB Master Tables Version Number - [Code Table 1.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-0.shtml)|
     513        | idsect[3] | GRIB Local Tables Version Number - [Code Table 1.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-1.shtml)|
     514        | idsect[4] | Significance of Reference Time - [Code Table 1.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-2.shtml)|
     515        | idsect[5] | Reference Time - Year (4 digits)|
     516        | idsect[6] | Reference Time - Month|
     517        | idsect[7] | Reference Time - Day|
     518        | idsect[8] | Reference Time - Hour|
     519        | idsect[9] | Reference Time - Minute|
     520        | idsect[10] | Reference Time - Second|
     521        | idsect[11] | Production status of data - [Code Table 1.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-3.shtml)|
     522        | idsect[12] | Type of processed data - [Code Table 1.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-4.shtml)|
     523        """
     524        self._source = source
     525        self._msgnum = num
     526        self._decode = decode
     527        self._pos = 0
     528        self._datapos = 0
     529        self._sections = []
     530        self.hasLocalUseSection = False
     531        self.isNDFD = False
     532        if discipline is not None and idsect is not None:
     533            # New message
     534            self._msg,self._pos = g2clib.grib2_create(np.array([discipline,GRIB2_EDITION_NUMBER],DEFAULT_NUMPY_INT),
     535                                                      np.array(idsect,DEFAULT_NUMPY_INT))
     536            self._sections += [0,1]
     537        else:
     538            # Existing message
     539            self._msg = msg
     540        #self.md5 = {}
     541        if self._msg is not None and self._source is not None: self.unpack()
     542
     543    def __repr__(self):
     544        """
     545        """
     546        strings = []
     547        for k,v in self.__dict__.items():
     548            if k.startswith('_'): continue
     549            if isinstance(v,str):
     550                strings.append('%s = \'%s\'\n'%(k,v))
     551            elif isinstance(v,int):
     552                strings.append('%s = %d\n'%(k,v))
     553            elif isinstance(v,float):
     554                strings.append('%s = %f\n'%(k,v))
     555            else:
     556                strings.append('%s = %s\n'%(k,v))
     557        return ''.join(strings)
     558
     559
     560    def unpack(self):
     561        """
     562        Unpacks GRIB2 section data from the packed, binary message.
     563        """
     564        # Section 0 - Indicator Section
     565        self.indicatorSection = []
     566        self.indicatorSection.append(struct.unpack('>4s',self._msg[0:4])[0])
     567        self.indicatorSection.append(struct.unpack('>H',self._msg[4:6])[0])
     568        self.indicatorSection.append(self._msg[6])
     569        self.indicatorSection.append(self._msg[7])
     570        self.indicatorSection.append(struct.unpack('>Q',self._msg[8:16])[0])
     571        self._pos = 16
     572        self._sections.append(0)
     573        #self.md5[0] = _getmd5str(self.indicatorSection)
     574
     575        # Section 1 - Identification Section via g2clib.unpack1()
     576        self.identificationSection,self._pos = g2clib.unpack1(self._msg,self._pos,np.empty)
     577        self.identificationSection = self.identificationSection.tolist()
     578        self._sections.append(1)
     579        if self.identificationSection[0:2] == [8,65535]: self.isNDFD = True
     580
     581        # After Section 1, perform rest of GRIB2 Decoding inside while loop
     582        # to account for sub-messages.
     583        sectnum = 1
     584        while True:
     585            if self._msg[self._pos:self._pos+4].decode('ascii','ignore') == '7777':
     586                break
     587
     588            # Read the length and section number.
     589            sectlen = struct.unpack('>i',self._msg[self._pos:self._pos+4])[0]
     590            prevsectnum = sectnum
     591            sectnum = struct.unpack('>B',self._msg[self._pos+4:self._pos+5])[0]
     592
     593            # If the previous section number is > current section number, then
     594            # we have encountered a submessage.
     595            if prevsectnum > sectnum: break
     596
     597            # Handle submessage accordingly.
     598            if isinstance(self._source,open):
     599                if self._source._index['isSubmessage'][self._msgnum]:
     600                    if sectnum == self._source._index['submessageBeginSection'][self._msgnum]:
     601                        self._pos = self._source._index['submessageOffset'][self._msgnum]
     602
     603            # Section 2 - Local Use Section.
     604            if sectnum == 2:
     605                self._lus = self._msg[self._pos+5:self._pos+sectlen]
     606                self._pos += sectlen
     607                self.hasLocalUseSection = True
     608                self._sections.append(2)
     609                #self.md5[2] = _getmd5str(self.identificationSection)
     610
     611            # Section 3 - Grid Definition Section.
     612            elif sectnum == 3:
     613                _gds,_gdt,_deflist,self._pos = g2clib.unpack3(self._msg,self._pos,np.empty)
     614                self.gridDefinitionSection = _gds.tolist()
     615                self.gridDefinitionTemplateNumber = Grib2Metadata(int(_gds[4]),table='3.1')
     616                self.gridDefinitionTemplate = _gdt.tolist()
     617                self.defList = _deflist.tolist()
     618                self._sections.append(3)
     619                #self.md5[3] = _getmd5str([self.gridDefinitionTemplateNumber]+self.gridDefinitionTemplate)
     620
     621            # Section 4 - Product Definition Section.
     622            elif sectnum == 4:
     623                _pdt,_pdtn,_coordlst,self._pos = g2clib.unpack4(self._msg,self._pos,np.empty)
     624                self.productDefinitionTemplate = _pdt.tolist()
     625                self.productDefinitionTemplateNumber = Grib2Metadata(int(_pdtn),table='4.0')
     626                self.coordinateList = _coordlst.tolist()
     627                self._sections.append(4)
     628                #self.md5[4] = _getmd5str([self.productDefinitionTemplateNumber]+self.productDefinitionTemplate)
     629
     630            # Section 5 - Data Representation Section.
     631            elif sectnum == 5:
     632                _drt,_drtn,_npts,self._pos = g2clib.unpack5(self._msg,self._pos,np.empty)
     633                self.dataRepresentationTemplate = _drt.tolist()
     634                self.dataRepresentationTemplateNumber = Grib2Metadata(int(_drtn),table='5.0')
     635                self.numberOfDataPoints = _npts
     636                self._sections.append(5)
     637                #self.md5[5] = _getmd5str([self.dataRepresentationTemplateNumber]+self.dataRepresentationTemplate)
     638
     639            # Section 6 - Bitmap Section.
     640            elif sectnum == 6:
     641                _bmap,_bmapflag = g2clib.unpack6(self._msg,self.gridDefinitionSection[1],self._pos,np.empty)
     642                self.bitMapFlag = _bmapflag
     643                if self.bitMapFlag == 0:
     644                    self.bitMap = _bmap
     645                elif self.bitMapFlag == 254:
     646                    # Value of 254 says to use a previous bitmap in the file.
     647                    self.bitMapFlag = 0
     648                    if isinstance(self._source,open):
     649                        self.bitMap = self._source._index['bitMap'][self._msgnum]
     650                self._pos += sectlen # IMPORTANT: This is here because g2clib.unpack6() does not return updated position.
     651                self._sections.append(6)
     652                #self.md5[6] = None
     653
     654            # Section 7 - Data Section (data unpacked when data() method is invoked).
     655            elif sectnum == 7:
     656                self._datapos = self._pos
     657                self._pos += sectlen # REMOVE THIS WHEN UNPACKING DATA IS IMPLEMENTED
     658                self._sections.append(7)
     659                #self.md5[7] = _getmd5str(self._msg[self._datapos:sectlen+1])
     660
     661            else:
     662                errmsg = 'Unknown section number = %i' % sectnum
     663                raise ValueError(errmsg)
     664
     665        if self._decode: self.decode()
     666
     667    def decode(self):
     668        """
     669        Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables.
     670        """
     671
     672        # Section 0 - Indictator Section
     673        self.discipline = Grib2Metadata(self.indicatorSection[2],table='0.0')
     674
     675        # Section 1 - Indentification Section.
     676        self.originatingCenter = Grib2Metadata(self.identificationSection[0],table='originating_centers')
     677        self.originatingSubCenter = Grib2Metadata(self.identificationSection[1],table='originating_subcenters')
     678        self.masterTableInfo = Grib2Metadata(self.identificationSection[2],table='1.0')
     679        self.localTableInfo = Grib2Metadata(self.identificationSection[3],table='1.1')
     680        self.significanceOfReferenceTime = Grib2Metadata(self.identificationSection[4],table='1.2')
     681        self.year = self.identificationSection[5]
     682        self.month = self.identificationSection[6]
     683        self.day = self.identificationSection[7]
     684        self.hour = self.identificationSection[8]
     685        self.minute = self.identificationSection[9]
     686        self.second = self.identificationSection[10]
     687        self.refDate = (self.year*1000000)+(self.month*10000)+(self.day*100)+self.hour
     688        self.dtReferenceDate = datetime.datetime(self.year,self.month,self.day,
     689                                                 hour=self.hour,minute=self.minute,
     690                                                 second=self.second)
     691        self.productionStatus = Grib2Metadata(self.identificationSection[11],table='1.3')
     692        self.typeOfData = Grib2Metadata(self.identificationSection[12],table='1.4')
     693
     694        # ----------------------------
     695        # Section 3 -- Grid Definition
     696        # ----------------------------
     697
     698        # Set shape of the Earth parameters
     699        if self.gridDefinitionTemplateNumber.value in [50,51,52,1200]:
     700            earthparams = None
     701        else:
     702            earthparams = tables.earth_params[str(self.gridDefinitionTemplate[0])]
     703        if earthparams['shape'] == 'spherical':
     704            if earthparams['radius'] is None:
     705                self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1])
     706                self.earthMajorAxis = None
     707                self.earthMinorAxis = None
     708            else:
     709                self.earthRadius = earthparams['radius']
     710                self.earthMajorAxis = None
     711                self.earthMinorAxis = None
     712        elif earthparams['shape'] == 'oblateSpheroid':
     713            if earthparams['radius'] is None and earthparams['major_axis'] is None and earthparams['minor_axis'] is None:
     714                self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1])
     715                self.earthMajorAxis = self.gridDefinitionTemplate[4]/(10.**self.gridDefinitionTemplate[3])
     716                self.earthMinorAxis = self.gridDefinitionTemplate[6]/(10.**self.gridDefinitionTemplate[5])
     717            else:
     718                self.earthRadius = earthparams['radius']
     719                self.earthMajorAxis = earthparams['major_axis']
     720                self.earthMinorAxis = earthparams['minor_axis']
     721
     722        reggrid = self.gridDefinitionSection[2] == 0 # self.gridDefinitionSection[2]=0 means regular 2-d grid
     723        if reggrid and self.gridDefinitionTemplateNumber.value not in [50,51,52,53,100,120,1000,1200]:
     724            self.nx = self.gridDefinitionTemplate[7]
     725            self.ny = self.gridDefinitionTemplate[8]
     726        if not reggrid and self.gridDefinitionTemplateNumber == 40:
     727            # Reduced Gaussian Grid
     728            self.ny = self.gridDefinitionTemplate[8]
     729        if self.gridDefinitionTemplateNumber.value in [0,1,203,205,32768,32769]:
     730            # Regular or Rotated Lat/Lon Grid
     731            scalefact = float(self.gridDefinitionTemplate[9])
     732            if self.gridDefinitionTemplate[10] == 4294967295:
     733                self.gridDefinitionTemplate[10] = -1
     734            divisor = float(self.gridDefinitionTemplate[10])
     735            if scalefact == 0: scalefact = 1.
     736            if divisor <= 0: divisor = 1.e6
     737            self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor
     738            self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor
     739            self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor
     740            self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor
     741            self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor
     742            self.gridlengthYDirection = scalefact*self.gridDefinitionTemplate[17]/divisor
     743            if self.latitudeFirstGridpoint > self.latitudeLastGridpoint:
     744                self.gridlengthYDirection = -self.gridlengthYDirection
     745            if self.longitudeFirstGridpoint > self.longitudeLastGridpoint:
     746                self.gridlengthXDirection = -self.gridlengthXDirection
     747            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
     748            if self.gridDefinitionTemplateNumber == 1:
     749                self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor
     750                self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor
     751                self.anglePoleRotation = self.gridDefinitionTemplate[21]
     752        elif self.gridDefinitionTemplateNumber == 10:
     753            # Mercator
     754            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     755            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     756            self.latitudeLastGridpoint = self.gridDefinitionTemplate[13]/1.e6
     757            self.longitudeLastGridpoint = self.gridDefinitionTemplate[14]/1.e6
     758            self.gridlengthXDirection = self.gridDefinitionTemplate[17]/1.e3
     759            self.gridlengthYDirection= self.gridDefinitionTemplate[18]/1.e3
     760            self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6
     761            self.proj4_lon_0 = 0.5*(self.longitudeFirstGridpoint+self.longitudeLastGridpoint)
     762            self.proj4_proj = 'merc'
     763            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4]
     764        elif self.gridDefinitionTemplateNumber == 20:
     765            # Stereographic
     766            projflag = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0]
     767            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     768            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     769            self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6
     770            if projflag == 0:
     771                self.proj4_lat_0 = 90
     772            elif projflag == 1:
     773                self.proj4_lat_0 = -90
     774            else:
     775                raise ValueError('Invalid projection center flag = %s'%projflag)
     776            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
     777            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
     778            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
     779            self.proj4_proj = 'stere'
     780            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
     781        elif self.gridDefinitionTemplateNumber == 30:
     782            # Lambert Conformal
     783            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     784            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     785            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
     786            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
     787            self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6
     788            self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6
     789            self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6
     790            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
     791            self.proj4_proj = 'lcc'
     792            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
     793        elif self.gridDefinitionTemplateNumber == 31:
     794            # Albers Equal Area
     795            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     796            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     797            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
     798            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
     799            self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6
     800            self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6
     801            self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6
     802            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
     803            self.proj4_proj = 'aea'
     804            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
     805        elif self.gridDefinitionTemplateNumber == 40 or self.gridDefinitionTemplateNumber == 41:
     806            # Gaussian Grid
     807            scalefact = float(self.gridDefinitionTemplate[9])
     808            if self.gridDefinitionTemplate[10] == 4294967295:
     809                self.gridDefinitionTemplate[10] = -1
     810            divisor = float(self.gridDefinitionTemplate[10])
     811            if scalefact == 0: scalefact = 1.
     812            if divisor <= 0: divisor = 1.e6
     813            self.pointsBetweenPoleAndEquator = self.gridDefinitionTemplate[17]
     814            self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor
     815            self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor
     816            self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor
     817            self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor
     818            if reggrid:
     819                self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor
     820                if self.longitudeFirstGridpoint > self.longitudeLastGridpoint:
     821                    self.gridlengthXDirection = -self.gridlengthXDirection
     822            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
     823            if self.gridDefinitionTemplateNumber == 41:
     824                self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor
     825                self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor
     826                self.anglePoleRotation = self.gridDefinitionTemplate[21]
     827        elif self.gridDefinitionTemplateNumber == 50:
     828            # Spectral Coefficients
     829            self.spectralFunctionParameters = (self.gridDefinitionTemplate[0],self.gridDefinitionTemplate[1],self.gridDefinitionTemplate[2])
     830            self.scanModeFlags = [None,None,None,None]
     831        elif self.gridDefinitionTemplateNumber == 90:
     832            # Near-sided Vertical Perspective Satellite Projection
     833            self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6
     834            self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6
     835            self.proj4_h = self.earthMajorAxis * (self.gridDefinitionTemplate[18]/1.e6)
     836            dx = self.gridDefinitionTemplate[12]
     837            dy = self.gridDefinitionTemplate[13]
     838            # if lat_0 is equator, it's a geostationary view.
     839            if self.proj4_lat_0 == 0.: # if lat_0 is equator, it's a
     840                self.proj4_proj = 'geos'
     841            # general case of 'near-side perspective projection' (untested)
     842            else:
     843                self.proj4_proj = 'nsper'
     844                msg = 'Only geostationary perspective is supported. Lat/Lon values returned by grid method may be incorrect.'
     845                warnings.warn(msg)
     846            # latitude of horizon on central meridian
     847            lonmax = 90.-(180./np.pi)*np.arcsin(self.earthMajorAxis/self.proj4_h)
     848            # longitude of horizon on equator
     849            latmax = 90.-(180./np.pi)*np.arcsin(self.earthMinorAxis/self.proj4_h)
     850            # truncate to nearest thousandth of a degree (to make sure
     851            # they aren't slightly over the horizon)
     852            latmax = int(1000*latmax)/1000.
     853            lonmax = int(1000*lonmax)/1000.
     854            # h is measured from surface of earth at equator.
     855            self.proj4_h = self.proj4_h - self.earthMajorAxis
     856            # width and height of visible projection
     857            P = pyproj.Proj(proj=self.proj4_proj,\
     858                            a=self.earthMajorAxis,b=self.earthMinorAxis,\
     859                            lat_0=0,lon_0=0,h=self.proj4_h)
     860            x1,y1 = P(0.,latmax)
     861            x2,y2 = P(lonmax,0.)
     862            width = 2*x2
     863            height = 2*y1
     864            self.gridlengthXDirection = width/dx
     865            self.gridlengthYDirection = height/dy
     866            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0:4]
     867        elif self.gridDefinitionTemplateNumber == 110:
     868            # Azimuthal Equidistant
     869            self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6
     870            self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6
     871            self.gridlengthXDirection = self.gridDefinitionTemplate[12]/1000.
     872            self.gridlengthYDirection = self.gridDefinitionTemplate[13]/1000.
     873            self.proj4_proj = 'aeqd'
     874            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4]
     875        elif self.gridDefinitionTemplateNumber == 204:
     876            # Curvilinear Orthogonal
     877            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
     878        else:
     879            errmsg = 'Unsupported Grid Definition Template Number - 3.%i' % self.gridDefinitionTemplateNumber.value
     880            raise ValueError(errmsg)
     881
     882        # -------------------------------
     883        # Section 4 -- Product Definition
     884        # -------------------------------
     885
     886        # Template 4.0 - NOTE: That is these attributes apply to other templates.
     887        self.parameterCategory = self.productDefinitionTemplate[0]
     888        self.parameterNumber = self.productDefinitionTemplate[1]
     889        self.fullName,self.units,self.shortName = tables.get_varinfo_from_table(self.discipline.value,
     890                                                                                self.parameterCategory,
     891                                                                                self.parameterNumber)
     892        self.typeOfGeneratingProcess = Grib2Metadata(self.productDefinitionTemplate[2],table='4.3')
     893        self.backgroundGeneratingProcessIdentifier = self.productDefinitionTemplate[3]
     894        self.generatingProcess = Grib2Metadata(self.productDefinitionTemplate[4],table='generating_process')
     895        self.unitOfTimeRange = Grib2Metadata(self.productDefinitionTemplate[7],table='4.4')
     896        self.leadTime = self.productDefinitionTemplate[8]
     897        self.typeOfFirstFixedSurface = Grib2Metadata(self.productDefinitionTemplate[9],table='4.5')
     898        self.scaleFactorOfFirstFixedSurface = self.productDefinitionTemplate[10]
     899        self.unitOfFirstFixedSurface = self.typeOfFirstFixedSurface.definition[1]
     900        self.scaledValueOfFirstFixedSurface = self.productDefinitionTemplate[11]
     901        self.valueOfFirstFixedSurface = self.scaledValueOfFirstFixedSurface/(10.**self.scaleFactorOfFirstFixedSurface)
     902        temp = tables.get_value_from_table(self.productDefinitionTemplate[12],'4.5')
     903        if temp[0] == 'Missing' and temp[1] == 'unknown':
     904            self.typeOfSecondFixedSurface = None
     905            self.scaleFactorOfSecondFixedSurface = None
     906            self.unitOfSecondFixedSurface = None
     907            self.valueOfSecondFixedSurface = None
     908        else:
     909            self.typeOfSecondFixedSurface = Grib2Metadata(self.productDefinitionTemplate[12],table='4.5')
     910            self.scaleFactorOfSecondFixedSurface = self.productDefinitionTemplate[13]
     911            self.unitOfSecondFixedSurface = self.typeOfSecondFixedSurface.definition[1]
     912            self.scaledValueOfSecondFixedSurface = self.productDefinitionTemplate[14]
     913            self.valueOfSecondFixedSurface = self.scaledValueOfSecondFixedSurface/(10.**self.scaleFactorOfSecondFixedSurface)
     914        self.level = tables.get_wgrib2_level_string(*self.productDefinitionTemplate[9:15])
     915
     916        # Template 4.1 -
     917        if self.productDefinitionTemplateNumber == 1:
     918            self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6')
     919            self.perturbationNumber = self.productDefinitionTemplate[16]
     920            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17]
     921
     922        # Template 4.2 -
     923        elif self.productDefinitionTemplateNumber == 2:
     924            self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7')
     925            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16]
     926
     927        # Template 4.5 -
     928        elif self.productDefinitionTemplateNumber == 5:
     929            self.forecastProbabilityNumber = self.productDefinitionTemplate[15]
     930            self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16]
     931            self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9')
     932            self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18]
     933            self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19]
     934            self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20]
     935            self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21]
     936            self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \
     937                                       self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18])
     938            self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \
     939                                       self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20])
     940            self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22])
     941
     942        # Template 4.6 -
     943        elif self.productDefinitionTemplateNumber == 6:
     944            self.percentileValue = self.productDefinitionTemplate[15]
     945
     946        # Template 4.8 -
     947        elif self.productDefinitionTemplateNumber == 8:
     948            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[15]
     949            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[16]
     950            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[17]
     951            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[18]
     952            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[19]
     953            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[20]
     954            self.numberOfTimeRanges = self.productDefinitionTemplate[21]
     955            self.numberOfMissingValues = self.productDefinitionTemplate[22]
     956            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[23],table='4.10')
     957            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.11')
     958            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.4')
     959            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[26]
     960            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4')
     961            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[28]
     962
     963        # Template 4.9 -
     964        elif self.productDefinitionTemplateNumber == 9:
     965            self.forecastProbabilityNumber = self.productDefinitionTemplate[15]
     966            self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16]
     967            self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9')
     968            self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18]
     969            self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19]
     970            self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20]
     971            self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21]
     972            self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \
     973                                       self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18])
     974            self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \
     975                                       self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20])
     976            self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22])
     977            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[22]
     978            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[23]
     979            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[24]
     980            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[25]
     981            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[26]
     982            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[27]
     983            self.numberOfTimeRanges = self.productDefinitionTemplate[28]
     984            self.numberOfMissingValues = self.productDefinitionTemplate[29]
     985            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[30],table='4.10')
     986            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[31],table='4.11')
     987            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[32],table='4.4')
     988            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[33]
     989            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[34],table='4.4')
     990            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[35]
     991
     992        # Template 4.10 -
     993        elif self.productDefinitionTemplateNumber == 10:
     994            self.percentileValue = self.productDefinitionTemplate[15]
     995            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[16]
     996            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[17]
     997            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[18]
     998            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[19]
     999            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[20]
    1000            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[21]
    1001            self.numberOfTimeRanges = self.productDefinitionTemplate[22]
    1002            self.numberOfMissingValues = self.productDefinitionTemplate[23]
    1003            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.10')
    1004            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.11')
    1005            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.4')
    1006            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[27]
    1007            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4')
    1008            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[29]
    1009
    1010        # Template 4.11 -
    1011        elif self.productDefinitionTemplateNumber == 11:
    1012            self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6')
    1013            self.perturbationNumber = self.productDefinitionTemplate[16]
    1014            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17]
    1015            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[18]
    1016            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[19]
    1017            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[20]
    1018            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[21]
    1019            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[22]
    1020            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[23]
    1021            self.numberOfTimeRanges = self.productDefinitionTemplate[24]
    1022            self.numberOfMissingValues = self.productDefinitionTemplate[25]
    1023            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.10')
    1024            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.11')
    1025            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4')
    1026            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[29]
    1027            self.unitOfTimeRangeOfSuccessiveFields = tables.get_value_from_table(self.productDefinitionTemplate[30],table='4.4')
    1028            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[31]
    1029
    1030        # Template 4.12 -
    1031        elif self.productDefinitionTemplateNumber == 12:
    1032            self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7')
    1033            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16]
    1034            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[17]
    1035            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[18]
    1036            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[19]
    1037            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[20]
    1038            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[21]
    1039            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[22]
    1040            self.numberOfTimeRanges = self.productDefinitionTemplate[23]
    1041            self.numberOfMissingValues = self.productDefinitionTemplate[24]
    1042            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.10')
    1043            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.11')
    1044            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4')
    1045            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[28]
    1046            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[29],table='4.4')
    1047            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[30]
    1048
    1049        # Template 4.15 -
    1050        elif self.productDefinitionTemplateNumber == 15:
    1051            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[15],table='4.10')
    1052            self.typeOfSpatialProcessing = Grib2Metadata(self.productDefinitionTemplate[16],table='4.15')
    1053            self.numberOfDataPointsForSpatialProcessing = self.productDefinitionTemplate[17]
    1054
    1055        else:
    1056            if self.productDefinitionTemplateNumber != 0:
    1057                errmsg = 'Unsupported Product Definition Template Number - 4.%i' % self.productDefinitionTemplateNumber.value
    1058                raise ValueError(errmsg)
    1059
    1060
    1061        self.leadTime = utils.getleadtime(self.identificationSection,
    1062                                          self.productDefinitionTemplateNumber.value,
    1063                                          self.productDefinitionTemplate)
    1064
    1065        if self.productDefinitionTemplateNumber.value in [8,9,10,11,12]:
    1066            self.dtEndOfTimePeriod = datetime.datetime(self.yearOfEndOfTimePeriod,self.monthOfEndOfTimePeriod,
    1067                                     self.dayOfEndOfTimePeriod,hour=self.hourOfEndOfTimePeriod,
    1068                                     minute=self.minuteOfEndOfTimePeriod,
    1069                                     second=self.secondOfEndOfTimePeriod)
    1070
    1071        # --------------------------------
    1072        # Section 5 -- Data Representation
    1073        # --------------------------------
    1074
    1075        # Template 5.0 - Simple Packing
    1076        if self.dataRepresentationTemplateNumber == 0:
    1077            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1078            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1079            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1080            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1081            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[3],table='5.1')
    1082
    1083        # Template 5.2 - Complex Packing
    1084        elif self.dataRepresentationTemplateNumber == 2:
    1085            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1086            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1087            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1088            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1089            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1090            self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4')
    1091            self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5')
    1092            self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None
    1093            self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None
    1094            self.nGroups = self.dataRepresentationTemplate[9]
    1095            self.refGroupWidth = self.dataRepresentationTemplate[10]
    1096            self.nBitsGroupWidth = self.dataRepresentationTemplate[11]
    1097            self.refGroupLength = self.dataRepresentationTemplate[12]
    1098            self.groupLengthIncrement = self.dataRepresentationTemplate[13]
    1099            self.lengthOfLastGroup = self.dataRepresentationTemplate[14]
    1100            self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15]
    1101
    1102        # Template 5.3 - Complex Packing and Spatial Differencing
    1103        elif self.dataRepresentationTemplateNumber == 3:
    1104            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1105            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1106            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1107            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1108            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1109            self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4')
    1110            self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5')
    1111            self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None
    1112            self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None
    1113            self.nGroups = self.dataRepresentationTemplate[9]
    1114            self.refGroupWidth = self.dataRepresentationTemplate[10]
    1115            self.nBitsGroupWidth = self.dataRepresentationTemplate[11]
    1116            self.refGroupLength = self.dataRepresentationTemplate[12]
    1117            self.groupLengthIncrement = self.dataRepresentationTemplate[13]
    1118            self.lengthOfLastGroup = self.dataRepresentationTemplate[14]
    1119            self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15]
    1120            self.spatialDifferenceOrder = Grib2Metadata(self.dataRepresentationTemplate[16],table='5.6')
    1121            self.nBytesSpatialDifference = self.dataRepresentationTemplate[17]
    1122
    1123        # Template 5.4 - IEEE Floating Point Data
    1124        elif self.dataRepresentationTemplateNumber == 4:
    1125            self.precision = Grib2Metadata(self.dataRepresentationTemplate[0],table='5.7')
    1126
    1127        # Template 5.40 - JPEG2000 Compression
    1128        elif self.dataRepresentationTemplateNumber == 40:
    1129            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1130            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1131            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1132            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1133            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1134            self.typeOfCompression = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.40')
    1135            self.targetCompressionRatio = self.dataRepresentationTemplate[6]
    1136
    1137        # Template 5.41 - PNG Compression
    1138        elif self.dataRepresentationTemplateNumber == 41:
    1139            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1140            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1141            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1142            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1143            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1144
    1145        else:
    1146            errmsg = 'Unsupported Data Representation Definition Template Number - 5.%i' % self.dataRepresentationTemplateNumber.value
    1147            raise ValueError(errmsg)
    1148
    1149
    1150    def data(self, fill_value=DEFAULT_FILL_VALUE, masked_array=True, expand=True, order=None,
    1151             map_keys=False):
    1152        """
    1153        Returns an unpacked data grid.
    1154
    1155        Parameters
    1156        ----------
    1157
    1158        **`fill_value`**: Missing or masked data is filled with this value or default value given by
    1159        `DEFAULT_FILL_VALUE`
    1160
    1161        **`masked_array`**: If `True` [DEFAULT], return masked array if there is bitmap for missing
    1162        or masked data.
    1163
    1164        **`expand`**: If `True` [DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids.
    1165
    1166        **`order`**: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing
    1167        or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids.
    1168
    1169        **`map_keys`**: If `True`, data values will be mapped to the string-based keys that are stored
    1170        in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the
    1171        units (i.e. "See Table 4.xxx").
    1172
    1173        Returns
    1174        -------
    1175
    1176        **`numpy.ndarray`**: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32,
    1177        but could be np.int32 if Grib2Message.typeOfValues is integer.  The array dtype will be
    1178        string-based if map_keys=True.
    1179        """
    1180        if not hasattr(self,'scanModeFlags'):
    1181            raise ValueError('Unsupported grid definition template number %s'%self.gridDefinitionTemplateNumber)
    1182        else:
    1183            if self.scanModeFlags[2]:
    1184                storageorder='F'
    1185            else:
    1186                storageorder='C'
    1187        if order is None:
    1188            if (self.dataRepresentationTemplateNumber in [2,3] and
    1189                self.dataRepresentationTemplate[6] != 0) or self.bitMapFlag == 0:
    1190                order = 0
    1191            else:
    1192                order = 1
    1193        drtnum = self.dataRepresentationTemplateNumber.value
    1194        drtmpl = np.asarray(self.dataRepresentationTemplate,dtype=DEFAULT_NUMPY_INT)
    1195        gdtnum = self.gridDefinitionTemplateNumber.value
    1196        gdtmpl = np.asarray(self.gridDefinitionTemplate,dtype=DEFAULT_NUMPY_INT)
    1197        ndpts = self.numberOfDataPoints
    1198        gds = self.gridDefinitionSection
    1199        ngrdpts = gds[1]
    1200        ipos = self._datapos
    1201        fld1 = g2clib.unpack7(self._msg,gdtnum,gdtmpl,drtnum,drtmpl,ndpts,ipos,np.empty,storageorder=storageorder)
    1202        # Apply bitmap.
    1203        if self.bitMapFlag == 0:
    1204            fld = fill_value*np.ones(ngrdpts,'f')
    1205            np.put(fld,np.nonzero(self.bitMap),fld1)
    1206            if masked_array:
    1207                fld = ma.masked_values(fld,fill_value)
    1208        # Missing values instead of bitmap
    1209        elif masked_array and hasattr(self,'priMissingValue'):
    1210            if hasattr(self,'secMissingValue'):
    1211                mask = np.logical_or(fld1==self.priMissingValue,fld1==self.secMissingValue)
    1212            else:
    1213                mask = fld1 == self.priMissingValue
    1214            fld = ma.array(fld1,mask=mask)
    1215        else:
    1216            fld = fld1
    1217        if self.nx is not None and self.ny is not None: # Rectangular grid.
    1218            if ma.isMA(fld):
    1219                fld = ma.reshape(fld,(self.ny,self.nx))
    1220            else:
    1221                fld = np.reshape(fld,(self.ny,self.nx))
    1222        else:
    1223            if gds[2] and gdtnum == 40: # Reduced global Gaussian grid.
    1224                if expand:
    1225                    from . import redtoreg
    1226                    self.nx = 2*self.ny
    1227                    lonsperlat = self.defList
    1228                    if ma.isMA(fld):
    1229                        fld = ma.filled(fld)
    1230                        fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long),
    1231                                                 fld.astype(np.double),fill_value)
    1232                        fld = ma.masked_values(fld,fill_value)
    1233                    else:
    1234                        fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long),
    1235                                                 fld.astype(np.double),fill_value)
    1236        # Check scan modes for rect grids.
    1237        if self.nx is not None and self.ny is not None:
    1238            if self.scanModeFlags[3]:
    1239                fldsave = fld.astype('f') # casting makes a copy
    1240                fld[1::2,:] = fldsave[1::2,::-1]
    1241
    1242        # Set data to integer according to GRIB metadata
    1243        if self.typeOfValues == "Integer": fld = fld.astype(np.int32)
    1244
    1245        # Map the data values to their respective definitions.
    1246        if map_keys:
    1247            fld = fld.astype(np.int32).astype(str)
    1248            if self.identificationSection[0] == 7 and \
    1249               self.identificationSection[1] == 14 and \
    1250               self.shortName == 'PWTHER':
    1251                # MDL Predominant Weather Grid
    1252                #keys = utils.decode_mdl_wx_strings(self._lus)
    1253                keys = utils.decode_ndfd_wx_strings(self._lus)
    1254                for n,k in enumerate(keys):
    1255                    fld = np.where(fld==str(n+1),k,fld)
    1256            elif self.identificationSection[0] == 8 and \
    1257                 self.identificationSection[1] == 65535 and \
    1258                 self.shortName == 'CRAIN':
    1259                # NDFD Predominant Weather Grid
    1260                keys = utils.decode_ndfd_wx_strings(self._lus)
    1261                for n,k in enumerate(keys):
    1262                    fld = np.where(fld==str(n+1),k,fld)
    1263            else:
    1264                # For data whose units are defined in a code table
    1265                tbl = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0]
    1266                for k,v in tables.get_table(tbl).items():
    1267                    fld = np.where(fld==k,v,fld)
    1268        return fld
    1269
    1270
    1271    def latlons(self):
    1272        """Alias for `grib2io.Grib2Message.grid` method"""
    1273        return self.grid()
    1274
    1275
    1276    def grid(self):
    1277        """
    1278        Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon,
    1279        global Gaussian, mercator, stereographic, lambert conformal, albers equal-area,
    1280        space-view and azimuthal equidistant grids.
    1281
    1282        Returns
    1283        -------
    1284
    1285        **`lats, lons : numpy.ndarray`**
    1286
    1287        Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and
    1288        longitudes in units of degrees.
    1289        """
    1290        gdtnum = self.gridDefinitionTemplateNumber
    1291        gdtmpl = self.gridDefinitionTemplate
    1292        reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid
    1293        self.projparams = {}
    1294        if self.earthMajorAxis is not None: self.projparams['a']=self.earthMajorAxis
    1295        if self.earthMajorAxis is not None: self.projparams['b']=self.earthMinorAxis
    1296        if gdtnum == 0:
    1297            # Regular lat/lon grid
    1298            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
    1299            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
    1300            dlon = self.gridlengthXDirection
    1301            dlat = self.gridlengthYDirection
    1302            lats = np.arange(lat1,lat2+dlat,dlat)
    1303            lons = np.arange(lon1,lon2+dlon,dlon)
    1304            # flip if scan mode says to.
    1305            #if self.scanModeFlags[0]:
    1306            #    lons = lons[::-1]
    1307            #if not self.scanModeFlags[1]:
    1308            #    lats = lats[::-1]
    1309            self.projparams['proj'] = 'cyl'
    1310            lons,lats = np.meshgrid(lons,lats) # make 2-d arrays.
    1311        elif gdtnum == 40: # Gaussian grid (only works for global!)
    1312            from utils.gauss_grids import gaussian_latitudes
    1313            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
    1314            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
    1315            nlats = self.ny
    1316            if not reggrid: # Reduced Gaussian grid.
    1317                nlons = 2*nlats
    1318                dlon = 360./nlons
    1319            else:
    1320                nlons = self.nx
    1321                dlon = self.gridlengthXDirection
    1322            lons = np.arange(lon1,lon2+dlon,dlon)
    1323            # Compute Gaussian lats (north to south)
    1324            lats = gaussian_latitudes(nlats)
    1325            if lat1 < lat2:  # reverse them if necessary
    1326                lats = lats[::-1]
    1327            # flip if scan mode says to.
    1328            #if self.scanModeFlags[0]:
    1329            #    lons = lons[::-1]
    1330            #if not self.scanModeFlags[1]:
    1331            #    lats = lats[::-1]
    1332            self.projparams['proj'] = 'cyl'
    1333            lons,lats = np.meshgrid(lons,lats) # make 2-d arrays
    1334        elif gdtnum in [10,20,30,31,110]:
    1335            # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant
    1336            dx,dy = self.gridlengthXDirection, self.gridlengthYDirection
    1337            lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
    1338            if gdtnum == 10: # Mercator.
    1339                self.projparams['lat_ts']=self.proj4_lat_ts
    1340                self.projparams['proj']=self.proj4_proj
    1341                self.projparams['lon_0']=self.proj4_lon_0
    1342                pj = pyproj.Proj(self.projparams)
    1343                llcrnrx, llcrnry = pj(lon1,lat1)
    1344                x = llcrnrx+dx*np.arange(self.nx)
    1345                y = llcrnry+dy*np.arange(self.ny)
    1346                x,y = np.meshgrid(x, y)
    1347                lons,lats = pj(x, y, inverse=True)
    1348            elif gdtnum == 20:  # Stereographic
    1349                self.projparams['lat_ts']=self.proj4_lat_ts
    1350                self.projparams['proj']=self.proj4_proj
    1351                self.projparams['lat_0']=self.proj4_lat_0
    1352                self.projparams['lon_0']=self.proj4_lon_0
    1353                pj = pyproj.Proj(self.projparams)
    1354                llcrnrx, llcrnry = pj(lon1,lat1)
    1355                x = llcrnrx+dx*np.arange(self.nx)
    1356                y = llcrnry+dy*np.arange(self.ny)
    1357                x,y = np.meshgrid(x, y)
    1358                lons,lats = pj(x, y, inverse=True)
    1359            elif gdtnum in [30,31]: # Lambert, Albers
    1360                self.projparams['lat_1']=self.proj4_lat_1
    1361                self.projparams['lat_2']=self.proj4_lat_2
    1362                self.projparams['proj']=self.proj4_proj
    1363                self.projparams['lon_0']=self.proj4_lon_0
    1364                pj = pyproj.Proj(self.projparams)
    1365                llcrnrx, llcrnry = pj(lon1,lat1)
    1366                x = llcrnrx+dx*np.arange(self.nx)
    1367                y = llcrnry+dy*np.arange(self.ny)
    1368                x,y = np.meshgrid(x, y)
    1369                lons,lats = pj(x, y, inverse=True)
    1370            elif gdtnum == 110: # Azimuthal Equidistant
    1371                self.projparams['proj']=self.proj4_proj
    1372                self.projparams['lat_0']=self.proj4_lat_0
    1373                self.projparams['lon_0']=self.proj4_lon_0
    1374                pj = pyproj.Proj(self.projparams)
    1375                llcrnrx, llcrnry = pj(lon1,lat1)
    1376                x = llcrnrx+dx*np.arange(self.nx)
    1377                y = llcrnry+dy*np.arange(self.ny)
    1378                x,y = np.meshgrid(x, y)
    1379                lons,lats = pj(x, y, inverse=True)
    1380        elif gdtnum == 90:
    1381            # Satellite Projection
    1382            dx = self.gridlengthXDirection
    1383            dy = self.gridlengthYDirection
    1384            self.projparams['proj']=self.proj4_proj
    1385            self.projparams['lon_0']=self.proj4_lon_0
    1386            self.projparams['lat_0']=self.proj4_lat_0
    1387            self.projparams['h']=self.proj4_h
    1388            pj = pyproj.Proj(self.projparams)
    1389            x = dx*np.indices((self.ny,self.nx),'f')[1,:,:]
    1390            x -= 0.5*x.max()
    1391            y = dy*np.indices((self.ny,self.nx),'f')[0,:,:]
    1392            y -= 0.5*y.max()
    1393            lons,lats = pj(x,y,inverse=True)
    1394            # Set lons,lats to 1.e30 where undefined
    1395            abslons = np.fabs(lons)
    1396            abslats = np.fabs(lats)
    1397            lons = np.where(abslons < 1.e20, lons, 1.e30)
    1398            lats = np.where(abslats < 1.e20, lats, 1.e30)
    1399        else:
    1400            raise ValueError('Unsupported grid')
    1401
    1402        return lats.astype('f'), lons.astype('f')
    1403
    1404
    1405    def addlocal(self, ludata):
    1406        """
    1407        Add a Local Use Section [(Section 2)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect2.shtml)
    1408        to the GRIB2 message.
    1409
    1410        Parameters
    1411        ----------
    1412
    1413        **`ludata : bytes`**: Local Use data.
    1414        """
    1415        assert isinstance(ludata,bytes)
    1416        self._msg,self._pos = g2clib.grib2_addlocal(self._msg,ludata)
    1417        self.hasLocalUseSection = True
    1418        self._sections.append(2)
    1419
    1420
    1421    def addgrid(self, gdsinfo, gdtmpl, deflist=None):
    1422        """
    1423        Add a Grid Definition Section [(Section 3)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_doc/grib2_sect3.shtml)
    1424        to the GRIB2 message.
    1425
    1426        Parameters
    1427        ----------
    1428
    1429        **`gdsinfo`**: Sequence containing information needed for the grid definition section.
    1430
    1431        | Index | Description |
    1432        | :---: | :---        |
    1433        | gdsinfo[0] | Source of grid definition - [Code Table 3.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-0.shtml)|
    1434        | gdsinfo[1] | Number of data points|
    1435        | gdsinfo[2] | Number of octets for optional list of numbers defining number of points|
    1436        | gdsinfo[3] | Interpetation of list of numbers defining number of points - [Code Table 3.11](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-11.shtml)|
    1437        | gdsinfo[4] | Grid Definition Template Number - [Code Table 3.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml)|
    1438
    1439        **`gdtmpl`**: Sequence of values for the specified Grid Definition Template. Each
    1440        element of this integer array contains an entry (in the order specified) of Grid
    1441        Definition Template 3.NN
    1442
    1443        **`deflist`**: Sequence containing the number of grid points contained in each
    1444        row (or column) of a non-regular grid.  Used if gdsinfo[2] != 0.
    1445        """
    1446        if 3 in self._sections:
    1447            raise ValueError('GRIB2 Message already contains Grid Definition Section.')
    1448        if deflist is not None:
    1449            _deflist = np.array(deflist,dtype=DEFAULT_NUMPY_INT)
    1450        else:
    1451            _deflist = None
    1452        gdtnum = gdsinfo[4]
    1453        if gdtnum in [0,1,2,3,40,41,42,43,44,203,205,32768,32769]:
    1454            self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4]
    1455        elif gdtnum == 10: # mercator
    1456            self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4]
    1457        elif gdtnum == 20: # stereographic
    1458            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
    1459        elif gdtnum == 30: # lambert conformal
    1460            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
    1461        elif gdtnum == 31: # albers equal area.
    1462            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
    1463        elif gdtnum == 90: # near-sided vertical perspective satellite projection
    1464            self.scanModeFlags = utils.int2bin(gdtmpl[16],output=list)[0:4]
    1465        elif gdtnum == 110: # azimuthal equidistant.
    1466            self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4]
    1467        elif gdtnum == 120:
    1468            self.scanModeFlags = utils.int2bin(gdtmpl[6],output=list)[0:4]
    1469        elif gdtnum == 204: # curvilinear orthogonal
    1470            self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4]
    1471        elif gdtnum in [1000,1100]:
    1472            self.scanModeFlags = utils.int2bin(gdtmpl[12],output=list)[0:4]
    1473        self._msg,self._pos = g2clib.grib2_addgrid(self._msg,
    1474                                                   np.array(gdsinfo,dtype=DEFAULT_NUMPY_INT),
    1475                                                   np.array(gdtmpl,dtype=DEFAULT_NUMPY_INT),
    1476                                                   _deflist)
    1477        self._sections.append(3)
    1478
    1479
    1480    def addfield(self, field, pdtnum, pdtmpl, coordlist=None, packing="complex-spdiff", **packing_opts):
    1481        """
    1482        Add a Product Definition, Data Representation, Bitmap, and Data Sections
    1483        to `Grib2Message` instance (i.e. Sections 4-7).  Must be called after the grid
    1484        definition section has been added (`addfield`).
    1485
    1486        Parameters
    1487        ----------
    1488
    1489        **`field`**: Numpy array of data values to pack.  If field is a masked array, then
    1490        a bitmap is created from the mask.
    1491
    1492        **`pdtnum`**: integer Product Definition Template Number - [Code Table 4.0](http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-0.shtml)
    1493
    1494        **`pdtmpl`**: Sequence with the data values for the specified Product Definition
    1495        Template (N=pdtnum).  Each element of this integer array contains an entry (in
    1496        the order specified) of Product Definition Template 4.N.
    1497
    1498        **`coordlist`**: Sequence containing floating point values intended to document the
    1499        vertical discretization with model data on hybrid coordinate vertical levels. Default is `None`.
    1500
    1501        **`packing`**: String to specify the type of packing. Valid options are the following:
    1502
    1503        | Packing Scheme | Description |
    1504        | :---:          | :---:       |
    1505        | 'simple'         | [Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-0.shtml) |
    1506        | 'complex'        | [Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-2.shtml) |
    1507        | 'complex-spdiff' | [Complex packing with Spatial Differencing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-3.shtml) |
    1508        | 'jpeg'           | [JPEG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-40.shtml) |
    1509        | 'png'            | [PNG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-41.shtml) |
    1510        | 'spectral-simple'| [Spectral Data - Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-50.shtml) |
    1511        | 'spectral-complex'| [Spectral Data - Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-51.shtml) |
    1512
    1513        **`**packing_opts`**: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for
    1514        the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following:
    1515
    1516        | Packing Scheme | Keyword Arguments |
    1517        | :---:          | :---:                      |
    1518        | 'simple'     | `binScaleFactor`, `decScaleFactor` |
    1519        | 'complex'     | `binScaleFactor`, `decScaleFactor`, `priMissingValue`, [`secMissingValue`] |
    1520        | 'complex-spdiff'     | `binScaleFactor`, `decScaleFactor`, `spatialDifferenceOrder`, `priMissingValue`, [`secMissingValue`] |
    1521        | 'jpeg'     | `binScaleFactor`, `decScaleFactor` |
    1522        | 'png'     | `binScaleFactor`, `decScaleFactor` |
    1523        | 'spectral-simple'     | `binScaleFactor`, `decScaleFactor` |
    1524        | 'spectral-complex'     | `binScaleFactor`, `decScaleFactor` |
    1525        """
    1526        if self._sections[-1] != 3:
    1527            raise ValueError('addgrid() must be called before addfield()')
    1528        if self.scanModeFlags is not None:
    1529            if self.scanModeFlags[3]:
    1530                fieldsave = field.astype('f') # Casting makes a copy
    1531                field[1::2,:] = fieldsave[1::2,::-1]
    1532        fld = field.astype('f')
    1533        if ma.isMA(field) and ma.count_masked(field) > 0:
    1534            bitmapflag = 0
    1535            bmap = 1-np.ravel(field.mask.astype(DEFAULT_NUMPY_INT))
    1536        else:
    1537            bitmapflag = 255
    1538            bmap = None
    1539        if coordlist is not None:
    1540            crdlist = np.array(coordlist,'f')
    1541        else:
    1542            crdlist = None
    1543
    1544        # Set data representation template number and template values
    1545        drtnum = -1
    1546        drtmpl = np.zeros((DEFAULT_DRT_LEN),dtype=DEFAULT_NUMPY_INT)
    1547        if packing == "simple":
    1548            drtnum = 0
    1549            drtmpl[1] = packing_opts["binScaleFactor"]
    1550            drtmpl[2] = packing_opts["decScaleFactor"]
    1551        elif packing == "complex" or packing == "complex-spdiff":
    1552            if packing == "complex":
    1553                drtnum = 2
    1554            if packing == "complex-spdiff":
    1555                drtnum = 3
    1556                drtmpl[16] = packing_opts['spatialDifferenceOrder']
    1557            drtmpl[1] = packing_opts["binScaleFactor"]
    1558            drtmpl[2] = packing_opts["decScaleFactor"]
    1559            if set(("priMissingValue","secMissingValue")).issubset(packing_opts):
    1560                drtmpl[6] = 2
    1561                drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"])
    1562                drtmpl[8] = utils.putieeeint(packing_opts["secMissingValue"])
    1563            else:
    1564                if "priMissingValue" in packing_opts.keys():
    1565                    drtmpl[6] = 1
    1566                    drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"])
    1567                else:
    1568                    drtmpl[6] = 0
    1569        elif packing == "jpeg":
    1570            drtnum = 40
    1571            drtmpl[1] = packing_opts["binScaleFactor"]
    1572            drtmpl[2] = packing_opts["decScaleFactor"]
    1573        elif packing == "png":
    1574            drtnum = 41
    1575            drtmpl[1] = packing_opts["binScaleFactor"]
    1576            drtmpl[2] = packing_opts["decScaleFactor"]
    1577        elif packing == "spectral-simple":
    1578            drtnum = 50
    1579            drtmpl[1] = packing_opts["binScaleFactor"]
    1580            drtmpl[2] = packing_opts["decScaleFactor"]
    1581        elif packing == "spectral-complex":
    1582            drtnum = 51
    1583            drtmpl[1] = packing_opts["binScaleFactor"]
    1584            drtmpl[2] = packing_opts["decScaleFactor"]
    1585
    1586        pdtnum = pdtnum.value if isinstance(pdtnum,Grib2Metadata) else pdtnum
    1587
    1588        self._msg,self._pos = g2clib.grib2_addfield(self._msg,
    1589                                                    pdtnum,
    1590                                                    np.array(pdtmpl,dtype=DEFAULT_NUMPY_INT),
    1591                                                    crdlist,
    1592                                                    drtnum,
    1593                                                    drtmpl,
    1594                                                    np.ravel(fld),
    1595                                                    bitmapflag,
    1596                                                    bmap)
    1597        self._sections.append(4)
    1598        self._sections.append(5)
    1599        if bmap is not None: self._sections.append(6)
    1600        self._sections.append(7)
    1601
    1602
    1603    def end(self):
    1604        """
    1605        Add End Section (Section 8) to the GRIB2 message. A GRIB2 message
    1606        is not complete without an end section.  Once an end section is added,
    1607        the GRIB2 message can be written to file.
    1608        """
    1609        self._msg, self._pos = g2clib.grib2_end(self._msg)
    1610        self._sections.append(8)
    1611
    1612    def to_bytes(self, validate=True):
    1613        """
    1614        Return grib data in byte format. Useful for exporting data in non-file formats.
    1615        For example, can be used to output grib data directly to S3 using the boto3 client
    1616        without the need to write a temporary file to upload first.
    1617
    1618        Parameters
    1619        ----------
    1620        **`validate`**: bool (Default: True) If true, validates first/last four bytes for proper formatting, else
    1621        returns None. If False, message is output as is.
    1622
    1623        Returns
    1624        -------
    1625        Returns GRIB2 formatted message as bytes.
    1626        """
    1627        if validate:
    1628            if str(self._msg[0:4] + self._msg[-4:], 'utf-8') == 'GRIB7777':
    1629                return self._msg
    1630            else:
    1631                return None
    1632        else:
    1633            return self._msg

Grib2Message( msg=None, source=None, num=-1, decode=True, discipline=None, idsect=None) View Source

    480    def __init__(self, msg=None, source=None, num=-1, decode=True, discipline=None, idsect=None):
    481        """
    482        Class Constructor. Instantiation of this class can handle a GRIB2 message from an existing
    483        file or the creation of new GRIB2 message.  To create a new GRIB2 message, provide the
    484        appropriate values to the arguments `discipline` and `idsect`.  When these 2 arguments
    485        are not `None`, then a new GRIB2 message is created. NOTE: All other keyword arguments
    486        are ignored when a new message is created.
    487
    488        ...
    489
    490        Parameters
    491        ----------
    492
    493        **`msg`**: Binary string representing the GRIB2 Message read from file.
    494
    495        **`source`**: Source of where where this GRIB2 message originated
    496        from (i.e. the input file). This allow for interaction with the
    497        instance of `grib2io.open`. Default is None.
    498
    499        **`num`**: integer GRIB2 Message number from `grib2io.open`. Default value is -1.
    500
    501        **`decode`**: If True [DEFAULT], decode GRIB2 section lists into metadata
    502        instance variables.
    503
    504        **`discipline`**: integer GRIB2 Discipline [GRIB2 Table 0.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table0-0.shtml)
    505
    506        **`idsect`**: Sequence containing GRIB1 Identification Section values (Section 1).
    507
    508        | Index | Description |
    509        | :---: | :---        |
    510        | idsect[0] | Id of orginating centre - [ON388 - Table 0](https://www.nco.ncep.noaa.gov/pmb/docs/on388/table0.html)|
    511        | idsect[1] | Id of orginating sub-centre - [ON388 - Table C](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablec.html)|
    512        | idsect[2] | GRIB Master Tables Version Number - [Code Table 1.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-0.shtml)|
    513        | idsect[3] | GRIB Local Tables Version Number - [Code Table 1.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-1.shtml)|
    514        | idsect[4] | Significance of Reference Time - [Code Table 1.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-2.shtml)|
    515        | idsect[5] | Reference Time - Year (4 digits)|
    516        | idsect[6] | Reference Time - Month|
    517        | idsect[7] | Reference Time - Day|
    518        | idsect[8] | Reference Time - Hour|
    519        | idsect[9] | Reference Time - Minute|
    520        | idsect[10] | Reference Time - Second|
    521        | idsect[11] | Production status of data - [Code Table 1.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-3.shtml)|
    522        | idsect[12] | Type of processed data - [Code Table 1.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-4.shtml)|
    523        """
    524        self._source = source
    525        self._msgnum = num
    526        self._decode = decode
    527        self._pos = 0
    528        self._datapos = 0
    529        self._sections = []
    530        self.hasLocalUseSection = False
    531        self.isNDFD = False
    532        if discipline is not None and idsect is not None:
    533            # New message
    534            self._msg,self._pos = g2clib.grib2_create(np.array([discipline,GRIB2_EDITION_NUMBER],DEFAULT_NUMPY_INT),
    535                                                      np.array(idsect,DEFAULT_NUMPY_INT))
    536            self._sections += [0,1]
    537        else:
    538            # Existing message
    539            self._msg = msg
    540        #self.md5 = {}
    541        if self._msg is not None and self._source is not None: self.unpack()

Class Constructor. Instantiation of this class can handle a GRIB2 message from an existing file or the creation of new GRIB2 message. To create a new GRIB2 message, provide the appropriate values to the arguments `discipline` and `idsect`. When these 2 arguments are not `None`, then a new GRIB2 message is created. NOTE: All other keyword arguments are ignored when a new message is created.

...

## Parameters

`msg`: Binary string representing the GRIB2 Message read from file.

`source`: Source of where where this GRIB2 message originated from (i.e. the input file). This allow for interaction with the instance of `grib2io.open`. Default is None.

`num`: integer GRIB2 Message number from `grib2io.open`. Default value is -1.

`decode`: If True [DEFAULT], decode GRIB2 section lists into metadata instance variables.

`discipline`: integer GRIB2 Discipline GRIB2 Table 0.0

`idsect`: Sequence containing GRIB1 Identification Section values (Section 1).

Index | Description
--- | ---
idsect[0] | Id of orginating centre - ON388 - Table 0
idsect[1] | Id of orginating sub-centre - ON388 - Table C
idsect[2] | GRIB Master Tables Version Number - Code Table 1.0
idsect[3] | GRIB Local Tables Version Number - Code Table 1.1
idsect[4] | Significance of Reference Time - Code Table 1.2
idsect[5] | Reference Time - Year (4 digits)
idsect[6] | Reference Time - Month
idsect[7] | Reference Time - Day
idsect[8] | Reference Time - Hour
idsect[9] | Reference Time - Minute
idsect[10] | Reference Time - Second
idsect[11] | Production status of data - Code Table 1.3
idsect[12] | Type of processed data - Code Table 1.4

def unpack(self) View Source

    560    def unpack(self):
    561        """
    562        Unpacks GRIB2 section data from the packed, binary message.
    563        """
    564        # Section 0 - Indicator Section
    565        self.indicatorSection = []
    566        self.indicatorSection.append(struct.unpack('>4s',self._msg[0:4])[0])
    567        self.indicatorSection.append(struct.unpack('>H',self._msg[4:6])[0])
    568        self.indicatorSection.append(self._msg[6])
    569        self.indicatorSection.append(self._msg[7])
    570        self.indicatorSection.append(struct.unpack('>Q',self._msg[8:16])[0])
    571        self._pos = 16
    572        self._sections.append(0)
    573        #self.md5[0] = _getmd5str(self.indicatorSection)
    574
    575        # Section 1 - Identification Section via g2clib.unpack1()
    576        self.identificationSection,self._pos = g2clib.unpack1(self._msg,self._pos,np.empty)
    577        self.identificationSection = self.identificationSection.tolist()
    578        self._sections.append(1)
    579        if self.identificationSection[0:2] == [8,65535]: self.isNDFD = True
    580
    581        # After Section 1, perform rest of GRIB2 Decoding inside while loop
    582        # to account for sub-messages.
    583        sectnum = 1
    584        while True:
    585            if self._msg[self._pos:self._pos+4].decode('ascii','ignore') == '7777':
    586                break
    587
    588            # Read the length and section number.
    589            sectlen = struct.unpack('>i',self._msg[self._pos:self._pos+4])[0]
    590            prevsectnum = sectnum
    591            sectnum = struct.unpack('>B',self._msg[self._pos+4:self._pos+5])[0]
    592
    593            # If the previous section number is > current section number, then
    594            # we have encountered a submessage.
    595            if prevsectnum > sectnum: break
    596
    597            # Handle submessage accordingly.
    598            if isinstance(self._source,open):
    599                if self._source._index['isSubmessage'][self._msgnum]:
    600                    if sectnum == self._source._index['submessageBeginSection'][self._msgnum]:
    601                        self._pos = self._source._index['submessageOffset'][self._msgnum]
    602
    603            # Section 2 - Local Use Section.
    604            if sectnum == 2:
    605                self._lus = self._msg[self._pos+5:self._pos+sectlen]
    606                self._pos += sectlen
    607                self.hasLocalUseSection = True
    608                self._sections.append(2)
    609                #self.md5[2] = _getmd5str(self.identificationSection)
    610
    611            # Section 3 - Grid Definition Section.
    612            elif sectnum == 3:
    613                _gds,_gdt,_deflist,self._pos = g2clib.unpack3(self._msg,self._pos,np.empty)
    614                self.gridDefinitionSection = _gds.tolist()
    615                self.gridDefinitionTemplateNumber = Grib2Metadata(int(_gds[4]),table='3.1')
    616                self.gridDefinitionTemplate = _gdt.tolist()
    617                self.defList = _deflist.tolist()
    618                self._sections.append(3)
    619                #self.md5[3] = _getmd5str([self.gridDefinitionTemplateNumber]+self.gridDefinitionTemplate)
    620
    621            # Section 4 - Product Definition Section.
    622            elif sectnum == 4:
    623                _pdt,_pdtn,_coordlst,self._pos = g2clib.unpack4(self._msg,self._pos,np.empty)
    624                self.productDefinitionTemplate = _pdt.tolist()
    625                self.productDefinitionTemplateNumber = Grib2Metadata(int(_pdtn),table='4.0')
    626                self.coordinateList = _coordlst.tolist()
    627                self._sections.append(4)
    628                #self.md5[4] = _getmd5str([self.productDefinitionTemplateNumber]+self.productDefinitionTemplate)
    629
    630            # Section 5 - Data Representation Section.
    631            elif sectnum == 5:
    632                _drt,_drtn,_npts,self._pos = g2clib.unpack5(self._msg,self._pos,np.empty)
    633                self.dataRepresentationTemplate = _drt.tolist()
    634                self.dataRepresentationTemplateNumber = Grib2Metadata(int(_drtn),table='5.0')
    635                self.numberOfDataPoints = _npts
    636                self._sections.append(5)
    637                #self.md5[5] = _getmd5str([self.dataRepresentationTemplateNumber]+self.dataRepresentationTemplate)
    638
    639            # Section 6 - Bitmap Section.
    640            elif sectnum == 6:
    641                _bmap,_bmapflag = g2clib.unpack6(self._msg,self.gridDefinitionSection[1],self._pos,np.empty)
    642                self.bitMapFlag = _bmapflag
    643                if self.bitMapFlag == 0:
    644                    self.bitMap = _bmap
    645                elif self.bitMapFlag == 254:
    646                    # Value of 254 says to use a previous bitmap in the file.
    647                    self.bitMapFlag = 0
    648                    if isinstance(self._source,open):
    649                        self.bitMap = self._source._index['bitMap'][self._msgnum]
    650                self._pos += sectlen # IMPORTANT: This is here because g2clib.unpack6() does not return updated position.
    651                self._sections.append(6)
    652                #self.md5[6] = None
    653
    654            # Section 7 - Data Section (data unpacked when data() method is invoked).
    655            elif sectnum == 7:
    656                self._datapos = self._pos
    657                self._pos += sectlen # REMOVE THIS WHEN UNPACKING DATA IS IMPLEMENTED
    658                self._sections.append(7)
    659                #self.md5[7] = _getmd5str(self._msg[self._datapos:sectlen+1])
    660
    661            else:
    662                errmsg = 'Unknown section number = %i' % sectnum
    663                raise ValueError(errmsg)
    664
    665        if self._decode: self.decode()

Unpacks GRIB2 section data from the packed, binary message.

def decode(self) View Source

     667    def decode(self):
     668        """
     669        Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables.
     670        """
     671
     672        # Section 0 - Indictator Section
     673        self.discipline = Grib2Metadata(self.indicatorSection[2],table='0.0')
     674
     675        # Section 1 - Indentification Section.
     676        self.originatingCenter = Grib2Metadata(self.identificationSection[0],table='originating_centers')
     677        self.originatingSubCenter = Grib2Metadata(self.identificationSection[1],table='originating_subcenters')
     678        self.masterTableInfo = Grib2Metadata(self.identificationSection[2],table='1.0')
     679        self.localTableInfo = Grib2Metadata(self.identificationSection[3],table='1.1')
     680        self.significanceOfReferenceTime = Grib2Metadata(self.identificationSection[4],table='1.2')
     681        self.year = self.identificationSection[5]
     682        self.month = self.identificationSection[6]
     683        self.day = self.identificationSection[7]
     684        self.hour = self.identificationSection[8]
     685        self.minute = self.identificationSection[9]
     686        self.second = self.identificationSection[10]
     687        self.refDate = (self.year*1000000)+(self.month*10000)+(self.day*100)+self.hour
     688        self.dtReferenceDate = datetime.datetime(self.year,self.month,self.day,
     689                                                 hour=self.hour,minute=self.minute,
     690                                                 second=self.second)
     691        self.productionStatus = Grib2Metadata(self.identificationSection[11],table='1.3')
     692        self.typeOfData = Grib2Metadata(self.identificationSection[12],table='1.4')
     693
     694        # ----------------------------
     695        # Section 3 -- Grid Definition
     696        # ----------------------------
     697
     698        # Set shape of the Earth parameters
     699        if self.gridDefinitionTemplateNumber.value in [50,51,52,1200]:
     700            earthparams = None
     701        else:
     702            earthparams = tables.earth_params[str(self.gridDefinitionTemplate[0])]
     703        if earthparams['shape'] == 'spherical':
     704            if earthparams['radius'] is None:
     705                self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1])
     706                self.earthMajorAxis = None
     707                self.earthMinorAxis = None
     708            else:
     709                self.earthRadius = earthparams['radius']
     710                self.earthMajorAxis = None
     711                self.earthMinorAxis = None
     712        elif earthparams['shape'] == 'oblateSpheroid':
     713            if earthparams['radius'] is None and earthparams['major_axis'] is None and earthparams['minor_axis'] is None:
     714                self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1])
     715                self.earthMajorAxis = self.gridDefinitionTemplate[4]/(10.**self.gridDefinitionTemplate[3])
     716                self.earthMinorAxis = self.gridDefinitionTemplate[6]/(10.**self.gridDefinitionTemplate[5])
     717            else:
     718                self.earthRadius = earthparams['radius']
     719                self.earthMajorAxis = earthparams['major_axis']
     720                self.earthMinorAxis = earthparams['minor_axis']
     721
     722        reggrid = self.gridDefinitionSection[2] == 0 # self.gridDefinitionSection[2]=0 means regular 2-d grid
     723        if reggrid and self.gridDefinitionTemplateNumber.value not in [50,51,52,53,100,120,1000,1200]:
     724            self.nx = self.gridDefinitionTemplate[7]
     725            self.ny = self.gridDefinitionTemplate[8]
     726        if not reggrid and self.gridDefinitionTemplateNumber == 40:
     727            # Reduced Gaussian Grid
     728            self.ny = self.gridDefinitionTemplate[8]
     729        if self.gridDefinitionTemplateNumber.value in [0,1,203,205,32768,32769]:
     730            # Regular or Rotated Lat/Lon Grid
     731            scalefact = float(self.gridDefinitionTemplate[9])
     732            if self.gridDefinitionTemplate[10] == 4294967295:
     733                self.gridDefinitionTemplate[10] = -1
     734            divisor = float(self.gridDefinitionTemplate[10])
     735            if scalefact == 0: scalefact = 1.
     736            if divisor <= 0: divisor = 1.e6
     737            self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor
     738            self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor
     739            self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor
     740            self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor
     741            self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor
     742            self.gridlengthYDirection = scalefact*self.gridDefinitionTemplate[17]/divisor
     743            if self.latitudeFirstGridpoint > self.latitudeLastGridpoint:
     744                self.gridlengthYDirection = -self.gridlengthYDirection
     745            if self.longitudeFirstGridpoint > self.longitudeLastGridpoint:
     746                self.gridlengthXDirection = -self.gridlengthXDirection
     747            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
     748            if self.gridDefinitionTemplateNumber == 1:
     749                self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor
     750                self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor
     751                self.anglePoleRotation = self.gridDefinitionTemplate[21]
     752        elif self.gridDefinitionTemplateNumber == 10:
     753            # Mercator
     754            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     755            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     756            self.latitudeLastGridpoint = self.gridDefinitionTemplate[13]/1.e6
     757            self.longitudeLastGridpoint = self.gridDefinitionTemplate[14]/1.e6
     758            self.gridlengthXDirection = self.gridDefinitionTemplate[17]/1.e3
     759            self.gridlengthYDirection= self.gridDefinitionTemplate[18]/1.e3
     760            self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6
     761            self.proj4_lon_0 = 0.5*(self.longitudeFirstGridpoint+self.longitudeLastGridpoint)
     762            self.proj4_proj = 'merc'
     763            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4]
     764        elif self.gridDefinitionTemplateNumber == 20:
     765            # Stereographic
     766            projflag = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0]
     767            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     768            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     769            self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6
     770            if projflag == 0:
     771                self.proj4_lat_0 = 90
     772            elif projflag == 1:
     773                self.proj4_lat_0 = -90
     774            else:
     775                raise ValueError('Invalid projection center flag = %s'%projflag)
     776            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
     777            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
     778            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
     779            self.proj4_proj = 'stere'
     780            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
     781        elif self.gridDefinitionTemplateNumber == 30:
     782            # Lambert Conformal
     783            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     784            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     785            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
     786            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
     787            self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6
     788            self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6
     789            self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6
     790            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
     791            self.proj4_proj = 'lcc'
     792            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
     793        elif self.gridDefinitionTemplateNumber == 31:
     794            # Albers Equal Area
     795            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
     796            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
     797            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
     798            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
     799            self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6
     800            self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6
     801            self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6
     802            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
     803            self.proj4_proj = 'aea'
     804            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
     805        elif self.gridDefinitionTemplateNumber == 40 or self.gridDefinitionTemplateNumber == 41:
     806            # Gaussian Grid
     807            scalefact = float(self.gridDefinitionTemplate[9])
     808            if self.gridDefinitionTemplate[10] == 4294967295:
     809                self.gridDefinitionTemplate[10] = -1
     810            divisor = float(self.gridDefinitionTemplate[10])
     811            if scalefact == 0: scalefact = 1.
     812            if divisor <= 0: divisor = 1.e6
     813            self.pointsBetweenPoleAndEquator = self.gridDefinitionTemplate[17]
     814            self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor
     815            self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor
     816            self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor
     817            self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor
     818            if reggrid:
     819                self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor
     820                if self.longitudeFirstGridpoint > self.longitudeLastGridpoint:
     821                    self.gridlengthXDirection = -self.gridlengthXDirection
     822            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
     823            if self.gridDefinitionTemplateNumber == 41:
     824                self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor
     825                self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor
     826                self.anglePoleRotation = self.gridDefinitionTemplate[21]
     827        elif self.gridDefinitionTemplateNumber == 50:
     828            # Spectral Coefficients
     829            self.spectralFunctionParameters = (self.gridDefinitionTemplate[0],self.gridDefinitionTemplate[1],self.gridDefinitionTemplate[2])
     830            self.scanModeFlags = [None,None,None,None]
     831        elif self.gridDefinitionTemplateNumber == 90:
     832            # Near-sided Vertical Perspective Satellite Projection
     833            self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6
     834            self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6
     835            self.proj4_h = self.earthMajorAxis * (self.gridDefinitionTemplate[18]/1.e6)
     836            dx = self.gridDefinitionTemplate[12]
     837            dy = self.gridDefinitionTemplate[13]
     838            # if lat_0 is equator, it's a geostationary view.
     839            if self.proj4_lat_0 == 0.: # if lat_0 is equator, it's a
     840                self.proj4_proj = 'geos'
     841            # general case of 'near-side perspective projection' (untested)
     842            else:
     843                self.proj4_proj = 'nsper'
     844                msg = 'Only geostationary perspective is supported. Lat/Lon values returned by grid method may be incorrect.'
     845                warnings.warn(msg)
     846            # latitude of horizon on central meridian
     847            lonmax = 90.-(180./np.pi)*np.arcsin(self.earthMajorAxis/self.proj4_h)
     848            # longitude of horizon on equator
     849            latmax = 90.-(180./np.pi)*np.arcsin(self.earthMinorAxis/self.proj4_h)
     850            # truncate to nearest thousandth of a degree (to make sure
     851            # they aren't slightly over the horizon)
     852            latmax = int(1000*latmax)/1000.
     853            lonmax = int(1000*lonmax)/1000.
     854            # h is measured from surface of earth at equator.
     855            self.proj4_h = self.proj4_h - self.earthMajorAxis
     856            # width and height of visible projection
     857            P = pyproj.Proj(proj=self.proj4_proj,\
     858                            a=self.earthMajorAxis,b=self.earthMinorAxis,\
     859                            lat_0=0,lon_0=0,h=self.proj4_h)
     860            x1,y1 = P(0.,latmax)
     861            x2,y2 = P(lonmax,0.)
     862            width = 2*x2
     863            height = 2*y1
     864            self.gridlengthXDirection = width/dx
     865            self.gridlengthYDirection = height/dy
     866            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0:4]
     867        elif self.gridDefinitionTemplateNumber == 110:
     868            # Azimuthal Equidistant
     869            self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6
     870            self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6
     871            self.gridlengthXDirection = self.gridDefinitionTemplate[12]/1000.
     872            self.gridlengthYDirection = self.gridDefinitionTemplate[13]/1000.
     873            self.proj4_proj = 'aeqd'
     874            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4]
     875        elif self.gridDefinitionTemplateNumber == 204:
     876            # Curvilinear Orthogonal
     877            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
     878        else:
     879            errmsg = 'Unsupported Grid Definition Template Number - 3.%i' % self.gridDefinitionTemplateNumber.value
     880            raise ValueError(errmsg)
     881
     882        # -------------------------------
     883        # Section 4 -- Product Definition
     884        # -------------------------------
     885
     886        # Template 4.0 - NOTE: That is these attributes apply to other templates.
     887        self.parameterCategory = self.productDefinitionTemplate[0]
     888        self.parameterNumber = self.productDefinitionTemplate[1]
     889        self.fullName,self.units,self.shortName = tables.get_varinfo_from_table(self.discipline.value,
     890                                                                                self.parameterCategory,
     891                                                                                self.parameterNumber)
     892        self.typeOfGeneratingProcess = Grib2Metadata(self.productDefinitionTemplate[2],table='4.3')
     893        self.backgroundGeneratingProcessIdentifier = self.productDefinitionTemplate[3]
     894        self.generatingProcess = Grib2Metadata(self.productDefinitionTemplate[4],table='generating_process')
     895        self.unitOfTimeRange = Grib2Metadata(self.productDefinitionTemplate[7],table='4.4')
     896        self.leadTime = self.productDefinitionTemplate[8]
     897        self.typeOfFirstFixedSurface = Grib2Metadata(self.productDefinitionTemplate[9],table='4.5')
     898        self.scaleFactorOfFirstFixedSurface = self.productDefinitionTemplate[10]
     899        self.unitOfFirstFixedSurface = self.typeOfFirstFixedSurface.definition[1]
     900        self.scaledValueOfFirstFixedSurface = self.productDefinitionTemplate[11]
     901        self.valueOfFirstFixedSurface = self.scaledValueOfFirstFixedSurface/(10.**self.scaleFactorOfFirstFixedSurface)
     902        temp = tables.get_value_from_table(self.productDefinitionTemplate[12],'4.5')
     903        if temp[0] == 'Missing' and temp[1] == 'unknown':
     904            self.typeOfSecondFixedSurface = None
     905            self.scaleFactorOfSecondFixedSurface = None
     906            self.unitOfSecondFixedSurface = None
     907            self.valueOfSecondFixedSurface = None
     908        else:
     909            self.typeOfSecondFixedSurface = Grib2Metadata(self.productDefinitionTemplate[12],table='4.5')
     910            self.scaleFactorOfSecondFixedSurface = self.productDefinitionTemplate[13]
     911            self.unitOfSecondFixedSurface = self.typeOfSecondFixedSurface.definition[1]
     912            self.scaledValueOfSecondFixedSurface = self.productDefinitionTemplate[14]
     913            self.valueOfSecondFixedSurface = self.scaledValueOfSecondFixedSurface/(10.**self.scaleFactorOfSecondFixedSurface)
     914        self.level = tables.get_wgrib2_level_string(*self.productDefinitionTemplate[9:15])
     915
     916        # Template 4.1 -
     917        if self.productDefinitionTemplateNumber == 1:
     918            self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6')
     919            self.perturbationNumber = self.productDefinitionTemplate[16]
     920            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17]
     921
     922        # Template 4.2 -
     923        elif self.productDefinitionTemplateNumber == 2:
     924            self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7')
     925            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16]
     926
     927        # Template 4.5 -
     928        elif self.productDefinitionTemplateNumber == 5:
     929            self.forecastProbabilityNumber = self.productDefinitionTemplate[15]
     930            self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16]
     931            self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9')
     932            self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18]
     933            self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19]
     934            self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20]
     935            self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21]
     936            self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \
     937                                       self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18])
     938            self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \
     939                                       self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20])
     940            self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22])
     941
     942        # Template 4.6 -
     943        elif self.productDefinitionTemplateNumber == 6:
     944            self.percentileValue = self.productDefinitionTemplate[15]
     945
     946        # Template 4.8 -
     947        elif self.productDefinitionTemplateNumber == 8:
     948            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[15]
     949            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[16]
     950            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[17]
     951            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[18]
     952            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[19]
     953            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[20]
     954            self.numberOfTimeRanges = self.productDefinitionTemplate[21]
     955            self.numberOfMissingValues = self.productDefinitionTemplate[22]
     956            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[23],table='4.10')
     957            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.11')
     958            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.4')
     959            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[26]
     960            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4')
     961            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[28]
     962
     963        # Template 4.9 -
     964        elif self.productDefinitionTemplateNumber == 9:
     965            self.forecastProbabilityNumber = self.productDefinitionTemplate[15]
     966            self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16]
     967            self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9')
     968            self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18]
     969            self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19]
     970            self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20]
     971            self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21]
     972            self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \
     973                                       self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18])
     974            self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \
     975                                       self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20])
     976            self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22])
     977            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[22]
     978            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[23]
     979            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[24]
     980            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[25]
     981            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[26]
     982            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[27]
     983            self.numberOfTimeRanges = self.productDefinitionTemplate[28]
     984            self.numberOfMissingValues = self.productDefinitionTemplate[29]
     985            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[30],table='4.10')
     986            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[31],table='4.11')
     987            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[32],table='4.4')
     988            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[33]
     989            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[34],table='4.4')
     990            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[35]
     991
     992        # Template 4.10 -
     993        elif self.productDefinitionTemplateNumber == 10:
     994            self.percentileValue = self.productDefinitionTemplate[15]
     995            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[16]
     996            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[17]
     997            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[18]
     998            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[19]
     999            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[20]
    1000            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[21]
    1001            self.numberOfTimeRanges = self.productDefinitionTemplate[22]
    1002            self.numberOfMissingValues = self.productDefinitionTemplate[23]
    1003            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.10')
    1004            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.11')
    1005            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.4')
    1006            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[27]
    1007            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4')
    1008            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[29]
    1009
    1010        # Template 4.11 -
    1011        elif self.productDefinitionTemplateNumber == 11:
    1012            self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6')
    1013            self.perturbationNumber = self.productDefinitionTemplate[16]
    1014            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17]
    1015            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[18]
    1016            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[19]
    1017            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[20]
    1018            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[21]
    1019            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[22]
    1020            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[23]
    1021            self.numberOfTimeRanges = self.productDefinitionTemplate[24]
    1022            self.numberOfMissingValues = self.productDefinitionTemplate[25]
    1023            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.10')
    1024            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.11')
    1025            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4')
    1026            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[29]
    1027            self.unitOfTimeRangeOfSuccessiveFields = tables.get_value_from_table(self.productDefinitionTemplate[30],table='4.4')
    1028            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[31]
    1029
    1030        # Template 4.12 -
    1031        elif self.productDefinitionTemplateNumber == 12:
    1032            self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7')
    1033            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16]
    1034            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[17]
    1035            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[18]
    1036            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[19]
    1037            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[20]
    1038            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[21]
    1039            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[22]
    1040            self.numberOfTimeRanges = self.productDefinitionTemplate[23]
    1041            self.numberOfMissingValues = self.productDefinitionTemplate[24]
    1042            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.10')
    1043            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.11')
    1044            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4')
    1045            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[28]
    1046            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[29],table='4.4')
    1047            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[30]
    1048
    1049        # Template 4.15 -
    1050        elif self.productDefinitionTemplateNumber == 15:
    1051            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[15],table='4.10')
    1052            self.typeOfSpatialProcessing = Grib2Metadata(self.productDefinitionTemplate[16],table='4.15')
    1053            self.numberOfDataPointsForSpatialProcessing = self.productDefinitionTemplate[17]
    1054
    1055        else:
    1056            if self.productDefinitionTemplateNumber != 0:
    1057                errmsg = 'Unsupported Product Definition Template Number - 4.%i' % self.productDefinitionTemplateNumber.value
    1058                raise ValueError(errmsg)
    1059
    1060
    1061        self.leadTime = utils.getleadtime(self.identificationSection,
    1062                                          self.productDefinitionTemplateNumber.value,
    1063                                          self.productDefinitionTemplate)
    1064
    1065        if self.productDefinitionTemplateNumber.value in [8,9,10,11,12]:
    1066            self.dtEndOfTimePeriod = datetime.datetime(self.yearOfEndOfTimePeriod,self.monthOfEndOfTimePeriod,
    1067                                     self.dayOfEndOfTimePeriod,hour=self.hourOfEndOfTimePeriod,
    1068                                     minute=self.minuteOfEndOfTimePeriod,
    1069                                     second=self.secondOfEndOfTimePeriod)
    1070
    1071        # --------------------------------
    1072        # Section 5 -- Data Representation
    1073        # --------------------------------
    1074
    1075        # Template 5.0 - Simple Packing
    1076        if self.dataRepresentationTemplateNumber == 0:
    1077            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1078            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1079            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1080            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1081            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[3],table='5.1')
    1082
    1083        # Template 5.2 - Complex Packing
    1084        elif self.dataRepresentationTemplateNumber == 2:
    1085            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1086            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1087            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1088            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1089            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1090            self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4')
    1091            self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5')
    1092            self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None
    1093            self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None
    1094            self.nGroups = self.dataRepresentationTemplate[9]
    1095            self.refGroupWidth = self.dataRepresentationTemplate[10]
    1096            self.nBitsGroupWidth = self.dataRepresentationTemplate[11]
    1097            self.refGroupLength = self.dataRepresentationTemplate[12]
    1098            self.groupLengthIncrement = self.dataRepresentationTemplate[13]
    1099            self.lengthOfLastGroup = self.dataRepresentationTemplate[14]
    1100            self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15]
    1101
    1102        # Template 5.3 - Complex Packing and Spatial Differencing
    1103        elif self.dataRepresentationTemplateNumber == 3:
    1104            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1105            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1106            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1107            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1108            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1109            self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4')
    1110            self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5')
    1111            self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None
    1112            self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None
    1113            self.nGroups = self.dataRepresentationTemplate[9]
    1114            self.refGroupWidth = self.dataRepresentationTemplate[10]
    1115            self.nBitsGroupWidth = self.dataRepresentationTemplate[11]
    1116            self.refGroupLength = self.dataRepresentationTemplate[12]
    1117            self.groupLengthIncrement = self.dataRepresentationTemplate[13]
    1118            self.lengthOfLastGroup = self.dataRepresentationTemplate[14]
    1119            self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15]
    1120            self.spatialDifferenceOrder = Grib2Metadata(self.dataRepresentationTemplate[16],table='5.6')
    1121            self.nBytesSpatialDifference = self.dataRepresentationTemplate[17]
    1122
    1123        # Template 5.4 - IEEE Floating Point Data
    1124        elif self.dataRepresentationTemplateNumber == 4:
    1125            self.precision = Grib2Metadata(self.dataRepresentationTemplate[0],table='5.7')
    1126
    1127        # Template 5.40 - JPEG2000 Compression
    1128        elif self.dataRepresentationTemplateNumber == 40:
    1129            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1130            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1131            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1132            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1133            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1134            self.typeOfCompression = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.40')
    1135            self.targetCompressionRatio = self.dataRepresentationTemplate[6]
    1136
    1137        # Template 5.41 - PNG Compression
    1138        elif self.dataRepresentationTemplateNumber == 41:
    1139            self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0])
    1140            self.binScaleFactor = self.dataRepresentationTemplate[1]
    1141            self.decScaleFactor = self.dataRepresentationTemplate[2]
    1142            self.nBitsPacking = self.dataRepresentationTemplate[3]
    1143            self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
    1144
    1145        else:
    1146            errmsg = 'Unsupported Data Representation Definition Template Number - 5.%i' % self.dataRepresentationTemplateNumber.value
    1147            raise ValueError(errmsg)

Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables.

def data( self, fill_value=9.969209968386869e+36, masked_array=True, expand=True, order=None, map_keys=False) View Source

    1150    def data(self, fill_value=DEFAULT_FILL_VALUE, masked_array=True, expand=True, order=None,
    1151             map_keys=False):
    1152        """
    1153        Returns an unpacked data grid.
    1154
    1155        Parameters
    1156        ----------
    1157
    1158        **`fill_value`**: Missing or masked data is filled with this value or default value given by
    1159        `DEFAULT_FILL_VALUE`
    1160
    1161        **`masked_array`**: If `True` [DEFAULT], return masked array if there is bitmap for missing
    1162        or masked data.
    1163
    1164        **`expand`**: If `True` [DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids.
    1165
    1166        **`order`**: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing
    1167        or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids.
    1168
    1169        **`map_keys`**: If `True`, data values will be mapped to the string-based keys that are stored
    1170        in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the
    1171        units (i.e. "See Table 4.xxx").
    1172
    1173        Returns
    1174        -------
    1175
    1176        **`numpy.ndarray`**: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32,
    1177        but could be np.int32 if Grib2Message.typeOfValues is integer.  The array dtype will be
    1178        string-based if map_keys=True.
    1179        """
    1180        if not hasattr(self,'scanModeFlags'):
    1181            raise ValueError('Unsupported grid definition template number %s'%self.gridDefinitionTemplateNumber)
    1182        else:
    1183            if self.scanModeFlags[2]:
    1184                storageorder='F'
    1185            else:
    1186                storageorder='C'
    1187        if order is None:
    1188            if (self.dataRepresentationTemplateNumber in [2,3] and
    1189                self.dataRepresentationTemplate[6] != 0) or self.bitMapFlag == 0:
    1190                order = 0
    1191            else:
    1192                order = 1
    1193        drtnum = self.dataRepresentationTemplateNumber.value
    1194        drtmpl = np.asarray(self.dataRepresentationTemplate,dtype=DEFAULT_NUMPY_INT)
    1195        gdtnum = self.gridDefinitionTemplateNumber.value
    1196        gdtmpl = np.asarray(self.gridDefinitionTemplate,dtype=DEFAULT_NUMPY_INT)
    1197        ndpts = self.numberOfDataPoints
    1198        gds = self.gridDefinitionSection
    1199        ngrdpts = gds[1]
    1200        ipos = self._datapos
    1201        fld1 = g2clib.unpack7(self._msg,gdtnum,gdtmpl,drtnum,drtmpl,ndpts,ipos,np.empty,storageorder=storageorder)
    1202        # Apply bitmap.
    1203        if self.bitMapFlag == 0:
    1204            fld = fill_value*np.ones(ngrdpts,'f')
    1205            np.put(fld,np.nonzero(self.bitMap),fld1)
    1206            if masked_array:
    1207                fld = ma.masked_values(fld,fill_value)
    1208        # Missing values instead of bitmap
    1209        elif masked_array and hasattr(self,'priMissingValue'):
    1210            if hasattr(self,'secMissingValue'):
    1211                mask = np.logical_or(fld1==self.priMissingValue,fld1==self.secMissingValue)
    1212            else:
    1213                mask = fld1 == self.priMissingValue
    1214            fld = ma.array(fld1,mask=mask)
    1215        else:
    1216            fld = fld1
    1217        if self.nx is not None and self.ny is not None: # Rectangular grid.
    1218            if ma.isMA(fld):
    1219                fld = ma.reshape(fld,(self.ny,self.nx))
    1220            else:
    1221                fld = np.reshape(fld,(self.ny,self.nx))
    1222        else:
    1223            if gds[2] and gdtnum == 40: # Reduced global Gaussian grid.
    1224                if expand:
    1225                    from . import redtoreg
    1226                    self.nx = 2*self.ny
    1227                    lonsperlat = self.defList
    1228                    if ma.isMA(fld):
    1229                        fld = ma.filled(fld)
    1230                        fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long),
    1231                                                 fld.astype(np.double),fill_value)
    1232                        fld = ma.masked_values(fld,fill_value)
    1233                    else:
    1234                        fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long),
    1235                                                 fld.astype(np.double),fill_value)
    1236        # Check scan modes for rect grids.
    1237        if self.nx is not None and self.ny is not None:
    1238            if self.scanModeFlags[3]:
    1239                fldsave = fld.astype('f') # casting makes a copy
    1240                fld[1::2,:] = fldsave[1::2,::-1]
    1241
    1242        # Set data to integer according to GRIB metadata
    1243        if self.typeOfValues == "Integer": fld = fld.astype(np.int32)
    1244
    1245        # Map the data values to their respective definitions.
    1246        if map_keys:
    1247            fld = fld.astype(np.int32).astype(str)
    1248            if self.identificationSection[0] == 7 and \
    1249               self.identificationSection[1] == 14 and \
    1250               self.shortName == 'PWTHER':
    1251                # MDL Predominant Weather Grid
    1252                #keys = utils.decode_mdl_wx_strings(self._lus)
    1253                keys = utils.decode_ndfd_wx_strings(self._lus)
    1254                for n,k in enumerate(keys):
    1255                    fld = np.where(fld==str(n+1),k,fld)
    1256            elif self.identificationSection[0] == 8 and \
    1257                 self.identificationSection[1] == 65535 and \
    1258                 self.shortName == 'CRAIN':
    1259                # NDFD Predominant Weather Grid
    1260                keys = utils.decode_ndfd_wx_strings(self._lus)
    1261                for n,k in enumerate(keys):
    1262                    fld = np.where(fld==str(n+1),k,fld)
    1263            else:
    1264                # For data whose units are defined in a code table
    1265                tbl = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0]
    1266                for k,v in tables.get_table(tbl).items():
    1267                    fld = np.where(fld==k,v,fld)
    1268        return fld

Returns an unpacked data grid.

## Parameters

`fill_value`: Missing or masked data is filled with this value or default value given by `DEFAULT_FILL_VALUE`

`masked_array`: If `True` [DEFAULT], return masked array if there is bitmap for missing or masked data.

`expand`: If `True` [DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids.

`order`: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids.

`map_keys`: If `True`, data values will be mapped to the string-based keys that are stored in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the units (i.e. "See Table 4.xxx").

## Returns

`numpy.ndarray`: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32, but could be np.int32 if Grib2Message.typeOfValues is integer. The array dtype will be string-based if map_keys=True.

def latlons(self) View Source

    1271    def latlons(self):
    1272        """Alias for `grib2io.Grib2Message.grid` method"""
    1273        return self.grid()

Alias for `grib2io.Grib2Message.grid` method

def grid(self) View Source

    1276    def grid(self):
    1277        """
    1278        Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon,
    1279        global Gaussian, mercator, stereographic, lambert conformal, albers equal-area,
    1280        space-view and azimuthal equidistant grids.
    1281
    1282        Returns
    1283        -------
    1284
    1285        **`lats, lons : numpy.ndarray`**
    1286
    1287        Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and
    1288        longitudes in units of degrees.
    1289        """
    1290        gdtnum = self.gridDefinitionTemplateNumber
    1291        gdtmpl = self.gridDefinitionTemplate
    1292        reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid
    1293        self.projparams = {}
    1294        if self.earthMajorAxis is not None: self.projparams['a']=self.earthMajorAxis
    1295        if self.earthMajorAxis is not None: self.projparams['b']=self.earthMinorAxis
    1296        if gdtnum == 0:
    1297            # Regular lat/lon grid
    1298            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
    1299            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
    1300            dlon = self.gridlengthXDirection
    1301            dlat = self.gridlengthYDirection
    1302            lats = np.arange(lat1,lat2+dlat,dlat)
    1303            lons = np.arange(lon1,lon2+dlon,dlon)
    1304            # flip if scan mode says to.
    1305            #if self.scanModeFlags[0]:
    1306            #    lons = lons[::-1]
    1307            #if not self.scanModeFlags[1]:
    1308            #    lats = lats[::-1]
    1309            self.projparams['proj'] = 'cyl'
    1310            lons,lats = np.meshgrid(lons,lats) # make 2-d arrays.
    1311        elif gdtnum == 40: # Gaussian grid (only works for global!)
    1312            from utils.gauss_grids import gaussian_latitudes
    1313            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
    1314            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
    1315            nlats = self.ny
    1316            if not reggrid: # Reduced Gaussian grid.
    1317                nlons = 2*nlats
    1318                dlon = 360./nlons
    1319            else:
    1320                nlons = self.nx
    1321                dlon = self.gridlengthXDirection
    1322            lons = np.arange(lon1,lon2+dlon,dlon)
    1323            # Compute Gaussian lats (north to south)
    1324            lats = gaussian_latitudes(nlats)
    1325            if lat1 < lat2:  # reverse them if necessary
    1326                lats = lats[::-1]
    1327            # flip if scan mode says to.
    1328            #if self.scanModeFlags[0]:
    1329            #    lons = lons[::-1]
    1330            #if not self.scanModeFlags[1]:
    1331            #    lats = lats[::-1]
    1332            self.projparams['proj'] = 'cyl'
    1333            lons,lats = np.meshgrid(lons,lats) # make 2-d arrays
    1334        elif gdtnum in [10,20,30,31,110]:
    1335            # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant
    1336            dx,dy = self.gridlengthXDirection, self.gridlengthYDirection
    1337            lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
    1338            if gdtnum == 10: # Mercator.
    1339                self.projparams['lat_ts']=self.proj4_lat_ts
    1340                self.projparams['proj']=self.proj4_proj
    1341                self.projparams['lon_0']=self.proj4_lon_0
    1342                pj = pyproj.Proj(self.projparams)
    1343                llcrnrx, llcrnry = pj(lon1,lat1)
    1344                x = llcrnrx+dx*np.arange(self.nx)
    1345                y = llcrnry+dy*np.arange(self.ny)
    1346                x,y = np.meshgrid(x, y)
    1347                lons,lats = pj(x, y, inverse=True)
    1348            elif gdtnum == 20:  # Stereographic
    1349                self.projparams['lat_ts']=self.proj4_lat_ts
    1350                self.projparams['proj']=self.proj4_proj
    1351                self.projparams['lat_0']=self.proj4_lat_0
    1352                self.projparams['lon_0']=self.proj4_lon_0
    1353                pj = pyproj.Proj(self.projparams)
    1354                llcrnrx, llcrnry = pj(lon1,lat1)
    1355                x = llcrnrx+dx*np.arange(self.nx)
    1356                y = llcrnry+dy*np.arange(self.ny)
    1357                x,y = np.meshgrid(x, y)
    1358                lons,lats = pj(x, y, inverse=True)
    1359            elif gdtnum in [30,31]: # Lambert, Albers
    1360                self.projparams['lat_1']=self.proj4_lat_1
    1361                self.projparams['lat_2']=self.proj4_lat_2
    1362                self.projparams['proj']=self.proj4_proj
    1363                self.projparams['lon_0']=self.proj4_lon_0
    1364                pj = pyproj.Proj(self.projparams)
    1365                llcrnrx, llcrnry = pj(lon1,lat1)
    1366                x = llcrnrx+dx*np.arange(self.nx)
    1367                y = llcrnry+dy*np.arange(self.ny)
    1368                x,y = np.meshgrid(x, y)
    1369                lons,lats = pj(x, y, inverse=True)
    1370            elif gdtnum == 110: # Azimuthal Equidistant
    1371                self.projparams['proj']=self.proj4_proj
    1372                self.projparams['lat_0']=self.proj4_lat_0
    1373                self.projparams['lon_0']=self.proj4_lon_0
    1374                pj = pyproj.Proj(self.projparams)
    1375                llcrnrx, llcrnry = pj(lon1,lat1)
    1376                x = llcrnrx+dx*np.arange(self.nx)
    1377                y = llcrnry+dy*np.arange(self.ny)
    1378                x,y = np.meshgrid(x, y)
    1379                lons,lats = pj(x, y, inverse=True)
    1380        elif gdtnum == 90:
    1381            # Satellite Projection
    1382            dx = self.gridlengthXDirection
    1383            dy = self.gridlengthYDirection
    1384            self.projparams['proj']=self.proj4_proj
    1385            self.projparams['lon_0']=self.proj4_lon_0
    1386            self.projparams['lat_0']=self.proj4_lat_0
    1387            self.projparams['h']=self.proj4_h
    1388            pj = pyproj.Proj(self.projparams)
    1389            x = dx*np.indices((self.ny,self.nx),'f')[1,:,:]
    1390            x -= 0.5*x.max()
    1391            y = dy*np.indices((self.ny,self.nx),'f')[0,:,:]
    1392            y -= 0.5*y.max()
    1393            lons,lats = pj(x,y,inverse=True)
    1394            # Set lons,lats to 1.e30 where undefined
    1395            abslons = np.fabs(lons)
    1396            abslats = np.fabs(lats)
    1397            lons = np.where(abslons < 1.e20, lons, 1.e30)
    1398            lats = np.where(abslats < 1.e20, lats, 1.e30)
    1399        else:
    1400            raise ValueError('Unsupported grid')
    1401
    1402        return lats.astype('f'), lons.astype('f')

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.

## Returns

`lats, lons : numpy.ndarray`

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

def addlocal(self, ludata) View Source

    1405    def addlocal(self, ludata):
    1406        """
    1407        Add a Local Use Section [(Section 2)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect2.shtml)
    1408        to the GRIB2 message.
    1409
    1410        Parameters
    1411        ----------
    1412
    1413        **`ludata : bytes`**: Local Use data.
    1414        """
    1415        assert isinstance(ludata,bytes)
    1416        self._msg,self._pos = g2clib.grib2_addlocal(self._msg,ludata)
    1417        self.hasLocalUseSection = True
    1418        self._sections.append(2)

Add a Local Use Section (Section 2) to the GRIB2 message.

## Parameters

`ludata : bytes`: Local Use data.

def addgrid(self, gdsinfo, gdtmpl, deflist=None) View Source

    1421    def addgrid(self, gdsinfo, gdtmpl, deflist=None):
    1422        """
    1423        Add a Grid Definition Section [(Section 3)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_doc/grib2_sect3.shtml)
    1424        to the GRIB2 message.
    1425
    1426        Parameters
    1427        ----------
    1428
    1429        **`gdsinfo`**: Sequence containing information needed for the grid definition section.
    1430
    1431        | Index | Description |
    1432        | :---: | :---        |
    1433        | gdsinfo[0] | Source of grid definition - [Code Table 3.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-0.shtml)|
    1434        | gdsinfo[1] | Number of data points|
    1435        | gdsinfo[2] | Number of octets for optional list of numbers defining number of points|
    1436        | gdsinfo[3] | Interpetation of list of numbers defining number of points - [Code Table 3.11](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-11.shtml)|
    1437        | gdsinfo[4] | Grid Definition Template Number - [Code Table 3.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml)|
    1438
    1439        **`gdtmpl`**: Sequence of values for the specified Grid Definition Template. Each
    1440        element of this integer array contains an entry (in the order specified) of Grid
    1441        Definition Template 3.NN
    1442
    1443        **`deflist`**: Sequence containing the number of grid points contained in each
    1444        row (or column) of a non-regular grid.  Used if gdsinfo[2] != 0.
    1445        """
    1446        if 3 in self._sections:
    1447            raise ValueError('GRIB2 Message already contains Grid Definition Section.')
    1448        if deflist is not None:
    1449            _deflist = np.array(deflist,dtype=DEFAULT_NUMPY_INT)
    1450        else:
    1451            _deflist = None
    1452        gdtnum = gdsinfo[4]
    1453        if gdtnum in [0,1,2,3,40,41,42,43,44,203,205,32768,32769]:
    1454            self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4]
    1455        elif gdtnum == 10: # mercator
    1456            self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4]
    1457        elif gdtnum == 20: # stereographic
    1458            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
    1459        elif gdtnum == 30: # lambert conformal
    1460            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
    1461        elif gdtnum == 31: # albers equal area.
    1462            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
    1463        elif gdtnum == 90: # near-sided vertical perspective satellite projection
    1464            self.scanModeFlags = utils.int2bin(gdtmpl[16],output=list)[0:4]
    1465        elif gdtnum == 110: # azimuthal equidistant.
    1466            self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4]
    1467        elif gdtnum == 120:
    1468            self.scanModeFlags = utils.int2bin(gdtmpl[6],output=list)[0:4]
    1469        elif gdtnum == 204: # curvilinear orthogonal
    1470            self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4]
    1471        elif gdtnum in [1000,1100]:
    1472            self.scanModeFlags = utils.int2bin(gdtmpl[12],output=list)[0:4]
    1473        self._msg,self._pos = g2clib.grib2_addgrid(self._msg,
    1474                                                   np.array(gdsinfo,dtype=DEFAULT_NUMPY_INT),
    1475                                                   np.array(gdtmpl,dtype=DEFAULT_NUMPY_INT),
    1476                                                   _deflist)
    1477        self._sections.append(3)

Add a Grid Definition Section (Section 3) to the GRIB2 message.

## Parameters

`gdsinfo`: Sequence containing information needed for the grid definition section.

Index | Description
--- | ---
gdsinfo[0] | Source of grid definition - Code Table 3.0
gdsinfo[1] | Number of data points
gdsinfo[2] | Number of octets for optional list of numbers defining number of points
gdsinfo[3] | Interpetation of list of numbers defining number of points - Code Table 3.11
gdsinfo[4] | Grid Definition Template Number - Code Table 3.1

`gdtmpl`: Sequence of values for the specified Grid Definition Template. Each element of this integer array contains an entry (in the order specified) of Grid Definition Template 3.NN

`deflist`: Sequence containing the number of grid points contained in each row (or column) of a non-regular grid. Used if gdsinfo[2] != 0.

def addfield( self, field, pdtnum, pdtmpl, coordlist=None, packing='complex-spdiff', **packing_opts) View Source

    1480    def addfield(self, field, pdtnum, pdtmpl, coordlist=None, packing="complex-spdiff", **packing_opts):
    1481        """
    1482        Add a Product Definition, Data Representation, Bitmap, and Data Sections
    1483        to `Grib2Message` instance (i.e. Sections 4-7).  Must be called after the grid
    1484        definition section has been added (`addfield`).
    1485
    1486        Parameters
    1487        ----------
    1488
    1489        **`field`**: Numpy array of data values to pack.  If field is a masked array, then
    1490        a bitmap is created from the mask.
    1491
    1492        **`pdtnum`**: integer Product Definition Template Number - [Code Table 4.0](http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-0.shtml)
    1493
    1494        **`pdtmpl`**: Sequence with the data values for the specified Product Definition
    1495        Template (N=pdtnum).  Each element of this integer array contains an entry (in
    1496        the order specified) of Product Definition Template 4.N.
    1497
    1498        **`coordlist`**: Sequence containing floating point values intended to document the
    1499        vertical discretization with model data on hybrid coordinate vertical levels. Default is `None`.
    1500
    1501        **`packing`**: String to specify the type of packing. Valid options are the following:
    1502
    1503        | Packing Scheme | Description |
    1504        | :---:          | :---:       |
    1505        | 'simple'         | [Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-0.shtml) |
    1506        | 'complex'        | [Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-2.shtml) |
    1507        | 'complex-spdiff' | [Complex packing with Spatial Differencing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-3.shtml) |
    1508        | 'jpeg'           | [JPEG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-40.shtml) |
    1509        | 'png'            | [PNG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-41.shtml) |
    1510        | 'spectral-simple'| [Spectral Data - Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-50.shtml) |
    1511        | 'spectral-complex'| [Spectral Data - Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-51.shtml) |
    1512
    1513        **`**packing_opts`**: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for
    1514        the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following:
    1515
    1516        | Packing Scheme | Keyword Arguments |
    1517        | :---:          | :---:                      |
    1518        | 'simple'     | `binScaleFactor`, `decScaleFactor` |
    1519        | 'complex'     | `binScaleFactor`, `decScaleFactor`, `priMissingValue`, [`secMissingValue`] |
    1520        | 'complex-spdiff'     | `binScaleFactor`, `decScaleFactor`, `spatialDifferenceOrder`, `priMissingValue`, [`secMissingValue`] |
    1521        | 'jpeg'     | `binScaleFactor`, `decScaleFactor` |
    1522        | 'png'     | `binScaleFactor`, `decScaleFactor` |
    1523        | 'spectral-simple'     | `binScaleFactor`, `decScaleFactor` |
    1524        | 'spectral-complex'     | `binScaleFactor`, `decScaleFactor` |
    1525        """
    1526        if self._sections[-1] != 3:
    1527            raise ValueError('addgrid() must be called before addfield()')
    1528        if self.scanModeFlags is not None:
    1529            if self.scanModeFlags[3]:
    1530                fieldsave = field.astype('f') # Casting makes a copy
    1531                field[1::2,:] = fieldsave[1::2,::-1]
    1532        fld = field.astype('f')
    1533        if ma.isMA(field) and ma.count_masked(field) > 0:
    1534            bitmapflag = 0
    1535            bmap = 1-np.ravel(field.mask.astype(DEFAULT_NUMPY_INT))
    1536        else:
    1537            bitmapflag = 255
    1538            bmap = None
    1539        if coordlist is not None:
    1540            crdlist = np.array(coordlist,'f')
    1541        else:
    1542            crdlist = None
    1543
    1544        # Set data representation template number and template values
    1545        drtnum = -1
    1546        drtmpl = np.zeros((DEFAULT_DRT_LEN),dtype=DEFAULT_NUMPY_INT)
    1547        if packing == "simple":
    1548            drtnum = 0
    1549            drtmpl[1] = packing_opts["binScaleFactor"]
    1550            drtmpl[2] = packing_opts["decScaleFactor"]
    1551        elif packing == "complex" or packing == "complex-spdiff":
    1552            if packing == "complex":
    1553                drtnum = 2
    1554            if packing == "complex-spdiff":
    1555                drtnum = 3
    1556                drtmpl[16] = packing_opts['spatialDifferenceOrder']
    1557            drtmpl[1] = packing_opts["binScaleFactor"]
    1558            drtmpl[2] = packing_opts["decScaleFactor"]
    1559            if set(("priMissingValue","secMissingValue")).issubset(packing_opts):
    1560                drtmpl[6] = 2
    1561                drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"])
    1562                drtmpl[8] = utils.putieeeint(packing_opts["secMissingValue"])
    1563            else:
    1564                if "priMissingValue" in packing_opts.keys():
    1565                    drtmpl[6] = 1
    1566                    drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"])
    1567                else:
    1568                    drtmpl[6] = 0
    1569        elif packing == "jpeg":
    1570            drtnum = 40
    1571            drtmpl[1] = packing_opts["binScaleFactor"]
    1572            drtmpl[2] = packing_opts["decScaleFactor"]
    1573        elif packing == "png":
    1574            drtnum = 41
    1575            drtmpl[1] = packing_opts["binScaleFactor"]
    1576            drtmpl[2] = packing_opts["decScaleFactor"]
    1577        elif packing == "spectral-simple":
    1578            drtnum = 50
    1579            drtmpl[1] = packing_opts["binScaleFactor"]
    1580            drtmpl[2] = packing_opts["decScaleFactor"]
    1581        elif packing == "spectral-complex":
    1582            drtnum = 51
    1583            drtmpl[1] = packing_opts["binScaleFactor"]
    1584            drtmpl[2] = packing_opts["decScaleFactor"]
    1585
    1586        pdtnum = pdtnum.value if isinstance(pdtnum,Grib2Metadata) else pdtnum
    1587
    1588        self._msg,self._pos = g2clib.grib2_addfield(self._msg,
    1589                                                    pdtnum,
    1590                                                    np.array(pdtmpl,dtype=DEFAULT_NUMPY_INT),
    1591                                                    crdlist,
    1592                                                    drtnum,
    1593                                                    drtmpl,
    1594                                                    np.ravel(fld),
    1595                                                    bitmapflag,
    1596                                                    bmap)
    1597        self._sections.append(4)
    1598        self._sections.append(5)
    1599        if bmap is not None: self._sections.append(6)
    1600        self._sections.append(7)

Add a Product Definition, Data Representation, Bitmap, and Data Sections to `Grib2Message` instance (i.e. Sections 4-7). Must be called after the grid definition section has been added (`addfield`).

## Parameters

`field`: Numpy array of data values to pack. If field is a masked array, then a bitmap is created from the mask.

`pdtnum`: integer Product Definition Template Number - Code Table 4.0

`pdtmpl`: Sequence with the data values for the specified Product Definition Template (N=pdtnum). Each element of this integer array contains an entry (in the order specified) of Product Definition Template 4.N.

`coordlist`: Sequence containing floating point values intended to document the vertical discretization with model data on hybrid coordinate vertical levels. Default is `None`.

`packing`: String to specify the type of packing. Valid options are the following:

Packing Scheme | Description
--- | ---
'simple' | Simple packing
'complex' | Complex packing
'complex-spdiff' | Complex packing with Spatial Differencing
'jpeg' | JPEG compression
'png' | PNG compression
'spectral-simple' | Spectral Data - Simple packing
'spectral-complex' | Spectral Data - Complex packing

`**packing_opts`: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following:

Packing Scheme | Keyword Arguments
--- | ---
'simple' | `binScaleFactor`, `decScaleFactor`
'complex' | `binScaleFactor`, `decScaleFactor`, `priMissingValue`, [`secMissingValue`]
'complex-spdiff' | `binScaleFactor`, `decScaleFactor`, `spatialDifferenceOrder`, `priMissingValue`, [`secMissingValue`]
'jpeg' | `binScaleFactor`, `decScaleFactor`
'png' | `binScaleFactor`, `decScaleFactor`
'spectral-simple' | `binScaleFactor`, `decScaleFactor`
'spectral-complex' | `binScaleFactor`, `decScaleFactor`

def end(self) View Source

    1603    def end(self):
    1604        """
    1605        Add End Section (Section 8) to the GRIB2 message. A GRIB2 message
    1606        is not complete without an end section.  Once an end section is added,
    1607        the GRIB2 message can be written to file.
    1608        """
    1609        self._msg, self._pos = g2clib.grib2_end(self._msg)
    1610        self._sections.append(8)

Add End Section (Section 8) to the GRIB2 message. A GRIB2 message is not complete without an end section. Once an end section is added, the GRIB2 message can be written to file.

def to_bytes(self, validate=True) View Source

    1612    def to_bytes(self, validate=True):
    1613        """
    1614        Return grib data in byte format. Useful for exporting data in non-file formats.
    1615        For example, can be used to output grib data directly to S3 using the boto3 client
    1616        without the need to write a temporary file to upload first.
    1617
    1618        Parameters
    1619        ----------
    1620        **`validate`**: bool (Default: True) If true, validates first/last four bytes for proper formatting, else
    1621        returns None. If False, message is output as is.
    1622
    1623        Returns
    1624        -------
    1625        Returns GRIB2 formatted message as bytes.
    1626        """
    1627        if validate:
    1628            if str(self._msg[0:4] + self._msg[-4:], 'utf-8') == 'GRIB7777':
    1629                return self._msg
    1630            else:
    1631                return None
    1632        else:
    1633            return self._msg

Return grib data in byte format. 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 (Default: True) If true, 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.

class Grib2Metadata: View Source

    1636class Grib2Metadata():
    1637    """
    1638    Class to hold GRIB2 metadata both as numeric code value as stored in
    1639    GRIB2 and its plain langauge definition.
    1640
    1641    **`value : int`**
    1642
    1643    GRIB2 metadata integer code value.
    1644
    1645    **`table : str, optional`**
    1646
    1647    GRIB2 table to lookup the `value`. Default is None.
    1648    """
    1649    def __init__(self, value, table=None):
    1650        self.value = value
    1651        self.table = table
    1652        if self.table is None:
    1653            self.definition = None
    1654        else:
    1655            self.definition = tables.get_value_from_table(self.value,self.table)
    1656    def __call__(self):
    1657        return self.value
    1658    def __repr__(self):
    1659        return '%s(%d, table = %s)' % (self.__class__.__name__,self.value,self.table)
    1660    def __str__(self):
    1661        return '%d - %s' % (self.value,self.definition)
    1662    def __eq__(self,other):
    1663        return self.value == other
    1664    def __gt__(self,other):
    1665        return self.value > other
    1666    def __ge__(self,other):
    1667        return self.value >= other
    1668    def __lt__(self,other):
    1669        return self.value < other
    1670    def __le__(self,other):
    1671        return self.value <= other
    1672    def __contains__(self,other):
    1673        return other in self.definition

Class to hold GRIB2 metadata both as numeric code value as stored in GRIB2 and its plain langauge definition.

`value : int`

GRIB2 metadata integer code value.

`table : str, optional`

GRIB2 table to lookup the `value`. Default is None.

Grib2Metadata(value, table=None) View Source

    1649    def __init__(self, value, table=None):
    1650        self.value = value
    1651        self.table = table
    1652        if self.table is None:
    1653            self.definition = None
    1654        else:
    1655            self.definition = tables.get_value_from_table(self.value,self.table)

def show_config() View Source

    13def show_config():
    14    """
    15    Print grib2io build configuration information.
    16    """
    17    from g2clib import __version__ as g2clib_version
    18    have_jpeg = True if 'jasper' in __config__.libraries or 'openjp2' in __config__.libraries else False
    19    have_png = True if 'png' in __config__.libraries else False
    20    jpeglib = 'OpenJPEG' if 'openjp2' in __config__.libraries else ('Jasper' if 'jasper' in __config__.libraries else None)
    21    pnglib = 'libpng' if 'png' in __config__.libraries else None
    22    print("grib2io version %s Configuration:\n"%(__version__))
    23    print("\tg2c library version:".expandtabs(4),g2clib_version)
    24    print("\tJPEG compression support:".expandtabs(4),have_jpeg)
    25    if have_jpeg: print("\t\tLibrary:".expandtabs(4),jpeglib)
    26    print("\tPNG compression support:".expandtabs(4),have_png)
    27    if have_png: print("\t\tLibrary:".expandtabs(4),pnglib)

Print grib2io build configuration information.