Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
Changelog for eccodes-python
============================

0.9.2 (unreleased)
0.9.2
------------------

- Nothing changed yet.

- All ecCodes tests now pass
- Simplify the xx_new_from_file calls
- Fix for grib_set_string_array
- Use ECCODES_DIR to locate the library

0.9.1 (2019-06-06)
------------------
Expand Down
8 changes: 3 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ Limitations:
- *CFFI* ABI level, in-line mode is almost twice as slow as the original *ecCodes* bindings,
- only experimental support for the much faster *CFFI* API level, out-of-line mode,
- *PyPI* binary packages do not include *ecCodes*,
- incomplete documentation, for now,
- no Windows support, for now.
- Microsoft Windows support is untested.


Installation
Expand Down Expand Up @@ -71,15 +70,14 @@ Fast bindings
-------------

To test the much faster *CFFI* API level, out-of-line mode you need the *ecCodes*
source tree available, because we need two include files ``grib_api_internal.h``
and ``grib_api_prototypes.h`` that are not installed by default.
header files.
Then you need to clone the repo in the same folder as your *ecCodes* source tree,
make a ``pip`` development install and custom compile the binary bindings::

$ git clone https://github.com/ecmwf/eccodes-python
$ cd eccodes-python
$ pip install -e .
$ CPPFLAGS=-I../eccodes/src python builder.py
$ python builder.py

To revert back to ABI level, in-line more just remove the compiled bindings::

Expand Down
5 changes: 3 additions & 2 deletions builder.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import logging

import cffi
import sys

ffibuilder = cffi.FFI()
ffibuilder.set_source(
"gribapi._bindings",
'#include "grib_api_internal.h"\n#include <eccodes.h>',
'#include <eccodes.h>',
libraries=["eccodes"],
)
ffibuilder.cdef(
open("gribapi/grib_api.h").read() +
open("gribapi/grib_api_prototypes.h").read() +
open("gribapi/eccodes.h").read()
)

Expand All @@ -19,3 +19,4 @@
ffibuilder.compile(verbose=True)
except Exception:
logging.exception("can't compile ecCodes bindings")
sys.exit(1)
2 changes: 1 addition & 1 deletion gribapi/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

import cffi

__version__ = '0.9.2.dev0'
__version__ = '0.9.2'

LOG = logging.getLogger(__name__)

Expand Down
45 changes: 45 additions & 0 deletions gribapi/grib_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ typedef struct grib_context grib_context;
*/
typedef struct grib_iterator grib_iterator;

typedef struct grib_nearest grib_nearest;

/*! Grib keys iterator. Iterator over keys.
\ingroup keys_iterator
*/
Expand Down Expand Up @@ -386,6 +388,48 @@ int grib_iterator_next(grib_iterator *i, double* lat,double* lon,double* value);
*/
int grib_iterator_delete(grib_iterator *i);

/*!
* \brief Create a new nearest from a handle, using current geometry .
*
* \param h : the handle from which the iterator will be created
* \param error : error code
* \return the new nearest, NULL if no nearest can be created
*/
grib_nearest* grib_nearest_new(grib_handle* h, int* error);

/**
* Find the 4 nearest points of a latitude longitude point.
* The flags are provided to speed up the process of searching. If you are
* sure that the point you are asking for is not changing from a call
* to another you can use GRIB_NEAREST_SAME_POINT. The same is valid for
* the grid. Flags can be used together doing a bitwise OR.
* The distances are given in kilometres.
*
* @param nearest : nearest structure
* @param h : handle from which geography and data values are taken
* @param inlat : latitude of the point to search for
* @param inlon : longitude of the point to search for
* @param flags : GRIB_NEAREST_SAME_POINT, GRIB_NEAREST_SAME_GRID
* @param outlats : returned array of latitudes of the nearest points
* @param outlons : returned array of longitudes of the nearest points
* @param values : returned array of data values of the nearest points
* @param distances : returned array of distances from the nearest points
* @param indexes : returned array of indexes of the nearest points
* @param len : size of the arrays
* @return 0 if OK, integer value on error
*/
int grib_nearest_find(grib_nearest *nearest,grib_handle* h,double inlat,double inlon,
unsigned long flags,double* outlats,double* outlons,
double* values,double* distances,int* indexes,size_t *len);

/**
* Frees an nearest from memory
*
* @param nearest : the nearest
* @return 0 if OK, integer value on error
*/
int grib_nearest_delete(grib_nearest *nearest);

/**
* Find the nearest point of a set of points whose latitudes and longitudes
* are given in the inlats, inlons arrays respectively.
Expand Down Expand Up @@ -753,6 +797,7 @@ int grib_is_defined(grib_handle* h, const char* key);
int grib_set_missing(grib_handle* h, const char* key);

int grib_get_message_size ( grib_handle* h,size_t* size);
int parse_keyval_string(const char *grib_tool, char *arg, int values_required, int default_type, grib_values values[], int *count);

/*! \defgroup errors Error codes
Error codes returned by the grib_api functions.
Expand Down
3 changes: 0 additions & 3 deletions gribapi/grib_api_prototypes.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@

/* grib_handle.c */
grib_handle *grib_new_from_file(grib_context *c, FILE *f, int headers_only, int *error);
grib_handle *gts_new_from_file(grib_context *c, FILE *f, int *error);
grib_handle *metar_new_from_file(grib_context *c, FILE *f, int *error);

int parse_keyval_string(const char *grib_tool, char *arg, int values_required, int default_type, grib_values values[], int *count);
33 changes: 18 additions & 15 deletions gribapi/gribapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,14 @@ def gts_new_from_file(fileobj, headers_only=False):
@brief Load in memory a GTS message from a file.

The message can be accessed through its id and will be available\n
until @ref grib_release is called.\n
until @ref codes_release is called.\n

@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the GTS loaded in memory
@return id of the GTS loaded in memory or None
@exception GribInternalError
"""
#err, h = err_last(lib.gts_new_from_file)(ffi.NULL, fileobj)
# err, h = err_last(lib.gts_new_from_file)(ffi.NULL, fileobj)
err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_GTS)
if err:
if err == lib.GRIB_END_OF_FILE:
Expand All @@ -241,14 +241,14 @@ def metar_new_from_file(fileobj, headers_only=False):
@brief Load in memory a METAR message from a file.

The message can be accessed through its id and will be available\n
until @ref grib_release is called.\n
until @ref codes_release is called.\n

@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the METAR loaded in memory
@return id of the METAR loaded in memory or None
@exception GribInternalError
"""
#err, h = err_last(lib.metar_new_from_file)(ffi.NULL, fileobj)
# err, h = err_last(lib.metar_new_from_file)(ffi.NULL, fileobj)
err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_METAR)
if err:
if err == lib.GRIB_END_OF_FILE:
Expand All @@ -268,14 +268,14 @@ def codes_new_from_file(fileobj, product_kind, headers_only=False):
@brief Load in memory a message from a file for a given product.

The message can be accessed through its id and will be available\n
until @ref grib_release is called.\n
until @ref codes_release is called.\n

\b Examples: \ref get_product_kind.py "get_product_kind.py"

@param fileobj python file object
@param product_kind one of CODES_PRODUCT_GRIB, CODES_PRODUCT_BUFR, CODES_PRODUCT_METAR or CODES_PRODUCT_GTS
@param headers_only whether or not to load the message with the headers only
@return id of the message loaded in memory
@return id of the message loaded in memory or None
@exception GribInternalError
"""
if product_kind == CODES_PRODUCT_GRIB:
Expand All @@ -297,13 +297,13 @@ def any_new_from_file(fileobj, headers_only=False):
@brief Load in memory a message from a file.

The message can be accessed through its id and will be available\n
until @ref grib_release is called.\n
until @ref codes_release is called.\n

\b Examples: \ref grib_get_keys.py "grib_get_keys.py"

@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the message loaded in memory
@return id of the message loaded in memory or None
@exception GribInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_ANY)
Expand All @@ -325,13 +325,13 @@ def bufr_new_from_file(fileobj, headers_only=False):
@brief Load in memory a BUFR message from a file.

The message can be accessed through its id and will be available\n
until @ref grib_release is called.\n
until @ref codes_release is called.\n

\b Examples: \ref bufr_get_keys.py "bufr_get_keys.py"

@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the BUFR loaded in memory
@return id of the BUFR loaded in memory or None
@exception GribInternalError
"""
err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_BUFR)
Expand All @@ -353,7 +353,7 @@ def grib_new_from_file(fileobj, headers_only=False):
@brief Load in memory a GRIB message from a file.

The message can be accessed through its gribid and will be available\n
until @ref grib_release is called.\n
until @ref codes_release is called.\n

The message can be loaded headers only by using the headers_only argument.
Default is to have the headers only option set to off (False). If set to on (True),
Expand All @@ -366,10 +366,10 @@ def grib_new_from_file(fileobj, headers_only=False):

@param fileobj python file object
@param headers_only whether or not to load the message with the headers only
@return id of the grib loaded in memory
@return id of the grib loaded in memory or None
@exception GribInternalError
"""
#err, h = err_last(lib.grib_new_from_file)(ffi.NULL, fileobj, headers_only)
# err, h = err_last(lib.grib_new_from_file)(ffi.NULL, fileobj, headers_only)
err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_GRIB)
if err:
if err == lib.GRIB_END_OF_FILE:
Expand Down Expand Up @@ -2060,6 +2060,9 @@ def grib_get_api_version():
def div(v, d):
return (v / d, v % d)

if not lib:
raise RuntimeError("Could not load the ecCodes library!")

v = lib.grib_get_api_version()
v, revision = div(v, 100)
v, minor = div(v, 100)
Expand Down