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