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 have_jpeg = True if 'jasper' in __config__.libraries or 'openjp2' in __config__.libraries else False 18 have_png = True if 'png' in __config__.libraries else False 19 jpeglib = 'OpenJPEG' if 'openjp2' in __config__.libraries else ('Jasper' if 'jasper' in __config__.libraries else None) 20 pnglib = 'libpng' if 'png' in __config__.libraries else None 21 print("grib2io version %s Configuration:\n"%(__version__)) 22 print("\tg2c library version:".expandtabs(4),g2clib_version) 23 print("\tJPEG compression support:".expandtabs(4),have_jpeg) 24 if have_jpeg: print("\t\tLibrary:".expandtabs(4),jpeglib) 25 print("\tPNG compression support:".expandtabs(4),have_png) 26 if have_png: print("\t\tLibrary:".expandtabs(4),pnglib)
41class open(): 42 """ 43 GRIB2 File Object. A physical file can contain one or more GRIB2 messages. When instantiated, 44 class `grib2io.open`, the file named `filename` is opened for reading (`mode = 'r'`) and is 45 automatically indexed. The indexing procedure reads some of the GRIB2 metadata for all GRIB2 Messages. 46 47 A GRIB2 Message may contain submessages whereby Section 2-7 can be repeated. grib2io accommodates 48 for this by flattening any GRIB2 submessages into multiple individual messages. 49 50 Attributes 51 ---------- 52 53 **`mode`**: File IO mode of opening the file. 54 55 **`name`**: Full path name of the GRIB2 file. 56 57 **`messages`**: Count of GRIB2 Messages contained in the file. 58 59 **`current_message`**: Current position of the file in units of GRIB2 Messages. 60 61 **`size`**: Size of the file in units of bytes. 62 63 **`closed`** `True` is file handle is close; `False` otherwise. 64 65 **`decode `**: If `True` [DEFAULT] automatically decode metadata from unpacked 66 section data for Grib2Messages. 67 68 **`variables`**: Tuple containing a unique list of variable short names (i.e. GRIB2 abbreviation names). 69 70 **`levels`**: Tuple containing a unique list of wgrib2-formatted level/layer strings. 71 """ 72 def __init__(self, filename, mode='r', decode=True): 73 """ 74 `open` Constructor 75 76 Parameters 77 ---------- 78 79 **`filename`**: File name containing GRIB2 messages. 80 81 **`mode `**: File access mode where `r` opens the files for reading only; 82 `w` opens the file for writing. 83 84 **`decode `**: If `True` [DEFAULT] automatically decode metadata from 85 unpacked section data for Grib2Messages. 86 """ 87 if mode in ['a','r','w']: 88 mode = mode+'b' 89 self._filehandle = builtins.open(filename,mode=mode,buffering=ONE_MB) 90 self._hasindex = False 91 self._index = {} 92 self.mode = mode 93 self.name = os.path.abspath(filename) 94 self.messages = 0 95 self.current_message = 0 96 self.size = os.path.getsize(self.name) 97 self.closed = self._filehandle.closed 98 self.decode = decode 99 if 'r' in self.mode: self._build_index() 100 # FIX: Cannot perform reads on mode='a' 101 #if 'a' in self.mode and self.size > 0: self._build_index() 102 if self._hasindex: 103 self.variables = tuple(sorted(set(filter(None,self._index['shortName'])))) 104 self.levels = tuple(sorted(set(filter(None,self._index['levelString'])))) 105 106 107 def __delete__(self, instance): 108 """ 109 """ 110 self.close() 111 del self._index 112 113 114 def __enter__(self): 115 """ 116 """ 117 return self 118 119 120 def __exit__(self, atype, value, traceback): 121 """ 122 """ 123 self.close() 124 125 126 def __iter__(self): 127 """ 128 """ 129 return self 130 131 132 def __next__(self): 133 """ 134 """ 135 if self.current_message < self.messages: 136 return self.read()[0] 137 else: 138 self.seek(0) 139 raise StopIteration 140 141 142 def __repr__(self): 143 """ 144 """ 145 strings = [] 146 keys = self.__dict__.keys() 147 for k in keys: 148 if not k.startswith('_'): 149 strings.append('%s = %s\n'%(k,self.__dict__[k])) 150 return ''.join(strings) 151 152 153 def __getitem__(self, key): 154 """ 155 """ 156 if isinstance(key,slice): 157 if key.start is None and key.stop is None and key.step is None: 158 beg = 1 159 end = self.messages+1 160 inc = 1 161 else: 162 beg, end, inc = key.indices(self.messages) 163 return [self[i][0] for i in range(beg,end,inc)] 164 elif isinstance(key,int): 165 if key == 0: return None 166 self._filehandle.seek(self._index['offset'][key]) 167 return [Grib2Message(msg=self._filehandle.read(self._index['size'][key]), 168 source=self, 169 num=self._index['messageNumber'][key], 170 decode=self.decode)] 171 elif isinstance(key,str): 172 return self.select(shortName=key) 173 else: 174 raise KeyError('Key must be an integer, slice, or GRIB2 variable shortName.') 175 176 177 def _build_index(self): 178 """ 179 Perform indexing of GRIB2 Messages. 180 """ 181 # Initialize index dictionary 182 self._index['offset'] = [None] 183 self._index['discipline'] = [None] 184 self._index['edition'] = [None] 185 self._index['size'] = [None] 186 self._index['submessageOffset'] = [None] 187 self._index['submessageBeginSection'] = [None] 188 self._index['isSubmessage'] = [None] 189 self._index['messageNumber'] = [None] 190 self._index['identificationSection'] = [None] 191 self._index['refDate'] = [None] 192 self._index['productDefinitionTemplateNumber'] = [None] 193 self._index['productDefinitionTemplate'] = [None] 194 self._index['leadTime'] = [None] 195 self._index['duration'] = [None] 196 self._index['shortName'] = [None] 197 self._index['bitMap'] = [None] 198 self._index['levelString'] = [None] 199 self._index['probString'] = [None] 200 201 # Iterate 202 while True: 203 try: 204 # Read first 4 bytes and decode...looking for "GRIB" 205 pos = self._filehandle.tell() 206 header = struct.unpack('>4s',self._filehandle.read(4))[0].decode() 207 208 # Test header. Then get information from GRIB2 Section 0: the discipline 209 # number, edition number (should always be 2), and GRIB2 message size. 210 # Then iterate to check for submessages. 211 if header == 'GRIB': 212 _issubmessage = False 213 _submsgoffset = 0 214 _submsgbegin = 0 215 216 # Read and unpack Section 0. Note that this is not done through 217 # the g2clib. 218 self._filehandle.seek(self._filehandle.tell()+2) 219 discipline = int(struct.unpack('>B',self._filehandle.read(1))[0]) 220 edition = int(struct.unpack('>B',self._filehandle.read(1))[0]) 221 assert edition == 2 222 size = struct.unpack('>Q',self._filehandle.read(8))[0] 223 224 # Read and unpack Section 1 225 secsize = struct.unpack('>i',self._filehandle.read(4))[0] 226 secnum = struct.unpack('>B',self._filehandle.read(1))[0] 227 assert secnum == 1 228 self._filehandle.seek(self._filehandle.tell()-5) 229 _grbmsg = self._filehandle.read(secsize) 230 _grbpos = 0 231 _grbsec1,_grbpos = g2clib.unpack1(_grbmsg,_grbpos,np.empty) 232 _grbsec1 = _grbsec1.tolist() 233 _refdate = utils.getdate(_grbsec1[5],_grbsec1[6],_grbsec1[7],_grbsec1[8]) 234 secrange = range(2,8) 235 while 1: 236 for num in secrange: 237 secsize = struct.unpack('>i',self._filehandle.read(4))[0] 238 secnum = struct.unpack('>B',self._filehandle.read(1))[0] 239 if secnum == num: 240 if secnum == 3: 241 self._filehandle.seek(self._filehandle.tell()-5) 242 _grbmsg = self._filehandle.read(secsize) 243 _grbpos = 0 244 # Unpack Section 3 245 _gds,_gdtn,_deflist,_grbpos = g2clib.unpack3(_grbmsg,_grbpos,np.empty) 246 elif secnum == 4: 247 self._filehandle.seek(self._filehandle.tell()-5) 248 _grbmsg = self._filehandle.read(secsize) 249 _grbpos = 0 250 # Unpack Section 4 251 _pdt,_pdtnum,_coordlist,_grbpos = g2clib.unpack4(_grbmsg,_grbpos,np.empty) 252 _pdt = _pdt.tolist() 253 _varinfo = tables.get_varinfo_from_table(discipline,_pdt[0],_pdt[1]) 254 elif secnum == 6: 255 self._filehandle.seek(self._filehandle.tell()-5) 256 _grbmsg = self._filehandle.read(secsize) 257 _grbpos = 0 258 # Unpack Section 6. Save bitmap 259 _bmap,_bmapflag = g2clib.unpack6(_grbmsg,_gds[1],_grbpos,np.empty) 260 if _bmapflag == 0: 261 _bmap_save = copy.deepcopy(_bmap) 262 elif _bmapflag == 254: 263 _bmap = copy.deepcopy(_bmap_save) 264 else: 265 self._filehandle.seek(self._filehandle.tell()+secsize-5) 266 else: 267 if num == 2 and secnum == 3: 268 pass # Allow this. Just means no Local Use Section. 269 else: 270 _issubmessage = True 271 _submsgoffset = (self._filehandle.tell()-5)-(self._index['offset'][self.messages]) 272 _submsgbegin = secnum 273 self._filehandle.seek(self._filehandle.tell()-5) 274 continue 275 trailer = struct.unpack('>4s',self._filehandle.read(4))[0].decode() 276 if trailer == '7777': 277 self.messages += 1 278 self._index['offset'].append(pos) 279 self._index['discipline'].append(discipline) 280 self._index['edition'].append(edition) 281 self._index['size'].append(size) 282 self._index['messageNumber'].append(self.messages) 283 self._index['isSubmessage'].append(_issubmessage) 284 self._index['identificationSection'].append(_grbsec1) 285 self._index['refDate'].append(_refdate) 286 self._index['productDefinitionTemplateNumber'].append(_pdtnum) 287 self._index['productDefinitionTemplate'].append(_pdt) 288 self._index['leadTime'].append(utils.getleadtime(_grbsec1,_pdtnum,_pdt)) 289 self._index['duration'].append(utils.getduration(_pdtnum,_pdt)) 290 self._index['shortName'].append(_varinfo[2]) 291 self._index['bitMap'].append(_bmap) 292 self._index['levelString'].append(tables.get_wgrib2_level_string(*_pdt[9:15])) 293 if _pdtnum in [5,9]: 294 self._index['probString'].append(utils.get_wgrib2_prob_string(*_pdt[17:22])) 295 else: 296 self._index['probString'].append('') 297 if _issubmessage: 298 self._index['submessageOffset'].append(_submsgoffset) 299 self._index['submessageBeginSection'].append(_submsgbegin) 300 else: 301 self._index['submessageOffset'].append(0) 302 self._index['submessageBeginSection'].append(_submsgbegin) 303 break 304 else: 305 self._filehandle.seek(self._filehandle.tell()-4) 306 self.messages += 1 307 self._index['offset'].append(pos) 308 self._index['discipline'].append(discipline) 309 self._index['edition'].append(edition) 310 self._index['size'].append(size) 311 self._index['messageNumber'].append(self.messages) 312 self._index['isSubmessage'].append(_issubmessage) 313 self._index['identificationSection'].append(_grbsec1) 314 self._index['refDate'].append(_refdate) 315 self._index['productDefinitionTemplateNumber'].append(_pdtnum) 316 self._index['productDefinitionTemplate'].append(_pdt) 317 self._index['leadTime'].append(utils.getleadtime(_grbsec1,_pdtnum,_pdt)) 318 self._index['duration'].append(utils.getduration(_pdtnum,_pdt)) 319 self._index['shortName'].append(_varinfo[2]) 320 self._index['bitMap'].append(_bmap) 321 self._index['levelString'].append(tables.get_wgrib2_level_string(*_pdt[9:15])) 322 if _pdtnum in [5,9]: 323 self._index['probString'].append(utils.get_wgrib2_prob_string(*_pdt[17:22])) 324 else: 325 self._index['probString'].append('') 326 self._index['submessageOffset'].append(_submsgoffset) 327 self._index['submessageBeginSection'].append(_submsgbegin) 328 continue 329 330 except(struct.error): 331 self._filehandle.seek(0) 332 break 333 334 self._hasindex = True 335 336 337 def close(self): 338 """ 339 Close the file handle 340 """ 341 if not self._filehandle.closed: 342 self._filehandle.close() 343 self.closed = self._filehandle.closed 344 345 346 def read(self, num=1): 347 """ 348 Read num GRIB2 messages from the current position 349 350 Parameters 351 ---------- 352 353 **`num`**: integer number of GRIB2 Message to read. 354 355 Returns 356 ------- 357 358 **`list`**: list of `grib2io.Grib2Message` instances. 359 """ 360 msgs = [] 361 if self.tell() >= self.messages: return msgs 362 if num > 0: 363 if num == 1: 364 msgrange = [self.tell()+1] 365 else: 366 beg = self.tell()+1 367 end = self.tell()+1+num if self.tell()+1+num <= self.messages else self.messages 368 msgrange = range(beg,end+1) 369 for n in msgrange: 370 self._filehandle.seek(self._index['offset'][n]) 371 msgs.append(Grib2Message(msg=self._filehandle.read(self._index['size'][n]), 372 source=self, 373 num=self._index['messageNumber'][n], 374 decode=self.decode)) 375 self.current_message += 1 376 return msgs 377 378 379 def rewind(self): 380 """ 381 Set the position of the file to zero in units of GRIB2 messages. 382 """ 383 self.seek(0) 384 385 386 def seek(self, pos): 387 """ 388 Set the position within the file in units of GRIB2 messages. 389 390 Parameters 391 ---------- 392 393 **`pos`**: GRIB2 Message number to set the read pointer to. 394 """ 395 if self._hasindex: 396 if pos == 0: 397 self._filehandle.seek(pos) 398 self.current_message = pos 399 elif pos > 0: 400 self._filehandle.seek(self._index['offset'][pos-1]) 401 self.current_message = pos 402 403 404 def tell(self): 405 """ 406 Returns the position of the file in units of GRIB2 Messages. 407 """ 408 return self.current_message 409 410 411 def select(self,**kwargs): 412 """ 413 Returns a list of `grib2io.Grib2Message` instances based on the selection **`**kwargs`**. 414 415 The following keywords are currently supported: 416 417 **`duration : int`** specifiying the time duration (in unit of hours) of a GRIB2 Message that is 418 determined from a period of time. 419 420 **`leadTime : int`** specifying ending lead time (in units of hours) of a GRIB2 Message. 421 422 **`level : str`** wgrib2-formatted layer/level string. 423 424 **`percentile : int`** specify the percentile value. 425 426 **`refDate : int`** specifying the reference date in `YYYYMMDDHH[MMSS]` format. 427 428 **`shortName : str`** the GRIB2 `shortName`. This is the abbreviation name found in the NCEP GRIB2 tables. 429 430 **`threshold : str`** wgrib2-formatted probability threshold string. 431 """ 432 kwargs_allowed = ['duration','leadTime','level','percentile','refDate','shortName','threshold'] 433 idxs = {} 434 for k,v in kwargs.items(): 435 if k not in kwargs_allowed: continue 436 if k == 'duration': 437 idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['duration']])==v)[0] 438 elif k == 'leadTime': 439 idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['leadTime']])==v)[0] 440 elif k == 'level': 441 idxs[k] = np.where(np.array(self._index['levelString'])==v)[0] 442 elif k == 'percentile': 443 tmp1 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==6)[0] 444 tmp2 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==10)[0] 445 idxs[k] = [i for i in np.concatenate((tmp1,tmp2)) if self._index["productDefinitionTemplate"][i][15]==v] 446 del tmp1,tmp2 447 elif k == 'refDate': 448 idxs[k] = np.where(np.asarray(self._index['refDate'])==v)[0] 449 elif k == 'shortName': 450 idxs[k] = np.where(np.array(self._index['shortName'])==v)[0] 451 elif k == 'threshold': 452 idxs[k] = np.where(np.array(self._index['probString'])==v)[0] 453 idxsarr = np.concatenate(tuple(idxs.values())) 454 nidxs = len(idxs.keys()) 455 if nidxs == 1: 456 return [self[int(i)][0] for i in idxsarr] 457 elif nidxs > 1: 458 return [self[int(i)][0] for i in [ii[0] for ii in collections.Counter(idxsarr).most_common() if ii[1] == nidxs]] 459 460 461 def write(self, msg): 462 """ 463 Writes a packed GRIB2 message to file. 464 465 Parameters 466 ---------- 467 468 **`msg`**: instance of `Grib2Message`. 469 """ 470 if isinstance(msg,Grib2Message): 471 self._filehandle.write(msg._msg) 472 self.size = os.path.getsize(self.name) 473 self.messages += 1 474 self.current_message += 1 475 else: 476 raise TypeError("msg must be a Grib2Message object.")
GRIB2 File Object. A physical file can contain one or more GRIB2 messages. When instantiated,
class grib2io.open
, the file named filename
is opened for reading (mode = 'r'
) and is
automatically indexed. The indexing procedure reads some of the GRIB2 metadata for all GRIB2 Messages.
A GRIB2 Message may contain submessages whereby Section 2-7 can be repeated. grib2io accommodates for this by flattening any GRIB2 submessages into multiple individual messages.
Attributes
mode
: File IO mode of opening the file.
name
: Full path name of the GRIB2 file.
messages
: Count of GRIB2 Messages contained in the file.
current_message
: Current position of the file in units of GRIB2 Messages.
size
: Size of the file in units of bytes.
closed
True
is file handle is close; False
otherwise.
decode
: If True
[DEFAULT] automatically decode metadata from unpacked
section data for Grib2Messages.
variables
: Tuple containing a unique list of variable short names (i.e. GRIB2 abbreviation names).
levels
: Tuple containing a unique list of wgrib2-formatted level/layer strings.
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.
337 def close(self): 338 """ 339 Close the file handle 340 """ 341 if not self._filehandle.closed: 342 self._filehandle.close() 343 self.closed = self._filehandle.closed
Close the file handle
346 def read(self, num=1): 347 """ 348 Read num GRIB2 messages from the current position 349 350 Parameters 351 ---------- 352 353 **`num`**: integer number of GRIB2 Message to read. 354 355 Returns 356 ------- 357 358 **`list`**: list of `grib2io.Grib2Message` instances. 359 """ 360 msgs = [] 361 if self.tell() >= self.messages: return msgs 362 if num > 0: 363 if num == 1: 364 msgrange = [self.tell()+1] 365 else: 366 beg = self.tell()+1 367 end = self.tell()+1+num if self.tell()+1+num <= self.messages else self.messages 368 msgrange = range(beg,end+1) 369 for n in msgrange: 370 self._filehandle.seek(self._index['offset'][n]) 371 msgs.append(Grib2Message(msg=self._filehandle.read(self._index['size'][n]), 372 source=self, 373 num=self._index['messageNumber'][n], 374 decode=self.decode)) 375 self.current_message += 1 376 return msgs
Read num GRIB2 messages from the current position
Parameters
num
: integer number of GRIB2 Message to read.
Returns
list
: list of grib2io.Grib2Message
instances.
379 def rewind(self): 380 """ 381 Set the position of the file to zero in units of GRIB2 messages. 382 """ 383 self.seek(0)
Set the position of the file to zero in units of GRIB2 messages.
386 def seek(self, pos): 387 """ 388 Set the position within the file in units of GRIB2 messages. 389 390 Parameters 391 ---------- 392 393 **`pos`**: GRIB2 Message number to set the read pointer to. 394 """ 395 if self._hasindex: 396 if pos == 0: 397 self._filehandle.seek(pos) 398 self.current_message = pos 399 elif pos > 0: 400 self._filehandle.seek(self._index['offset'][pos-1]) 401 self.current_message = pos
Set the position within the file in units of GRIB2 messages.
Parameters
pos
: GRIB2 Message number to set the read pointer to.
404 def tell(self): 405 """ 406 Returns the position of the file in units of GRIB2 Messages. 407 """ 408 return self.current_message
Returns the position of the file in units of GRIB2 Messages.
411 def select(self,**kwargs): 412 """ 413 Returns a list of `grib2io.Grib2Message` instances based on the selection **`**kwargs`**. 414 415 The following keywords are currently supported: 416 417 **`duration : int`** specifiying the time duration (in unit of hours) of a GRIB2 Message that is 418 determined from a period of time. 419 420 **`leadTime : int`** specifying ending lead time (in units of hours) of a GRIB2 Message. 421 422 **`level : str`** wgrib2-formatted layer/level string. 423 424 **`percentile : int`** specify the percentile value. 425 426 **`refDate : int`** specifying the reference date in `YYYYMMDDHH[MMSS]` format. 427 428 **`shortName : str`** the GRIB2 `shortName`. This is the abbreviation name found in the NCEP GRIB2 tables. 429 430 **`threshold : str`** wgrib2-formatted probability threshold string. 431 """ 432 kwargs_allowed = ['duration','leadTime','level','percentile','refDate','shortName','threshold'] 433 idxs = {} 434 for k,v in kwargs.items(): 435 if k not in kwargs_allowed: continue 436 if k == 'duration': 437 idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['duration']])==v)[0] 438 elif k == 'leadTime': 439 idxs[k] = np.where(np.asarray([i if i is not None else None for i in self._index['leadTime']])==v)[0] 440 elif k == 'level': 441 idxs[k] = np.where(np.array(self._index['levelString'])==v)[0] 442 elif k == 'percentile': 443 tmp1 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==6)[0] 444 tmp2 = np.where(np.asarray(self._index["productDefinitionTemplateNumber"])==10)[0] 445 idxs[k] = [i for i in np.concatenate((tmp1,tmp2)) if self._index["productDefinitionTemplate"][i][15]==v] 446 del tmp1,tmp2 447 elif k == 'refDate': 448 idxs[k] = np.where(np.asarray(self._index['refDate'])==v)[0] 449 elif k == 'shortName': 450 idxs[k] = np.where(np.array(self._index['shortName'])==v)[0] 451 elif k == 'threshold': 452 idxs[k] = np.where(np.array(self._index['probString'])==v)[0] 453 idxsarr = np.concatenate(tuple(idxs.values())) 454 nidxs = len(idxs.keys()) 455 if nidxs == 1: 456 return [self[int(i)][0] for i in idxsarr] 457 elif nidxs > 1: 458 return [self[int(i)][0] for i in [ii[0] for ii in collections.Counter(idxsarr).most_common() if ii[1] == nidxs]]
Returns a list of grib2io.Grib2Message
instances based on the selection **kwargs
.
The following keywords are currently supported:
duration : int
specifiying the time duration (in unit of hours) of a GRIB2 Message that is
determined from a period of time.
leadTime : int
specifying ending lead time (in units of hours) of a GRIB2 Message.
level : str
wgrib2-formatted layer/level string.
percentile : int
specify the percentile value.
refDate : int
specifying the reference date in YYYYMMDDHH[MMSS]
format.
shortName : str
the GRIB2 shortName
. This is the abbreviation name found in the NCEP GRIB2 tables.
threshold : str
wgrib2-formatted probability threshold string.
461 def write(self, msg): 462 """ 463 Writes a packed GRIB2 message to file. 464 465 Parameters 466 ---------- 467 468 **`msg`**: instance of `Grib2Message`. 469 """ 470 if isinstance(msg,Grib2Message): 471 self._filehandle.write(msg._msg) 472 self.size = os.path.getsize(self.name) 473 self.messages += 1 474 self.current_message += 1 475 else: 476 raise TypeError("msg must be a Grib2Message object.")
479class Grib2Message: 480 def __init__(self, msg=None, source=None, num=-1, decode=True, discipline=None, idsect=None): 481 """ 482 Class Constructor. Instantiation of this class can handle a GRIB2 message from an existing 483 file or the creation of new GRIB2 message. To create a new GRIB2 message, provide the 484 appropriate values to the arguments `discipline` and `idsect`. When these 2 arguments 485 are not `None`, then a new GRIB2 message is created. NOTE: All other keyword arguments 486 are ignored when a new message is created. 487 488 ... 489 490 Parameters 491 ---------- 492 493 **`msg`**: Binary string representing the GRIB2 Message read from file. 494 495 **`source`**: Source of where where this GRIB2 message originated 496 from (i.e. the input file). This allow for interaction with the 497 instance of `grib2io.open`. Default is None. 498 499 **`num`**: integer GRIB2 Message number from `grib2io.open`. Default value is -1. 500 501 **`decode`**: If True [DEFAULT], decode GRIB2 section lists into metadata 502 instance variables. 503 504 **`discipline`**: integer GRIB2 Discipline [GRIB2 Table 0.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table0-0.shtml) 505 506 **`idsect`**: Sequence containing GRIB1 Identification Section values (Section 1). 507 508 | Index | Description | 509 | :---: | :--- | 510 | idsect[0] | Id of orginating centre - [ON388 - Table 0](https://www.nco.ncep.noaa.gov/pmb/docs/on388/table0.html)| 511 | idsect[1] | Id of orginating sub-centre - [ON388 - Table C](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablec.html)| 512 | idsect[2] | GRIB Master Tables Version Number - [Code Table 1.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-0.shtml)| 513 | idsect[3] | GRIB Local Tables Version Number - [Code Table 1.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-1.shtml)| 514 | idsect[4] | Significance of Reference Time - [Code Table 1.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-2.shtml)| 515 | idsect[5] | Reference Time - Year (4 digits)| 516 | idsect[6] | Reference Time - Month| 517 | idsect[7] | Reference Time - Day| 518 | idsect[8] | Reference Time - Hour| 519 | idsect[9] | Reference Time - Minute| 520 | idsect[10] | Reference Time - Second| 521 | idsect[11] | Production status of data - [Code Table 1.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-3.shtml)| 522 | idsect[12] | Type of processed data - [Code Table 1.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-4.shtml)| 523 """ 524 self._source = source 525 self._msgnum = num 526 self._decode = decode 527 self._pos = 0 528 self._datapos = 0 529 self._sections = [] 530 self.hasLocalUseSection = False 531 self.isNDFD = False 532 if discipline is not None and idsect is not None: 533 # New message 534 self._msg,self._pos = g2clib.grib2_create(np.array([discipline,GRIB2_EDITION_NUMBER],DEFAULT_NUMPY_INT), 535 np.array(idsect,DEFAULT_NUMPY_INT)) 536 self._sections += [0,1] 537 else: 538 # Existing message 539 self._msg = msg 540 #self.md5 = {} 541 if self._msg is not None and self._source is not None: self.unpack() 542 543 def __repr__(self): 544 """ 545 """ 546 strings = [] 547 for k,v in self.__dict__.items(): 548 if k.startswith('_'): continue 549 if isinstance(v,str): 550 strings.append('%s = \'%s\'\n'%(k,v)) 551 elif isinstance(v,int): 552 strings.append('%s = %d\n'%(k,v)) 553 elif isinstance(v,float): 554 strings.append('%s = %f\n'%(k,v)) 555 else: 556 strings.append('%s = %s\n'%(k,v)) 557 return ''.join(strings) 558 559 560 def unpack(self): 561 """ 562 Unpacks GRIB2 section data from the packed, binary message. 563 """ 564 # Section 0 - Indicator Section 565 self.indicatorSection = [] 566 self.indicatorSection.append(struct.unpack('>4s',self._msg[0:4])[0]) 567 self.indicatorSection.append(struct.unpack('>H',self._msg[4:6])[0]) 568 self.indicatorSection.append(self._msg[6]) 569 self.indicatorSection.append(self._msg[7]) 570 self.indicatorSection.append(struct.unpack('>Q',self._msg[8:16])[0]) 571 self._pos = 16 572 self._sections.append(0) 573 #self.md5[0] = _getmd5str(self.indicatorSection) 574 575 # Section 1 - Identification Section via g2clib.unpack1() 576 self.identificationSection,self._pos = g2clib.unpack1(self._msg,self._pos,np.empty) 577 self.identificationSection = self.identificationSection.tolist() 578 self._sections.append(1) 579 if self.identificationSection[0:2] == [8,65535]: self.isNDFD = True 580 581 # After Section 1, perform rest of GRIB2 Decoding inside while loop 582 # to account for sub-messages. 583 sectnum = 1 584 while True: 585 if self._msg[self._pos:self._pos+4].decode('ascii','ignore') == '7777': 586 break 587 588 # Read the length and section number. 589 sectlen = struct.unpack('>i',self._msg[self._pos:self._pos+4])[0] 590 prevsectnum = sectnum 591 sectnum = struct.unpack('>B',self._msg[self._pos+4:self._pos+5])[0] 592 593 # If the previous section number is > current section number, then 594 # we have encountered a submessage. 595 if prevsectnum > sectnum: break 596 597 # Handle submessage accordingly. 598 if isinstance(self._source,open): 599 if self._source._index['isSubmessage'][self._msgnum]: 600 if sectnum == self._source._index['submessageBeginSection'][self._msgnum]: 601 self._pos = self._source._index['submessageOffset'][self._msgnum] 602 603 # Section 2 - Local Use Section. 604 if sectnum == 2: 605 self._lus = self._msg[self._pos+5:self._pos+sectlen] 606 self._pos += sectlen 607 self.hasLocalUseSection = True 608 self._sections.append(2) 609 #self.md5[2] = _getmd5str(self.identificationSection) 610 611 # Section 3 - Grid Definition Section. 612 elif sectnum == 3: 613 _gds,_gdt,_deflist,self._pos = g2clib.unpack3(self._msg,self._pos,np.empty) 614 self.gridDefinitionSection = _gds.tolist() 615 self.gridDefinitionTemplateNumber = Grib2Metadata(int(_gds[4]),table='3.1') 616 self.gridDefinitionTemplate = _gdt.tolist() 617 self.defList = _deflist.tolist() 618 self._sections.append(3) 619 #self.md5[3] = _getmd5str([self.gridDefinitionTemplateNumber]+self.gridDefinitionTemplate) 620 621 # Section 4 - Product Definition Section. 622 elif sectnum == 4: 623 _pdt,_pdtn,_coordlst,self._pos = g2clib.unpack4(self._msg,self._pos,np.empty) 624 self.productDefinitionTemplate = _pdt.tolist() 625 self.productDefinitionTemplateNumber = Grib2Metadata(int(_pdtn),table='4.0') 626 self.coordinateList = _coordlst.tolist() 627 self._sections.append(4) 628 #self.md5[4] = _getmd5str([self.productDefinitionTemplateNumber]+self.productDefinitionTemplate) 629 630 # Section 5 - Data Representation Section. 631 elif sectnum == 5: 632 _drt,_drtn,_npts,self._pos = g2clib.unpack5(self._msg,self._pos,np.empty) 633 self.dataRepresentationTemplate = _drt.tolist() 634 self.dataRepresentationTemplateNumber = Grib2Metadata(int(_drtn),table='5.0') 635 self.numberOfDataPoints = _npts 636 self._sections.append(5) 637 #self.md5[5] = _getmd5str([self.dataRepresentationTemplateNumber]+self.dataRepresentationTemplate) 638 639 # Section 6 - Bitmap Section. 640 elif sectnum == 6: 641 _bmap,_bmapflag = g2clib.unpack6(self._msg,self.gridDefinitionSection[1],self._pos,np.empty) 642 self.bitMapFlag = _bmapflag 643 if self.bitMapFlag == 0: 644 self.bitMap = _bmap 645 elif self.bitMapFlag == 254: 646 # Value of 254 says to use a previous bitmap in the file. 647 self.bitMapFlag = 0 648 if isinstance(self._source,open): 649 self.bitMap = self._source._index['bitMap'][self._msgnum] 650 self._pos += sectlen # IMPORTANT: This is here because g2clib.unpack6() does not return updated position. 651 self._sections.append(6) 652 #self.md5[6] = None 653 654 # Section 7 - Data Section (data unpacked when data() method is invoked). 655 elif sectnum == 7: 656 self._datapos = self._pos 657 self._pos += sectlen # REMOVE THIS WHEN UNPACKING DATA IS IMPLEMENTED 658 self._sections.append(7) 659 #self.md5[7] = _getmd5str(self._msg[self._datapos:sectlen+1]) 660 661 else: 662 errmsg = 'Unknown section number = %i' % sectnum 663 raise ValueError(errmsg) 664 665 if self._decode: self.decode() 666 667 def decode(self): 668 """ 669 Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables. 670 """ 671 672 # Section 0 - Indictator Section 673 self.discipline = Grib2Metadata(self.indicatorSection[2],table='0.0') 674 675 # Section 1 - Indentification Section. 676 self.originatingCenter = Grib2Metadata(self.identificationSection[0],table='originating_centers') 677 self.originatingSubCenter = Grib2Metadata(self.identificationSection[1],table='originating_subcenters') 678 self.masterTableInfo = Grib2Metadata(self.identificationSection[2],table='1.0') 679 self.localTableInfo = Grib2Metadata(self.identificationSection[3],table='1.1') 680 self.significanceOfReferenceTime = Grib2Metadata(self.identificationSection[4],table='1.2') 681 self.year = self.identificationSection[5] 682 self.month = self.identificationSection[6] 683 self.day = self.identificationSection[7] 684 self.hour = self.identificationSection[8] 685 self.minute = self.identificationSection[9] 686 self.second = self.identificationSection[10] 687 self.refDate = (self.year*1000000)+(self.month*10000)+(self.day*100)+self.hour 688 self.dtReferenceDate = datetime.datetime(self.year,self.month,self.day, 689 hour=self.hour,minute=self.minute, 690 second=self.second) 691 self.productionStatus = Grib2Metadata(self.identificationSection[11],table='1.3') 692 self.typeOfData = Grib2Metadata(self.identificationSection[12],table='1.4') 693 694 # ---------------------------- 695 # Section 3 -- Grid Definition 696 # ---------------------------- 697 698 # Set shape of the Earth parameters 699 if self.gridDefinitionTemplateNumber.value in [50,51,52,1200]: 700 earthparams = None 701 else: 702 earthparams = tables.earth_params[str(self.gridDefinitionTemplate[0])] 703 if earthparams['shape'] == 'spherical': 704 if earthparams['radius'] is None: 705 self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1]) 706 self.earthMajorAxis = None 707 self.earthMinorAxis = None 708 else: 709 self.earthRadius = earthparams['radius'] 710 self.earthMajorAxis = None 711 self.earthMinorAxis = None 712 elif earthparams['shape'] == 'oblateSpheroid': 713 if earthparams['radius'] is None and earthparams['major_axis'] is None and earthparams['minor_axis'] is None: 714 self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1]) 715 self.earthMajorAxis = self.gridDefinitionTemplate[4]/(10.**self.gridDefinitionTemplate[3]) 716 self.earthMinorAxis = self.gridDefinitionTemplate[6]/(10.**self.gridDefinitionTemplate[5]) 717 else: 718 self.earthRadius = earthparams['radius'] 719 self.earthMajorAxis = earthparams['major_axis'] 720 self.earthMinorAxis = earthparams['minor_axis'] 721 722 reggrid = self.gridDefinitionSection[2] == 0 # self.gridDefinitionSection[2]=0 means regular 2-d grid 723 if reggrid and self.gridDefinitionTemplateNumber.value not in [50,51,52,53,100,120,1000,1200]: 724 self.nx = self.gridDefinitionTemplate[7] 725 self.ny = self.gridDefinitionTemplate[8] 726 if not reggrid and self.gridDefinitionTemplateNumber == 40: 727 # Reduced Gaussian Grid 728 self.ny = self.gridDefinitionTemplate[8] 729 if self.gridDefinitionTemplateNumber.value in [0,1,203,205,32768,32769]: 730 # Regular or Rotated Lat/Lon Grid 731 scalefact = float(self.gridDefinitionTemplate[9]) 732 if self.gridDefinitionTemplate[10] == 4294967295: 733 self.gridDefinitionTemplate[10] = -1 734 divisor = float(self.gridDefinitionTemplate[10]) 735 if scalefact == 0: scalefact = 1. 736 if divisor <= 0: divisor = 1.e6 737 self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor 738 self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor 739 self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor 740 self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor 741 self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor 742 self.gridlengthYDirection = scalefact*self.gridDefinitionTemplate[17]/divisor 743 if self.latitudeFirstGridpoint > self.latitudeLastGridpoint: 744 self.gridlengthYDirection = -self.gridlengthYDirection 745 if self.longitudeFirstGridpoint > self.longitudeLastGridpoint: 746 self.gridlengthXDirection = -self.gridlengthXDirection 747 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4] 748 if self.gridDefinitionTemplateNumber == 1: 749 self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor 750 self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor 751 self.anglePoleRotation = self.gridDefinitionTemplate[21] 752 elif self.gridDefinitionTemplateNumber == 10: 753 # Mercator 754 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 755 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 756 self.latitudeLastGridpoint = self.gridDefinitionTemplate[13]/1.e6 757 self.longitudeLastGridpoint = self.gridDefinitionTemplate[14]/1.e6 758 self.gridlengthXDirection = self.gridDefinitionTemplate[17]/1.e3 759 self.gridlengthYDirection= self.gridDefinitionTemplate[18]/1.e3 760 self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6 761 self.proj4_lon_0 = 0.5*(self.longitudeFirstGridpoint+self.longitudeLastGridpoint) 762 self.proj4_proj = 'merc' 763 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4] 764 elif self.gridDefinitionTemplateNumber == 20: 765 # Stereographic 766 projflag = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0] 767 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 768 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 769 self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6 770 if projflag == 0: 771 self.proj4_lat_0 = 90 772 elif projflag == 1: 773 self.proj4_lat_0 = -90 774 else: 775 raise ValueError('Invalid projection center flag = %s'%projflag) 776 self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6 777 self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000. 778 self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000. 779 self.proj4_proj = 'stere' 780 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4] 781 elif self.gridDefinitionTemplateNumber == 30: 782 # Lambert Conformal 783 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 784 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 785 self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000. 786 self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000. 787 self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6 788 self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6 789 self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6 790 self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6 791 self.proj4_proj = 'lcc' 792 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4] 793 elif self.gridDefinitionTemplateNumber == 31: 794 # Albers Equal Area 795 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 796 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 797 self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000. 798 self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000. 799 self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6 800 self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6 801 self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6 802 self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6 803 self.proj4_proj = 'aea' 804 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4] 805 elif self.gridDefinitionTemplateNumber == 40 or self.gridDefinitionTemplateNumber == 41: 806 # Gaussian Grid 807 scalefact = float(self.gridDefinitionTemplate[9]) 808 if self.gridDefinitionTemplate[10] == 4294967295: 809 self.gridDefinitionTemplate[10] = -1 810 divisor = float(self.gridDefinitionTemplate[10]) 811 if scalefact == 0: scalefact = 1. 812 if divisor <= 0: divisor = 1.e6 813 self.pointsBetweenPoleAndEquator = self.gridDefinitionTemplate[17] 814 self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor 815 self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor 816 self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor 817 self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor 818 if reggrid: 819 self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor 820 if self.longitudeFirstGridpoint > self.longitudeLastGridpoint: 821 self.gridlengthXDirection = -self.gridlengthXDirection 822 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4] 823 if self.gridDefinitionTemplateNumber == 41: 824 self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor 825 self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor 826 self.anglePoleRotation = self.gridDefinitionTemplate[21] 827 elif self.gridDefinitionTemplateNumber == 50: 828 # Spectral Coefficients 829 self.spectralFunctionParameters = (self.gridDefinitionTemplate[0],self.gridDefinitionTemplate[1],self.gridDefinitionTemplate[2]) 830 self.scanModeFlags = [None,None,None,None] 831 elif self.gridDefinitionTemplateNumber == 90: 832 # Near-sided Vertical Perspective Satellite Projection 833 self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6 834 self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6 835 self.proj4_h = self.earthMajorAxis * (self.gridDefinitionTemplate[18]/1.e6) 836 dx = self.gridDefinitionTemplate[12] 837 dy = self.gridDefinitionTemplate[13] 838 # if lat_0 is equator, it's a geostationary view. 839 if self.proj4_lat_0 == 0.: # if lat_0 is equator, it's a 840 self.proj4_proj = 'geos' 841 # general case of 'near-side perspective projection' (untested) 842 else: 843 self.proj4_proj = 'nsper' 844 msg = 'Only geostationary perspective is supported. Lat/Lon values returned by grid method may be incorrect.' 845 warnings.warn(msg) 846 # latitude of horizon on central meridian 847 lonmax = 90.-(180./np.pi)*np.arcsin(self.earthMajorAxis/self.proj4_h) 848 # longitude of horizon on equator 849 latmax = 90.-(180./np.pi)*np.arcsin(self.earthMinorAxis/self.proj4_h) 850 # truncate to nearest thousandth of a degree (to make sure 851 # they aren't slightly over the horizon) 852 latmax = int(1000*latmax)/1000. 853 lonmax = int(1000*lonmax)/1000. 854 # h is measured from surface of earth at equator. 855 self.proj4_h = self.proj4_h - self.earthMajorAxis 856 # width and height of visible projection 857 P = pyproj.Proj(proj=self.proj4_proj,\ 858 a=self.earthMajorAxis,b=self.earthMinorAxis,\ 859 lat_0=0,lon_0=0,h=self.proj4_h) 860 x1,y1 = P(0.,latmax) 861 x2,y2 = P(lonmax,0.) 862 width = 2*x2 863 height = 2*y1 864 self.gridlengthXDirection = width/dx 865 self.gridlengthYDirection = height/dy 866 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0:4] 867 elif self.gridDefinitionTemplateNumber == 110: 868 # Azimuthal Equidistant 869 self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6 870 self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6 871 self.gridlengthXDirection = self.gridDefinitionTemplate[12]/1000. 872 self.gridlengthYDirection = self.gridDefinitionTemplate[13]/1000. 873 self.proj4_proj = 'aeqd' 874 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4] 875 elif self.gridDefinitionTemplateNumber == 204: 876 # Curvilinear Orthogonal 877 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4] 878 else: 879 errmsg = 'Unsupported Grid Definition Template Number - 3.%i' % self.gridDefinitionTemplateNumber.value 880 raise ValueError(errmsg) 881 882 # ------------------------------- 883 # Section 4 -- Product Definition 884 # ------------------------------- 885 886 # Template 4.0 - NOTE: That is these attributes apply to other templates. 887 self.parameterCategory = self.productDefinitionTemplate[0] 888 self.parameterNumber = self.productDefinitionTemplate[1] 889 self.fullName,self.units,self.shortName = tables.get_varinfo_from_table(self.discipline.value, 890 self.parameterCategory, 891 self.parameterNumber) 892 self.typeOfGeneratingProcess = Grib2Metadata(self.productDefinitionTemplate[2],table='4.3') 893 self.backgroundGeneratingProcessIdentifier = self.productDefinitionTemplate[3] 894 self.generatingProcess = Grib2Metadata(self.productDefinitionTemplate[4],table='generating_process') 895 self.unitOfTimeRange = Grib2Metadata(self.productDefinitionTemplate[7],table='4.4') 896 self.leadTime = self.productDefinitionTemplate[8] 897 self.typeOfFirstFixedSurface = Grib2Metadata(self.productDefinitionTemplate[9],table='4.5') 898 self.scaleFactorOfFirstFixedSurface = self.productDefinitionTemplate[10] 899 self.unitOfFirstFixedSurface = self.typeOfFirstFixedSurface.definition[1] 900 self.scaledValueOfFirstFixedSurface = self.productDefinitionTemplate[11] 901 self.valueOfFirstFixedSurface = self.scaledValueOfFirstFixedSurface/(10.**self.scaleFactorOfFirstFixedSurface) 902 temp = tables.get_value_from_table(self.productDefinitionTemplate[12],'4.5') 903 if temp[0] == 'Missing' and temp[1] == 'unknown': 904 self.typeOfSecondFixedSurface = None 905 self.scaleFactorOfSecondFixedSurface = None 906 self.unitOfSecondFixedSurface = None 907 self.valueOfSecondFixedSurface = None 908 else: 909 self.typeOfSecondFixedSurface = Grib2Metadata(self.productDefinitionTemplate[12],table='4.5') 910 self.scaleFactorOfSecondFixedSurface = self.productDefinitionTemplate[13] 911 self.unitOfSecondFixedSurface = self.typeOfSecondFixedSurface.definition[1] 912 self.scaledValueOfSecondFixedSurface = self.productDefinitionTemplate[14] 913 self.valueOfSecondFixedSurface = self.scaledValueOfSecondFixedSurface/(10.**self.scaleFactorOfSecondFixedSurface) 914 self.level = tables.get_wgrib2_level_string(*self.productDefinitionTemplate[9:15]) 915 916 # Template 4.1 - 917 if self.productDefinitionTemplateNumber == 1: 918 self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6') 919 self.perturbationNumber = self.productDefinitionTemplate[16] 920 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17] 921 922 # Template 4.2 - 923 elif self.productDefinitionTemplateNumber == 2: 924 self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7') 925 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16] 926 927 # Template 4.5 - 928 elif self.productDefinitionTemplateNumber == 5: 929 self.forecastProbabilityNumber = self.productDefinitionTemplate[15] 930 self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16] 931 self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9') 932 self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18] 933 self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19] 934 self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20] 935 self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21] 936 self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \ 937 self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18]) 938 self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \ 939 self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20]) 940 self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22]) 941 942 # Template 4.6 - 943 elif self.productDefinitionTemplateNumber == 6: 944 self.percentileValue = self.productDefinitionTemplate[15] 945 946 # Template 4.8 - 947 elif self.productDefinitionTemplateNumber == 8: 948 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[15] 949 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[16] 950 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[17] 951 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[18] 952 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[19] 953 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[20] 954 self.numberOfTimeRanges = self.productDefinitionTemplate[21] 955 self.numberOfMissingValues = self.productDefinitionTemplate[22] 956 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[23],table='4.10') 957 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.11') 958 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.4') 959 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[26] 960 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4') 961 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[28] 962 963 # Template 4.9 - 964 elif self.productDefinitionTemplateNumber == 9: 965 self.forecastProbabilityNumber = self.productDefinitionTemplate[15] 966 self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16] 967 self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9') 968 self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18] 969 self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19] 970 self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20] 971 self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21] 972 self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \ 973 self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18]) 974 self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \ 975 self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20]) 976 self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22]) 977 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[22] 978 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[23] 979 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[24] 980 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[25] 981 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[26] 982 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[27] 983 self.numberOfTimeRanges = self.productDefinitionTemplate[28] 984 self.numberOfMissingValues = self.productDefinitionTemplate[29] 985 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[30],table='4.10') 986 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[31],table='4.11') 987 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[32],table='4.4') 988 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[33] 989 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[34],table='4.4') 990 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[35] 991 992 # Template 4.10 - 993 elif self.productDefinitionTemplateNumber == 10: 994 self.percentileValue = self.productDefinitionTemplate[15] 995 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[16] 996 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[17] 997 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[18] 998 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[19] 999 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[20] 1000 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[21] 1001 self.numberOfTimeRanges = self.productDefinitionTemplate[22] 1002 self.numberOfMissingValues = self.productDefinitionTemplate[23] 1003 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.10') 1004 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.11') 1005 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.4') 1006 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[27] 1007 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4') 1008 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[29] 1009 1010 # Template 4.11 - 1011 elif self.productDefinitionTemplateNumber == 11: 1012 self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6') 1013 self.perturbationNumber = self.productDefinitionTemplate[16] 1014 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17] 1015 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[18] 1016 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[19] 1017 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[20] 1018 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[21] 1019 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[22] 1020 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[23] 1021 self.numberOfTimeRanges = self.productDefinitionTemplate[24] 1022 self.numberOfMissingValues = self.productDefinitionTemplate[25] 1023 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.10') 1024 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.11') 1025 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4') 1026 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[29] 1027 self.unitOfTimeRangeOfSuccessiveFields = tables.get_value_from_table(self.productDefinitionTemplate[30],table='4.4') 1028 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[31] 1029 1030 # Template 4.12 - 1031 elif self.productDefinitionTemplateNumber == 12: 1032 self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7') 1033 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16] 1034 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[17] 1035 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[18] 1036 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[19] 1037 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[20] 1038 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[21] 1039 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[22] 1040 self.numberOfTimeRanges = self.productDefinitionTemplate[23] 1041 self.numberOfMissingValues = self.productDefinitionTemplate[24] 1042 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.10') 1043 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.11') 1044 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4') 1045 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[28] 1046 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[29],table='4.4') 1047 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[30] 1048 1049 # Template 4.15 - 1050 elif self.productDefinitionTemplateNumber == 15: 1051 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[15],table='4.10') 1052 self.typeOfSpatialProcessing = Grib2Metadata(self.productDefinitionTemplate[16],table='4.15') 1053 self.numberOfDataPointsForSpatialProcessing = self.productDefinitionTemplate[17] 1054 1055 else: 1056 if self.productDefinitionTemplateNumber != 0: 1057 errmsg = 'Unsupported Product Definition Template Number - 4.%i' % self.productDefinitionTemplateNumber.value 1058 raise ValueError(errmsg) 1059 1060 1061 self.leadTime = utils.getleadtime(self.identificationSection, 1062 self.productDefinitionTemplateNumber.value, 1063 self.productDefinitionTemplate) 1064 1065 if self.productDefinitionTemplateNumber.value in [8,9,10,11,12]: 1066 self.dtEndOfTimePeriod = datetime.datetime(self.yearOfEndOfTimePeriod,self.monthOfEndOfTimePeriod, 1067 self.dayOfEndOfTimePeriod,hour=self.hourOfEndOfTimePeriod, 1068 minute=self.minuteOfEndOfTimePeriod, 1069 second=self.secondOfEndOfTimePeriod) 1070 1071 # -------------------------------- 1072 # Section 5 -- Data Representation 1073 # -------------------------------- 1074 1075 # Template 5.0 - Simple Packing 1076 if self.dataRepresentationTemplateNumber == 0: 1077 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1078 self.binScaleFactor = self.dataRepresentationTemplate[1] 1079 self.decScaleFactor = self.dataRepresentationTemplate[2] 1080 self.nBitsPacking = self.dataRepresentationTemplate[3] 1081 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[3],table='5.1') 1082 1083 # Template 5.2 - Complex Packing 1084 elif self.dataRepresentationTemplateNumber == 2: 1085 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1086 self.binScaleFactor = self.dataRepresentationTemplate[1] 1087 self.decScaleFactor = self.dataRepresentationTemplate[2] 1088 self.nBitsPacking = self.dataRepresentationTemplate[3] 1089 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1090 self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4') 1091 self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5') 1092 self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None 1093 self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None 1094 self.nGroups = self.dataRepresentationTemplate[9] 1095 self.refGroupWidth = self.dataRepresentationTemplate[10] 1096 self.nBitsGroupWidth = self.dataRepresentationTemplate[11] 1097 self.refGroupLength = self.dataRepresentationTemplate[12] 1098 self.groupLengthIncrement = self.dataRepresentationTemplate[13] 1099 self.lengthOfLastGroup = self.dataRepresentationTemplate[14] 1100 self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15] 1101 1102 # Template 5.3 - Complex Packing and Spatial Differencing 1103 elif self.dataRepresentationTemplateNumber == 3: 1104 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1105 self.binScaleFactor = self.dataRepresentationTemplate[1] 1106 self.decScaleFactor = self.dataRepresentationTemplate[2] 1107 self.nBitsPacking = self.dataRepresentationTemplate[3] 1108 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1109 self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4') 1110 self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5') 1111 self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None 1112 self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None 1113 self.nGroups = self.dataRepresentationTemplate[9] 1114 self.refGroupWidth = self.dataRepresentationTemplate[10] 1115 self.nBitsGroupWidth = self.dataRepresentationTemplate[11] 1116 self.refGroupLength = self.dataRepresentationTemplate[12] 1117 self.groupLengthIncrement = self.dataRepresentationTemplate[13] 1118 self.lengthOfLastGroup = self.dataRepresentationTemplate[14] 1119 self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15] 1120 self.spatialDifferenceOrder = Grib2Metadata(self.dataRepresentationTemplate[16],table='5.6') 1121 self.nBytesSpatialDifference = self.dataRepresentationTemplate[17] 1122 1123 # Template 5.4 - IEEE Floating Point Data 1124 elif self.dataRepresentationTemplateNumber == 4: 1125 self.precision = Grib2Metadata(self.dataRepresentationTemplate[0],table='5.7') 1126 1127 # Template 5.40 - JPEG2000 Compression 1128 elif self.dataRepresentationTemplateNumber == 40: 1129 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1130 self.binScaleFactor = self.dataRepresentationTemplate[1] 1131 self.decScaleFactor = self.dataRepresentationTemplate[2] 1132 self.nBitsPacking = self.dataRepresentationTemplate[3] 1133 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1134 self.typeOfCompression = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.40') 1135 self.targetCompressionRatio = self.dataRepresentationTemplate[6] 1136 1137 # Template 5.41 - PNG Compression 1138 elif self.dataRepresentationTemplateNumber == 41: 1139 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1140 self.binScaleFactor = self.dataRepresentationTemplate[1] 1141 self.decScaleFactor = self.dataRepresentationTemplate[2] 1142 self.nBitsPacking = self.dataRepresentationTemplate[3] 1143 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1144 1145 else: 1146 errmsg = 'Unsupported Data Representation Definition Template Number - 5.%i' % self.dataRepresentationTemplateNumber.value 1147 raise ValueError(errmsg) 1148 1149 1150 def data(self, fill_value=DEFAULT_FILL_VALUE, masked_array=True, expand=True, order=None, 1151 map_keys=False): 1152 """ 1153 Returns an unpacked data grid. 1154 1155 Parameters 1156 ---------- 1157 1158 **`fill_value`**: Missing or masked data is filled with this value or default value given by 1159 `DEFAULT_FILL_VALUE` 1160 1161 **`masked_array`**: If `True` [DEFAULT], return masked array if there is bitmap for missing 1162 or masked data. 1163 1164 **`expand`**: If `True` [DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids. 1165 1166 **`order`**: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing 1167 or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids. 1168 1169 **`map_keys`**: If `True`, data values will be mapped to the string-based keys that are stored 1170 in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the 1171 units (i.e. "See Table 4.xxx"). 1172 1173 Returns 1174 ------- 1175 1176 **`numpy.ndarray`**: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32, 1177 but could be np.int32 if Grib2Message.typeOfValues is integer. The array dtype will be 1178 string-based if map_keys=True. 1179 """ 1180 if not hasattr(self,'scanModeFlags'): 1181 raise ValueError('Unsupported grid definition template number %s'%self.gridDefinitionTemplateNumber) 1182 else: 1183 if self.scanModeFlags[2]: 1184 storageorder='F' 1185 else: 1186 storageorder='C' 1187 if order is None: 1188 if (self.dataRepresentationTemplateNumber in [2,3] and 1189 self.dataRepresentationTemplate[6] != 0) or self.bitMapFlag == 0: 1190 order = 0 1191 else: 1192 order = 1 1193 drtnum = self.dataRepresentationTemplateNumber.value 1194 drtmpl = np.asarray(self.dataRepresentationTemplate,dtype=DEFAULT_NUMPY_INT) 1195 gdtnum = self.gridDefinitionTemplateNumber.value 1196 gdtmpl = np.asarray(self.gridDefinitionTemplate,dtype=DEFAULT_NUMPY_INT) 1197 ndpts = self.numberOfDataPoints 1198 gds = self.gridDefinitionSection 1199 ngrdpts = gds[1] 1200 ipos = self._datapos 1201 fld1 = g2clib.unpack7(self._msg,gdtnum,gdtmpl,drtnum,drtmpl,ndpts,ipos,np.empty,storageorder=storageorder) 1202 # Apply bitmap. 1203 if self.bitMapFlag == 0: 1204 fld = fill_value*np.ones(ngrdpts,'f') 1205 np.put(fld,np.nonzero(self.bitMap),fld1) 1206 if masked_array: 1207 fld = ma.masked_values(fld,fill_value) 1208 # Missing values instead of bitmap 1209 elif masked_array and hasattr(self,'priMissingValue'): 1210 if hasattr(self,'secMissingValue'): 1211 mask = np.logical_or(fld1==self.priMissingValue,fld1==self.secMissingValue) 1212 else: 1213 mask = fld1 == self.priMissingValue 1214 fld = ma.array(fld1,mask=mask) 1215 else: 1216 fld = fld1 1217 if self.nx is not None and self.ny is not None: # Rectangular grid. 1218 if ma.isMA(fld): 1219 fld = ma.reshape(fld,(self.ny,self.nx)) 1220 else: 1221 fld = np.reshape(fld,(self.ny,self.nx)) 1222 else: 1223 if gds[2] and gdtnum == 40: # Reduced global Gaussian grid. 1224 if expand: 1225 from . import redtoreg 1226 self.nx = 2*self.ny 1227 lonsperlat = self.defList 1228 if ma.isMA(fld): 1229 fld = ma.filled(fld) 1230 fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long), 1231 fld.astype(np.double),fill_value) 1232 fld = ma.masked_values(fld,fill_value) 1233 else: 1234 fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long), 1235 fld.astype(np.double),fill_value) 1236 # Check scan modes for rect grids. 1237 if self.nx is not None and self.ny is not None: 1238 if self.scanModeFlags[3]: 1239 fldsave = fld.astype('f') # casting makes a copy 1240 fld[1::2,:] = fldsave[1::2,::-1] 1241 1242 # Set data to integer according to GRIB metadata 1243 if self.typeOfValues == "Integer": fld = fld.astype(np.int32) 1244 1245 # Map the data values to their respective definitions. 1246 if map_keys: 1247 fld = fld.astype(np.int32).astype(str) 1248 if self.identificationSection[0] == 7 and \ 1249 self.identificationSection[1] == 14 and \ 1250 self.shortName == 'PWTHER': 1251 # MDL Predominant Weather Grid 1252 #keys = utils.decode_mdl_wx_strings(self._lus) 1253 keys = utils.decode_ndfd_wx_strings(self._lus) 1254 for n,k in enumerate(keys): 1255 fld = np.where(fld==str(n+1),k,fld) 1256 elif self.identificationSection[0] == 8 and \ 1257 self.identificationSection[1] == 65535 and \ 1258 self.shortName == 'CRAIN': 1259 # NDFD Predominant Weather Grid 1260 keys = utils.decode_ndfd_wx_strings(self._lus) 1261 for n,k in enumerate(keys): 1262 fld = np.where(fld==str(n+1),k,fld) 1263 else: 1264 # For data whose units are defined in a code table 1265 tbl = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0] 1266 for k,v in tables.get_table(tbl).items(): 1267 fld = np.where(fld==k,v,fld) 1268 return fld 1269 1270 1271 def latlons(self): 1272 """Alias for `grib2io.Grib2Message.grid` method""" 1273 return self.grid() 1274 1275 1276 def grid(self): 1277 """ 1278 Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon, 1279 global Gaussian, mercator, stereographic, lambert conformal, albers equal-area, 1280 space-view and azimuthal equidistant grids. 1281 1282 Returns 1283 ------- 1284 1285 **`lats, lons : numpy.ndarray`** 1286 1287 Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and 1288 longitudes in units of degrees. 1289 """ 1290 gdtnum = self.gridDefinitionTemplateNumber 1291 gdtmpl = self.gridDefinitionTemplate 1292 reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid 1293 self.projparams = {} 1294 if self.earthMajorAxis is not None: self.projparams['a']=self.earthMajorAxis 1295 if self.earthMajorAxis is not None: self.projparams['b']=self.earthMinorAxis 1296 if gdtnum == 0: 1297 # Regular lat/lon grid 1298 lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint 1299 lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint 1300 dlon = self.gridlengthXDirection 1301 dlat = self.gridlengthYDirection 1302 lats = np.arange(lat1,lat2+dlat,dlat) 1303 lons = np.arange(lon1,lon2+dlon,dlon) 1304 # flip if scan mode says to. 1305 #if self.scanModeFlags[0]: 1306 # lons = lons[::-1] 1307 #if not self.scanModeFlags[1]: 1308 # lats = lats[::-1] 1309 self.projparams['proj'] = 'cyl' 1310 lons,lats = np.meshgrid(lons,lats) # make 2-d arrays. 1311 elif gdtnum == 40: # Gaussian grid (only works for global!) 1312 from utils.gauss_grids import gaussian_latitudes 1313 lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint 1314 lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint 1315 nlats = self.ny 1316 if not reggrid: # Reduced Gaussian grid. 1317 nlons = 2*nlats 1318 dlon = 360./nlons 1319 else: 1320 nlons = self.nx 1321 dlon = self.gridlengthXDirection 1322 lons = np.arange(lon1,lon2+dlon,dlon) 1323 # Compute Gaussian lats (north to south) 1324 lats = gaussian_latitudes(nlats) 1325 if lat1 < lat2: # reverse them if necessary 1326 lats = lats[::-1] 1327 # flip if scan mode says to. 1328 #if self.scanModeFlags[0]: 1329 # lons = lons[::-1] 1330 #if not self.scanModeFlags[1]: 1331 # lats = lats[::-1] 1332 self.projparams['proj'] = 'cyl' 1333 lons,lats = np.meshgrid(lons,lats) # make 2-d arrays 1334 elif gdtnum in [10,20,30,31,110]: 1335 # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant 1336 dx,dy = self.gridlengthXDirection, self.gridlengthYDirection 1337 lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint 1338 if gdtnum == 10: # Mercator. 1339 self.projparams['lat_ts']=self.proj4_lat_ts 1340 self.projparams['proj']=self.proj4_proj 1341 self.projparams['lon_0']=self.proj4_lon_0 1342 pj = pyproj.Proj(self.projparams) 1343 llcrnrx, llcrnry = pj(lon1,lat1) 1344 x = llcrnrx+dx*np.arange(self.nx) 1345 y = llcrnry+dy*np.arange(self.ny) 1346 x,y = np.meshgrid(x, y) 1347 lons,lats = pj(x, y, inverse=True) 1348 elif gdtnum == 20: # Stereographic 1349 self.projparams['lat_ts']=self.proj4_lat_ts 1350 self.projparams['proj']=self.proj4_proj 1351 self.projparams['lat_0']=self.proj4_lat_0 1352 self.projparams['lon_0']=self.proj4_lon_0 1353 pj = pyproj.Proj(self.projparams) 1354 llcrnrx, llcrnry = pj(lon1,lat1) 1355 x = llcrnrx+dx*np.arange(self.nx) 1356 y = llcrnry+dy*np.arange(self.ny) 1357 x,y = np.meshgrid(x, y) 1358 lons,lats = pj(x, y, inverse=True) 1359 elif gdtnum in [30,31]: # Lambert, Albers 1360 self.projparams['lat_1']=self.proj4_lat_1 1361 self.projparams['lat_2']=self.proj4_lat_2 1362 self.projparams['proj']=self.proj4_proj 1363 self.projparams['lon_0']=self.proj4_lon_0 1364 pj = pyproj.Proj(self.projparams) 1365 llcrnrx, llcrnry = pj(lon1,lat1) 1366 x = llcrnrx+dx*np.arange(self.nx) 1367 y = llcrnry+dy*np.arange(self.ny) 1368 x,y = np.meshgrid(x, y) 1369 lons,lats = pj(x, y, inverse=True) 1370 elif gdtnum == 110: # Azimuthal Equidistant 1371 self.projparams['proj']=self.proj4_proj 1372 self.projparams['lat_0']=self.proj4_lat_0 1373 self.projparams['lon_0']=self.proj4_lon_0 1374 pj = pyproj.Proj(self.projparams) 1375 llcrnrx, llcrnry = pj(lon1,lat1) 1376 x = llcrnrx+dx*np.arange(self.nx) 1377 y = llcrnry+dy*np.arange(self.ny) 1378 x,y = np.meshgrid(x, y) 1379 lons,lats = pj(x, y, inverse=True) 1380 elif gdtnum == 90: 1381 # Satellite Projection 1382 dx = self.gridlengthXDirection 1383 dy = self.gridlengthYDirection 1384 self.projparams['proj']=self.proj4_proj 1385 self.projparams['lon_0']=self.proj4_lon_0 1386 self.projparams['lat_0']=self.proj4_lat_0 1387 self.projparams['h']=self.proj4_h 1388 pj = pyproj.Proj(self.projparams) 1389 x = dx*np.indices((self.ny,self.nx),'f')[1,:,:] 1390 x -= 0.5*x.max() 1391 y = dy*np.indices((self.ny,self.nx),'f')[0,:,:] 1392 y -= 0.5*y.max() 1393 lons,lats = pj(x,y,inverse=True) 1394 # Set lons,lats to 1.e30 where undefined 1395 abslons = np.fabs(lons) 1396 abslats = np.fabs(lats) 1397 lons = np.where(abslons < 1.e20, lons, 1.e30) 1398 lats = np.where(abslats < 1.e20, lats, 1.e30) 1399 else: 1400 raise ValueError('Unsupported grid') 1401 1402 return lats.astype('f'), lons.astype('f') 1403 1404 1405 def addlocal(self, ludata): 1406 """ 1407 Add a Local Use Section [(Section 2)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect2.shtml) 1408 to the GRIB2 message. 1409 1410 Parameters 1411 ---------- 1412 1413 **`ludata : bytes`**: Local Use data. 1414 """ 1415 assert isinstance(ludata,bytes) 1416 self._msg,self._pos = g2clib.grib2_addlocal(self._msg,ludata) 1417 self.hasLocalUseSection = True 1418 self._sections.append(2) 1419 1420 1421 def addgrid(self, gdsinfo, gdtmpl, deflist=None): 1422 """ 1423 Add a Grid Definition Section [(Section 3)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_doc/grib2_sect3.shtml) 1424 to the GRIB2 message. 1425 1426 Parameters 1427 ---------- 1428 1429 **`gdsinfo`**: Sequence containing information needed for the grid definition section. 1430 1431 | Index | Description | 1432 | :---: | :--- | 1433 | gdsinfo[0] | Source of grid definition - [Code Table 3.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-0.shtml)| 1434 | gdsinfo[1] | Number of data points| 1435 | gdsinfo[2] | Number of octets for optional list of numbers defining number of points| 1436 | gdsinfo[3] | Interpetation of list of numbers defining number of points - [Code Table 3.11](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-11.shtml)| 1437 | gdsinfo[4] | Grid Definition Template Number - [Code Table 3.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml)| 1438 1439 **`gdtmpl`**: Sequence of values for the specified Grid Definition Template. Each 1440 element of this integer array contains an entry (in the order specified) of Grid 1441 Definition Template 3.NN 1442 1443 **`deflist`**: Sequence containing the number of grid points contained in each 1444 row (or column) of a non-regular grid. Used if gdsinfo[2] != 0. 1445 """ 1446 if 3 in self._sections: 1447 raise ValueError('GRIB2 Message already contains Grid Definition Section.') 1448 if deflist is not None: 1449 _deflist = np.array(deflist,dtype=DEFAULT_NUMPY_INT) 1450 else: 1451 _deflist = None 1452 gdtnum = gdsinfo[4] 1453 if gdtnum in [0,1,2,3,40,41,42,43,44,203,205,32768,32769]: 1454 self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4] 1455 elif gdtnum == 10: # mercator 1456 self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4] 1457 elif gdtnum == 20: # stereographic 1458 self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4] 1459 elif gdtnum == 30: # lambert conformal 1460 self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4] 1461 elif gdtnum == 31: # albers equal area. 1462 self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4] 1463 elif gdtnum == 90: # near-sided vertical perspective satellite projection 1464 self.scanModeFlags = utils.int2bin(gdtmpl[16],output=list)[0:4] 1465 elif gdtnum == 110: # azimuthal equidistant. 1466 self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4] 1467 elif gdtnum == 120: 1468 self.scanModeFlags = utils.int2bin(gdtmpl[6],output=list)[0:4] 1469 elif gdtnum == 204: # curvilinear orthogonal 1470 self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4] 1471 elif gdtnum in [1000,1100]: 1472 self.scanModeFlags = utils.int2bin(gdtmpl[12],output=list)[0:4] 1473 self._msg,self._pos = g2clib.grib2_addgrid(self._msg, 1474 np.array(gdsinfo,dtype=DEFAULT_NUMPY_INT), 1475 np.array(gdtmpl,dtype=DEFAULT_NUMPY_INT), 1476 _deflist) 1477 self._sections.append(3) 1478 1479 1480 def addfield(self, field, pdtnum, pdtmpl, coordlist=None, packing="complex-spdiff", **packing_opts): 1481 """ 1482 Add a Product Definition, Data Representation, Bitmap, and Data Sections 1483 to `Grib2Message` instance (i.e. Sections 4-7). Must be called after the grid 1484 definition section has been added (`addfield`). 1485 1486 Parameters 1487 ---------- 1488 1489 **`field`**: Numpy array of data values to pack. If field is a masked array, then 1490 a bitmap is created from the mask. 1491 1492 **`pdtnum`**: integer Product Definition Template Number - [Code Table 4.0](http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-0.shtml) 1493 1494 **`pdtmpl`**: Sequence with the data values for the specified Product Definition 1495 Template (N=pdtnum). Each element of this integer array contains an entry (in 1496 the order specified) of Product Definition Template 4.N. 1497 1498 **`coordlist`**: Sequence containing floating point values intended to document the 1499 vertical discretization with model data on hybrid coordinate vertical levels. Default is `None`. 1500 1501 **`packing`**: String to specify the type of packing. Valid options are the following: 1502 1503 | Packing Scheme | Description | 1504 | :---: | :---: | 1505 | 'simple' | [Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-0.shtml) | 1506 | 'complex' | [Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-2.shtml) | 1507 | 'complex-spdiff' | [Complex packing with Spatial Differencing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-3.shtml) | 1508 | 'jpeg' | [JPEG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-40.shtml) | 1509 | 'png' | [PNG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-41.shtml) | 1510 | 'spectral-simple'| [Spectral Data - Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-50.shtml) | 1511 | 'spectral-complex'| [Spectral Data - Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-51.shtml) | 1512 1513 **`**packing_opts`**: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for 1514 the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following: 1515 1516 | Packing Scheme | Keyword Arguments | 1517 | :---: | :---: | 1518 | 'simple' | `binScaleFactor`, `decScaleFactor` | 1519 | 'complex' | `binScaleFactor`, `decScaleFactor`, `priMissingValue`, [`secMissingValue`] | 1520 | 'complex-spdiff' | `binScaleFactor`, `decScaleFactor`, `spatialDifferenceOrder`, `priMissingValue`, [`secMissingValue`] | 1521 | 'jpeg' | `binScaleFactor`, `decScaleFactor` | 1522 | 'png' | `binScaleFactor`, `decScaleFactor` | 1523 | 'spectral-simple' | `binScaleFactor`, `decScaleFactor` | 1524 | 'spectral-complex' | `binScaleFactor`, `decScaleFactor` | 1525 """ 1526 if self._sections[-1] != 3: 1527 raise ValueError('addgrid() must be called before addfield()') 1528 if self.scanModeFlags is not None: 1529 if self.scanModeFlags[3]: 1530 fieldsave = field.astype('f') # Casting makes a copy 1531 field[1::2,:] = fieldsave[1::2,::-1] 1532 fld = field.astype('f') 1533 if ma.isMA(field) and ma.count_masked(field) > 0: 1534 bitmapflag = 0 1535 bmap = 1-np.ravel(field.mask.astype(DEFAULT_NUMPY_INT)) 1536 else: 1537 bitmapflag = 255 1538 bmap = None 1539 if coordlist is not None: 1540 crdlist = np.array(coordlist,'f') 1541 else: 1542 crdlist = None 1543 1544 # Set data representation template number and template values 1545 drtnum = -1 1546 drtmpl = np.zeros((DEFAULT_DRT_LEN),dtype=DEFAULT_NUMPY_INT) 1547 if packing == "simple": 1548 drtnum = 0 1549 drtmpl[1] = packing_opts["binScaleFactor"] 1550 drtmpl[2] = packing_opts["decScaleFactor"] 1551 elif packing == "complex" or packing == "complex-spdiff": 1552 if packing == "complex": 1553 drtnum = 2 1554 if packing == "complex-spdiff": 1555 drtnum = 3 1556 drtmpl[16] = packing_opts['spatialDifferenceOrder'] 1557 drtmpl[1] = packing_opts["binScaleFactor"] 1558 drtmpl[2] = packing_opts["decScaleFactor"] 1559 if set(("priMissingValue","secMissingValue")).issubset(packing_opts): 1560 drtmpl[6] = 2 1561 drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"]) 1562 drtmpl[8] = utils.putieeeint(packing_opts["secMissingValue"]) 1563 else: 1564 if "priMissingValue" in packing_opts.keys(): 1565 drtmpl[6] = 1 1566 drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"]) 1567 else: 1568 drtmpl[6] = 0 1569 elif packing == "jpeg": 1570 drtnum = 40 1571 drtmpl[1] = packing_opts["binScaleFactor"] 1572 drtmpl[2] = packing_opts["decScaleFactor"] 1573 elif packing == "png": 1574 drtnum = 41 1575 drtmpl[1] = packing_opts["binScaleFactor"] 1576 drtmpl[2] = packing_opts["decScaleFactor"] 1577 elif packing == "spectral-simple": 1578 drtnum = 50 1579 drtmpl[1] = packing_opts["binScaleFactor"] 1580 drtmpl[2] = packing_opts["decScaleFactor"] 1581 elif packing == "spectral-complex": 1582 drtnum = 51 1583 drtmpl[1] = packing_opts["binScaleFactor"] 1584 drtmpl[2] = packing_opts["decScaleFactor"] 1585 1586 pdtnum = pdtnum.value if isinstance(pdtnum,Grib2Metadata) else pdtnum 1587 1588 self._msg,self._pos = g2clib.grib2_addfield(self._msg, 1589 pdtnum, 1590 np.array(pdtmpl,dtype=DEFAULT_NUMPY_INT), 1591 crdlist, 1592 drtnum, 1593 drtmpl, 1594 np.ravel(fld), 1595 bitmapflag, 1596 bmap) 1597 self._sections.append(4) 1598 self._sections.append(5) 1599 if bmap is not None: self._sections.append(6) 1600 self._sections.append(7) 1601 1602 1603 def end(self): 1604 """ 1605 Add End Section (Section 8) to the GRIB2 message. A GRIB2 message 1606 is not complete without an end section. Once an end section is added, 1607 the GRIB2 message can be written to file. 1608 """ 1609 self._msg, self._pos = g2clib.grib2_end(self._msg) 1610 self._sections.append(8) 1611 1612 def to_bytes(self, validate=True): 1613 """ 1614 Return grib data in byte format. Useful for exporting data in non-file formats. 1615 For example, can be used to output grib data directly to S3 using the boto3 client 1616 without the need to write a temporary file to upload first. 1617 1618 Parameters 1619 ---------- 1620 **`validate`**: bool (Default: True) If true, validates first/last four bytes for proper formatting, else 1621 returns None. If False, message is output as is. 1622 1623 Returns 1624 ------- 1625 Returns GRIB2 formatted message as bytes. 1626 """ 1627 if validate: 1628 if str(self._msg[0:4] + self._msg[-4:], 'utf-8') == 'GRIB7777': 1629 return self._msg 1630 else: 1631 return None 1632 else: 1633 return self._msg
480 def __init__(self, msg=None, source=None, num=-1, decode=True, discipline=None, idsect=None): 481 """ 482 Class Constructor. Instantiation of this class can handle a GRIB2 message from an existing 483 file or the creation of new GRIB2 message. To create a new GRIB2 message, provide the 484 appropriate values to the arguments `discipline` and `idsect`. When these 2 arguments 485 are not `None`, then a new GRIB2 message is created. NOTE: All other keyword arguments 486 are ignored when a new message is created. 487 488 ... 489 490 Parameters 491 ---------- 492 493 **`msg`**: Binary string representing the GRIB2 Message read from file. 494 495 **`source`**: Source of where where this GRIB2 message originated 496 from (i.e. the input file). This allow for interaction with the 497 instance of `grib2io.open`. Default is None. 498 499 **`num`**: integer GRIB2 Message number from `grib2io.open`. Default value is -1. 500 501 **`decode`**: If True [DEFAULT], decode GRIB2 section lists into metadata 502 instance variables. 503 504 **`discipline`**: integer GRIB2 Discipline [GRIB2 Table 0.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table0-0.shtml) 505 506 **`idsect`**: Sequence containing GRIB1 Identification Section values (Section 1). 507 508 | Index | Description | 509 | :---: | :--- | 510 | idsect[0] | Id of orginating centre - [ON388 - Table 0](https://www.nco.ncep.noaa.gov/pmb/docs/on388/table0.html)| 511 | idsect[1] | Id of orginating sub-centre - [ON388 - Table C](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablec.html)| 512 | idsect[2] | GRIB Master Tables Version Number - [Code Table 1.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-0.shtml)| 513 | idsect[3] | GRIB Local Tables Version Number - [Code Table 1.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-1.shtml)| 514 | idsect[4] | Significance of Reference Time - [Code Table 1.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-2.shtml)| 515 | idsect[5] | Reference Time - Year (4 digits)| 516 | idsect[6] | Reference Time - Month| 517 | idsect[7] | Reference Time - Day| 518 | idsect[8] | Reference Time - Hour| 519 | idsect[9] | Reference Time - Minute| 520 | idsect[10] | Reference Time - Second| 521 | idsect[11] | Production status of data - [Code Table 1.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-3.shtml)| 522 | idsect[12] | Type of processed data - [Code Table 1.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table1-4.shtml)| 523 """ 524 self._source = source 525 self._msgnum = num 526 self._decode = decode 527 self._pos = 0 528 self._datapos = 0 529 self._sections = [] 530 self.hasLocalUseSection = False 531 self.isNDFD = False 532 if discipline is not None and idsect is not None: 533 # New message 534 self._msg,self._pos = g2clib.grib2_create(np.array([discipline,GRIB2_EDITION_NUMBER],DEFAULT_NUMPY_INT), 535 np.array(idsect,DEFAULT_NUMPY_INT)) 536 self._sections += [0,1] 537 else: 538 # Existing message 539 self._msg = msg 540 #self.md5 = {} 541 if self._msg is not None and self._source is not None: self.unpack()
Class Constructor. Instantiation of this class can handle a GRIB2 message from an existing
file or the creation of new GRIB2 message. To create a new GRIB2 message, provide the
appropriate values to the arguments discipline
and idsect
. When these 2 arguments
are not None
, then a new GRIB2 message is created. NOTE: All other keyword arguments
are ignored when a new message is created.
...
Parameters
msg
: Binary string representing the GRIB2 Message read from file.
source
: Source of where where this GRIB2 message originated
from (i.e. the input file). This allow for interaction with the
instance of grib2io.open
. Default is None.
num
: integer GRIB2 Message number from grib2io.open
. Default value is -1.
decode
: If True [DEFAULT], decode GRIB2 section lists into metadata
instance variables.
discipline
: integer GRIB2 Discipline GRIB2 Table 0.0
idsect
: Sequence containing GRIB1 Identification Section values (Section 1).
Index | Description |
---|---|
idsect[0] | Id of orginating centre - ON388 - Table 0 |
idsect[1] | Id of orginating sub-centre - ON388 - Table C |
idsect[2] | GRIB Master Tables Version Number - Code Table 1.0 |
idsect[3] | GRIB Local Tables Version Number - Code Table 1.1 |
idsect[4] | Significance of Reference Time - Code Table 1.2 |
idsect[5] | Reference Time - Year (4 digits) |
idsect[6] | Reference Time - Month |
idsect[7] | Reference Time - Day |
idsect[8] | Reference Time - Hour |
idsect[9] | Reference Time - Minute |
idsect[10] | Reference Time - Second |
idsect[11] | Production status of data - Code Table 1.3 |
idsect[12] | Type of processed data - Code Table 1.4 |
560 def unpack(self): 561 """ 562 Unpacks GRIB2 section data from the packed, binary message. 563 """ 564 # Section 0 - Indicator Section 565 self.indicatorSection = [] 566 self.indicatorSection.append(struct.unpack('>4s',self._msg[0:4])[0]) 567 self.indicatorSection.append(struct.unpack('>H',self._msg[4:6])[0]) 568 self.indicatorSection.append(self._msg[6]) 569 self.indicatorSection.append(self._msg[7]) 570 self.indicatorSection.append(struct.unpack('>Q',self._msg[8:16])[0]) 571 self._pos = 16 572 self._sections.append(0) 573 #self.md5[0] = _getmd5str(self.indicatorSection) 574 575 # Section 1 - Identification Section via g2clib.unpack1() 576 self.identificationSection,self._pos = g2clib.unpack1(self._msg,self._pos,np.empty) 577 self.identificationSection = self.identificationSection.tolist() 578 self._sections.append(1) 579 if self.identificationSection[0:2] == [8,65535]: self.isNDFD = True 580 581 # After Section 1, perform rest of GRIB2 Decoding inside while loop 582 # to account for sub-messages. 583 sectnum = 1 584 while True: 585 if self._msg[self._pos:self._pos+4].decode('ascii','ignore') == '7777': 586 break 587 588 # Read the length and section number. 589 sectlen = struct.unpack('>i',self._msg[self._pos:self._pos+4])[0] 590 prevsectnum = sectnum 591 sectnum = struct.unpack('>B',self._msg[self._pos+4:self._pos+5])[0] 592 593 # If the previous section number is > current section number, then 594 # we have encountered a submessage. 595 if prevsectnum > sectnum: break 596 597 # Handle submessage accordingly. 598 if isinstance(self._source,open): 599 if self._source._index['isSubmessage'][self._msgnum]: 600 if sectnum == self._source._index['submessageBeginSection'][self._msgnum]: 601 self._pos = self._source._index['submessageOffset'][self._msgnum] 602 603 # Section 2 - Local Use Section. 604 if sectnum == 2: 605 self._lus = self._msg[self._pos+5:self._pos+sectlen] 606 self._pos += sectlen 607 self.hasLocalUseSection = True 608 self._sections.append(2) 609 #self.md5[2] = _getmd5str(self.identificationSection) 610 611 # Section 3 - Grid Definition Section. 612 elif sectnum == 3: 613 _gds,_gdt,_deflist,self._pos = g2clib.unpack3(self._msg,self._pos,np.empty) 614 self.gridDefinitionSection = _gds.tolist() 615 self.gridDefinitionTemplateNumber = Grib2Metadata(int(_gds[4]),table='3.1') 616 self.gridDefinitionTemplate = _gdt.tolist() 617 self.defList = _deflist.tolist() 618 self._sections.append(3) 619 #self.md5[3] = _getmd5str([self.gridDefinitionTemplateNumber]+self.gridDefinitionTemplate) 620 621 # Section 4 - Product Definition Section. 622 elif sectnum == 4: 623 _pdt,_pdtn,_coordlst,self._pos = g2clib.unpack4(self._msg,self._pos,np.empty) 624 self.productDefinitionTemplate = _pdt.tolist() 625 self.productDefinitionTemplateNumber = Grib2Metadata(int(_pdtn),table='4.0') 626 self.coordinateList = _coordlst.tolist() 627 self._sections.append(4) 628 #self.md5[4] = _getmd5str([self.productDefinitionTemplateNumber]+self.productDefinitionTemplate) 629 630 # Section 5 - Data Representation Section. 631 elif sectnum == 5: 632 _drt,_drtn,_npts,self._pos = g2clib.unpack5(self._msg,self._pos,np.empty) 633 self.dataRepresentationTemplate = _drt.tolist() 634 self.dataRepresentationTemplateNumber = Grib2Metadata(int(_drtn),table='5.0') 635 self.numberOfDataPoints = _npts 636 self._sections.append(5) 637 #self.md5[5] = _getmd5str([self.dataRepresentationTemplateNumber]+self.dataRepresentationTemplate) 638 639 # Section 6 - Bitmap Section. 640 elif sectnum == 6: 641 _bmap,_bmapflag = g2clib.unpack6(self._msg,self.gridDefinitionSection[1],self._pos,np.empty) 642 self.bitMapFlag = _bmapflag 643 if self.bitMapFlag == 0: 644 self.bitMap = _bmap 645 elif self.bitMapFlag == 254: 646 # Value of 254 says to use a previous bitmap in the file. 647 self.bitMapFlag = 0 648 if isinstance(self._source,open): 649 self.bitMap = self._source._index['bitMap'][self._msgnum] 650 self._pos += sectlen # IMPORTANT: This is here because g2clib.unpack6() does not return updated position. 651 self._sections.append(6) 652 #self.md5[6] = None 653 654 # Section 7 - Data Section (data unpacked when data() method is invoked). 655 elif sectnum == 7: 656 self._datapos = self._pos 657 self._pos += sectlen # REMOVE THIS WHEN UNPACKING DATA IS IMPLEMENTED 658 self._sections.append(7) 659 #self.md5[7] = _getmd5str(self._msg[self._datapos:sectlen+1]) 660 661 else: 662 errmsg = 'Unknown section number = %i' % sectnum 663 raise ValueError(errmsg) 664 665 if self._decode: self.decode()
Unpacks GRIB2 section data from the packed, binary message.
667 def decode(self): 668 """ 669 Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables. 670 """ 671 672 # Section 0 - Indictator Section 673 self.discipline = Grib2Metadata(self.indicatorSection[2],table='0.0') 674 675 # Section 1 - Indentification Section. 676 self.originatingCenter = Grib2Metadata(self.identificationSection[0],table='originating_centers') 677 self.originatingSubCenter = Grib2Metadata(self.identificationSection[1],table='originating_subcenters') 678 self.masterTableInfo = Grib2Metadata(self.identificationSection[2],table='1.0') 679 self.localTableInfo = Grib2Metadata(self.identificationSection[3],table='1.1') 680 self.significanceOfReferenceTime = Grib2Metadata(self.identificationSection[4],table='1.2') 681 self.year = self.identificationSection[5] 682 self.month = self.identificationSection[6] 683 self.day = self.identificationSection[7] 684 self.hour = self.identificationSection[8] 685 self.minute = self.identificationSection[9] 686 self.second = self.identificationSection[10] 687 self.refDate = (self.year*1000000)+(self.month*10000)+(self.day*100)+self.hour 688 self.dtReferenceDate = datetime.datetime(self.year,self.month,self.day, 689 hour=self.hour,minute=self.minute, 690 second=self.second) 691 self.productionStatus = Grib2Metadata(self.identificationSection[11],table='1.3') 692 self.typeOfData = Grib2Metadata(self.identificationSection[12],table='1.4') 693 694 # ---------------------------- 695 # Section 3 -- Grid Definition 696 # ---------------------------- 697 698 # Set shape of the Earth parameters 699 if self.gridDefinitionTemplateNumber.value in [50,51,52,1200]: 700 earthparams = None 701 else: 702 earthparams = tables.earth_params[str(self.gridDefinitionTemplate[0])] 703 if earthparams['shape'] == 'spherical': 704 if earthparams['radius'] is None: 705 self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1]) 706 self.earthMajorAxis = None 707 self.earthMinorAxis = None 708 else: 709 self.earthRadius = earthparams['radius'] 710 self.earthMajorAxis = None 711 self.earthMinorAxis = None 712 elif earthparams['shape'] == 'oblateSpheroid': 713 if earthparams['radius'] is None and earthparams['major_axis'] is None and earthparams['minor_axis'] is None: 714 self.earthRadius = self.gridDefinitionTemplate[2]/(10.**self.gridDefinitionTemplate[1]) 715 self.earthMajorAxis = self.gridDefinitionTemplate[4]/(10.**self.gridDefinitionTemplate[3]) 716 self.earthMinorAxis = self.gridDefinitionTemplate[6]/(10.**self.gridDefinitionTemplate[5]) 717 else: 718 self.earthRadius = earthparams['radius'] 719 self.earthMajorAxis = earthparams['major_axis'] 720 self.earthMinorAxis = earthparams['minor_axis'] 721 722 reggrid = self.gridDefinitionSection[2] == 0 # self.gridDefinitionSection[2]=0 means regular 2-d grid 723 if reggrid and self.gridDefinitionTemplateNumber.value not in [50,51,52,53,100,120,1000,1200]: 724 self.nx = self.gridDefinitionTemplate[7] 725 self.ny = self.gridDefinitionTemplate[8] 726 if not reggrid and self.gridDefinitionTemplateNumber == 40: 727 # Reduced Gaussian Grid 728 self.ny = self.gridDefinitionTemplate[8] 729 if self.gridDefinitionTemplateNumber.value in [0,1,203,205,32768,32769]: 730 # Regular or Rotated Lat/Lon Grid 731 scalefact = float(self.gridDefinitionTemplate[9]) 732 if self.gridDefinitionTemplate[10] == 4294967295: 733 self.gridDefinitionTemplate[10] = -1 734 divisor = float(self.gridDefinitionTemplate[10]) 735 if scalefact == 0: scalefact = 1. 736 if divisor <= 0: divisor = 1.e6 737 self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor 738 self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor 739 self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor 740 self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor 741 self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor 742 self.gridlengthYDirection = scalefact*self.gridDefinitionTemplate[17]/divisor 743 if self.latitudeFirstGridpoint > self.latitudeLastGridpoint: 744 self.gridlengthYDirection = -self.gridlengthYDirection 745 if self.longitudeFirstGridpoint > self.longitudeLastGridpoint: 746 self.gridlengthXDirection = -self.gridlengthXDirection 747 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4] 748 if self.gridDefinitionTemplateNumber == 1: 749 self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor 750 self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor 751 self.anglePoleRotation = self.gridDefinitionTemplate[21] 752 elif self.gridDefinitionTemplateNumber == 10: 753 # Mercator 754 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 755 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 756 self.latitudeLastGridpoint = self.gridDefinitionTemplate[13]/1.e6 757 self.longitudeLastGridpoint = self.gridDefinitionTemplate[14]/1.e6 758 self.gridlengthXDirection = self.gridDefinitionTemplate[17]/1.e3 759 self.gridlengthYDirection= self.gridDefinitionTemplate[18]/1.e3 760 self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6 761 self.proj4_lon_0 = 0.5*(self.longitudeFirstGridpoint+self.longitudeLastGridpoint) 762 self.proj4_proj = 'merc' 763 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4] 764 elif self.gridDefinitionTemplateNumber == 20: 765 # Stereographic 766 projflag = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0] 767 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 768 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 769 self.proj4_lat_ts = self.gridDefinitionTemplate[12]/1.e6 770 if projflag == 0: 771 self.proj4_lat_0 = 90 772 elif projflag == 1: 773 self.proj4_lat_0 = -90 774 else: 775 raise ValueError('Invalid projection center flag = %s'%projflag) 776 self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6 777 self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000. 778 self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000. 779 self.proj4_proj = 'stere' 780 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4] 781 elif self.gridDefinitionTemplateNumber == 30: 782 # Lambert Conformal 783 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 784 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 785 self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000. 786 self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000. 787 self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6 788 self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6 789 self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6 790 self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6 791 self.proj4_proj = 'lcc' 792 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4] 793 elif self.gridDefinitionTemplateNumber == 31: 794 # Albers Equal Area 795 self.latitudeFirstGridpoint = self.gridDefinitionTemplate[9]/1.e6 796 self.longitudeFirstGridpoint = self.gridDefinitionTemplate[10]/1.e6 797 self.gridlengthXDirection = self.gridDefinitionTemplate[14]/1000. 798 self.gridlengthYDirection = self.gridDefinitionTemplate[15]/1000. 799 self.proj4_lat_1 = self.gridDefinitionTemplate[18]/1.e6 800 self.proj4_lat_2 = self.gridDefinitionTemplate[19]/1.e6 801 self.proj4_lat_0 = self.gridDefinitionTemplate[12]/1.e6 802 self.proj4_lon_0 = self.gridDefinitionTemplate[13]/1.e6 803 self.proj4_proj = 'aea' 804 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[17],output=list)[0:4] 805 elif self.gridDefinitionTemplateNumber == 40 or self.gridDefinitionTemplateNumber == 41: 806 # Gaussian Grid 807 scalefact = float(self.gridDefinitionTemplate[9]) 808 if self.gridDefinitionTemplate[10] == 4294967295: 809 self.gridDefinitionTemplate[10] = -1 810 divisor = float(self.gridDefinitionTemplate[10]) 811 if scalefact == 0: scalefact = 1. 812 if divisor <= 0: divisor = 1.e6 813 self.pointsBetweenPoleAndEquator = self.gridDefinitionTemplate[17] 814 self.latitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[11]/divisor 815 self.longitudeFirstGridpoint = scalefact*self.gridDefinitionTemplate[12]/divisor 816 self.latitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[14]/divisor 817 self.longitudeLastGridpoint = scalefact*self.gridDefinitionTemplate[15]/divisor 818 if reggrid: 819 self.gridlengthXDirection = scalefact*self.gridDefinitionTemplate[16]/divisor 820 if self.longitudeFirstGridpoint > self.longitudeLastGridpoint: 821 self.gridlengthXDirection = -self.gridlengthXDirection 822 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4] 823 if self.gridDefinitionTemplateNumber == 41: 824 self.latitudeSouthernPole = scalefact*self.gridDefinitionTemplate[19]/divisor 825 self.longitudeSouthernPole = scalefact*self.gridDefinitionTemplate[20]/divisor 826 self.anglePoleRotation = self.gridDefinitionTemplate[21] 827 elif self.gridDefinitionTemplateNumber == 50: 828 # Spectral Coefficients 829 self.spectralFunctionParameters = (self.gridDefinitionTemplate[0],self.gridDefinitionTemplate[1],self.gridDefinitionTemplate[2]) 830 self.scanModeFlags = [None,None,None,None] 831 elif self.gridDefinitionTemplateNumber == 90: 832 # Near-sided Vertical Perspective Satellite Projection 833 self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6 834 self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6 835 self.proj4_h = self.earthMajorAxis * (self.gridDefinitionTemplate[18]/1.e6) 836 dx = self.gridDefinitionTemplate[12] 837 dy = self.gridDefinitionTemplate[13] 838 # if lat_0 is equator, it's a geostationary view. 839 if self.proj4_lat_0 == 0.: # if lat_0 is equator, it's a 840 self.proj4_proj = 'geos' 841 # general case of 'near-side perspective projection' (untested) 842 else: 843 self.proj4_proj = 'nsper' 844 msg = 'Only geostationary perspective is supported. Lat/Lon values returned by grid method may be incorrect.' 845 warnings.warn(msg) 846 # latitude of horizon on central meridian 847 lonmax = 90.-(180./np.pi)*np.arcsin(self.earthMajorAxis/self.proj4_h) 848 # longitude of horizon on equator 849 latmax = 90.-(180./np.pi)*np.arcsin(self.earthMinorAxis/self.proj4_h) 850 # truncate to nearest thousandth of a degree (to make sure 851 # they aren't slightly over the horizon) 852 latmax = int(1000*latmax)/1000. 853 lonmax = int(1000*lonmax)/1000. 854 # h is measured from surface of earth at equator. 855 self.proj4_h = self.proj4_h - self.earthMajorAxis 856 # width and height of visible projection 857 P = pyproj.Proj(proj=self.proj4_proj,\ 858 a=self.earthMajorAxis,b=self.earthMinorAxis,\ 859 lat_0=0,lon_0=0,h=self.proj4_h) 860 x1,y1 = P(0.,latmax) 861 x2,y2 = P(lonmax,0.) 862 width = 2*x2 863 height = 2*y1 864 self.gridlengthXDirection = width/dx 865 self.gridlengthYDirection = height/dy 866 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[16],output=list)[0:4] 867 elif self.gridDefinitionTemplateNumber == 110: 868 # Azimuthal Equidistant 869 self.proj4_lat_0 = self.gridDefinitionTemplate[9]/1.e6 870 self.proj4_lon_0 = self.gridDefinitionTemplate[10]/1.e6 871 self.gridlengthXDirection = self.gridDefinitionTemplate[12]/1000. 872 self.gridlengthYDirection = self.gridDefinitionTemplate[13]/1000. 873 self.proj4_proj = 'aeqd' 874 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[15],output=list)[0:4] 875 elif self.gridDefinitionTemplateNumber == 204: 876 # Curvilinear Orthogonal 877 self.scanModeFlags = utils.int2bin(self.gridDefinitionTemplate[18],output=list)[0:4] 878 else: 879 errmsg = 'Unsupported Grid Definition Template Number - 3.%i' % self.gridDefinitionTemplateNumber.value 880 raise ValueError(errmsg) 881 882 # ------------------------------- 883 # Section 4 -- Product Definition 884 # ------------------------------- 885 886 # Template 4.0 - NOTE: That is these attributes apply to other templates. 887 self.parameterCategory = self.productDefinitionTemplate[0] 888 self.parameterNumber = self.productDefinitionTemplate[1] 889 self.fullName,self.units,self.shortName = tables.get_varinfo_from_table(self.discipline.value, 890 self.parameterCategory, 891 self.parameterNumber) 892 self.typeOfGeneratingProcess = Grib2Metadata(self.productDefinitionTemplate[2],table='4.3') 893 self.backgroundGeneratingProcessIdentifier = self.productDefinitionTemplate[3] 894 self.generatingProcess = Grib2Metadata(self.productDefinitionTemplate[4],table='generating_process') 895 self.unitOfTimeRange = Grib2Metadata(self.productDefinitionTemplate[7],table='4.4') 896 self.leadTime = self.productDefinitionTemplate[8] 897 self.typeOfFirstFixedSurface = Grib2Metadata(self.productDefinitionTemplate[9],table='4.5') 898 self.scaleFactorOfFirstFixedSurface = self.productDefinitionTemplate[10] 899 self.unitOfFirstFixedSurface = self.typeOfFirstFixedSurface.definition[1] 900 self.scaledValueOfFirstFixedSurface = self.productDefinitionTemplate[11] 901 self.valueOfFirstFixedSurface = self.scaledValueOfFirstFixedSurface/(10.**self.scaleFactorOfFirstFixedSurface) 902 temp = tables.get_value_from_table(self.productDefinitionTemplate[12],'4.5') 903 if temp[0] == 'Missing' and temp[1] == 'unknown': 904 self.typeOfSecondFixedSurface = None 905 self.scaleFactorOfSecondFixedSurface = None 906 self.unitOfSecondFixedSurface = None 907 self.valueOfSecondFixedSurface = None 908 else: 909 self.typeOfSecondFixedSurface = Grib2Metadata(self.productDefinitionTemplate[12],table='4.5') 910 self.scaleFactorOfSecondFixedSurface = self.productDefinitionTemplate[13] 911 self.unitOfSecondFixedSurface = self.typeOfSecondFixedSurface.definition[1] 912 self.scaledValueOfSecondFixedSurface = self.productDefinitionTemplate[14] 913 self.valueOfSecondFixedSurface = self.scaledValueOfSecondFixedSurface/(10.**self.scaleFactorOfSecondFixedSurface) 914 self.level = tables.get_wgrib2_level_string(*self.productDefinitionTemplate[9:15]) 915 916 # Template 4.1 - 917 if self.productDefinitionTemplateNumber == 1: 918 self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6') 919 self.perturbationNumber = self.productDefinitionTemplate[16] 920 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17] 921 922 # Template 4.2 - 923 elif self.productDefinitionTemplateNumber == 2: 924 self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7') 925 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16] 926 927 # Template 4.5 - 928 elif self.productDefinitionTemplateNumber == 5: 929 self.forecastProbabilityNumber = self.productDefinitionTemplate[15] 930 self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16] 931 self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9') 932 self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18] 933 self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19] 934 self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20] 935 self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21] 936 self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \ 937 self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18]) 938 self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \ 939 self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20]) 940 self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22]) 941 942 # Template 4.6 - 943 elif self.productDefinitionTemplateNumber == 6: 944 self.percentileValue = self.productDefinitionTemplate[15] 945 946 # Template 4.8 - 947 elif self.productDefinitionTemplateNumber == 8: 948 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[15] 949 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[16] 950 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[17] 951 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[18] 952 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[19] 953 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[20] 954 self.numberOfTimeRanges = self.productDefinitionTemplate[21] 955 self.numberOfMissingValues = self.productDefinitionTemplate[22] 956 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[23],table='4.10') 957 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.11') 958 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.4') 959 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[26] 960 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4') 961 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[28] 962 963 # Template 4.9 - 964 elif self.productDefinitionTemplateNumber == 9: 965 self.forecastProbabilityNumber = self.productDefinitionTemplate[15] 966 self.totalNumberOfForecastProbabilities = self.productDefinitionTemplate[16] 967 self.typeOfProbability = Grib2Metadata(self.productDefinitionTemplate[17],table='4.9') 968 self.scaleFactorOfThresholdLowerLimit = self.productDefinitionTemplate[18] 969 self.scaledValueOfThresholdLowerLimit = self.productDefinitionTemplate[19] 970 self.scaleFactorOfThresholdUpperLimit = self.productDefinitionTemplate[20] 971 self.scaledValueOfThresholdUpperLimit = self.productDefinitionTemplate[21] 972 self.thresholdLowerLimit = 0.0 if self.productDefinitionTemplate[19] == 255 else \ 973 self.productDefinitionTemplate[19]/(10.**self.productDefinitionTemplate[18]) 974 self.thresholdUpperLimit = 0.0 if self.productDefinitionTemplate[21] == 255 else \ 975 self.productDefinitionTemplate[21]/(10.**self.productDefinitionTemplate[20]) 976 self.threshold = utils.get_wgrib2_prob_string(*self.productDefinitionTemplate[17:22]) 977 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[22] 978 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[23] 979 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[24] 980 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[25] 981 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[26] 982 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[27] 983 self.numberOfTimeRanges = self.productDefinitionTemplate[28] 984 self.numberOfMissingValues = self.productDefinitionTemplate[29] 985 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[30],table='4.10') 986 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[31],table='4.11') 987 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[32],table='4.4') 988 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[33] 989 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[34],table='4.4') 990 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[35] 991 992 # Template 4.10 - 993 elif self.productDefinitionTemplateNumber == 10: 994 self.percentileValue = self.productDefinitionTemplate[15] 995 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[16] 996 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[17] 997 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[18] 998 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[19] 999 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[20] 1000 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[21] 1001 self.numberOfTimeRanges = self.productDefinitionTemplate[22] 1002 self.numberOfMissingValues = self.productDefinitionTemplate[23] 1003 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[24],table='4.10') 1004 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.11') 1005 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.4') 1006 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[27] 1007 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4') 1008 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[29] 1009 1010 # Template 4.11 - 1011 elif self.productDefinitionTemplateNumber == 11: 1012 self.typeOfEnsembleForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.6') 1013 self.perturbationNumber = self.productDefinitionTemplate[16] 1014 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[17] 1015 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[18] 1016 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[19] 1017 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[20] 1018 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[21] 1019 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[22] 1020 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[23] 1021 self.numberOfTimeRanges = self.productDefinitionTemplate[24] 1022 self.numberOfMissingValues = self.productDefinitionTemplate[25] 1023 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.10') 1024 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.11') 1025 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[28],table='4.4') 1026 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[29] 1027 self.unitOfTimeRangeOfSuccessiveFields = tables.get_value_from_table(self.productDefinitionTemplate[30],table='4.4') 1028 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[31] 1029 1030 # Template 4.12 - 1031 elif self.productDefinitionTemplateNumber == 12: 1032 self.typeOfDerivedForecast = Grib2Metadata(self.productDefinitionTemplate[15],table='4.7') 1033 self.numberOfEnsembleForecasts = self.productDefinitionTemplate[16] 1034 self.yearOfEndOfTimePeriod = self.productDefinitionTemplate[17] 1035 self.monthOfEndOfTimePeriod = self.productDefinitionTemplate[18] 1036 self.dayOfEndOfTimePeriod = self.productDefinitionTemplate[19] 1037 self.hourOfEndOfTimePeriod = self.productDefinitionTemplate[20] 1038 self.minuteOfEndOfTimePeriod = self.productDefinitionTemplate[21] 1039 self.secondOfEndOfTimePeriod = self.productDefinitionTemplate[22] 1040 self.numberOfTimeRanges = self.productDefinitionTemplate[23] 1041 self.numberOfMissingValues = self.productDefinitionTemplate[24] 1042 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[25],table='4.10') 1043 self.typeOfTimeIncrementOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[26],table='4.11') 1044 self.unitOfTimeRangeOfStatisticalProcess = Grib2Metadata(self.productDefinitionTemplate[27],table='4.4') 1045 self.timeRangeOfStatisticalProcess = self.productDefinitionTemplate[28] 1046 self.unitOfTimeRangeOfSuccessiveFields = Grib2Metadata(self.productDefinitionTemplate[29],table='4.4') 1047 self.timeIncrementOfSuccessiveFields = self.productDefinitionTemplate[30] 1048 1049 # Template 4.15 - 1050 elif self.productDefinitionTemplateNumber == 15: 1051 self.statisticalProcess = Grib2Metadata(self.productDefinitionTemplate[15],table='4.10') 1052 self.typeOfSpatialProcessing = Grib2Metadata(self.productDefinitionTemplate[16],table='4.15') 1053 self.numberOfDataPointsForSpatialProcessing = self.productDefinitionTemplate[17] 1054 1055 else: 1056 if self.productDefinitionTemplateNumber != 0: 1057 errmsg = 'Unsupported Product Definition Template Number - 4.%i' % self.productDefinitionTemplateNumber.value 1058 raise ValueError(errmsg) 1059 1060 1061 self.leadTime = utils.getleadtime(self.identificationSection, 1062 self.productDefinitionTemplateNumber.value, 1063 self.productDefinitionTemplate) 1064 1065 if self.productDefinitionTemplateNumber.value in [8,9,10,11,12]: 1066 self.dtEndOfTimePeriod = datetime.datetime(self.yearOfEndOfTimePeriod,self.monthOfEndOfTimePeriod, 1067 self.dayOfEndOfTimePeriod,hour=self.hourOfEndOfTimePeriod, 1068 minute=self.minuteOfEndOfTimePeriod, 1069 second=self.secondOfEndOfTimePeriod) 1070 1071 # -------------------------------- 1072 # Section 5 -- Data Representation 1073 # -------------------------------- 1074 1075 # Template 5.0 - Simple Packing 1076 if self.dataRepresentationTemplateNumber == 0: 1077 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1078 self.binScaleFactor = self.dataRepresentationTemplate[1] 1079 self.decScaleFactor = self.dataRepresentationTemplate[2] 1080 self.nBitsPacking = self.dataRepresentationTemplate[3] 1081 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[3],table='5.1') 1082 1083 # Template 5.2 - Complex Packing 1084 elif self.dataRepresentationTemplateNumber == 2: 1085 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1086 self.binScaleFactor = self.dataRepresentationTemplate[1] 1087 self.decScaleFactor = self.dataRepresentationTemplate[2] 1088 self.nBitsPacking = self.dataRepresentationTemplate[3] 1089 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1090 self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4') 1091 self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5') 1092 self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None 1093 self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None 1094 self.nGroups = self.dataRepresentationTemplate[9] 1095 self.refGroupWidth = self.dataRepresentationTemplate[10] 1096 self.nBitsGroupWidth = self.dataRepresentationTemplate[11] 1097 self.refGroupLength = self.dataRepresentationTemplate[12] 1098 self.groupLengthIncrement = self.dataRepresentationTemplate[13] 1099 self.lengthOfLastGroup = self.dataRepresentationTemplate[14] 1100 self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15] 1101 1102 # Template 5.3 - Complex Packing and Spatial Differencing 1103 elif self.dataRepresentationTemplateNumber == 3: 1104 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1105 self.binScaleFactor = self.dataRepresentationTemplate[1] 1106 self.decScaleFactor = self.dataRepresentationTemplate[2] 1107 self.nBitsPacking = self.dataRepresentationTemplate[3] 1108 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1109 self.groupSplitMethod = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.4') 1110 self.typeOfMissingValue = Grib2Metadata(self.dataRepresentationTemplate[6],table='5.5') 1111 self.priMissingValue = utils.getieeeint(self.dataRepresentationTemplate[7]) if self.dataRepresentationTemplate[6] in [1,2] else None 1112 self.secMissingValue = utils.getieeeint(self.dataRepresentationTemplate[8]) if self.dataRepresentationTemplate[6] == 2 else None 1113 self.nGroups = self.dataRepresentationTemplate[9] 1114 self.refGroupWidth = self.dataRepresentationTemplate[10] 1115 self.nBitsGroupWidth = self.dataRepresentationTemplate[11] 1116 self.refGroupLength = self.dataRepresentationTemplate[12] 1117 self.groupLengthIncrement = self.dataRepresentationTemplate[13] 1118 self.lengthOfLastGroup = self.dataRepresentationTemplate[14] 1119 self.nBitsScaledGroupLength = self.dataRepresentationTemplate[15] 1120 self.spatialDifferenceOrder = Grib2Metadata(self.dataRepresentationTemplate[16],table='5.6') 1121 self.nBytesSpatialDifference = self.dataRepresentationTemplate[17] 1122 1123 # Template 5.4 - IEEE Floating Point Data 1124 elif self.dataRepresentationTemplateNumber == 4: 1125 self.precision = Grib2Metadata(self.dataRepresentationTemplate[0],table='5.7') 1126 1127 # Template 5.40 - JPEG2000 Compression 1128 elif self.dataRepresentationTemplateNumber == 40: 1129 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1130 self.binScaleFactor = self.dataRepresentationTemplate[1] 1131 self.decScaleFactor = self.dataRepresentationTemplate[2] 1132 self.nBitsPacking = self.dataRepresentationTemplate[3] 1133 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1134 self.typeOfCompression = Grib2Metadata(self.dataRepresentationTemplate[5],table='5.40') 1135 self.targetCompressionRatio = self.dataRepresentationTemplate[6] 1136 1137 # Template 5.41 - PNG Compression 1138 elif self.dataRepresentationTemplateNumber == 41: 1139 self.refValue = utils.getieeeint(self.dataRepresentationTemplate[0]) 1140 self.binScaleFactor = self.dataRepresentationTemplate[1] 1141 self.decScaleFactor = self.dataRepresentationTemplate[2] 1142 self.nBitsPacking = self.dataRepresentationTemplate[3] 1143 self.typeOfValues = Grib2Metadata(self.dataRepresentationTemplate[4],table='5.1') 1144 1145 else: 1146 errmsg = 'Unsupported Data Representation Definition Template Number - 5.%i' % self.dataRepresentationTemplateNumber.value 1147 raise ValueError(errmsg)
Decode the unpacked GRIB2 integer-coded metadata in human-readable form and linked to GRIB2 tables.
1150 def data(self, fill_value=DEFAULT_FILL_VALUE, masked_array=True, expand=True, order=None, 1151 map_keys=False): 1152 """ 1153 Returns an unpacked data grid. 1154 1155 Parameters 1156 ---------- 1157 1158 **`fill_value`**: Missing or masked data is filled with this value or default value given by 1159 `DEFAULT_FILL_VALUE` 1160 1161 **`masked_array`**: If `True` [DEFAULT], return masked array if there is bitmap for missing 1162 or masked data. 1163 1164 **`expand`**: If `True` [DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids. 1165 1166 **`order`**: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing 1167 or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids. 1168 1169 **`map_keys`**: If `True`, data values will be mapped to the string-based keys that are stored 1170 in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the 1171 units (i.e. "See Table 4.xxx"). 1172 1173 Returns 1174 ------- 1175 1176 **`numpy.ndarray`**: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32, 1177 but could be np.int32 if Grib2Message.typeOfValues is integer. The array dtype will be 1178 string-based if map_keys=True. 1179 """ 1180 if not hasattr(self,'scanModeFlags'): 1181 raise ValueError('Unsupported grid definition template number %s'%self.gridDefinitionTemplateNumber) 1182 else: 1183 if self.scanModeFlags[2]: 1184 storageorder='F' 1185 else: 1186 storageorder='C' 1187 if order is None: 1188 if (self.dataRepresentationTemplateNumber in [2,3] and 1189 self.dataRepresentationTemplate[6] != 0) or self.bitMapFlag == 0: 1190 order = 0 1191 else: 1192 order = 1 1193 drtnum = self.dataRepresentationTemplateNumber.value 1194 drtmpl = np.asarray(self.dataRepresentationTemplate,dtype=DEFAULT_NUMPY_INT) 1195 gdtnum = self.gridDefinitionTemplateNumber.value 1196 gdtmpl = np.asarray(self.gridDefinitionTemplate,dtype=DEFAULT_NUMPY_INT) 1197 ndpts = self.numberOfDataPoints 1198 gds = self.gridDefinitionSection 1199 ngrdpts = gds[1] 1200 ipos = self._datapos 1201 fld1 = g2clib.unpack7(self._msg,gdtnum,gdtmpl,drtnum,drtmpl,ndpts,ipos,np.empty,storageorder=storageorder) 1202 # Apply bitmap. 1203 if self.bitMapFlag == 0: 1204 fld = fill_value*np.ones(ngrdpts,'f') 1205 np.put(fld,np.nonzero(self.bitMap),fld1) 1206 if masked_array: 1207 fld = ma.masked_values(fld,fill_value) 1208 # Missing values instead of bitmap 1209 elif masked_array and hasattr(self,'priMissingValue'): 1210 if hasattr(self,'secMissingValue'): 1211 mask = np.logical_or(fld1==self.priMissingValue,fld1==self.secMissingValue) 1212 else: 1213 mask = fld1 == self.priMissingValue 1214 fld = ma.array(fld1,mask=mask) 1215 else: 1216 fld = fld1 1217 if self.nx is not None and self.ny is not None: # Rectangular grid. 1218 if ma.isMA(fld): 1219 fld = ma.reshape(fld,(self.ny,self.nx)) 1220 else: 1221 fld = np.reshape(fld,(self.ny,self.nx)) 1222 else: 1223 if gds[2] and gdtnum == 40: # Reduced global Gaussian grid. 1224 if expand: 1225 from . import redtoreg 1226 self.nx = 2*self.ny 1227 lonsperlat = self.defList 1228 if ma.isMA(fld): 1229 fld = ma.filled(fld) 1230 fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long), 1231 fld.astype(np.double),fill_value) 1232 fld = ma.masked_values(fld,fill_value) 1233 else: 1234 fld = redtoreg._redtoreg(self.nx,lonsperlat.astype(np.long), 1235 fld.astype(np.double),fill_value) 1236 # Check scan modes for rect grids. 1237 if self.nx is not None and self.ny is not None: 1238 if self.scanModeFlags[3]: 1239 fldsave = fld.astype('f') # casting makes a copy 1240 fld[1::2,:] = fldsave[1::2,::-1] 1241 1242 # Set data to integer according to GRIB metadata 1243 if self.typeOfValues == "Integer": fld = fld.astype(np.int32) 1244 1245 # Map the data values to their respective definitions. 1246 if map_keys: 1247 fld = fld.astype(np.int32).astype(str) 1248 if self.identificationSection[0] == 7 and \ 1249 self.identificationSection[1] == 14 and \ 1250 self.shortName == 'PWTHER': 1251 # MDL Predominant Weather Grid 1252 #keys = utils.decode_mdl_wx_strings(self._lus) 1253 keys = utils.decode_ndfd_wx_strings(self._lus) 1254 for n,k in enumerate(keys): 1255 fld = np.where(fld==str(n+1),k,fld) 1256 elif self.identificationSection[0] == 8 and \ 1257 self.identificationSection[1] == 65535 and \ 1258 self.shortName == 'CRAIN': 1259 # NDFD Predominant Weather Grid 1260 keys = utils.decode_ndfd_wx_strings(self._lus) 1261 for n,k in enumerate(keys): 1262 fld = np.where(fld==str(n+1),k,fld) 1263 else: 1264 # For data whose units are defined in a code table 1265 tbl = re.findall(r'\d\.\d+',self.units,re.IGNORECASE)[0] 1266 for k,v in tables.get_table(tbl).items(): 1267 fld = np.where(fld==k,v,fld) 1268 return fld
Returns an unpacked data grid.
Parameters
fill_value
: Missing or masked data is filled with this value or default value given by
DEFAULT_FILL_VALUE
masked_array
: If True
[DEFAULT], return masked array if there is bitmap for missing
or masked data.
expand
: If True
[DEFAULT], Reduced Gaussian grids are expanded to regular Gaussian grids.
order
: If 0 [DEFAULT], nearest neighbor interpolation is used if grid has missing
or bitmapped values. If 1, linear interpolation is used for expanding reduced Gaussian grids.
map_keys
: If True
, data values will be mapped to the string-based keys that are stored
in the Local Use Section (section 2) of the GRIB2 Message or in a code table as specified in the
units (i.e. "See Table 4.xxx").
Returns
numpy.ndarray
: A numpy.ndarray with shape (ny,nx). By default the array dtype=np.float32,
but could be np.int32 if Grib2Message.typeOfValues is integer. The array dtype will be
string-based if map_keys=True.
1271 def latlons(self): 1272 """Alias for `grib2io.Grib2Message.grid` method""" 1273 return self.grid()
Alias for grib2io.Grib2Message.grid
method
1276 def grid(self): 1277 """ 1278 Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon, 1279 global Gaussian, mercator, stereographic, lambert conformal, albers equal-area, 1280 space-view and azimuthal equidistant grids. 1281 1282 Returns 1283 ------- 1284 1285 **`lats, lons : numpy.ndarray`** 1286 1287 Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and 1288 longitudes in units of degrees. 1289 """ 1290 gdtnum = self.gridDefinitionTemplateNumber 1291 gdtmpl = self.gridDefinitionTemplate 1292 reggrid = self.gridDefinitionSection[2] == 0 # This means regular 2-d grid 1293 self.projparams = {} 1294 if self.earthMajorAxis is not None: self.projparams['a']=self.earthMajorAxis 1295 if self.earthMajorAxis is not None: self.projparams['b']=self.earthMinorAxis 1296 if gdtnum == 0: 1297 # Regular lat/lon grid 1298 lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint 1299 lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint 1300 dlon = self.gridlengthXDirection 1301 dlat = self.gridlengthYDirection 1302 lats = np.arange(lat1,lat2+dlat,dlat) 1303 lons = np.arange(lon1,lon2+dlon,dlon) 1304 # flip if scan mode says to. 1305 #if self.scanModeFlags[0]: 1306 # lons = lons[::-1] 1307 #if not self.scanModeFlags[1]: 1308 # lats = lats[::-1] 1309 self.projparams['proj'] = 'cyl' 1310 lons,lats = np.meshgrid(lons,lats) # make 2-d arrays. 1311 elif gdtnum == 40: # Gaussian grid (only works for global!) 1312 from utils.gauss_grids import gaussian_latitudes 1313 lon1, lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint 1314 lon2, lat2 = self.longitudeLastGridpoint, self.latitudeLastGridpoint 1315 nlats = self.ny 1316 if not reggrid: # Reduced Gaussian grid. 1317 nlons = 2*nlats 1318 dlon = 360./nlons 1319 else: 1320 nlons = self.nx 1321 dlon = self.gridlengthXDirection 1322 lons = np.arange(lon1,lon2+dlon,dlon) 1323 # Compute Gaussian lats (north to south) 1324 lats = gaussian_latitudes(nlats) 1325 if lat1 < lat2: # reverse them if necessary 1326 lats = lats[::-1] 1327 # flip if scan mode says to. 1328 #if self.scanModeFlags[0]: 1329 # lons = lons[::-1] 1330 #if not self.scanModeFlags[1]: 1331 # lats = lats[::-1] 1332 self.projparams['proj'] = 'cyl' 1333 lons,lats = np.meshgrid(lons,lats) # make 2-d arrays 1334 elif gdtnum in [10,20,30,31,110]: 1335 # Mercator, Lambert Conformal, Stereographic, Albers Equal Area, Azimuthal Equidistant 1336 dx,dy = self.gridlengthXDirection, self.gridlengthYDirection 1337 lon1,lat1 = self.longitudeFirstGridpoint, self.latitudeFirstGridpoint 1338 if gdtnum == 10: # Mercator. 1339 self.projparams['lat_ts']=self.proj4_lat_ts 1340 self.projparams['proj']=self.proj4_proj 1341 self.projparams['lon_0']=self.proj4_lon_0 1342 pj = pyproj.Proj(self.projparams) 1343 llcrnrx, llcrnry = pj(lon1,lat1) 1344 x = llcrnrx+dx*np.arange(self.nx) 1345 y = llcrnry+dy*np.arange(self.ny) 1346 x,y = np.meshgrid(x, y) 1347 lons,lats = pj(x, y, inverse=True) 1348 elif gdtnum == 20: # Stereographic 1349 self.projparams['lat_ts']=self.proj4_lat_ts 1350 self.projparams['proj']=self.proj4_proj 1351 self.projparams['lat_0']=self.proj4_lat_0 1352 self.projparams['lon_0']=self.proj4_lon_0 1353 pj = pyproj.Proj(self.projparams) 1354 llcrnrx, llcrnry = pj(lon1,lat1) 1355 x = llcrnrx+dx*np.arange(self.nx) 1356 y = llcrnry+dy*np.arange(self.ny) 1357 x,y = np.meshgrid(x, y) 1358 lons,lats = pj(x, y, inverse=True) 1359 elif gdtnum in [30,31]: # Lambert, Albers 1360 self.projparams['lat_1']=self.proj4_lat_1 1361 self.projparams['lat_2']=self.proj4_lat_2 1362 self.projparams['proj']=self.proj4_proj 1363 self.projparams['lon_0']=self.proj4_lon_0 1364 pj = pyproj.Proj(self.projparams) 1365 llcrnrx, llcrnry = pj(lon1,lat1) 1366 x = llcrnrx+dx*np.arange(self.nx) 1367 y = llcrnry+dy*np.arange(self.ny) 1368 x,y = np.meshgrid(x, y) 1369 lons,lats = pj(x, y, inverse=True) 1370 elif gdtnum == 110: # Azimuthal Equidistant 1371 self.projparams['proj']=self.proj4_proj 1372 self.projparams['lat_0']=self.proj4_lat_0 1373 self.projparams['lon_0']=self.proj4_lon_0 1374 pj = pyproj.Proj(self.projparams) 1375 llcrnrx, llcrnry = pj(lon1,lat1) 1376 x = llcrnrx+dx*np.arange(self.nx) 1377 y = llcrnry+dy*np.arange(self.ny) 1378 x,y = np.meshgrid(x, y) 1379 lons,lats = pj(x, y, inverse=True) 1380 elif gdtnum == 90: 1381 # Satellite Projection 1382 dx = self.gridlengthXDirection 1383 dy = self.gridlengthYDirection 1384 self.projparams['proj']=self.proj4_proj 1385 self.projparams['lon_0']=self.proj4_lon_0 1386 self.projparams['lat_0']=self.proj4_lat_0 1387 self.projparams['h']=self.proj4_h 1388 pj = pyproj.Proj(self.projparams) 1389 x = dx*np.indices((self.ny,self.nx),'f')[1,:,:] 1390 x -= 0.5*x.max() 1391 y = dy*np.indices((self.ny,self.nx),'f')[0,:,:] 1392 y -= 0.5*y.max() 1393 lons,lats = pj(x,y,inverse=True) 1394 # Set lons,lats to 1.e30 where undefined 1395 abslons = np.fabs(lons) 1396 abslats = np.fabs(lats) 1397 lons = np.where(abslons < 1.e20, lons, 1.e30) 1398 lats = np.where(abslats < 1.e20, lats, 1.e30) 1399 else: 1400 raise ValueError('Unsupported grid') 1401 1402 return lats.astype('f'), lons.astype('f')
Return lats,lons (in degrees) of grid. Currently can handle reg. lat/lon, global Gaussian, mercator, stereographic, lambert conformal, albers equal-area, space-view and azimuthal equidistant grids.
Returns
lats, lons : numpy.ndarray
Returns two numpy.ndarrays with dtype=numpy.float32 of grid latitudes and longitudes in units of degrees.
1405 def addlocal(self, ludata): 1406 """ 1407 Add a Local Use Section [(Section 2)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect2.shtml) 1408 to the GRIB2 message. 1409 1410 Parameters 1411 ---------- 1412 1413 **`ludata : bytes`**: Local Use data. 1414 """ 1415 assert isinstance(ludata,bytes) 1416 self._msg,self._pos = g2clib.grib2_addlocal(self._msg,ludata) 1417 self.hasLocalUseSection = True 1418 self._sections.append(2)
Add a Local Use Section (Section 2) to the GRIB2 message.
Parameters
ludata : bytes
: Local Use data.
1421 def addgrid(self, gdsinfo, gdtmpl, deflist=None): 1422 """ 1423 Add a Grid Definition Section [(Section 3)](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_doc/grib2_sect3.shtml) 1424 to the GRIB2 message. 1425 1426 Parameters 1427 ---------- 1428 1429 **`gdsinfo`**: Sequence containing information needed for the grid definition section. 1430 1431 | Index | Description | 1432 | :---: | :--- | 1433 | gdsinfo[0] | Source of grid definition - [Code Table 3.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-0.shtml)| 1434 | gdsinfo[1] | Number of data points| 1435 | gdsinfo[2] | Number of octets for optional list of numbers defining number of points| 1436 | gdsinfo[3] | Interpetation of list of numbers defining number of points - [Code Table 3.11](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-11.shtml)| 1437 | gdsinfo[4] | Grid Definition Template Number - [Code Table 3.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml)| 1438 1439 **`gdtmpl`**: Sequence of values for the specified Grid Definition Template. Each 1440 element of this integer array contains an entry (in the order specified) of Grid 1441 Definition Template 3.NN 1442 1443 **`deflist`**: Sequence containing the number of grid points contained in each 1444 row (or column) of a non-regular grid. Used if gdsinfo[2] != 0. 1445 """ 1446 if 3 in self._sections: 1447 raise ValueError('GRIB2 Message already contains Grid Definition Section.') 1448 if deflist is not None: 1449 _deflist = np.array(deflist,dtype=DEFAULT_NUMPY_INT) 1450 else: 1451 _deflist = None 1452 gdtnum = gdsinfo[4] 1453 if gdtnum in [0,1,2,3,40,41,42,43,44,203,205,32768,32769]: 1454 self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4] 1455 elif gdtnum == 10: # mercator 1456 self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4] 1457 elif gdtnum == 20: # stereographic 1458 self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4] 1459 elif gdtnum == 30: # lambert conformal 1460 self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4] 1461 elif gdtnum == 31: # albers equal area. 1462 self.scanModeFlags = utils.int2bin(gdtmpl[17],output=list)[0:4] 1463 elif gdtnum == 90: # near-sided vertical perspective satellite projection 1464 self.scanModeFlags = utils.int2bin(gdtmpl[16],output=list)[0:4] 1465 elif gdtnum == 110: # azimuthal equidistant. 1466 self.scanModeFlags = utils.int2bin(gdtmpl[15],output=list)[0:4] 1467 elif gdtnum == 120: 1468 self.scanModeFlags = utils.int2bin(gdtmpl[6],output=list)[0:4] 1469 elif gdtnum == 204: # curvilinear orthogonal 1470 self.scanModeFlags = utils.int2bin(gdtmpl[18],output=list)[0:4] 1471 elif gdtnum in [1000,1100]: 1472 self.scanModeFlags = utils.int2bin(gdtmpl[12],output=list)[0:4] 1473 self._msg,self._pos = g2clib.grib2_addgrid(self._msg, 1474 np.array(gdsinfo,dtype=DEFAULT_NUMPY_INT), 1475 np.array(gdtmpl,dtype=DEFAULT_NUMPY_INT), 1476 _deflist) 1477 self._sections.append(3)
Add a Grid Definition Section (Section 3) to the GRIB2 message.
Parameters
gdsinfo
: Sequence containing information needed for the grid definition section.
Index | Description |
---|---|
gdsinfo[0] | Source of grid definition - Code Table 3.0 |
gdsinfo[1] | Number of data points |
gdsinfo[2] | Number of octets for optional list of numbers defining number of points |
gdsinfo[3] | Interpetation of list of numbers defining number of points - Code Table 3.11 |
gdsinfo[4] | Grid Definition Template Number - Code Table 3.1 |
gdtmpl
: Sequence of values for the specified Grid Definition Template. Each
element of this integer array contains an entry (in the order specified) of Grid
Definition Template 3.NN
deflist
: Sequence containing the number of grid points contained in each
row (or column) of a non-regular grid. Used if gdsinfo[2] != 0.
1480 def addfield(self, field, pdtnum, pdtmpl, coordlist=None, packing="complex-spdiff", **packing_opts): 1481 """ 1482 Add a Product Definition, Data Representation, Bitmap, and Data Sections 1483 to `Grib2Message` instance (i.e. Sections 4-7). Must be called after the grid 1484 definition section has been added (`addfield`). 1485 1486 Parameters 1487 ---------- 1488 1489 **`field`**: Numpy array of data values to pack. If field is a masked array, then 1490 a bitmap is created from the mask. 1491 1492 **`pdtnum`**: integer Product Definition Template Number - [Code Table 4.0](http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-0.shtml) 1493 1494 **`pdtmpl`**: Sequence with the data values for the specified Product Definition 1495 Template (N=pdtnum). Each element of this integer array contains an entry (in 1496 the order specified) of Product Definition Template 4.N. 1497 1498 **`coordlist`**: Sequence containing floating point values intended to document the 1499 vertical discretization with model data on hybrid coordinate vertical levels. Default is `None`. 1500 1501 **`packing`**: String to specify the type of packing. Valid options are the following: 1502 1503 | Packing Scheme | Description | 1504 | :---: | :---: | 1505 | 'simple' | [Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-0.shtml) | 1506 | 'complex' | [Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-2.shtml) | 1507 | 'complex-spdiff' | [Complex packing with Spatial Differencing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-3.shtml) | 1508 | 'jpeg' | [JPEG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-40.shtml) | 1509 | 'png' | [PNG compression](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-41.shtml) | 1510 | 'spectral-simple'| [Spectral Data - Simple packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-50.shtml) | 1511 | 'spectral-complex'| [Spectral Data - Complex packing](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-51.shtml) | 1512 1513 **`**packing_opts`**: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for 1514 the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following: 1515 1516 | Packing Scheme | Keyword Arguments | 1517 | :---: | :---: | 1518 | 'simple' | `binScaleFactor`, `decScaleFactor` | 1519 | 'complex' | `binScaleFactor`, `decScaleFactor`, `priMissingValue`, [`secMissingValue`] | 1520 | 'complex-spdiff' | `binScaleFactor`, `decScaleFactor`, `spatialDifferenceOrder`, `priMissingValue`, [`secMissingValue`] | 1521 | 'jpeg' | `binScaleFactor`, `decScaleFactor` | 1522 | 'png' | `binScaleFactor`, `decScaleFactor` | 1523 | 'spectral-simple' | `binScaleFactor`, `decScaleFactor` | 1524 | 'spectral-complex' | `binScaleFactor`, `decScaleFactor` | 1525 """ 1526 if self._sections[-1] != 3: 1527 raise ValueError('addgrid() must be called before addfield()') 1528 if self.scanModeFlags is not None: 1529 if self.scanModeFlags[3]: 1530 fieldsave = field.astype('f') # Casting makes a copy 1531 field[1::2,:] = fieldsave[1::2,::-1] 1532 fld = field.astype('f') 1533 if ma.isMA(field) and ma.count_masked(field) > 0: 1534 bitmapflag = 0 1535 bmap = 1-np.ravel(field.mask.astype(DEFAULT_NUMPY_INT)) 1536 else: 1537 bitmapflag = 255 1538 bmap = None 1539 if coordlist is not None: 1540 crdlist = np.array(coordlist,'f') 1541 else: 1542 crdlist = None 1543 1544 # Set data representation template number and template values 1545 drtnum = -1 1546 drtmpl = np.zeros((DEFAULT_DRT_LEN),dtype=DEFAULT_NUMPY_INT) 1547 if packing == "simple": 1548 drtnum = 0 1549 drtmpl[1] = packing_opts["binScaleFactor"] 1550 drtmpl[2] = packing_opts["decScaleFactor"] 1551 elif packing == "complex" or packing == "complex-spdiff": 1552 if packing == "complex": 1553 drtnum = 2 1554 if packing == "complex-spdiff": 1555 drtnum = 3 1556 drtmpl[16] = packing_opts['spatialDifferenceOrder'] 1557 drtmpl[1] = packing_opts["binScaleFactor"] 1558 drtmpl[2] = packing_opts["decScaleFactor"] 1559 if set(("priMissingValue","secMissingValue")).issubset(packing_opts): 1560 drtmpl[6] = 2 1561 drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"]) 1562 drtmpl[8] = utils.putieeeint(packing_opts["secMissingValue"]) 1563 else: 1564 if "priMissingValue" in packing_opts.keys(): 1565 drtmpl[6] = 1 1566 drtmpl[7] = utils.putieeeint(packing_opts["priMissingValue"]) 1567 else: 1568 drtmpl[6] = 0 1569 elif packing == "jpeg": 1570 drtnum = 40 1571 drtmpl[1] = packing_opts["binScaleFactor"] 1572 drtmpl[2] = packing_opts["decScaleFactor"] 1573 elif packing == "png": 1574 drtnum = 41 1575 drtmpl[1] = packing_opts["binScaleFactor"] 1576 drtmpl[2] = packing_opts["decScaleFactor"] 1577 elif packing == "spectral-simple": 1578 drtnum = 50 1579 drtmpl[1] = packing_opts["binScaleFactor"] 1580 drtmpl[2] = packing_opts["decScaleFactor"] 1581 elif packing == "spectral-complex": 1582 drtnum = 51 1583 drtmpl[1] = packing_opts["binScaleFactor"] 1584 drtmpl[2] = packing_opts["decScaleFactor"] 1585 1586 pdtnum = pdtnum.value if isinstance(pdtnum,Grib2Metadata) else pdtnum 1587 1588 self._msg,self._pos = g2clib.grib2_addfield(self._msg, 1589 pdtnum, 1590 np.array(pdtmpl,dtype=DEFAULT_NUMPY_INT), 1591 crdlist, 1592 drtnum, 1593 drtmpl, 1594 np.ravel(fld), 1595 bitmapflag, 1596 bmap) 1597 self._sections.append(4) 1598 self._sections.append(5) 1599 if bmap is not None: self._sections.append(6) 1600 self._sections.append(7)
Add a Product Definition, Data Representation, Bitmap, and Data Sections
to Grib2Message
instance (i.e. Sections 4-7). Must be called after the grid
definition section has been added (addfield
).
Parameters
field
: Numpy array of data values to pack. If field is a masked array, then
a bitmap is created from the mask.
pdtnum
: integer Product Definition Template Number - Code Table 4.0
pdtmpl
: Sequence with the data values for the specified Product Definition
Template (N=pdtnum). Each element of this integer array contains an entry (in
the order specified) of Product Definition Template 4.N.
coordlist
: Sequence containing floating point values intended to document the
vertical discretization with model data on hybrid coordinate vertical levels. Default is None
.
packing
: String to specify the type of packing. Valid options are the following:
Packing Scheme | Description |
---|---|
'simple' | Simple packing |
'complex' | Complex packing |
'complex-spdiff' | Complex packing with Spatial Differencing |
'jpeg' | JPEG compression |
'png' | PNG compression |
'spectral-simple' | Spectral Data - Simple packing |
'spectral-complex' | Spectral Data - Complex packing |
**packing_opts
: Packing keyword arguments. The keywords are the same as Grib2Message attribute names for
the Data Representation Template (Section 5) metadata. Valid keywords per packing scheme are the following:
Packing Scheme | Keyword Arguments |
---|---|
'simple' | binScaleFactor , decScaleFactor |
'complex' | binScaleFactor , decScaleFactor , priMissingValue , [secMissingValue ] |
'complex-spdiff' | binScaleFactor , decScaleFactor , spatialDifferenceOrder , priMissingValue , [secMissingValue ] |
'jpeg' | binScaleFactor , decScaleFactor |
'png' | binScaleFactor , decScaleFactor |
'spectral-simple' | binScaleFactor , decScaleFactor |
'spectral-complex' | binScaleFactor , decScaleFactor |
1603 def end(self): 1604 """ 1605 Add End Section (Section 8) to the GRIB2 message. A GRIB2 message 1606 is not complete without an end section. Once an end section is added, 1607 the GRIB2 message can be written to file. 1608 """ 1609 self._msg, self._pos = g2clib.grib2_end(self._msg) 1610 self._sections.append(8)
Add End Section (Section 8) to the GRIB2 message. A GRIB2 message is not complete without an end section. Once an end section is added, the GRIB2 message can be written to file.
1612 def to_bytes(self, validate=True): 1613 """ 1614 Return grib data in byte format. Useful for exporting data in non-file formats. 1615 For example, can be used to output grib data directly to S3 using the boto3 client 1616 without the need to write a temporary file to upload first. 1617 1618 Parameters 1619 ---------- 1620 **`validate`**: bool (Default: True) If true, validates first/last four bytes for proper formatting, else 1621 returns None. If False, message is output as is. 1622 1623 Returns 1624 ------- 1625 Returns GRIB2 formatted message as bytes. 1626 """ 1627 if validate: 1628 if str(self._msg[0:4] + self._msg[-4:], 'utf-8') == 'GRIB7777': 1629 return self._msg 1630 else: 1631 return None 1632 else: 1633 return self._msg
Return grib data in byte format. Useful for exporting data in non-file formats. For example, can be used to output grib data directly to S3 using the boto3 client without the need to write a temporary file to upload first.
Parameters
validate
: bool (Default: True) If true, validates first/last four bytes for proper formatting, else
returns None. If False, message is output as is.
Returns
Returns GRIB2 formatted message as bytes.
1636class Grib2Metadata(): 1637 """ 1638 Class to hold GRIB2 metadata both as numeric code value as stored in 1639 GRIB2 and its plain langauge definition. 1640 1641 **`value : int`** 1642 1643 GRIB2 metadata integer code value. 1644 1645 **`table : str, optional`** 1646 1647 GRIB2 table to lookup the `value`. Default is None. 1648 """ 1649 def __init__(self, value, table=None): 1650 self.value = value 1651 self.table = table 1652 if self.table is None: 1653 self.definition = None 1654 else: 1655 self.definition = tables.get_value_from_table(self.value,self.table) 1656 def __call__(self): 1657 return self.value 1658 def __repr__(self): 1659 return '%s(%d, table = %s)' % (self.__class__.__name__,self.value,self.table) 1660 def __str__(self): 1661 return '%d - %s' % (self.value,self.definition) 1662 def __eq__(self,other): 1663 return self.value == other 1664 def __gt__(self,other): 1665 return self.value > other 1666 def __ge__(self,other): 1667 return self.value >= other 1668 def __lt__(self,other): 1669 return self.value < other 1670 def __le__(self,other): 1671 return self.value <= other 1672 def __contains__(self,other): 1673 return other in self.definition
Class to hold GRIB2 metadata both as numeric code value as stored in GRIB2 and its plain langauge definition.
value : int
GRIB2 metadata integer code value.
table : str, optional
GRIB2 table to lookup the value
. Default is None.
13def show_config(): 14 """ 15 Print grib2io build configuration information. 16 """ 17 from g2clib import __version__ as g2clib_version 18 have_jpeg = True if 'jasper' in __config__.libraries or 'openjp2' in __config__.libraries else False 19 have_png = True if 'png' in __config__.libraries else False 20 jpeglib = 'OpenJPEG' if 'openjp2' in __config__.libraries else ('Jasper' if 'jasper' in __config__.libraries else None) 21 pnglib = 'libpng' if 'png' in __config__.libraries else None 22 print("grib2io version %s Configuration:\n"%(__version__)) 23 print("\tg2c library version:".expandtabs(4),g2clib_version) 24 print("\tJPEG compression support:".expandtabs(4),have_jpeg) 25 if have_jpeg: print("\t\tLibrary:".expandtabs(4),jpeglib) 26 print("\tPNG compression support:".expandtabs(4),have_png) 27 if have_png: print("\t\tLibrary:".expandtabs(4),pnglib)
Print grib2io build configuration information.