/pygrib.pyx

http://pygrib.googlecode.com/ · Cython · 1509 lines · 1371 code · 66 blank · 72 comment · 324 complexity · acfb2d207daacd518fded0d0f0765311 MD5 · raw file

Large files are truncated click here to view the full file

  1. """
  2. Introduction
  3. ============
  4. Python module for reading and writing GRIB (editions 1 and 2) files.
  5. GRIB is the World Meterological Organization
  6. U{standard<http://www.wmo.ch/pages/prog/www/WMOCodes/GRIB.html>}
  7. for distributing gridded data.
  8. The module is a python interface to the
  9. U{GRIB API<http://www.ecmwf.int/products/data/software/grib_api.html>} C library
  10. from the European Centre for Medium-Range Weather Forecasts
  11. (U{ECMWF<http://www.ecmwf.int>}).
  12. Required
  13. ========
  14. - U{Python<http://python.org>} 2.4 or higher.
  15. - U{numpy<http://sourceforge.net/project/showfiles.php?group_id=1369>}
  16. N-dimensional array object for python. Version 1.2.1 or higher (version
  17. 1.5.1 required for Python 3).
  18. - U{pyproj<http://code.google.com/p/pyproj/>} Python interface to
  19. U{PROJ.4<http://trac.osgeo.org/proj>} library for cartographic
  20. transformations B{or} U{matplotlib<http://matplotlib.sf.net>} and
  21. the U{basemap<http://matplotlib.sf.net/basemap/doc/html>} toolkit.
  22. Pyproj 1.8.9 is required for Python 3.
  23. - U{GRIB API<http://www.ecmwf.int/products/data/software/grib_api.html>} C library
  24. for encoding and decoding GRIB messages (edition 1 and edition 2).
  25. Version 1.8.0 or higher required.
  26. To be fully functional, the GRIB API library requires
  27. U{Jasper<http://www.ece.uvic.ca/~mdadams/jasper>} or
  28. U{OpenJPEG<http://www.openjpeg.org>} for JPEG200 encoding,
  29. and U{PNG<http://www.libpng.org/pub/png/libpng.html>} for PNG encoding.
  30. These dependencies are available via the package management system of most
  31. Linux distributions, and on MacOS X using U{macports<http://macports.org/>}.
  32. If you build grib_api yourself as a static library on a 64-bit system
  33. you may need to set C{CFLAGS} to C{'-O2 -fPIC'} before running the C{configure}
  34. script. To use pygrib on Windows, you must use the
  35. U{Cygwin<http://cygwin.com>} environment, since the grib_api library requires a
  36. posix environment. Cygwin installation instructions are available
  37. U{here<http://code.google.com/p/pygrib/wiki/CygwinWindowsInstall>}.
  38. Installation
  39. ============
  40. - U{Download<http://code.google.com/p/pygrib/downloads/list>} the source code.
  41. - pygrib installation options can either be set with environment variables,
  42. or specified in a text file (setup.cfg). To use environment variables,
  43. set C{$GRIBAPI_DIR}, C{$JASPER_DIR}, C{$OPENJPEG_DIR},
  44. C{$PNG_DIR} and C{$ZLIB_DIR} so that the include files and libraries for
  45. GRIB API, Jasper, OpenJPEG, PNG and zlib will be found.
  46. For example, the include files for
  47. jasper should be found in C{$JASPER_DIR/include} or
  48. C{$JASPER_DIR/include/jasper}, and the jasper
  49. library should be found in C{$JASPER_DIR/lib} or C{$JASPER_DIR/lib64}. If any of
  50. those environment variables are not set, then it is assumed that GRIB API was
  51. not built with support for that library.
  52. If the libraries and
  53. include files are installed in separate locations, the environment variables
  54. C{$GRIBAPI_INCDIR} and C{$GRIBAPI_LIBDIR} can be used to define the locations
  55. separately (same goes for C{JASPER}, C{OPENJPEG}, C{PNG} and C{ZLIB}).
  56. To use setup.cfg, copy setup.cfg.template to setup.cfg, open setup.cfg in a
  57. text editor and follow the instructions in the comments for editing.
  58. - Run 'python setup.py build' and then 'python setup.py install', as root if necessary.
  59. Note that if you are using environment variables to specify the build options,
  60. you cannot build and install in one step with 'sudo python setup.py install',
  61. since sudo does not pass environment variables. Instead, run 'python setup.py
  62. build' first as a regular user, then run 'sudo python setup.py install' if the
  63. install directory requires admin or root privileges.
  64. - Run 'python test.py' to test your installation.
  65. - Look at examples in C{test} directory (most require
  66. U{matplotlib<http://matplotlib.sf.net>} and
  67. U{basemap<http://matplotlib.sourceforge.net/basemap/doc/html/>}).
  68. - If you're on MacOS X, see
  69. U{README.macosx<http://pygrib.googlecode.com/svn/trunk/README.macosx>}
  70. for special instruction on how to install pygrib and all it's
  71. dependencies using U{macports<http://macports.org>}.
  72. Example usage
  73. =============
  74. - from the python interpreter prompt, import the package::
  75. >>> import pygrib
  76. - open a GRIB file, create a grib message iterator::
  77. >>> grbs = pygrib.open('sampledata/flux.grb')
  78. - pygrib open instances behave like regular python file objects, with
  79. C{seek}, C{tell}, C{read}, C{readline} and C{close} methods, except that offsets
  80. are measured in grib messages instead of bytes::
  81. >>> grbs.seek(2)
  82. >>> grbs.tell()
  83. 2
  84. >>> grb = grbs.read(1)[0] # read returns a list with the next N (N=1 in this case) messages.
  85. >>> grb # printing a grib message object displays summary info
  86. 3:Maximum temperature:K (instant):regular_gg:heightAboveGround:level 2 m:fcst time 108-120 hrs:from 200402291200
  87. >>> grbs.tell()
  88. 3
  89. - print an inventory of the file::
  90. >>> grbs.seek(0)
  91. >>> for grb in grbs:
  92. >>> grb
  93. 1:Precipitation rate:kg m**-2 s**-1 (avg):regular_gg:surface:level 0:fcst time 108-120 hrs (avg):from 200402291200
  94. 2:Surface pressure:Pa (instant):regular_gg:surface:level 0:fcst time 120 hrs:from 200402291200
  95. 3:Maximum temperature:K (instant):regular_gg:heightAboveGround:level 2 m:fcst time 108-120 hrs:from 200402291200
  96. 4:Minimum temperature:K (instant):regular_gg:heightAboveGround:level 2 m:fcst time 108-120 hrs:from 200402291200
  97. - find the first grib message with a matching name::
  98. >>> grb = grbs.select(name='Maximum temperature')[0]
  99. - extract the data values using the 'values' key
  100. (grb.keys() will return a list of the available keys)::
  101. # The data is returned as a numpy array, or if missing values or a bitmap
  102. # are present, a numpy masked array. Reduced lat/lon or gaussian grid
  103. # data is automatically expanded to a regular grid. Details of the internal
  104. # representation of the grib data (such as the scanning mode) are handled
  105. # automatically.
  106. >>> maxt = grb.values # same as grb['values']
  107. >>> maxt.shape, maxt.min(), maxt.max()
  108. (94, 192) 223.7 319.9
  109. - get the latitudes and longitudes of the grid::
  110. >>> lats, lons = grb.latlons()
  111. >>> lats.shape, lats.min(), lats.max(), lons.shape, lons.min(), lons.max()
  112. (94, 192) -88.5419501373 88.5419501373 0.0 358.125
  113. - get the second grib message::
  114. >>> grb = grbs.message(2) # same as grbs.seek(1); grb=grbs.readline()
  115. >>> grb
  116. 2:Surface pressure:Pa (instant):regular_gg:surface:level 0:fcst time 120 hrs:from 200402291200
  117. - modify the values associated with existing keys (either via attribute or
  118. dictionary access)::
  119. >>> grb['forecastTime'] = 240
  120. >>> grb.dataDate = 20100101
  121. - get the binary string associated with the coded message::
  122. >>> msg = grb.tostring()
  123. >>> grbs.close() # close the grib file.
  124. - write the modified message to a new GRIB file::
  125. >>> grbout = open('test.grb','wb')
  126. >>> grbout.write(msg)
  127. >>> grbout.close()
  128. >>> pygrib.open('test.grb').readline()
  129. 1:Surface pressure:Pa (instant):regular_gg:surface:level 0:fcst time 240 hrs:from 201001011200
  130. Documentation
  131. =============
  132. - see below for the full python API documentation.
  133. Changelog
  134. =========
  135. - see U{Changelog<http://pygrib.googlecode.com/svn/trunk/Changelog>} file.
  136. @author: Jeffrey Whitaker.
  137. @contact: U{Jeff Whitaker<mailto:jeffrey.s.whitaker@noaa.gov>}
  138. @version: 1.9.4
  139. @copyright: copyright 2010 by Jeffrey Whitaker.
  140. @license: Permission to use, copy, modify, and distribute this software and its
  141. documentation for any purpose and without fee is hereby granted,
  142. provided that the above copyright notice appear in all copies and that
  143. both that copyright notice and this permission notice appear in
  144. supporting documentation.
  145. THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  146. INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
  147. EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  148. CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  149. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  150. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  151. PERFORMANCE OF THIS SOFTWARE."""
  152. __test__ = None
  153. del __test__ # hack so epydoc doesn't show __test__
  154. __version__ = '1.9.4'
  155. import numpy as np
  156. from datetime import datetime
  157. from numpy import ma
  158. try:
  159. import pyproj
  160. except ImportError:
  161. try:
  162. from mpl_toolkits.basemap import pyproj
  163. except:
  164. raise ImportError("either pyproj or basemap required")
  165. from ncepgrib2 import Grib2Decode
  166. import_array()
  167. cdef extern from "stdlib.h":
  168. ctypedef long size_t
  169. void *malloc(size_t size)
  170. void free(void *ptr)
  171. cdef extern from "stdio.h":
  172. ctypedef struct FILE
  173. FILE *fopen(char *path, char *mode)
  174. int fclose(FILE *)
  175. size_t fwrite(void *ptr, size_t size, size_t nitems, FILE *stream)
  176. void rewind (FILE *)
  177. cdef extern from "Python.h":
  178. object PyBytes_FromStringAndSize(char *s, size_t size)
  179. default_encoding = 'ascii'
  180. cdef extern from "numpy/arrayobject.h":
  181. ctypedef int npy_intp
  182. ctypedef extern class numpy.ndarray [object PyArrayObject]:
  183. cdef char *data
  184. cdef int nd
  185. cdef npy_intp *dimensions
  186. cdef npy_intp *strides
  187. cdef object base
  188. cdef int flags
  189. npy_intp PyArray_SIZE(ndarray arr)
  190. npy_intp PyArray_ISCONTIGUOUS(ndarray arr)
  191. npy_intp PyArray_ISALIGNED(ndarray arr)
  192. void import_array()
  193. cdef extern from "grib_api.h":
  194. ctypedef struct grib_handle
  195. ctypedef struct grib_index
  196. ctypedef struct grib_keys_iterator
  197. ctypedef struct grib_context
  198. cdef enum:
  199. GRIB_TYPE_UNDEFINED
  200. GRIB_TYPE_LONG
  201. GRIB_TYPE_DOUBLE
  202. GRIB_TYPE_STRING
  203. GRIB_TYPE_BYTES
  204. GRIB_TYPE_SECTION
  205. GRIB_TYPE_LABEL
  206. GRIB_TYPE_MISSING
  207. GRIB_KEYS_ITERATOR_ALL_KEYS
  208. GRIB_KEYS_ITERATOR_SKIP_READ_ONLY
  209. GRIB_KEYS_ITERATOR_SKIP_OPTIONAL
  210. GRIB_KEYS_ITERATOR_SKIP_EDITION_SPECIFIC
  211. GRIB_KEYS_ITERATOR_SKIP_CODED
  212. GRIB_KEYS_ITERATOR_SKIP_COMPUTED
  213. GRIB_KEYS_ITERATOR_SKIP_FUNCTION
  214. GRIB_KEYS_ITERATOR_SKIP_DUPLICATES
  215. GRIB_MISSING_LONG
  216. GRIB_MISSING_DOUBLE
  217. int grib_get_size(grib_handle *h, char *name, size_t *size)
  218. int grib_get_native_type(grib_handle *h, char *name, int *type)
  219. int grib_get_long(grib_handle *h, char *name, long *ival)
  220. int grib_set_long(grib_handle *h, char *name, long val)
  221. int grib_get_long_array(grib_handle *h, char *name, long *ival, size_t *size)
  222. int grib_set_long_array(grib_handle *h, char *name, long *ival, size_t size)
  223. int grib_get_double(grib_handle *h, char *name, double *dval)
  224. int grib_set_double(grib_handle *h, char *name, double dval)
  225. int grib_get_double_array(grib_handle *h, char *name, double *dval, size_t *size)
  226. int grib_set_double_array(grib_handle *h, char *name, double *dval, size_t size)
  227. int grib_get_string(grib_handle *h, char *name, char *mesg, size_t *size)
  228. int grib_set_string(grib_handle *h, char *name, char *mesg, size_t *size)
  229. grib_keys_iterator* grib_keys_iterator_new(grib_handle* h,unsigned long filter_flags, char* name_space)
  230. int grib_keys_iterator_next(grib_keys_iterator *kiter)
  231. char* grib_keys_iterator_get_name(grib_keys_iterator *kiter)
  232. int grib_handle_delete(grib_handle* h)
  233. grib_handle* grib_handle_new_from_file(grib_context* c, FILE* f, int* error)
  234. char* grib_get_error_message(int code)
  235. int grib_keys_iterator_delete( grib_keys_iterator* kiter)
  236. void grib_multi_support_on(grib_context* c)
  237. void grib_multi_support_off(grib_context* c)
  238. int grib_get_message(grib_handle* h , void** message,size_t *message_length)
  239. int grib_get_message_copy(grib_handle* h , void* message,size_t *message_length)
  240. long grib_get_api_version()
  241. grib_handle* grib_handle_clone(grib_handle* h)
  242. grib_index* grib_index_new_from_file(grib_context* c,\
  243. char* filename,char* keys,int *err)
  244. int grib_index_get_size(grib_index* index,char* key,size_t* size)
  245. int grib_index_get_long(grib_index* index,char* key,\
  246. long* values,size_t *size)
  247. int grib_index_get_double(grib_index* index,char* key,\
  248. double* values,size_t *size)
  249. int grib_index_get_string(grib_index* index,char* key,\
  250. char** values,size_t *size)
  251. int grib_index_select_long(grib_index* index,char* key,long value)
  252. int grib_index_select_double(grib_index* index,char* key,double value)
  253. int grib_index_select_string(grib_index* index,char* key,char* value)
  254. grib_handle* grib_handle_new_from_index(grib_index* index,int *err)
  255. void grib_index_delete(grib_index* index)
  256. int grib_is_missing(grib_handle* h, char* key, int* err)
  257. grib_handle* grib_handle_new_from_message(grib_context * c, void * data,\
  258. size_t data_len)
  259. grib_handle* grib_handle_new_from_message_copy(grib_context * c, void * data,\
  260. size_t data_len)
  261. int grib_julian_to_datetime(double jd, long *year, long *month, long *day, long *hour, long *minute, long *second)
  262. int grib_datetime_to_julian(long year, long month, long day, long hour, long minute, long second, double *jd)
  263. int grib_get_gaussian_latitudes(long truncation,double* latitudes)
  264. int grib_index_write(grib_index *index, char *filename)
  265. grib_index* grib_index_read(grib_context* c, char* filename,int *err)
  266. missingvalue_int = GRIB_MISSING_LONG
  267. #this doesn't work, since defined constants are assumed to be integers
  268. #missingvalue_float = GRIB_MISSING_DOUBLE
  269. missingvalue_float = -1.e100 # value given in grib_api.h version 1.90
  270. def _get_grib_api_version():
  271. div = lambda v,d: (v/d,v%d)
  272. v = grib_get_api_version()
  273. v,revision = div(v,100)
  274. v,minor = div(v,100)
  275. major = v
  276. return "%d.%d.%d" % (major,minor,revision)
  277. grib_api_version = _get_grib_api_version()
  278. def gaulats(object nlats):
  279. """
  280. gaulats(nlats)
  281. Returns nlats gaussian latitudes, in degrees, oriented from
  282. north to south. nlats must be even."""
  283. cdef ndarray lats
  284. if nlats%2:
  285. raise ValueError('nlats must be even')
  286. lats = np.empty(nlats, np.float64)
  287. grib_get_gaussian_latitudes(<long>nlats/2, <double *>lats.data)
  288. return lats
  289. # dict for forecast time units (Code Table 4.4).
  290. _ftimedict = {}
  291. _ftimedict[0]='mins'
  292. _ftimedict[1]='hrs'
  293. _ftimedict[2]='days'
  294. _ftimedict[3]='months'
  295. _ftimedict[4]='yrs'
  296. _ftimedict[5]='decades'
  297. _ftimedict[6]='30 yr periods'
  298. _ftimedict[7]='centuries'
  299. _ftimedict[10]='3 hr periods'
  300. _ftimedict[11]='6 hr periods'
  301. _ftimedict[12]='12 hr periods'
  302. _ftimedict[13]='secs'
  303. # turn on support for multi-field grib messages.
  304. grib_multi_support_on(NULL)
  305. cdef class open(object):
  306. """
  307. open(filename)
  308. returns GRIB file iterator object given GRIB filename. When iterated, returns
  309. instances of the L{gribmessage} class. Behaves much like a python file
  310. object, with L{seek}, L{tell}, L{read} and L{close} methods,
  311. except that offsets are measured in grib messages instead of bytes.
  312. Additional methods include L{rewind} (like C{seek(0)}), L{message}
  313. (like C{seek(N-1)}; followed by C{readline()}), and L{select} (filters
  314. messages based on specified conditions). The C{__call__} method forwards
  315. to L{select}, and instances can be sliced with C{__getitem__} (returning
  316. lists of L{gribmessage} instances). The position of the iterator is not
  317. altered by slicing with C{__getitem__}.
  318. @ivar messages: The total number of grib messages in the file.
  319. @ivar messagenumber: The grib message number that the iterator currently
  320. points to (the value returned by L{tell}).
  321. @ivar name: The GRIB file which the instance represents."""
  322. cdef FILE *_fd
  323. cdef grib_handle *_gh
  324. cdef public object name, messagenumber, messages, closed
  325. def __cinit__(self, filename):
  326. # initialize C level objects.
  327. cdef grib_handle *gh
  328. cdef FILE *_fd
  329. bytestr = _strencode(filename)
  330. self._fd = fopen(bytestr, "rb")
  331. if self._fd == NULL:
  332. raise IOError("could not open %s", filename)
  333. self._gh = NULL
  334. def __init__(self, filename):
  335. cdef int err
  336. cdef grib_handle *gh
  337. # initalize Python level objects
  338. self.name = filename
  339. self.closed = False
  340. self.messagenumber = 0
  341. # count number of messages in file.
  342. nmsgs = 0
  343. while 1:
  344. gh = grib_handle_new_from_file(NULL, self._fd, &err)
  345. err = grib_handle_delete(gh)
  346. if gh == NULL: break
  347. nmsgs = nmsgs + 1
  348. rewind(self._fd)
  349. self.messages = nmsgs
  350. def __iter__(self):
  351. return self
  352. def __next__(self):
  353. cdef grib_handle* gh
  354. cdef int err
  355. if self.messagenumber == self.messages:
  356. raise StopIteration
  357. if self._gh is not NULL:
  358. err = grib_handle_delete(self._gh)
  359. if err:
  360. raise RuntimeError(grib_get_error_message(err))
  361. gh = grib_handle_new_from_file(NULL, self._fd, &err)
  362. if err:
  363. raise RuntimeError(grib_get_error_message(err))
  364. if gh == NULL:
  365. raise StopIteration
  366. else:
  367. self._gh = gh
  368. self.messagenumber = self.messagenumber + 1
  369. return _create_gribmessage(self._gh, self.messagenumber)
  370. def __getitem__(self, key):
  371. if type(key) == slice:
  372. # for a slice, return a list of grib messages.
  373. beg, end, inc = key.indices(self.messages)
  374. msg = self.tell()
  375. grbs = [self.message(n+1) for n in xrange(beg,end,inc)]
  376. self.seek(msg) # put iterator back in original position
  377. return grbs
  378. elif type(key) == int or type(key) == long:
  379. # for an integer, return a single grib message.
  380. msg = self.tell()
  381. grb = self.message(key)
  382. self.seek(msg) # put iterator back in original position
  383. return grb
  384. else:
  385. raise KeyError('key must be an integer message number or a slice')
  386. def __call__(self, **kwargs):
  387. """same as L{select}"""
  388. return self.select(**kwargs)
  389. def __enter__(self):
  390. return self
  391. def __exit__(self,atype,value,traceback):
  392. self.close()
  393. def tell(self):
  394. """returns position of iterator (grib message number, 0 means iterator
  395. is positioned at beginning of file)."""
  396. return self.messagenumber
  397. def seek(self, msg, from_what=0):
  398. """
  399. seek(N,from_what=0)
  400. advance iterator N grib messages from beginning of file
  401. (if C{from_what=0}), from current position (if C{from_what=1})
  402. or from the end of file (if C{from_what=2})."""
  403. if from_what not in [0,1,2]:
  404. raise ValueError('from_what keyword arg to seek must be 0,1 or 2')
  405. if msg == 0:
  406. if from_what == 0:
  407. self.rewind()
  408. elif from_what == 1:
  409. return
  410. elif from_what == 2:
  411. self.message(self.messages)
  412. else:
  413. if from_what == 0:
  414. self.message(msg)
  415. elif from_what == 1:
  416. self.message(self.messagenumber+msg)
  417. elif from_what == 2:
  418. self.message(self.messages+msg)
  419. def readline(self):
  420. """
  421. readline()
  422. read one entire grib message from the file.
  423. Returns a L{gribmessage} instance, or None if an EOF is encountered."""
  424. try:
  425. if hasattr(self,'next'):
  426. grb = self.next()
  427. else:
  428. grb = next(self)
  429. except StopIteration:
  430. grb = None
  431. return grb
  432. def read(self,msgs=None):
  433. """
  434. read(N=None)
  435. read N messages from current position, returning grib messages instances in a
  436. list. If N=None, all the messages to the end of the file are read.
  437. C{pygrib.open(f).read()} is equivalent to C{list(pygrib.open(f))},
  438. both return a list containing L{gribmessage} instances for all the
  439. grib messages in the file C{f}.
  440. """
  441. if msgs is None:
  442. grbs = self._advance(self.messages-self.messagenumber,return_msgs=True)
  443. else:
  444. grbs = self._advance(msgs,return_msgs=True)
  445. return grbs
  446. def close(self):
  447. """
  448. close()
  449. close GRIB file, deallocate C structures associated with class instance"""
  450. # doesn't have __dealloc__, user must explicitly call close.
  451. # having both causes segfaults.
  452. cdef int err
  453. fclose(self._fd)
  454. if self._gh != NULL:
  455. err = grib_handle_delete(self._gh)
  456. if err:
  457. raise RuntimeError(grib_get_error_message(err))
  458. self.closed = True
  459. def rewind(self):
  460. """rewind iterator (same as seek(0))"""
  461. # before rewinding, move iterator to end of file
  462. # to make sure it is not left in a funky state
  463. # (such as in the middle of a multi-part message, issue 54)
  464. while 1:
  465. gh = grib_handle_new_from_file(NULL, self._fd, &err)
  466. err = grib_handle_delete(gh)
  467. if gh == NULL: break
  468. rewind(self._fd)
  469. self.messagenumber = 0
  470. def message(self, N):
  471. """
  472. message(N)
  473. retrieve N'th message in iterator.
  474. same as seek(N-1) followed by readline()."""
  475. if N < 1:
  476. raise IOError('grb message numbers start at 1')
  477. # if iterator positioned past message N, reposition at beginning.
  478. if self.messagenumber >= N:
  479. self.rewind()
  480. # move iterator forward to message N.
  481. self._advance(N-self.messagenumber)
  482. return _create_gribmessage(self._gh, self.messagenumber)
  483. def select(self, **kwargs):
  484. """
  485. select(**kwargs)
  486. return a list of L{gribmessage} instances from iterator filtered by kwargs.
  487. If keyword is a container object, each grib message
  488. in the iterator is searched for membership in the container.
  489. If keyword is a callable (has a _call__ method), each grib
  490. message in the iterator is tested using the callable (which should
  491. return a boolean).
  492. If keyword is not a container object or a callable, each
  493. grib message in the iterator is tested for equality.
  494. Example usage:
  495. >>> import pygrib
  496. >>> grbs = pygrib.open('sampledata/gfs.grb')
  497. >>> selected_grbs=grbs.select(shortName='gh',typeOfLevel='isobaricInhPa',level=10)
  498. >>> for grb in selected_grbs: grb
  499. 26:Geopotential height:gpm (instant):regular_ll:isobaricInhPa:level 10 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  500. >>> # the __call__ method does the same thing
  501. >>> selected_grbs=grbs(shortName='gh',typeOfLevel='isobaricInhPa',level=10)
  502. >>> for grb in selected_grbs: grb
  503. 26:Geopotential height:gpm (instant):regular_ll:isobaricInhPa:level 10 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  504. >>> # to select multiple specific key values, use containers (e.g. sequences)
  505. >>> selected_grbs=grbs(shortName=['u','v'],typeOfLevel='isobaricInhPa',level=[10,50])
  506. >>> for grb in selected_grbs: grb
  507. 193:u-component of wind:m s**-1 (instant):regular_ll:isobaricInhPa:level 50 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  508. 194:v-component of wind:m s**-1 (instant):regular_ll:isobaricInhPa:level 50 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  509. 199:u-component of wind:m s**-1 (instant):regular_ll:isobaricInhPa:level 10 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  510. 200:v-component of wind:m s**-1 (instant):regular_ll:isobaricInhPa:level 10 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  511. >>> # to select key values based on a conditional expression, use a function
  512. >>> selected_grbs=grbs(shortName='gh',level=lambda l: l < 500 and l >= 300)
  513. >>> for grb in selected_grbs: grb
  514. 14:Geopotential height:gpm (instant):regular_ll:isobaricInhPa:level 45000 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  515. 15:Geopotential height:gpm (instant):regular_ll:isobaricInhPa:level 40000 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  516. 16:Geopotential height:gpm (instant):regular_ll:isobaricInhPa:level 35000 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  517. 17:Geopotential height:gpm (instant):regular_ll:isobaricInhPa:level 30000 Pa:fcst time 72 hrs:from 200412091200:lo res cntl fcst
  518. """
  519. msgnum = self.tell()
  520. self.rewind() # always search from beginning
  521. grbs = [grb for grb in self if _find(grb, **kwargs)]
  522. self.seek(msgnum) # leave iterator in original position.
  523. return grbs
  524. def _advance(self,nmsgs,return_msgs=False):
  525. """advance iterator n messages from current position.
  526. if return_msgs==True, grib message instances are returned
  527. in a list"""
  528. cdef int err
  529. if nmsgs < 0:
  530. raise ValueError('nmsgs must be >= 0 in _advance')
  531. if return_msgs: grbs=[]
  532. for n in range(self.messagenumber,self.messagenumber+nmsgs):
  533. err = grib_handle_delete(self._gh)
  534. if err:
  535. raise RuntimeError(grib_get_error_message(err))
  536. self._gh = grib_handle_new_from_file(NULL, self._fd, &err)
  537. if err:
  538. raise RuntimeError(grib_get_error_message(err))
  539. if self._gh == NULL:
  540. raise IOError('not that many messages in file')
  541. self.messagenumber = self.messagenumber + 1
  542. if return_msgs: grbs.append(_create_gribmessage(self._gh, self.messagenumber))
  543. if return_msgs: return grbs
  544. # keep track of python gribmessage attributes so they can be
  545. # distinguished from grib keys.
  546. _private_atts =\
  547. ['_gh','fcstimeunits','expand_reduced','projparams','messagenumber','_all_keys','_ro_keys']
  548. def julian_to_datetime(object jd):
  549. """
  550. julian_to_datetime(julday)
  551. convert Julian day number to python datetime instance.
  552. Used to create validDate and analDate attributes from
  553. julianDay and forecastTime keys."""
  554. cdef double julday
  555. cdef long year, month, day, hour, minute, second
  556. cdef int err
  557. julday = jd
  558. err = grib_julian_to_datetime(julday, &year, &month, &day, &hour, &minute, &second)
  559. if err:
  560. raise RuntimeError(grib_get_error_message(err))
  561. return datetime(year, month, day, hour, minute, second)
  562. def datetime_to_julian(object d):
  563. """
  564. datetime_to_julian(date)
  565. convert python datetime instance to Julian day number."""
  566. cdef double julday
  567. cdef int err
  568. cdef long year, month, day, hour, minute, second
  569. year = d.year; month = d.month; day = d.day; hour = d.hour
  570. minute = d.minute; second = d.second
  571. err = grib_datetime_to_julian(year,month,day,hour,minute,second,&julday)
  572. if err:
  573. raise RuntimeError(grib_get_error_message(err))
  574. return julday
  575. cdef _create_gribmessage(grib_handle *gh, object messagenumber):
  576. """factory function for creating gribmessage instances"""
  577. cdef gribmessage grb = gribmessage.__new__(gribmessage)
  578. grb.messagenumber = messagenumber
  579. grb.expand_reduced = True
  580. grb._gh = grib_handle_clone(gh)
  581. grb._all_keys = grb.keys()
  582. grb._ro_keys = grb._read_only_keys()
  583. grb._set_projparams() # set projection parameter dict.
  584. return setdates(grb)
  585. def fromstring(gribstring):
  586. """
  587. fromstring(string)
  588. Create a gribmessage instance from a python bytes object
  589. representing a binary grib message (the reverse of L{gribmessage.tostring}).
  590. """
  591. cdef char* gribstr
  592. cdef grib_handle * gh
  593. cdef gribmessage grb
  594. gribstr = gribstring
  595. gh = grib_handle_new_from_message_copy(NULL, <void *>gribstr, len(gribstring))
  596. grb = gribmessage.__new__(gribmessage)
  597. grb.messagenumber = 1
  598. grb.expand_reduced = True
  599. grb._gh = gh
  600. grb._all_keys = grb.keys()
  601. grb._ro_keys = grb._read_only_keys()
  602. grb._set_projparams() # set projection parameter dict.
  603. return setdates(grb)
  604. def setdates(gribmessage grb):
  605. """
  606. setdates(grb)
  607. set fcstimeunits, analDate and validDate attributes using
  608. julianDay, forecastTime and indicatorOfUnitOfTimeRange.
  609. Called automatically when gribmessage instance created,
  610. but can be called manually to update keys if one of
  611. them is modified after instance creation.
  612. """
  613. grb.fcstimeunits = ""
  614. if grb.has_key('indicatorOfUnitOfTimeRange') and\
  615. grb.indicatorOfUnitOfTimeRange in _ftimedict:
  616. grb.fcstimeunits = _ftimedict[grb.indicatorOfUnitOfTimeRange]
  617. if grb.has_key('forecastTime'):
  618. if grb.has_key('stepRange'):
  619. # this is a hack to work around grib_api bug
  620. # sometimes stepUnits and indicatorOfUnitOfTimeRange
  621. # are inconsistent.
  622. grb.stepUnits = grb.indicatorOfUnitOfTimeRange
  623. ftime = grb['stepRange'] # computed key, uses stepUnits
  624. # if it's a range, use the end of the range to define validDate
  625. try:
  626. ftime = float(ftime.split('-')[1])
  627. except:
  628. ftime = grb.forecastTime
  629. else:
  630. ftime = grb.forecastTime
  631. else:
  632. ftime = 0
  633. if grb.has_key('julianDay'):
  634. grb.analDate =\
  635. julian_to_datetime(grb.julianDay)
  636. if grb.fcstimeunits == 'hrs':
  637. grb.validDate =\
  638. julian_to_datetime(grb.julianDay+ftime/24.)
  639. elif grb.fcstimeunits == 'mins':
  640. grb.validDate =\
  641. julian_to_datetime(grb.julianDay+ftime/1440.)
  642. elif grb.fcstimeunits == 'days':
  643. grb.validDate =\
  644. julian_to_datetime(grb.julianDay+ftime)
  645. elif grb.fcstimeunits == 'secs':
  646. grb.validDate =\
  647. julian_to_datetime(grb.julianDay+ftime/86400.)
  648. elif grb.fcstimeunits == '3 hr periods':
  649. grb.validDate =\
  650. julian_to_datetime(grb.julianDay+ftime/8.)
  651. elif grb.fcstimeunits == '6 hr periods':
  652. grb.validDate =\
  653. julian_to_datetime(grb.julianDay+ftime/4.)
  654. elif grb.fcstimeunits == '12 hr periods':
  655. grb.validDate =\
  656. julian_to_datetime(grb.julianDay+ftime/2.)
  657. return grb
  658. def reload(gribmessage grb):
  659. """
  660. reload(grb)
  661. Recreate gribmessage object, updating all the keys to be consistent
  662. with each other. For example, if the forecastTime key is changed,
  663. recreating the gribmessage object with this function will cause
  664. the analDate and verifDate keys to be updated accordingly.
  665. Equivalent to fromstring(grb.tostring())"""
  666. return fromstring(grb.tostring())
  667. cdef class gribmessage(object):
  668. """
  669. Grib message object.
  670. Each grib message has attributes corresponding to grib message
  671. keys for
  672. U{GRIB1 <http://www.ecmwf.int/publications/manuals/d/gribapi/fm92/grib1>}
  673. and
  674. U{GRIB2 <http://www.ecmwf.int/publications/manuals/d/gribapi/fm92/grib2/>}.
  675. Parameter names are
  676. are given by the C{name}, C{shortName} and C{paramID}
  677. U{keys <http://www.ecmwf.int/publications/manuals/d/gribapi/param/>}.
  678. pygrib also defines some special attributes which are defined below
  679. under the heading B{Instance Variables}.
  680. @ivar messagenumber: The grib message number in the file.
  681. @ivar projparams: A dictionary containing proj4 key/value pairs describing
  682. the grid. Set to C{None} for unsupported grid types.
  683. @ivar expand_reduced: If True (default), reduced lat/lon and gaussian grids
  684. will be expanded to regular grids when data is accessed via "values" key. If
  685. False, data is kept on unstructured reduced grid, and is returned in a 1-d
  686. array.
  687. @ivar fcstimeunits: A string representing the forecast time units
  688. (an empty string if not defined).
  689. @ivar analDate: A python datetime instance describing the analysis date
  690. and time for the forecast. Only set if forecastTime and julianDay keys
  691. exist.
  692. @ivar validDate: A python datetime instance describing the valid date
  693. and time for the forecast. Only set if forecastTime and julianDay keys
  694. exist, and fcstimeunits is defined. If forecast time
  695. is a range, then C{validDate} corresponds to the end of the range."""
  696. cdef grib_handle *_gh
  697. cdef public messagenumber, projparams, validDate, analDate,\
  698. expand_reduced, _ro_keys, _all_keys, fcstimeunits
  699. def __init__(self):
  700. # calling "__new__()" will not call "__init__()" !
  701. raise TypeError("This class cannot be instantiated from Python")
  702. def __dealloc__(self):
  703. # finalization (inverse of __cinit__): needed to allow garbage collector to free memory.
  704. cdef int err
  705. err = grib_handle_delete(self._gh)
  706. def __getattr__(self, item):
  707. # allow gribmessage keys to accessed like attributes.
  708. # this is tried after looking for item in self.__dict__.keys().
  709. try:
  710. return self.__getitem__(item)
  711. except KeyError:
  712. raise AttributeError(item)
  713. def __setattr__(self, name, value):
  714. # allow gribmessage keys to be set like attributes.
  715. if name not in _private_atts:
  716. # these are grib message keys
  717. self[name] = value
  718. else:
  719. # these are python attributes.
  720. self.__dict__[name]=value
  721. def __repr__(self):
  722. """prints a short inventory of the grib message"""
  723. inventory = []
  724. if self.valid_key('name'):
  725. if self['name'] != 'unknown':
  726. inventory.append(repr(self.messagenumber)+':'+self['name'])
  727. elif self.valid_key('parameterName'):
  728. inventory.append(repr(self.messagenumber)+':'+self['parameterName'])
  729. if self.valid_key('units'):
  730. if self['units'] != 'unknown':
  731. inventory.append(':'+self['units'])
  732. elif self.valid_key('parameterUnits'):
  733. inventory.append(':'+self['parameterUnits'])
  734. if self.valid_key('stepType'):
  735. inventory.append(' ('+self['stepType']+')')
  736. if self.valid_key('typeOfGrid') or self.valid_key('gridType'):
  737. if self.valid_key('typeOfGrid'):
  738. inventory.append(':'+self['typeOfGrid'])
  739. else:
  740. inventory.append(':'+self['gridType'])
  741. if self.valid_key('typeOfLevel'):
  742. inventory.append(':'+self['typeOfLevel'])
  743. if self.valid_key('topLevel') and self.valid_key('bottomLevel'):
  744. toplev = None; botlev = None
  745. levunits = 'unknown'
  746. if self.valid_key('unitsOfFirstFixedSurface'):
  747. levunits = self['unitsOfFirstFixedSurface']
  748. if self.valid_key('typeOfFirstFixedSurface') and self['typeOfFirstFixedSurface'] != 255:
  749. toplev = self['topLevel']
  750. if self.valid_key('scaledValueOfFirstFixedSurface') and\
  751. self.valid_key('scaleFactorOfFirstFixedSurface'):
  752. if self['scaleFactorOfFirstFixedSurface']:
  753. toplev = self['scaledValueOfFirstFixedSurface']/\
  754. np.power(10.0,self['scaleFactorOfFirstFixedSurface'])
  755. else:
  756. toplev = self['scaledValueOfFirstFixedSurface']
  757. if self.valid_key('typeOfSecondFixedSurface') and self['typeOfSecondFixedSurface'] != 255:
  758. botlev = self['bottomLevel']
  759. if self.valid_key('scaledValueOfSecondFixedSurface') and\
  760. self.valid_key('scaleFactorOfSecondFixedSurface'):
  761. if self['scaleFactorOfSecondFixedSurface']:
  762. botlev = self['scaledValueOfSecondFixedSurface']/\
  763. np.power(10.0,self['scaleFactorOfSecondFixedSurface'])
  764. else:
  765. botlev = self['scaledValueOfSecondFixedSurface']
  766. levstring = None
  767. if botlev is None or toplev == botlev:
  768. levstring = ':level %s' % toplev
  769. else:
  770. levstring = ':levels %s-%s' % (toplev,botlev)
  771. if levunits != 'unknown':
  772. levstring = levstring+' %s' % levunits
  773. if levstring is not None:
  774. inventory.append(levstring)
  775. elif self.valid_key('level'):
  776. inventory.append(':level %s' % self['level'])
  777. if self.has_key('stepRange'):
  778. ftime = self['stepRange'] # computed key, uses stepUnits
  779. if self.valid_key('stepType') and self['stepType'] != 'instant':
  780. inventory.append(':fcst time %s %s (%s)'%\
  781. (ftime,self.fcstimeunits,self.stepType))
  782. else:
  783. inventory.append(':fcst time %s %s'% (ftime,self.fcstimeunits))
  784. elif self.valid_key('forecastTime'):
  785. ftime = repr(self['forecastTime'])
  786. inventory.append(':fcst time %s %s'% (ftime,self.fcstimeunits))
  787. if self.valid_key('dataDate') and self.valid_key('dataTime'):
  788. inventory.append(
  789. ':from '+repr(self['dataDate'])+'%04i' % self['dataTime'])
  790. #if self.valid_key('validityDate') and self.valid_key('validityTime'):
  791. # inventory.append(
  792. # ':valid '+repr(self['validityDate'])+repr(self['validityTime']))
  793. if self.valid_key('perturbationNumber') and\
  794. self.valid_key('typeOfEnsembleForecast'):
  795. ens_type = self['typeOfEnsembleForecast']
  796. pert_num = self['perturbationNumber']
  797. if ens_type == 0:
  798. inventory.append(":lo res cntl fcst")
  799. elif ens_type == 1:
  800. inventory.append(":hi res cntl fcst")
  801. elif ens_type == 2:
  802. inventory.append(":neg ens pert %d" % pert_num)
  803. elif ens_type == 3:
  804. inventory.append(":pos ens pert %d" % pert_num)
  805. if self.valid_key('derivedForecast'):
  806. if self['derivedForecast'] == 0:
  807. inventory.append(":ens mean")
  808. elif self['derivedForecast'] == 1:
  809. inventory.append(":weighted ens mean")
  810. elif self['derivedForecast'] == 2:
  811. inventory.append(":ens std dev")
  812. elif self['derivedForecast'] == 3:
  813. inventory.append(":normalized ens std dev")
  814. elif self['derivedForecast'] == 4:
  815. inventory.append(":ens spread")
  816. elif self['derivedForecast'] == 5:
  817. inventory.append(":ens large anomaly index")
  818. elif self['derivedForecast'] == 6:
  819. inventory.append(":ens mean of cluster")
  820. if self.valid_key('probabilityTypeName'):
  821. inventory.append(":"+self['probabilityTypeName'])
  822. lowerlim = None
  823. if self.valid_key('scaledValueOfLowerLimit') and\
  824. self.valid_key('scaleFactorOfLowerLimit'):
  825. if self['scaledValueOfLowerLimit'] and\
  826. self['scaleFactorOfLowerLimit']:
  827. lowerlim = self['scaledValueOfLowerLimit']/\
  828. np.power(10.0,self['scaleFactorOfLowerLimit'])
  829. upperlim = None
  830. if self.valid_key('scaledValueOfUpperLimit') and\
  831. self.valid_key('scaleFactorOfUpperLimit'):
  832. if self['scaledValueOfUpperLimit'] and\
  833. self['scaleFactorOfUpperLimit']:
  834. upperlim = self['scaledValueOfUpperLimit']/\
  835. np.power(10.0,self['scaleFactorOfUpperLimit'])
  836. if upperlim is not None and lowerlim is not None:
  837. inventory.append(" (%s-%s)" % (upperlim,lowerlim))
  838. elif upperlim is not None:
  839. inventory.append(" (> %s)" % upperlim)
  840. elif lowerlim is not None:
  841. inventory.append(" (< %s)" % lowerlim)
  842. return ''.join(inventory)
  843. def is_missing(self,key):
  844. """
  845. is_missing(key)
  846. returns True if value associated with key is equal
  847. to grib missing value flag (False otherwise)"""
  848. cdef int err,miss
  849. cdef char *name
  850. bytestr = _strencode(key)
  851. name = bytestr
  852. miss = grib_is_missing(self._gh, name, &err)
  853. if err:
  854. raise RuntimeError(grib_get_error_message(err))
  855. if miss:
  856. return True
  857. else:
  858. return False
  859. def keys(self):
  860. """
  861. keys()
  862. return keys associated with a grib message (a dictionary-like object)
  863. """
  864. cdef grib_keys_iterator* gi
  865. cdef int err, typ
  866. cdef char *name
  867. # use cached keys if they exist.
  868. if self._all_keys is not None: return self._all_keys
  869. # if not, get keys from grib file.
  870. gi = grib_keys_iterator_new(self._gh,\
  871. GRIB_KEYS_ITERATOR_ALL_KEYS, NULL)
  872. keys = []
  873. while grib_keys_iterator_next(gi):
  874. name = grib_keys_iterator_get_name(gi)
  875. key = name.decode('ascii')
  876. # ignore these keys.
  877. if key in ["zero","one","eight","eleven","false","thousand","file",
  878. "localDir","7777","oneThousand"]:
  879. continue
  880. err = grib_get_native_type(self._gh, name, &typ)
  881. if err: # skip unreadable keys
  882. continue
  883. # keys with these types are ignored.
  884. if typ not in\
  885. [GRIB_TYPE_UNDEFINED,GRIB_TYPE_SECTION,GRIB_TYPE_BYTES,GRIB_TYPE_LABEL,GRIB_TYPE_MISSING]:
  886. keys.append(key)
  887. err = grib_keys_iterator_delete(gi)
  888. if err:
  889. raise RuntimeError(grib_get_error_message(err))
  890. return keys
  891. def _read_only_keys(self):
  892. """
  893. _read_only_keys()
  894. return read-only keys associated with a grib message (a dictionary-like object)
  895. """
  896. cdef grib_keys_iterator* gi
  897. cdef int err, typ
  898. cdef char *name
  899. if self._all_keys is None:
  900. self._all_keys = self.keys()
  901. gi = grib_keys_iterator_new(self._gh,\
  902. GRIB_KEYS_ITERATOR_SKIP_READ_ONLY, NULL)
  903. keys_noro = []
  904. while grib_keys_iterator_next(gi):
  905. name = grib_keys_iterator_get_name(gi)
  906. key = name.decode('ascii')
  907. keys_noro.append(key)
  908. err = grib_keys_iterator_delete(gi)
  909. if err:
  910. raise RuntimeError(grib_get_error_message(err))
  911. keys_ro = []
  912. for key in self._all_keys:
  913. if key not in keys_noro:
  914. keys_ro.append(key)
  915. return keys_ro
  916. def __setitem__(self, key, value):
  917. """
  918. change values associated with existing grib keys.
  919. """
  920. cdef int err, typ
  921. cdef size_t size
  922. cdef char *name
  923. cdef long longval
  924. cdef double doubleval
  925. cdef ndarray datarr
  926. cdef char *strdata
  927. if key in self._ro_keys:
  928. raise KeyError('key "%s" is read only' % key)
  929. if key not in self._all_keys:
  930. raise KeyError('can only modify existing grib keys (key "%s" not found)'
  931. % key )
  932. bytestr = _strencode(key)
  933. name = bytestr
  934. err = grib_get_native_type(self._gh, name, &typ)
  935. if err:
  936. raise RuntimeError(grib_get_error_message(err))
  937. elif typ == GRIB_TYPE_LONG:
  938. # is value an array or a scalar?
  939. datarr = np.asarray(value, np.int)
  940. is_array = False
  941. if datarr.shape:
  942. is_array = True
  943. if not is_array: # scalar
  944. longval = value
  945. err = grib_set_long(self._gh, name, longval)
  946. if err:
  947. raise RuntimeError(grib_get_error_message(err))
  948. else:
  949. if key == 'values':
  950. datarr = self._unshape_mask(datarr)
  951. if not PyArray_ISCONTIGUOUS(datarr):
  952. datarr = datarr.copy()
  953. size = datarr.size
  954. err = grib_set_long_array(self._gh, name, <long *>datarr.data, size)
  955. if err:
  956. raise RuntimeError(grib_get_error_message(err))
  957. elif typ == GRIB_TYPE_DOUBLE:
  958. # is value an array or a scalar?
  959. datarr = np.asarray(value, np.float)
  960. is_array = False
  961. if datarr.shape:
  962. is_array = True
  963. if not is_array: # scalar
  964. doubleval = value
  965. err = grib_set_double(self._gh, name, doubleval)
  966. if err:
  967. raise RuntimeError(grib_get_error_message(err))
  968. else:
  969. if key == 'values':
  970. datarr = self._unshape_mask(datarr)
  971. if not PyArray_ISCONTIGUOUS(datarr):
  972. datarr = datarr.copy()
  973. size = datarr.size
  974. err = grib_set_double_array(self._gh, name, <double *>datarr.data, size)
  975. if err:
  976. raise RuntimeError(grib_get_error_message(err))
  977. elif typ == GRIB_TYPE_STRING:
  978. size=len(value)
  979. bytestr = _strencode(value)
  980. strdata = bytestr
  981. err = grib_set_string(self._gh, name, strdata, &size)
  982. if err:
  983. raise RuntimeError(grib_get_error_message(err))
  984. else:
  985. raise ValueError("unrecognized grib type % d" % typ)
  986. def __getitem__(self, key):
  987. """
  988. access values associated with grib keys.
  989. The key "values" will return the data associated with the grib message.
  990. The data is returned as a numpy array, or if missing values or a bitmap
  991. are present, a numpy masked array. Reduced lat/lon or gaussian grid
  992. data is automatically expanded to a regular grid using linear
  993. interpolation (nearest neighbor if an adjacent grid point is a missing
  994. value)."""
  995. cdef int err, typ
  996. cdef size_t size
  997. cdef char *name
  998. cdef long longval
  999. cdef double doubleval
  1000. cdef ndarray datarr
  1001. cdef char strdata[1024]
  1002. bytestr = _strencode(key)
  1003. name = bytestr
  1004. usenceplib = key == 'values' and self.packingType.startswith('grid_complex')
  1005. # this workaround only needed for grib_api < 1.9.16.
  1006. if usenceplib:
  1007. size = self.numberOfValues
  1008. else:
  1009. err = grib_get_size(self._gh, name, &size)
  1010. if err:
  1011. raise RuntimeError(grib_get_error_message(err))
  1012. # this workaround only needed for grib_api < 1.9.16.
  1013. if usenceplib:
  1014. typ = 2
  1015. err = 0
  1016. else:
  1017. err = grib_get_native_type(self._gh, name, &typ)
  1018. # force 'paramId' to be size 1 (it returns a size of 7,
  1019. # which is a relic from earlier versions of grib_api in which
  1020. # paramId was a string and not an integer)
  1021. if key=='paramId' and typ == GRIB_TYPE_LONG: size=1
  1022. if err:
  1023. raise RuntimeError(grib_get_error_message(err))
  1024. elif typ == GRIB_TYPE_LONG:
  1025. if size == 1: # scalar
  1026. err = grib_get_long(self._gh, name, &longval)
  1027. if err:
  1028. raise RuntimeError(grib_get_error_message(err))
  1029. return longval
  1030. else: # array
  1031. if self.has_key('jPointsAreConsecutive') and\
  1032. self['jPointsAreConsecutive']:
  1033. storageorder='F'
  1034. else:
  1035. storageorder='C'
  1036. datarr = np.zeros(size, np.int, order=storageorder)
  1037. err = grib_get_long_array(self._gh, name, <long *>datarr.data, &size)
  1038. if err:
  1039. raise RuntimeError(grib_get_error_message(err))
  1040. if key == 'values':
  1041. return self._reshape_mask(datarr)
  1042. else:
  1043. return datarr
  1044. elif typ == GRIB_TYPE_DOUBLE:
  1045. if size == 1: # scalar
  1046. err = grib_get_double(self._gh, name, &doubleval)
  1047. if err:
  1048. raise RuntimeError(grib_get_error_message(err))
  1049. return doubleval
  1050. else: # array
  1051. if self.has_key('jPointsAreConsecutive') and\
  1052. self['jPointsAreConsecutive']:
  1053. storageorder='F'
  1054. else:
  1055. storageorder='C'
  1056. if usenceplib:
  1057. # use ncep lib to decode data (workaround for grib_api
  1058. # bug with second-order complex packing).
  1059. grb = Grib2Decode(self.tostring(), gribmsg=True)
  1060. return grb.data()
  1061. else:
  1062. datarr = np.zeros(size, np.double, order=storageorder)
  1063. err = grib_get_double_array(self._gh, name, <double *>datarr.data, &size)
  1064. if err:
  1065. raise RuntimeError(grib_get_error_message(err))
  1066. if key == 'values':
  1067. return self._reshape_mask(datarr)
  1068. else:
  1069. return datarr
  1070. elif typ == GRIB_TYPE_STRING:
  1071. size=1024 # grib_get_size returns 1 ?
  1072. err = grib_get_string(self._gh, name, strdata, &size)
  1073. if err:
  1074. raise RuntimeError(grib_get_error_message(err))
  1075. msg = strdata.decode(default_encoding)
  1076. return msg.rstrip()
  1077. else:
  1078. raise ValueError("unrecognized grib type % d" % typ)
  1079. def has_key(self,key):
  1080. """
  1081. has_key(key)
  1082. tests whether a grib message object has a specified key.
  1083. """
  1084. return key in self._all_keys
  1085. def valid_key(self,key):
  1086. """
  1087. valid_key(key)
  1088. tests whether a grib