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