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.

 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    from g2clib import _has_png as have_png
18    from g2clib import _has_jpeg as have_jpeg
19    print("grib2io version %s Configuration:\n"%(__version__))
20    print("\tg2c library version:".expandtabs(4),g2clib_version)
21    print("\tJPEG compression support:".expandtabs(4),bool(have_jpeg))
22    print("\tPNG compression support:".expandtabs(4),bool(have_png))
class open:
 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:
166                warnings.warn("GRIB2 Message number 0 does not exist.")
167                return None
168            self._filehandle.seek(self._index['offset'][key])
169            return [Grib2Message(msg=self._filehandle.read(self._index['size'][key]),
170                                 source=self,
171                                 num=self._index['messageNumber'][key],
172                                 decode=self.decode)]
173        elif isinstance(key,str):
174            return self.select(shortName=key)
175        else:
176            raise KeyError('Key must be an integer, slice, or GRIB2 variable shortName.')
177
178
179    def _build_index(self):
180        """
181        Perform indexing of GRIB2 Messages.
182        """
183        # Initialize index dictionary
184        self._index['offset'] = [None]
185        self._index['discipline'] = [None]
186        self._index['edition'] = [None]
187        self._index['size'] = [None]
188        self._index['submessageOffset'] = [None]
189        self._index['submessageBeginSection'] = [None]
190        self._index['isSubmessage'] = [None]
191        self._index['messageNumber'] = [None]
192        self._index['identificationSection'] = [None]
193        self._index['refDate'] = [None]
194        self._index['productDefinitionTemplateNumber'] = [None]
195        self._index['productDefinitionTemplate'] = [None]
196        self._index['typeOfFirstFixedSurface'] = [None]
197        self._index['valueOfFirstFixedSurface'] = [None]
198        self._index['typeOfGeneratingProcess'] = [None]
199        self._index['level'] = [None]
200        self._index['leadTime'] = [None]
201        self._index['duration'] = [None]
202        self._index['shortName'] = [None]
203        self._index['bitMap'] = [None]
204        self._index['levelString'] = [None]
205        self._index['probString'] = [None]
206        self._index['ny'] = [None]
207        self._index['nx'] = [None]
208
209        # Iterate
210        while True:
211            try:
212                # Read first 4 bytes and decode...looking for "GRIB"
213                pos = self._filehandle.tell()
214                header = struct.unpack('>4s',self._filehandle.read(4))[0].decode()
215
216                # Test header. Then get information from GRIB2 Section 0: the discipline
217                # number, edition number (should always be 2), and GRIB2 message size.
218                # Then iterate to check for submessages.
219                if header == 'GRIB':
220                    _issubmessage = False
221                    _submsgoffset = 0
222                    _submsgbegin = 0
223
224                    # Read and unpack Section 0. Note that this is not done through
225                    # the g2clib.
226                    self._filehandle.seek(self._filehandle.tell()+2)
227                    discipline = int(struct.unpack('>B',self._filehandle.read(1))[0])
228                    edition = int(struct.unpack('>B',self._filehandle.read(1))[0])
229                    assert edition == 2
230                    size = struct.unpack('>Q',self._filehandle.read(8))[0]
231
232                    # Read and unpack Section 1
233                    secsize = struct.unpack('>i',self._filehandle.read(4))[0]
234                    secnum = struct.unpack('>B',self._filehandle.read(1))[0]
235                    assert secnum == 1
236                    self._filehandle.seek(self._filehandle.tell()-5)
237                    _grbmsg = self._filehandle.read(secsize)
238                    _grbpos = 0
239                    _grbsec1,_grbpos = g2clib.unpack1(_grbmsg,_grbpos,np.empty)
240                    _grbsec1 = _grbsec1.tolist()
241                    _refdate = utils.getdate(_grbsec1[5],_grbsec1[6],_grbsec1[7],_grbsec1[8])
242                    _isndfd = True if _grbsec1[0:2] == [8,65535] else False
243                    secrange = range(2,8)
244                    while 1:
245                        for num in secrange:
246                            secsize = struct.unpack('>i',self._filehandle.read(4))[0]
247                            secnum = struct.unpack('>B',self._filehandle.read(1))[0]
248                            if secnum == num:
249                                if secnum == 3:
250                                    self._filehandle.seek(self._filehandle.tell()-5)
251                                    _grbmsg = self._filehandle.read(secsize)
252                                    _grbpos = 0
253                                    # Unpack Section 3
254                                    _gds,_gdtn,_deflist,_grbpos = g2clib.unpack3(_grbmsg,_grbpos,np.empty)
255                                    gdt = _gdtn.tolist()
256                                    self._index['nx'].append(int(gdt[7]))
257                                    self._index['ny'].append(int(gdt[8]))
258                                elif secnum == 4:
259                                    self._filehandle.seek(self._filehandle.tell()-5)
260                                    _grbmsg = self._filehandle.read(secsize)
261                                    _grbpos = 0
262                                    # Unpack Section 4
263                                    _pdt,_pdtnum,_coordlist,_grbpos = g2clib.unpack4(_grbmsg,_grbpos,np.empty)
264                                    _pdt = _pdt.tolist()
265                                    _varinfo = tables.get_varinfo_from_table(discipline,_pdt[0],_pdt[1],isNDFD=_isndfd)
266                                elif secnum == 6:
267                                    self._filehandle.seek(self._filehandle.tell()-5)
268                                    _grbmsg = self._filehandle.read(secsize)
269                                    _grbpos = 0
270                                    # Unpack Section 6. Save bitmap
271                                    _bmap,_bmapflag = g2clib.unpack6(_grbmsg,_gds[1],_grbpos,np.empty)
272                                    if _bmapflag == 0:
273                                        _bmap_save = copy.deepcopy(_bmap)
274                                    elif _bmapflag == 254:
275                                        _bmap = copy.deepcopy(_bmap_save)
276                                else:
277                                    self._filehandle.seek(self._filehandle.tell()+secsize-5)
278                            else:
279                                if num == 2 and secnum == 3:
280                                    pass # Allow this.  Just means no Local Use Section.
281                                else:
282                                    _issubmessage = True
283                                    _submsgoffset = (self._filehandle.tell()-5)-(self._index['offset'][self.messages])
284                                    _submsgbegin = secnum
285                                self._filehandle.seek(self._filehandle.tell()-5)
286                                continue
287                        trailer = struct.unpack('>4s',self._filehandle.read(4))[0].decode()
288                        if trailer == '7777':
289                            self.messages += 1
290                            self._index['offset'].append(pos)
291                            self._index['discipline'].append(discipline)
292                            self._index['edition'].append(edition)
293                            self._index['size'].append(size)
294                            self._index['messageNumber'].append(self.messages)
295                            self._index['isSubmessage'].append(_issubmessage)
296                            self._index['identificationSection'].append(_grbsec1)
297                            self._index['refDate'].append(_refdate)
298                            self._index['productDefinitionTemplateNumber'].append(_pdtnum)
299                            self._index['productDefinitionTemplate'].append(_pdt)
300                            self._index['typeOfFirstFixedSurface'].append(Grib2Metadata(_pdt[9],table='4.5').definition[0])
301                            scaleFactorOfFirstFixedSurface = _pdt[10]
302                            scaledValueOfFirstFixedSurface = _pdt[11]
303                            valueOfFirstFixedSurface = scaledValueOfFirstFixedSurface/(10.**scaleFactorOfFirstFixedSurface)
304                            self._index['typeOfGeneratingProcess'].append(_pdt[2])
305                            self._index['valueOfFirstFixedSurface'].append(valueOfFirstFixedSurface)
306                            self._index['level'].append(tables.get_wgrib2_level_string(*_pdt[9:15]))
307                            self._index['leadTime'].append(utils.getleadtime(_grbsec1,_pdtnum,_pdt))
308                            self._index['duration'].append(utils.getduration(_pdtnum,_pdt))
309                            self._index['shortName'].append(_varinfo[2])
310                            self._index['bitMap'].append(_bmap)
311                            self._index['levelString'].append(tables.get_wgrib2_level_string(*_pdt[9:15]))
312                            if _pdtnum in [5,9]:
313                                self._index['probString'].append(utils.get_wgrib2_prob_string(*_pdt[17:22]))
314                            else:
315                                self._index['probString'].append('')
316                            if _issubmessage:
317                                self._index['submessageOffset'].append(_submsgoffset)
318                                self._index['submessageBeginSection'].append(_submsgbegin)
319                            else:
320                                self._index['submessageOffset'].append(0)
321                                self._index['submessageBeginSection'].append(_submsgbegin)
322                            break
323                        else:
324                            self._filehandle.seek(self._filehandle.tell()-4)
325                            self.messages += 1
326                            self._index['offset'].append(pos)
327                            self._index['discipline'].append(discipline)
328                            self._index['edition'].append(edition)
329                            self._index['size'].append(size)
330                            self._index['messageNumber'].append(self.messages)
331                            self._index['isSubmessage'].append(_issubmessage)
332                            self._index['identificationSection'].append(_grbsec1)
333                            self._index['refDate'].append(_refdate)
334                            self._index['productDefinitionTemplateNumber'].append(_pdtnum)
335                            self._index['productDefinitionTemplate'].append(_pdt)
336                            self._index['leadTime'].append(utils.getleadtime(_grbsec1,_pdtnum,_pdt))
337                            self._index['duration'].append(utils.getduration(_pdtnum,_pdt))
338                            self._index['shortName'].append(_varinfo[2])
339                            self._index['bitMap'].append(_bmap)
340                            self._index['levelString'].append(tables.get_wgrib2_level_string(*_pdt[9:15]))
341                            if _pdtnum in [5,9]:
342                                self._index['probString'].append(utils.get_wgrib2_prob_string(*_pdt[17:22]))
343                            else:
344                                self._index['probString'].append('')
345                            self._index['submessageOffset'].append(_submsgoffset)
346                            self._index['submessageBeginSection'].append(_submsgbegin)
347                            continue
348
349            except(struct.error):
350                self._filehandle.seek(0)
351                break
352
353        self._hasindex = True
354
355
356    def close(self):
357        """
358        Close the file handle
359        """
360        if not self._filehandle.closed:
361            self._filehandle.close()
362            self.closed = self._filehandle.closed
363
364
365    def read(self, num=1):
366        """
367        Read num GRIB2 messages from the current position
368
369        Parameters
370        ----------
371
372        **`num`**: integer number of GRIB2 Message to read.
373
374        Returns
375        -------
376
377        **`list`**: list of `grib2io.Grib2Message` instances.
378        """
379        msgs = []
380        if self.tell() >= self.messages: return msgs
381        if num > 0:
382            if num == 1:
383                msgrange = [self.tell()+1]
384            else:
385                beg = self.tell()+1
386                end = self.tell()+1+num if self.tell()+1+num <= self.messages else self.messages
387                msgrange = range(beg,end+1)
388            for n in msgrange:
389                self._filehandle.seek(self._index['offset'][n])
390                msgs.append(Grib2Message(msg=self._filehandle.read(self._index['size'][n]),
391                                         source=self,
392                                         num=self._index['messageNumber'][n],
393                                         decode=self.decode))
394                self.current_message += 1
395        return msgs
396
397
398    def rewind(self):
399        """
400        Set the position of the file to zero in units of GRIB2 messages.
401        """
402        self.seek(0)
403
404
405    def seek(self, pos):
406        """
407        Set the position within the file in units of GRIB2 messages.
408
409        Parameters
410        ----------
411
412        **`pos`**: GRIB2 Message number to set the read pointer to.
413        """
414        if self._hasindex:
415            if pos == 0:
416                self._filehandle.seek(pos)
417                self.current_message = pos
418            elif pos > 0:
419                self._filehandle.seek(self._index['offset'][pos-1])
420                self.current_message = pos
421
422
423    def tell(self):
424        """
425        Returns the position of the file in units of GRIB2 Messages.
426        """
427        return self.current_message
428
429
430    def select(self,**kwargs):
431        """
432        Returns a list of `grib2io.Grib2Message` instances based on the selection **`**kwargs`**.
433
434        The following keywords are currently supported:
435
436        **`duration : int`** specifiying the time duration (in unit of hours) of a GRIB2 Message that is
437        determined from a period of time.
438
439        **`leadTime : int`** specifying ending lead time (in units of hours) of a GRIB2 Message.
440
441        **`level : str`** wgrib2-formatted layer/level string.
442
443        **`percentile : int`** specify the percentile value.
444
445        **`refDate : int`** specifying the reference date in `YYYYMMDDHH[MMSS]` format.
446
447        **`shortName : str`** the GRIB2 `shortName`.  This is the abbreviation name found in the NCEP GRIB2 tables.
448
449        **`threshold : str`** wgrib2-formatted probability threshold string.
450        """
451        kwargs_allowed = ['duration','leadTime','level','percentile','refDate','shortName','threshold']
452        idxs = {}
453        for k,v in kwargs.items():
454            if k not in kwargs_allowed: continue
455            if k == 'duration':
456                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['duration']])==v)[0]
457            elif k == 'leadTime':
458                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['leadTime']])==v)[0]
459            elif k == 'level':
460                idxs[k] = np.where(np.array(self._index['levelString'])==v)[0]
461            elif k == 'percentile':
462                tmp1 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==6)[0]
463                tmp2 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==10)[0]
464                idxs[k] = [i for i in np.concatenate((tmp1,tmp2)) if self._index["productDefinitionTemplate"][i][15]==v]
465                del tmp1,tmp2
466            elif k == 'refDate':
467                idxs[k] = np.where(np.asarray(self._index['refDate'])==v)[0]
468            elif k == 'shortName':
469                idxs[k] = np.where(np.array(self._index['shortName'])==v)[0]
470            elif k == 'threshold':
471                idxs[k] = np.where(np.array(self._index['probString'])==v)[0]
472        idxsarr = np.concatenate(tuple(idxs.values()))
473        nidxs = len(idxs.keys())
474        if nidxs == 1:
475            return [self[int(i)][0] for i in idxsarr]
476        elif nidxs > 1:
477            return [self[int(i)][0] for i in [ii[0] for ii in collections.Counter(idxsarr).most_common() if ii[1] == nidxs]]
478
479
480    def write(self, msg):
481        """
482        Writes a packed GRIB2 message to file.
483
484        Parameters
485        ----------
486
487        **`msg`**: instance of `Grib2Message`.
488        """
489        if isinstance(msg,Grib2Message):
490            self._filehandle.write(msg._msg)
491            self.size = os.path.getsize(self.name)
492            self.messages += 1
493            self.current_message += 1
494        else:
495            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)
 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):
356    def close(self):
357        """
358        Close the file handle
359        """
360        if not self._filehandle.closed:
361            self._filehandle.close()
362            self.closed = self._filehandle.closed

Close the file handle

def read(self, num=1):
365    def read(self, num=1):
366        """
367        Read num GRIB2 messages from the current position
368
369        Parameters
370        ----------
371
372        **`num`**: integer number of GRIB2 Message to read.
373
374        Returns
375        -------
376
377        **`list`**: list of `grib2io.Grib2Message` instances.
378        """
379        msgs = []
380        if self.tell() >= self.messages: return msgs
381        if num > 0:
382            if num == 1:
383                msgrange = [self.tell()+1]
384            else:
385                beg = self.tell()+1
386                end = self.tell()+1+num if self.tell()+1+num <= self.messages else self.messages
387                msgrange = range(beg,end+1)
388            for n in msgrange:
389                self._filehandle.seek(self._index['offset'][n])
390                msgs.append(Grib2Message(msg=self._filehandle.read(self._index['size'][n]),
391                                         source=self,
392                                         num=self._index['messageNumber'][n],
393                                         decode=self.decode))
394                self.current_message += 1
395        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):
398    def rewind(self):
399        """
400        Set the position of the file to zero in units of GRIB2 messages.
401        """
402        self.seek(0)

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

def seek(self, pos):
405    def seek(self, pos):
406        """
407        Set the position within the file in units of GRIB2 messages.
408
409        Parameters
410        ----------
411
412        **`pos`**: GRIB2 Message number to set the read pointer to.
413        """
414        if self._hasindex:
415            if pos == 0:
416                self._filehandle.seek(pos)
417                self.current_message = pos
418            elif pos > 0:
419                self._filehandle.seek(self._index['offset'][pos-1])
420                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):
423    def tell(self):
424        """
425        Returns the position of the file in units of GRIB2 Messages.
426        """
427        return self.current_message

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

def select(self, **kwargs):
430    def select(self,**kwargs):
431        """
432        Returns a list of `grib2io.Grib2Message` instances based on the selection **`**kwargs`**.
433
434        The following keywords are currently supported:
435
436        **`duration : int`** specifiying the time duration (in unit of hours) of a GRIB2 Message that is
437        determined from a period of time.
438
439        **`leadTime : int`** specifying ending lead time (in units of hours) of a GRIB2 Message.
440
441        **`level : str`** wgrib2-formatted layer/level string.
442
443        **`percentile : int`** specify the percentile value.
444
445        **`refDate : int`** specifying the reference date in `YYYYMMDDHH[MMSS]` format.
446
447        **`shortName : str`** the GRIB2 `shortName`.  This is the abbreviation name found in the NCEP GRIB2 tables.
448
449        **`threshold : str`** wgrib2-formatted probability threshold string.
450        """
451        kwargs_allowed = ['duration','leadTime','level','percentile','refDate','shortName','threshold']
452        idxs = {}
453        for k,v in kwargs.items():
454            if k not in kwargs_allowed: continue
455            if k == 'duration':
456                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['duration']])==v)[0]
457            elif k == 'leadTime':
458                idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['leadTime']])==v)[0]
459            elif k == 'level':
460                idxs[k] = np.where(np.array(self._index['levelString'])==v)[0]
461            elif k == 'percentile':
462                tmp1 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==6)[0]
463                tmp2 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==10)[0]
464                idxs[k] = [i for i in np.concatenate((tmp1,tmp2)) if self._index["productDefinitionTemplate"][i][15]==v]
465                del tmp1,tmp2
466            elif k == 'refDate':
467                idxs[k] = np.where(np.asarray(self._index['refDate'])==v)[0]
468            elif k == 'shortName':
469                idxs[k] = np.where(np.array(self._index['shortName'])==v)[0]
470            elif k == 'threshold':
471                idxs[k] = np.where(np.array(self._index['probString'])==v)[0]
472        idxsarr = np.concatenate(tuple(idxs.values()))
473        nidxs = len(idxs.keys())
474        if nidxs == 1:
475            return [self[int(i)][0] for i in idxsarr]
476        elif nidxs > 1:
477            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):
480    def write(self, msg):
481        """
482        Writes a packed GRIB2 message to file.
483
484        Parameters
485        ----------
486
487        **`msg`**: instance of `Grib2Message`.
488        """
489        if isinstance(msg,Grib2Message):
490            self._filehandle.write(msg._msg)
491            self.size = os.path.getsize(self.name)
492            self.messages += 1
493            self.current_message += 1
494        else:
495            raise TypeError("msg must be a Grib2Message object.")

Writes a packed GRIB2 message to file.

Parameters

msg: instance of Grib2Message.

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

Unpacks GRIB2 section data from the packed, binary message.

def decode(self):
 753    def decode(self):
 754        """
 755        Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables.
 756        """
 757
 758        # Section 0 - Indictator Section
 759        self.discipline = Grib2Metadata(self.indicatorSection[2],table='0.0')
 760
 761        # Section 1 - Indentification Section.
 762        self.originatingCenter = Grib2Metadata(self.identificationSection[0],table='originating_centers')
 763        self.originatingSubCenter = Grib2Metadata(self.identificationSection[1],table='originating_subcenters')
 764        self.masterTableInfo = Grib2Metadata(self.identificationSection[2],table='1.0')
 765        self.localTableInfo = Grib2Metadata(self.identificationSection[3],table='1.1')
 766        self.significanceOfReferenceTime = Grib2Metadata(self.identificationSection[4],table='1.2')
 767        self.year = self.identificationSection[5]
 768        self.month = self.identificationSection[6]
 769        self.day = self.identificationSection[7]
 770        self.hour = self.identificationSection[8]
 771        self.minute = self.identificationSection[9]
 772        self.second = self.identificationSection[10]
 773        self.refDate = (self.year*1000000)+(self.month*10000)+(self.day*100)+self.hour
 774        self.dtReferenceDate = datetime.datetime(self.year,self.month,self.day,
 775                                                 hour=self.hour,minute=self.minute,
 776                                                 second=self.second)
 777        self.productionStatus = Grib2Metadata(self.identificationSection[11],table='1.3')
 778        self.typeOfData = Grib2Metadata(self.identificationSection[12],table='1.4')
 779
 780        # ----------------------------
 781        # Section 3 -- Grid Definition
 782        # ----------------------------
 783
 784        # Set shape of the Earth parameters
 785        if self.gridDefinitionTemplateNumber.value in [50,51,52,1200]:
 786            earthparams = None
 787        else:
 788            earthparams = tables.earth_params[str(self.gridDefinitionTemplate[0])]
 789        if earthparams['shape'] == 'spherical':
 790            if earthparams['radius'] is None:
 791                self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1])
 792                self.earthMajorAxis = None
 793                self.earthMinorAxis = None
 794            else:
 795                self.earthRadius = earthparams['radius']
 796                self.earthMajorAxis = None
 797                self.earthMinorAxis = None
 798        elif earthparams['shape'] == 'oblateSpheroid':
 799            if earthparams['radius'] is None and earthparams['major_axis'] is None and earthparams['minor_axis'] is None:
 800                self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1])
 801                self.earthMajorAxis = self.gridDefinitionTemplate[4]/(10.**self.gridDefinitionTemplate[3])
 802                self.earthMinorAxis = self.gridDefinitionTemplate[6]/(10.**self.gridDefinitionTemplate[5])
 803            else:
 804                self.earthRadius = earthparams['radius']
 805                self.earthMajorAxis = earthparams['major_axis']
 806                self.earthMinorAxis = earthparams['minor_axis']
 807
 808        reggrid = self.gridDefinitionSection[2] == 0 # self.gridDefinitionSection[2]=0 means regular 2-d grid
 809       #if reggrid and self.gridDefinitionTemplateNumber.value not in [50,51,52,53,100,120,1000,1200]:
 810       #    self.nx = self.gridDefinitionTemplate[7]
 811       #    self.ny = self.gridDefinitionTemplate[8]
 812        if not reggrid and self.gridDefinitionTemplateNumber == 40:
 813            # Reduced Gaussian Grid
 814            self.ny = self.gridDefinitionTemplate[8]
 815        if self.gridDefinitionTemplateNumber.value in [0,1,203,205,32768,32769]:
 816            # Regular or Rotated Lat/Lon Grid
 817            scalefact = float(self.gridDefinitionTemplate[9])
 818            if self.gridDefinitionTemplate[10] == 4294967295:
 819                self.gridDefinitionTemplate[10] = -1
 820            divisor = float(self.gridDefinitionTemplate[10])
 821            if scalefact == 0: scalefact = 1.
 822            if divisor <= 0: divisor = 1.e6
 823            self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor
 824            self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor
 825            self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor
 826            self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor
 827            self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor
 828            self.gridlengthYDirection = scalefact*self.gridDefinitionTemplate[17]/divisor
 829            if self.latitudeFirstGridpoint > self.latitudeLastGridpoint:
 830                self.gridlengthYDirection = -self.gridlengthYDirection
 831            if self.longitudeFirstGridpoint > self.longitudeLastGridpoint:
 832                self.gridlengthXDirection = -self.gridlengthXDirection
 833            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
 834            if self.gridDefinitionTemplateNumber == 1:
 835                self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor
 836                self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor
 837                self.anglePoleRotation = self.gridDefinitionTemplate[21]
 838        elif self.gridDefinitionTemplateNumber == 10:
 839            # Mercator
 840            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
 841            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
 842            self.latitudeLastGridpoint = self.gridDefinitionTemplate[13]/1.e6
 843            self.longitudeLastGridpoint = self.gridDefinitionTemplate[14]/1.e6
 844            self.gridlengthXDirection = self.gridDefinitionTemplate[17]/1.e3
 845            self.gridlengthYDirection= self.gridDefinitionTemplate[18]/1.e3
 846            self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6
 847            self.proj4_lon_0 = 0.5*(self.longitudeFirstGridpoint+self.longitudeLastGridpoint)
 848            self.proj4_proj = 'merc'
 849            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4]
 850        elif self.gridDefinitionTemplateNumber == 20:
 851            # Stereographic
 852            projflag = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0]
 853            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
 854            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
 855            self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6
 856            if projflag == 0:
 857                self.proj4_lat_0 = 90
 858            elif projflag == 1:
 859                self.proj4_lat_0 = -90
 860            else:
 861                raise ValueError('Invalid projection center flag = %s'%projflag)
 862            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
 863            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
 864            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
 865            self.proj4_proj = 'stere'
 866            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
 867        elif self.gridDefinitionTemplateNumber == 30:
 868            # Lambert Conformal
 869            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
 870            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
 871            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
 872            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
 873            self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6
 874            self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6
 875            self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6
 876            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
 877            self.proj4_proj = 'lcc'
 878            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
 879        elif self.gridDefinitionTemplateNumber == 31:
 880            # Albers Equal Area
 881            self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6
 882            self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6
 883            self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000.
 884            self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000.
 885            self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6
 886            self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6
 887            self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6
 888            self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6
 889            self.proj4_proj = 'aea'
 890            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4]
 891        elif self.gridDefinitionTemplateNumber == 40 or self.gridDefinitionTemplateNumber == 41:
 892            # Gaussian Grid
 893            scalefact = float(self.gridDefinitionTemplate[9])
 894            if self.gridDefinitionTemplate[10] == 4294967295:
 895                self.gridDefinitionTemplate[10] = -1
 896            divisor = float(self.gridDefinitionTemplate[10])
 897            if scalefact == 0: scalefact = 1.
 898            if divisor <= 0: divisor = 1.e6
 899            self.pointsBetweenPoleAndEquator = self.gridDefinitionTemplate[17]
 900            self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor
 901            self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor
 902            self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor
 903            self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor
 904            if reggrid:
 905                self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor
 906                if self.longitudeFirstGridpoint > self.longitudeLastGridpoint:
 907                    self.gridlengthXDirection = -self.gridlengthXDirection
 908            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
 909            if self.gridDefinitionTemplateNumber == 41:
 910                self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor
 911                self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor
 912                self.anglePoleRotation = self.gridDefinitionTemplate[21]
 913        elif self.gridDefinitionTemplateNumber == 50:
 914            # Spectral Coefficients
 915            self.spectralFunctionParameters = (self.gridDefinitionTemplate[0],self.gridDefinitionTemplate[1],self.gridDefinitionTemplate[2])
 916            self.scanModeFlags = [None,None,None,None]
 917        elif self.gridDefinitionTemplateNumber == 90:
 918            # Near-sided Vertical Perspective Satellite Projection
 919            self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6
 920            self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6
 921            self.proj4_h = self.earthMajorAxis * (self.gridDefinitionTemplate[18]/1.e6)
 922            dx = self.gridDefinitionTemplate[12]
 923            dy = self.gridDefinitionTemplate[13]
 924            # if lat_0 is equator, it's a geostationary view.
 925            if self.proj4_lat_0 == 0.: # if lat_0 is equator, it's a
 926                self.proj4_proj = 'geos'
 927            # general case of 'near-side perspective projection' (untested)
 928            else:
 929                self.proj4_proj = 'nsper'
 930                msg = 'Only geostationary perspective is supported. Lat/Lon values returned by grid method may be incorrect.'
 931                warnings.warn(msg)
 932            # latitude of horizon on central meridian
 933            lonmax = 90.-(180./np.pi)*np.arcsin(self.earthMajorAxis/self.proj4_h)
 934            # longitude of horizon on equator
 935            latmax = 90.-(180./np.pi)*np.arcsin(self.earthMinorAxis/self.proj4_h)
 936            # truncate to nearest thousandth of a degree (to make sure
 937            # they aren't slightly over the horizon)
 938            latmax = int(1000*latmax)/1000.
 939            lonmax = int(1000*lonmax)/1000.
 940            # h is measured from surface of earth at equator.
 941            self.proj4_h = self.proj4_h - self.earthMajorAxis
 942            # width and height of visible projection
 943            P = pyproj.Proj(proj=self.proj4_proj,\
 944                            a=self.earthMajorAxis,b=self.earthMinorAxis,\
 945                            lat_0=0,lon_0=0,h=self.proj4_h)
 946            x1,y1 = P(0.,latmax)
 947            x2,y2 = P(lonmax,0.)
 948            width = 2*x2
 949            height = 2*y1
 950            self.gridlengthXDirection = width/dx
 951            self.gridlengthYDirection = height/dy
 952            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0:4]
 953        elif self.gridDefinitionTemplateNumber == 110:
 954            # Azimuthal Equidistant
 955            self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6
 956            self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6
 957            self.gridlengthXDirection = self.gridDefinitionTemplate[12]/1000.
 958            self.gridlengthYDirection = self.gridDefinitionTemplate[13]/1000.
 959            self.proj4_proj = 'aeqd'
 960            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4]
 961        elif self.gridDefinitionTemplateNumber == 204:
 962            # Curvilinear Orthogonal
 963            self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4]
 964        else:
 965            errmsg = 'Unsupported Grid Definition Template Number - 3.%i' % self.gridDefinitionTemplateNumber.value
 966            raise ValueError(errmsg)
 967
 968        # -------------------------------
 969        # Section 4 -- Product Definition
 970        # -------------------------------
 971
 972        # Template 4.0 - NOTE: That is these attributes apply to other templates.
 973        self.parameterCategory = self.productDefinitionTemplate[0]
 974        self.parameterNumber = self.productDefinitionTemplate[1]
 975        self.fullName,self.units,self.shortName = tables.get_varinfo_from_table(self.discipline.value,
 976                                                                                self.parameterCategory,
 977                                                                                self.parameterNumber,
 978                                                                                isNDFD=self.isNDFD)
 979        self.typeOfGeneratingProcess = Grib2Metadata(self.productDefinitionTemplate[2],table='4.3')
 980        self.backgroundGeneratingProcessIdentifier = self.productDefinitionTemplate[3]
 981        self.generatingProcess = Grib2Metadata(self.productDefinitionTemplate[4],table='generating_process')
 982        self.unitOfTimeRange = Grib2Metadata(self.productDefinitionTemplate[7],table='4.4')
 983        self.leadTime = self.productDefinitionTemplate[8]
 984        self.typeOfFirstFixedSurface = Grib2Metadata(self.productDefinitionTemplate[9],table='4.5')
 985        self.scaleFactorOfFirstFixedSurface = self.productDefinitionTemplate[10]
 986        self.unitOfFirstFixedSurface = self.typeOfFirstFixedSurface.definition[1]
 987        self.scaledValueOfFirstFixedSurface = self.productDefinitionTemplate[11]
 988        self.valueOfFirstFixedSurface = self.scaledValueOfFirstFixedSurface/(10.**self.scaleFactorOfFirstFixedSurface)
 989        temp = tables.get_value_from_table(self.productDefinitionTemplate[12],'4.5')
 990        if temp[0] == 'Missing' and temp[1] == 'unknown':
 991            self.typeOfSecondFixedSurface = None
 992            self.scaleFactorOfSecondFixedSurface = None
 993            self.unitOfSecondFixedSurface = None
 994            self.scaledValueOfSecondFixedSurface = None
 995            self.valueOfSecondFixedSurface = None
 996        else:
 997            self.typeOfSecondFixedSurface = Grib2Metadata(self.productDefinitionTemplate[12],table='4.5')
 998            self.scaleFactorOfSecondFixedSurface = self.productDefinitionTemplate[13]
 999            self.unitOfSecondFixedSurface = self.typeOfSecondFixedSurface.definition[1]
1000            self.scaledValueOfSecondFixedSurface = self.productDefinitionTemplate[14]
1001            self.valueOfSecondFixedSurface = self.scaledValueOfSecondFixedSurface/(10.**self.scaleFactorOfSecondFixedSurface)
1002        self.level = tables.get_wgrib2_level_string(*self.productDefinitionTemplate[9:15])
1003
1004        # Template 4.1 -
1005        if self.productDefinitionTemplateNumber == 1:
1006            self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6')
1007            self.perturbationNumber = self.productDefinitionTemplate[16]
1008            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17]
1009
1010        # Template 4.2 -
1011        elif self.productDefinitionTemplateNumber == 2:
1012            self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7')
1013            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16]
1014
1015        # Template 4.5 -
1016        elif self.productDefinitionTemplateNumber == 5:
1017            self.forecastProbabilityNumber = self.productDefinitionTemplate[15]
1018            self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16]
1019            self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9')
1020            self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18]
1021            self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19]
1022            self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20]
1023            self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21]
1024            if self.scaleFactorOfThresholdLowerLimit == -127 and \
1025               self.scaledValueOfThresholdLowerLimit == 255:
1026                self.thresholdLowerLimit = 0.0
1027            else:
1028                self.thresholdLowerLimit =self.scaledValueOfThresholdLowerLimit/(10.**self.scaleFactorOfThresholdLowerLimit)
1029            if self.scaleFactorOfThresholdUpperLimit == -127 and \
1030               self.scaledValueOfThresholdUpperLimit == 255:
1031                self.thresholdUpperLimit = 0.0
1032            else:
1033                self.thresholdUpperLimit = self.scaledValueOfThresholdUpperLimit/(10.**self.scaleFactorOfThresholdUpperLimit)
1034            self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22])
1035
1036        # Template 4.6 -
1037        elif self.productDefinitionTemplateNumber == 6:
1038            self.percentileValue = self.productDefinitionTemplate[15]
1039
1040        # Template 4.8 -
1041        elif self.productDefinitionTemplateNumber == 8:
1042            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[15]
1043            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[16]
1044            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[17]
1045            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[18]
1046            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[19]
1047            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[20]
1048            self.numberOfTimeRanges = self.productDefinitionTemplate[21]
1049            self.numberOfMissingValues = self.productDefinitionTemplate[22]
1050            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[23],table='4.10')
1051            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.11')
1052            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.4')
1053            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[26]
1054            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4')
1055            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[28]
1056
1057        # Template 4.9 -
1058        elif self.productDefinitionTemplateNumber == 9:
1059            self.forecastProbabilityNumber = self.productDefinitionTemplate[15]
1060            self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16]
1061            self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9')
1062            self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18]
1063            self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19]
1064            self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20]
1065            self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21]
1066            if self.scaleFactorOfThresholdLowerLimit == -127 and \
1067               self.scaledValueOfThresholdLowerLimit == 255:
1068                self.thresholdLowerLimit = 0.0
1069            else:
1070                self.thresholdLowerLimit =self.scaledValueOfThresholdLowerLimit/(10.**self.scaleFactorOfThresholdLowerLimit)
1071            if self.scaleFactorOfThresholdUpperLimit == -127 and \
1072               self.scaledValueOfThresholdUpperLimit == 255:
1073                self.thresholdUpperLimit = 0.0
1074            else:
1075                self.thresholdUpperLimit = self.scaledValueOfThresholdUpperLimit/(10.**self.scaleFactorOfThresholdUpperLimit)
1076            self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22])
1077            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[22]
1078            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[23]
1079            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[24]
1080            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[25]
1081            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[26]
1082            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[27]
1083            self.numberOfTimeRanges = self.productDefinitionTemplate[28]
1084            self.numberOfMissingValues = self.productDefinitionTemplate[29]
1085            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[30],table='4.10')
1086            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[31],table='4.11')
1087            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[32],table='4.4')
1088            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[33]
1089            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[34],table='4.4')
1090            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[35]
1091
1092        # Template 4.10 -
1093        elif self.productDefinitionTemplateNumber == 10:
1094            self.percentileValue = self.productDefinitionTemplate[15]
1095            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[16]
1096            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[17]
1097            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[18]
1098            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[19]
1099            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[20]
1100            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[21]
1101            self.numberOfTimeRanges = self.productDefinitionTemplate[22]
1102            self.numberOfMissingValues = self.productDefinitionTemplate[23]
1103            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.10')
1104            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.11')
1105            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.4')
1106            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[27]
1107            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4')
1108            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[29]
1109
1110        # Template 4.11 -
1111        elif self.productDefinitionTemplateNumber == 11:
1112            self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6')
1113            self.perturbationNumber = self.productDefinitionTemplate[16]
1114            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17]
1115            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[18]
1116            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[19]
1117            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[20]
1118            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[21]
1119            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[22]
1120            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[23]
1121            self.numberOfTimeRanges = self.productDefinitionTemplate[24]
1122            self.numberOfMissingValues = self.productDefinitionTemplate[25]
1123            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.10')
1124            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.11')
1125            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4')
1126            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[29]
1127            self.unitOfTimeRangeOfSuccessiveFields = tables.get_value_from_table(self.productDefinitionTemplate[30],table='4.4')
1128            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[31]
1129
1130        # Template 4.12 -
1131        elif self.productDefinitionTemplateNumber == 12:
1132            self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7')
1133            self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16]
1134            self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[17]
1135            self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[18]
1136            self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[19]
1137            self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[20]
1138            self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[21]
1139            self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[22]
1140            self.numberOfTimeRanges = self.productDefinitionTemplate[23]
1141            self.numberOfMissingValues = self.productDefinitionTemplate[24]
1142            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.10')
1143            self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.11')
1144            self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4')
1145            self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[28]
1146            self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[29],table='4.4')
1147            self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[30]
1148
1149        # Template 4.15 -
1150        elif self.productDefinitionTemplateNumber == 15:
1151            self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[15],table='4.10')
1152            self.typeOfSpatialProcessing = Grib2Metadata(self.productDefinitionTemplate[16],table='4.15')
1153            self.numberOfDataPointsForSpatialProcessing = self.productDefinitionTemplate[17]
1154
1155        else:
1156            if self.productDefinitionTemplateNumber != 0:
1157                errmsg = 'Unsupported Product Definition Template Number - 4.%i' % self.productDefinitionTemplateNumber.value
1158                raise ValueError(errmsg)
1159
1160
1161        self.leadTime = utils.getleadtime(self.identificationSection,
1162                                          self.productDefinitionTemplateNumber.value,
1163                                          self.productDefinitionTemplate)
1164
1165        if self.productDefinitionTemplateNumber.value in [8,9,10,11,12]:
1166            self.dtEndOfTimePeriod = datetime.datetime(self.yearOfEndOfTimePeriod,self.monthOfEndOfTimePeriod,
1167                                     self.dayOfEndOfTimePeriod,hour=self.hourOfEndOfTimePeriod,
1168                                     minute=self.minuteOfEndOfTimePeriod,
1169                                     second=self.secondOfEndOfTimePeriod)
1170
1171        # --------------------------------
1172        # Section 5 -- Data Representation
1173        # --------------------------------
1174
1175        # Template 5.0 - Simple Packing
1176        if self.dataRepresentationTemplateNumber == 0:
1177            self.refValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[0])
1178            self.binScaleFactor = self.dataRepresentationTemplate[1]
1179            self.decScaleFactor = self.dataRepresentationTemplate[2]
1180            self.nBitsPacking = self.dataRepresentationTemplate[3]
1181       #    self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[3],table='5.1')
1182
1183        # Template 5.2 - Complex Packing
1184        elif self.dataRepresentationTemplateNumber == 2:
1185            self.refValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[0])
1186            self.binScaleFactor = self.dataRepresentationTemplate[1]
1187            self.decScaleFactor = self.dataRepresentationTemplate[2]
1188            self.nBitsPacking = self.dataRepresentationTemplate[3]
1189       #    self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
1190            self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4')
1191            self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5')
1192            if self.typeOfValues == 0:
1193                # Floating Point
1194                self.priMissingValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None
1195                self.secMissingValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None
1196            elif self.typeOfValues == 1:
1197                # Integer
1198                self.priMissingValue = self.dataRepresentationTemplate[7] if self.dataRepresentationTemplate[6] in [1,2] else None
1199                self.secMissingValue = self.dataRepresentationTemplate[8] if self.dataRepresentationTemplate[6] == 2 else None
1200            self.nGroups = self.dataRepresentationTemplate[9]
1201            self.refGroupWidth = self.dataRepresentationTemplate[10]
1202            self.nBitsGroupWidth = self.dataRepresentationTemplate[11]
1203            self.refGroupLength = self.dataRepresentationTemplate[12]
1204            self.groupLengthIncrement = self.dataRepresentationTemplate[13]
1205            self.lengthOfLastGroup = self.dataRepresentationTemplate[14]
1206            self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15]
1207
1208        # Template 5.3 - Complex Packing and Spatial Differencing
1209        elif self.dataRepresentationTemplateNumber == 3:
1210            self.refValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[0])
1211            self.binScaleFactor = self.dataRepresentationTemplate[1]
1212            self.decScaleFactor = self.dataRepresentationTemplate[2]
1213            self.nBitsPacking = self.dataRepresentationTemplate[3]
1214       #    self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
1215            self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4')
1216            self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5')
1217            if self.typeOfValues == 0:
1218                # Floating Point
1219                self.priMissingValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None
1220                self.secMissingValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None
1221            elif self.typeOfValues == 1:
1222                # Integer
1223                self.priMissingValue = self.dataRepresentationTemplate[7] if self.dataRepresentationTemplate[6] in [1,2] else None
1224                self.secMissingValue = self.dataRepresentationTemplate[8] if self.dataRepresentationTemplate[6] == 2 else None
1225            self.nGroups = self.dataRepresentationTemplate[9]
1226            self.refGroupWidth = self.dataRepresentationTemplate[10]
1227            self.nBitsGroupWidth = self.dataRepresentationTemplate[11]
1228            self.refGroupLength = self.dataRepresentationTemplate[12]
1229            self.groupLengthIncrement = self.dataRepresentationTemplate[13]
1230            self.lengthOfLastGroup = self.dataRepresentationTemplate[14]
1231            self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15]
1232            self.spatialDifferenceOrder = Grib2Metadata(self.dataRepresentationTemplate[16],table='5.6')
1233            self.nBytesSpatialDifference = self.dataRepresentationTemplate[17]
1234
1235        # Template 5.4 - IEEE Floating Point Data
1236        elif self.dataRepresentationTemplateNumber == 4:
1237            self.precision = Grib2Metadata(self.dataRepresentationTemplate[0],table='5.7')
1238
1239        # Template 5.40 - JPEG2000 Compression
1240        elif self.dataRepresentationTemplateNumber == 40:
1241            self.refValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[0])
1242            self.binScaleFactor = self.dataRepresentationTemplate[1]
1243            self.decScaleFactor = self.dataRepresentationTemplate[2]
1244            self.nBitsPacking = self.dataRepresentationTemplate[3]
1245       #    self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
1246            self.typeOfCompression = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.40')
1247            self.targetCompressionRatio = self.dataRepresentationTemplate[6]
1248
1249        # Template 5.41 - PNG Compression
1250        elif self.dataRepresentationTemplateNumber == 41:
1251            self.refValue = utils.ieee_int_to_float(self.dataRepresentationTemplate[0])
1252            self.binScaleFactor = self.dataRepresentationTemplate[1]
1253            self.decScaleFactor = self.dataRepresentationTemplate[2]
1254            self.nBitsPacking = self.dataRepresentationTemplate[3]
1255       #    self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1')
1256
1257        else:
1258            errmsg = 'Unsupported Data Representation Definition Template Number - 5.%i' % self.dataRepresentationTemplateNumber.value
1259            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):
1262    def data(self, fill_value=DEFAULT_FILL_VALUE, masked_array=True, expand=True, order=None,
1263             map_keys=False):
1264        """
1265        Returns an unpacked data grid.
1266
1267        Parameters
1268        ----------
1269
1270        **`fill_value`**: Missing or masked data is filled with this value or default value given by
1271        `DEFAULT_FILL_VALUE`
1272
1273        **`masked_array`**: If `True` [DEFAULT], return masked array if there is bitmap for missing
1274        or masked data.
1275
1276        **`expand`**: If `True` [DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids.
1277
1278        **`order`**: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing
1279        or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids.
1280
1281        **`map_keys`**: If `True`, data values will be mapped to the string-based keys that are stored
1282        in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the
1283        units (i.e. "See Table 4.xxx").
1284
1285        Returns
1286        -------
1287
1288        **`numpy.ndarray`**: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32,
1289        but could be np.int32 if Grib2Message.typeOfValues is integer.  The array dtype will be
1290        string-based if map_keys=True.
1291        """
1292        t1 = datetime.datetime.now()
1293        t2 = datetime.datetime.now()
1294        if not hasattr(self,'scanModeFlags'):
1295        #if self.scanModeFlags is None:
1296            raise ValueError('Unsupported grid definition template number %s'%self.gridDefinitionTemplateNumber)
1297        else:
1298            if self.scanModeFlags[2]:
1299                storageorder='F'
1300            else:
1301                storageorder='C'
1302        #print(f'scan settings before array unpack took: {datetime.datetime.now() - t2}')
1303        if order is None:
1304            if (self.dataRepresentationTemplateNumber in [2,3] and
1305                self.dataRepresentationTemplate[6] != 0) or self.bitMapFlag == 0:
1306                order = 0
1307            else:
1308                order = 1
1309        drtnum = self.dataRepresentationTemplateNumber.value
1310        drtmpl = np.asarray(self.dataRepresentationTemplate,dtype=DEFAULT_NUMPY_INT)
1311        gdtnum = self.gridDefinitionTemplateNumber.value
1312        gdtmpl = np.asarray(self.gridDefinitionTemplate,dtype=DEFAULT_NUMPY_INT)
1313        t2 = datetime.datetime.now()
1314        ndpts = self.numberOfDataPoints
1315        gds = self.gridDefinitionSection
1316        ngrdpts = gds[1]
1317        ipos = self._datapos
1318        #print(f'before array unpack took: {datetime.datetime.now() - t1}')
1319        t1 = datetime.datetime.now()
1320        fld1 = g2clib.unpack7(self._msg,gdtnum,gdtmpl,drtnum,drtmpl,ndpts,ipos,np.empty,storageorder=storageorder)
1321        #print(f'array unpack took: {datetime.datetime.now() - t1}')
1322        t1 = datetime.datetime.now()
1323        # Apply bitmap.
1324        if self.bitMapFlag == 0:
1325            fld = fill_value*np.ones(ngrdpts,'f')
1326            np.put(fld,np.nonzero(self.bitMap),fld1)
1327            if masked_array:
1328                fld = ma.masked_values(fld,fill_value)
1329        # Missing values instead of bitmap
1330        elif masked_array and hasattr(self,'priMissingValue'):
1331            if hasattr(self,'secMissingValue'):
1332                mask = np.logical_or(fld1==self.priMissingValue,fld1==self.secMissingValue)
1333            else:
1334                mask = fld1 == self.priMissingValue
1335            fld = ma.array(fld1,mask=mask)
1336        else:
1337            fld = fld1
1338        if self.nx is not None and self.ny is not None: # Rectangular grid.
1339            if ma.isMA(fld):
1340                fld = ma.reshape(fld,(self.ny,self.nx))
1341            else:
1342                fld = np.reshape(fld,(self.ny,self.nx))
1343        else:
1344            if gds[2] and gdtnum == 40: # Reduced global Gaussian grid.
1345                if expand:
1346                    from . import redtoreg
1347                    self.nx = 2*self.ny
1348                    lonsperlat = self.defList
1349                    if ma.isMA(fld):
1350                        fld = ma.filled(fld)
1351                        fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long),
1352                                                 fld.astype(np.double),fill_value)
1353                        fld = ma.masked_values(fld,fill_value)
1354                    else:
1355                        fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long),
1356                                                 fld.astype(np.double),fill_value)
1357        #print(f'bitmap/missing: {datetime.datetime.now() - t1}')
1358        # Check scan modes for rect grids.
1359        if self.nx is not None and self.ny is not None:
1360            if self.scanModeFlags[3]:
1361                fldsave = fld.astype('f') # casting makes a copy
1362                fld[1::2,:] = fldsave[1::2,::-1]
1363        #print(f'bitmap/missing and scan modes for rect: {datetime.datetime.now() - t1}')
1364
1365        # Set data to integer according to GRIB metadata
1366        if self.typeOfValues == "Integer": fld = fld.astype(np.int32)
1367
1368        # Map the data values to their respective definitions.
1369        if map_keys:
1370            fld = fld.astype(np.int32).astype(str)
1371            if (self.identificationSection[0] == 7 and \
1372                self.identificationSection[1] == 14 and \
1373                self.shortName == 'PWTHER') or \
1374               (self.identificationSection[0] == 8 and \
1375                self.identificationSection[1] == 65535 and \
1376                self.shortName == 'WX'):
1377                keys = utils.decode_wx_strings(self._lus)
1378                for n,k in enumerate(keys):
1379                    fld = np.where(fld==str(n+1),k,fld)
1380            else:
1381                # For data whose units are defined in a code table
1382                tbl = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0]
1383                for k,v in tables.get_table(tbl).items():
1384                    fld = np.where(fld==k,v,fld)
1385        #print(f'after array unpack took: {datetime.datetime.now() - t1}')
1386        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):
1389    def latlons(self):
1390        """Alias for `grib2io.Grib2Message.grid` method"""
1391        return self.grid()

Alias for grib2io.Grib2Message.grid method

def grid(self):
1394    def grid(self):
1395        """
1396        Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon,
1397        global Gaussian, mercator, stereographic, lambert conformal, albers equal-area,
1398        space-view and azimuthal equidistant grids.
1399
1400        Returns
1401        -------
1402
1403        **`lats, lons : numpy.ndarray`**
1404
1405        Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and
1406        longitudes in units of degrees.
1407        """
1408        gdtnum = self.gridDefinitionTemplateNumber
1409        gdtmpl = self.gridDefinitionTemplate
1410        reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid
1411        self.projparams = {}
1412        if self.earthMajorAxis is not None: self.projparams['a']=self.earthMajorAxis
1413        if self.earthMajorAxis is not None: self.projparams['b']=self.earthMinorAxis
1414        if gdtnum == 0:
1415            # Regular lat/lon grid
1416            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
1417            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
1418            dlon = self.gridlengthXDirection
1419            dlat = self.gridlengthYDirection
1420            lats = np.arange(lat1,lat2+dlat,dlat)
1421            lons = np.arange(lon1,lon2+dlon,dlon)
1422            # flip if scan mode says to.
1423            #if self.scanModeFlags[0]:
1424            #    lons = lons[::-1]
1425            #if not self.scanModeFlags[1]:
1426            #    lats = lats[::-1]
1427            self.projparams['proj'] = 'cyl'
1428            lons,lats = np.meshgrid(lons,lats) # make 2-d arrays.
1429        elif gdtnum == 40: # Gaussian grid (only works for global!)
1430            from utils.gauss_grids import gaussian_latitudes
1431            lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
1432            lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint
1433            nlats = self.ny
1434            if not reggrid: # Reduced Gaussian grid.
1435                nlons = 2*nlats
1436                dlon = 360./nlons
1437            else:
1438                nlons = self.nx
1439                dlon = self.gridlengthXDirection
1440            lons = np.arange(lon1,lon2+dlon,dlon)
1441            # Compute Gaussian lats (north to south)
1442            lats = gaussian_latitudes(nlats)
1443            if lat1 < lat2:  # reverse them if necessary
1444                lats = lats[::-1]
1445            # flip if scan mode says to.
1446            #if self.scanModeFlags[0]:
1447            #    lons = lons[::-1]
1448            #if not self.scanModeFlags[1]:
1449            #    lats = lats[::-1]
1450            self.projparams['proj'] = 'cyl'
1451            lons,lats = np.meshgrid(lons,lats) # make 2-d arrays
1452        elif gdtnum in [10,20,30,31,110]:
1453            # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant
1454            dx,dy = self.gridlengthXDirection, self.gridlengthYDirection
1455            lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint
1456            if gdtnum == 10: # Mercator.
1457                self.projparams['lat_ts']=self.proj4_lat_ts
1458                self.projparams['proj']=self.proj4_proj
1459                self.projparams['lon_0']=self.proj4_lon_0
1460                pj = pyproj.Proj(self.projparams)
1461                llcrnrx, llcrnry = pj(lon1,lat1)
1462                x = llcrnrx+dx*np.arange(self.nx)
1463                y = llcrnry+dy*np.arange(self.ny)
1464                x,y = np.meshgrid(x, y)
1465                lons,lats = pj(x, y, inverse=True)
1466            elif gdtnum == 20:  # Stereographic
1467                self.projparams['lat_ts']=self.proj4_lat_ts
1468                self.projparams['proj']=self.proj4_proj
1469                self.projparams['lat_0']=self.proj4_lat_0
1470                self.projparams['lon_0']=self.proj4_lon_0
1471                pj = pyproj.Proj(self.projparams)
1472                llcrnrx, llcrnry = pj(lon1,lat1)
1473                x = llcrnrx+dx*np.arange(self.nx)
1474                y = llcrnry+dy*np.arange(self.ny)
1475                x,y = np.meshgrid(x, y)
1476                lons,lats = pj(x, y, inverse=True)
1477            elif gdtnum in [30,31]: # Lambert, Albers
1478                self.projparams['lat_1']=self.proj4_lat_1
1479                self.projparams['lat_2']=self.proj4_lat_2
1480                self.projparams['proj']=self.proj4_proj
1481                self.projparams['lon_0']=self.proj4_lon_0
1482                pj = pyproj.Proj(self.projparams)
1483                llcrnrx, llcrnry = pj(lon1,lat1)
1484                x = llcrnrx+dx*np.arange(self.nx)
1485                y = llcrnry+dy*np.arange(self.ny)
1486                x,y = np.meshgrid(x, y)
1487                lons,lats = pj(x, y, inverse=True)
1488            elif gdtnum == 110: # Azimuthal Equidistant
1489                self.projparams['proj']=self.proj4_proj
1490                self.projparams['lat_0']=self.proj4_lat_0
1491                self.projparams['lon_0']=self.proj4_lon_0
1492                pj = pyproj.Proj(self.projparams)
1493                llcrnrx, llcrnry = pj(lon1,lat1)
1494                x = llcrnrx+dx*np.arange(self.nx)
1495                y = llcrnry+dy*np.arange(self.ny)
1496                x,y = np.meshgrid(x, y)
1497                lons,lats = pj(x, y, inverse=True)
1498        elif gdtnum == 90:
1499            # Satellite Projection
1500            dx = self.gridlengthXDirection
1501            dy = self.gridlengthYDirection
1502            self.projparams['proj']=self.proj4_proj
1503            self.projparams['lon_0']=self.proj4_lon_0
1504            self.projparams['lat_0']=self.proj4_lat_0
1505            self.projparams['h']=self.proj4_h
1506            pj = pyproj.Proj(self.projparams)
1507            x = dx*np.indices((self.ny,self.nx),'f')[1,:,:]
1508            x -= 0.5*x.max()
1509            y = dy*np.indices((self.ny,self.nx),'f')[0,:,:]
1510            y -= 0.5*y.max()
1511            lons,lats = pj(x,y,inverse=True)
1512            # Set lons,lats to 1.e30 where undefined
1513            abslons = np.fabs(lons)
1514            abslats = np.fabs(lats)
1515            lons = np.where(abslons < 1.e20, lons, 1.e30)
1516            lats = np.where(abslats < 1.e20, lats, 1.e30)
1517        else:
1518            raise ValueError('Unsupported grid')
1519
1520        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):
1523    def addlocal(self, ludata):
1524        """
1525        Add a Local Use Section [(Section 2)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect2.shtml)
1526        to the GRIB2 message.
1527
1528        Parameters
1529        ----------
1530
1531        **`ludata : bytes`**: Local Use data.
1532        """
1533        assert isinstance(ludata,bytes)
1534        self._msg,self._pos = g2clib.grib2_addlocal(self._msg,ludata)
1535        self.hasLocalUseSection = True
1536        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):
1539    def addgrid(self, gdsinfo, gdtmpl, deflist=None):
1540        """
1541        Add a Grid Definition Section [(Section 3)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_doc/grib2_sect3.shtml)
1542        to the GRIB2 message.
1543
1544        Parameters
1545        ----------
1546
1547        **`gdsinfo`**: Sequence containing information needed for the grid definition section.
1548
1549        | Index | Description |
1550        | :---: | :---        |
1551        | 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)|
1552        | gdsinfo[1] | Number of data points|
1553        | gdsinfo[2] | Number of octets for optional list of numbers defining number of points|
1554        | 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)|
1555        | 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)|
1556
1557        **`gdtmpl`**: Sequence of values for the specified Grid Definition Template. Each
1558        element of this integer array contains an entry (in the order specified) of Grid
1559        Definition Template 3.NN
1560
1561        **`deflist`**: Sequence containing the number of grid points contained in each
1562        row (or column) of a non-regular grid.  Used if gdsinfo[2] != 0.
1563        """
1564        if 3 in self._sections:
1565            raise ValueError('GRIB2 Message already contains Grid Definition Section.')
1566        if deflist is not None:
1567            _deflist = np.array(deflist,dtype=DEFAULT_NUMPY_INT)
1568        else:
1569            _deflist = None
1570        gdtnum = gdsinfo[4]
1571        if gdtnum in [0,1,2,3,40,41,42,43,44,203,205,32768,32769]:
1572            self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4]
1573        elif gdtnum == 10: # mercator
1574            self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4]
1575        elif gdtnum == 20: # stereographic
1576            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
1577        elif gdtnum == 30: # lambert conformal
1578            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
1579        elif gdtnum == 31: # albers equal area.
1580            self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4]
1581        elif gdtnum == 90: # near-sided vertical perspective satellite projection
1582            self.scanModeFlags = utils.int2bin(gdtmpl[16],output=list)[0:4]
1583        elif gdtnum == 110: # azimuthal equidistant.
1584            self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4]
1585        elif gdtnum == 120:
1586            self.scanModeFlags = utils.int2bin(gdtmpl[6],output=list)[0:4]
1587        elif gdtnum == 204: # curvilinear orthogonal
1588            self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4]
1589        elif gdtnum in [1000,1100]:
1590            self.scanModeFlags = utils.int2bin(gdtmpl[12],output=list)[0:4]
1591        self._msg,self._pos = g2clib.grib2_addgrid(self._msg,
1592                                                   np.array(gdsinfo,dtype=DEFAULT_NUMPY_INT),
1593                                                   np.array(gdtmpl,dtype=DEFAULT_NUMPY_INT),
1594                                                   _deflist)
1595        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):
1598    def addfield(self, field, pdtnum, pdtmpl, coordlist=None, packing="complex-spdiff", **packing_opts):
1599        """
1600        Add a Product Definition, Data Representation, Bitmap, and Data Sections
1601        to `Grib2Message` instance (i.e. Sections 4-7).  Must be called after the grid
1602        definition section has been added (`addfield`).
1603
1604        Parameters
1605        ----------
1606
1607        **`field`**: Numpy array of data values to pack.  If field is a masked array, then
1608        a bitmap is created from the mask.
1609
1610        **`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)
1611
1612        **`pdtmpl`**: Sequence with the data values for the specified Product Definition
1613        Template (N=pdtnum).  Each element of this integer array contains an entry (in
1614        the order specified) of Product Definition Template 4.N.
1615
1616        **`coordlist`**: Sequence containing floating point values intended to document the
1617        vertical discretization with model data on hybrid coordinate vertical levels. Default is `None`.
1618
1619        **`packing`**: String to specify the type of packing. Valid options are the following:
1620
1621        | Packing Scheme | Description |
1622        | :---:          | :---:       |
1623        | 'simple'         | [Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-0.shtml) |
1624        | 'complex'        | [Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-2.shtml) |
1625        | 'complex-spdiff' | [Complex packing with Spatial Differencing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-3.shtml) |
1626        | 'jpeg'           | [JPEG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-40.shtml) |
1627        | 'png'            | [PNG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-41.shtml) |
1628        | 'spectral-simple'| [Spectral Data - Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-50.shtml) |
1629        | 'spectral-complex'| [Spectral Data - Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-51.shtml) |
1630
1631        **`**packing_opts`**: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for
1632        the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following:
1633
1634        | Packing Scheme | Keyword Arguments |
1635        | :---:          | :---:                      |
1636        | 'simple'     | `binScaleFactor`, `decScaleFactor` |
1637        | 'complex'     | `binScaleFactor`, `decScaleFactor`, `priMissingValue`, [`secMissingValue`] |
1638        | 'complex-spdiff'     | `binScaleFactor`, `decScaleFactor`, `spatialDifferenceOrder`, `priMissingValue`, [`secMissingValue`] |
1639        | 'jpeg'     | `binScaleFactor`, `decScaleFactor` |
1640        | 'png'     | `binScaleFactor`, `decScaleFactor` |
1641        | 'spectral-simple'     | `binScaleFactor`, `decScaleFactor` |
1642        | 'spectral-complex'     | `binScaleFactor`, `decScaleFactor` |
1643        """
1644        if self._sections[-1] != 3:
1645            raise ValueError('addgrid() must be called before addfield()')
1646        if self.scanModeFlags is not None:
1647            if self.scanModeFlags[3]:
1648                fieldsave = field.astype('f') # Casting makes a copy
1649                field[1::2,:] = fieldsave[1::2,::-1]
1650        fld = field.astype('f')
1651        if ma.isMA(field) and ma.count_masked(field) > 0:
1652            bitmapflag = 0
1653            bmap = 1-np.ravel(field.mask.astype(DEFAULT_NUMPY_INT))
1654        else:
1655            bitmapflag = 255
1656            bmap = None
1657        if coordlist is not None:
1658            crdlist = np.array(coordlist,'f')
1659        else:
1660            crdlist = None
1661
1662        # Set data representation template number and template values
1663        drtnum = -1
1664        drtmpl = np.zeros((DEFAULT_DRT_LEN),dtype=DEFAULT_NUMPY_INT)
1665        if packing == "simple":
1666            drtnum = 0
1667            drtmpl[1] = packing_opts["binScaleFactor"]
1668            drtmpl[2] = packing_opts["decScaleFactor"]
1669        elif packing == "complex" or packing == "complex-spdiff":
1670            if packing == "complex":
1671                drtnum = 2
1672            if packing == "complex-spdiff":
1673                drtnum = 3
1674                drtmpl[16] = packing_opts['spatialDifferenceOrder']
1675            drtmpl[1] = packing_opts["binScaleFactor"]
1676            drtmpl[2] = packing_opts["decScaleFactor"]
1677            if set(("priMissingValue","secMissingValue")).issubset(packing_opts):
1678                drtmpl[6] = 2
1679                drtmpl[7] = utils.ieee_float_to_int(packing_opts["priMissingValue"])
1680                drtmpl[8] = utils.ieee_float_to_int(packing_opts["secMissingValue"])
1681            else:
1682                if "priMissingValue" in packing_opts.keys():
1683                    drtmpl[6] = 1
1684                    drtmpl[7] = utils.ieee_float_to_int(packing_opts["priMissingValue"])
1685                else:
1686                    drtmpl[6] = 0
1687        elif packing == "jpeg":
1688            drtnum = 40
1689            drtmpl[1] = packing_opts["binScaleFactor"]
1690            drtmpl[2] = packing_opts["decScaleFactor"]
1691        elif packing == "png":
1692            drtnum = 41
1693            drtmpl[1] = packing_opts["binScaleFactor"]
1694            drtmpl[2] = packing_opts["decScaleFactor"]
1695        elif packing == "spectral-simple":
1696            drtnum = 50
1697            drtmpl[1] = packing_opts["binScaleFactor"]
1698            drtmpl[2] = packing_opts["decScaleFactor"]
1699        elif packing == "spectral-complex":
1700            drtnum = 51
1701            drtmpl[1] = packing_opts["binScaleFactor"]
1702            drtmpl[2] = packing_opts["decScaleFactor"]
1703
1704        pdtnum = pdtnum.value if isinstance(pdtnum,Grib2Metadata) else pdtnum
1705
1706        self._msg,self._pos = g2clib.grib2_addfield(self._msg,
1707                                                    pdtnum,
1708                                                    np.array(pdtmpl,dtype=DEFAULT_NUMPY_INT),
1709                                                    crdlist,
1710                                                    drtnum,
1711                                                    drtmpl,
1712                                                    np.ravel(fld),
1713                                                    bitmapflag,
1714                                                    bmap)
1715        self._sections.append(4)
1716        self._sections.append(5)
1717        if bmap is not None: self._sections.append(6)
1718        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):
1721    def end(self):
1722        """
1723        Add End Section (Section 8) to the GRIB2 message. A GRIB2 message
1724        is not complete without an end section.  Once an end section is added,
1725        the GRIB2 message can be written to file.
1726        """
1727        self._msg, self._pos = g2clib.grib2_end(self._msg)
1728        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):
1730    def to_bytes(self, validate=True):
1731        """
1732        Return grib data in byte format. Useful for exporting data in non-file formats.
1733        For example, can be used to output grib data directly to S3 using the boto3 client
1734        without the need to write a temporary file to upload first.
1735
1736        Parameters
1737        ----------
1738        **`validate`**: bool (Default: True) If true, validates first/last four bytes for proper formatting, else
1739        returns None. If False, message is output as is.
1740
1741        Returns
1742        -------
1743        Returns GRIB2 formatted message as bytes.
1744        """
1745        if validate:
1746            if str(self._msg[0:4] + self._msg[-4:], 'utf-8') == 'GRIB7777':
1747                return self._msg
1748            else:
1749                return None
1750        else:
1751            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:
1754class Grib2Metadata():
1755    """
1756    Class to hold GRIB2 metadata both as numeric code value as stored in
1757    GRIB2 and its plain langauge definition.
1758
1759    **`value : int`**
1760
1761    GRIB2 metadata integer code value.
1762
1763    **`table : str, optional`**
1764
1765    GRIB2 table to lookup the `value`. Default is None.
1766    """
1767    __slots__ = ('definition','table','value')
1768    def __init__(self, value, table=None):
1769        self.value = value
1770        self.table = table
1771        if self.table is None:
1772            self.definition = None
1773        else:
1774            self.definition = tables.get_value_from_table(self.value,self.table)
1775    def __call__(self):
1776        return self.value
1777    def __repr__(self):
1778        return '%s(%d, table = %s)' % (self.__class__.__name__,self.value,self.table)
1779    def __str__(self):
1780        return '%d - %s' % (self.value,self.definition)
1781    def __eq__(self,other):
1782        return self.value == other
1783    def __gt__(self,other):
1784        return self.value > other
1785    def __ge__(self,other):
1786        return self.value >= other
1787    def __lt__(self,other):
1788        return self.value < other
1789    def __le__(self,other):
1790        return self.value <= other
1791    def __contains__(self,other):
1792        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)
1768    def __init__(self, value, table=None):
1769        self.value = value
1770        self.table = table
1771        if self.table is None:
1772            self.definition = None
1773        else:
1774            self.definition = tables.get_value_from_table(self.value,self.table)
value
table
definition
def show_config():
13def show_config():
14    """
15    Print grib2io build configuration information.
16    """
17    from g2clib import __version__ as g2clib_version
18    from g2clib import _has_png as have_png
19    from g2clib import _has_jpeg as have_jpeg
20    print("grib2io version %s Configuration:\n"%(__version__))
21    print("\tg2c library version:".expandtabs(4),g2clib_version)
22    print("\tJPEG compression support:".expandtabs(4),bool(have_jpeg))
23    print("\tPNG compression support:".expandtabs(4),bool(have_png))

Print grib2io build configuration information.