RAVE
RAVE C Objects

On the previous page, we gave a brief overview on how you are supposed to work with the different support macros and functions we have delivered in the system.

Let's take a look at some of the objects we have at our disposal.

  • RaveDateTime_t This is a very simple object that provides the user with two different members, time and date. As is specified in the header file, the date should be specified in the format YYYYMMDD and the time should be specified in HHmmss. It is quite straight forward in usage.
    #include "rave_datetime.h"
    
    static void somefun(void)
    {
      RaveDateTime_t* datetime = RAVE_OBJECT_NEW(&RaveDateTime_TYPE);
      if (datetime != NULL) {
        RaveDateTime_setDate(datetime, "20091010");
        RaveDateTime_setTime(datetime, "100000");
      }
      ...
      RAVE_OBJECT_RELEASE(datetime);
    }
    
    What happens if you specify a badly-formatted date or time, or any other oddity? Try for yourself by checking the return value from RaveDateTime_setDate and RaveDateTime_setTime ... or you could read the documentation.
  • RaveData2D_t This object is a bit more interesting since it provides support for a two-dimensional data array. It is used internally by both PolarScan_t and Cartesian_t. When you are using this object you will get basic help on memory allocation, indexing and boundary checks.
    #include "rave_data2d.h"
    
    static void somefun(void)
    {
      RaveData2D_t* data = RAVE_OBJECT_NEW(&RaveData2D_TYPE);
      if (data != NULL) {
        if (RaveData2D_createData(data, 10, 10, RaveDataType_UCHAR)) {
          RaveData2D_setValue(data, 2, 2, 10.0);
        }
      }
      RAVE_OBJECT_RELEASE(data);
    }
    
    All setting and getting of data is actually done as doubles and the casting is done inside the functions. So, if you pass in a double that is > 255 into the above call, then the value in the data array will be set to 255.
  • RaveObjectList_t A list supporting RAVE objects. It manages the reference counts and ensures that all objects are released upon destruction of the list. A small caveat: if any objects are passed to the list and these objects are not cloneable, then, if this list is cloned, the non-cloneable objects will be ignored.
    #include "raveobject_list.h"
    
    static void somefun(RaveObjectList_t* list)
    {
      int len = RaveObjectList_size(list);
      int i = 0;
      for (i = 0; i < len; i++) {
        RaveCoreObject* object = RaveObjectList_get(list, i);
        ....
        RAVE_OBJECT_RELEASE(object);
      }
    }
    
    Currently, we only support size and indexing, but eventually we are going to introduce an iterator as well.
  • RaveList_t A basic list for storing pointers or values or whatever. It takes void pointers as list entries. However, it will not destroy any item for you when it is destroyed; that is up to the user.
    #include "rave_list.h"
    
    static void somefun(void)
    {
      RaveList_t* list = RAVE_OBJECT_NEW(&RaveList_TYPE);
      ...
      RaveList_add(list, ptr);
      ...
      while ((ptr = RaveList_removeLast(list)) != NULL) {
        RAVE_FREE(ptr);
      }
      
      RAVE_OBJECT_RELEASE(list);  
    }
    

That's the basic objects that only are there for making life a bit easier. We have a number of objects that are more interesting when working with radar data (or any other data) for that matter.

  • Projection_t This is a wrapper around PROJ.4. This object requires initialization after it has been created since it needs to know what projection definition it should support.
    #include "projection.h"
    
    static void somefun(void)
    {
      Projection_t* p1 = RAVE_OBJECT_NEW(&Projection_TYPE);
      Projection_t* p2 = RAVE_OBJECT_NEW(&Projection_TYPE);
      double x = 0.0L, y = 0.0L;
      if (!Projection_init(p1, "stere", "my projection", "+proj=stere +ellps=bessel +lat_0=90 +lon_0=14 +lat_ts=60 +datum=WGS84")) {
        goto error;
      }
      if (!Projection_init(p2, "lonlat", "another projection description", "+proj=longlat +ellps=WGS84 +datum=WGS84")) {
        goto error;
      }
      x = 60.0 * M_PI/180.0;
      y = 14.0 * M_PI/180.0;
      if (!Projection_transform(p2, p1, &x, &y, NULL)) {
        goto error;
      }
      fprintf(stderr, "Surface coordinates %f, %d\n", x, y);
    error:
      RAVE_OBJECT_RELEASE(p1);
      RAVE_OBJECT_RELEASE(p2); 
    }
    
    This code shows how you are able to translate a lon/lat coordinate into a surface coordinate represented by the stereographic projection p1. The most interesting function in this class is Projection_transform that will ensure that datum changes will be performed as long as +datum= has been specified. For further information on the usage, please refer to PROJ.4 documentation.
  • Area_t An area definition describing a surface. It contains corner coordinates, what projection it is defined by, what x/y - size and x/y - scale. So, it is quite useful if you are planning to create a Cartesian product or similar.

Currently we have three different products that define the basic needs when working with radar data. Instead of writing some examples on how these are used, a list of the other objects will be presented below and at the end a simple main program that creates a PPI will be shown.

  • Cartesian_t This class represents a Cartesian product, i.e. one or more two-dimensional surface images that are defined by a area extent, projection and the other bits that are defined in an Area_t.
  • PolarScanParam_t This class represents a physical parameter (variable) measured by the radar for a given scan of the horizon. This object contains one such parameter and any number of supporting quality datasets.
  • RaveField_t This class is a generic container for holding a dataset. It is often used (and intended) for representing data quality as a so-called quality-indicator dataset.
  • PolarScan_t This class represents one radar scan. I.e. one or more two-dimensional arrays defining the different quantities. This class may contain any number of PolarScanParam_t objects as long as they are all valid for the same scan of the horizon.
  • PolarVolume_t A polar volume is defined by a number of PolarScan_t objects and some basic information like what, where and how attributes.

Other than that we have a couple of utilities that are useful when working with the objects defined above.

  • RaveIO_t Basic loading and saving of products in the ODIM_H5 format.
  • Transform_t Some product generator algorithms that might be useful.
/**
 * Simple PPI generator example without any checks or controls.
 */
#include "rave_io.h"
#include "polarscan.h"
#include "cartesian.h"
#include "polarvolume.h"
#include "area.h"

/**
 * Arguments are: <filename> <scan index> <areaid>
int main(int argc, char** argv)
{
  RaveIO_t* raveio = RaveIO_open(argv[1]);
  PolarScan_t* scan = NULL;
  Cartesian_t* result = NULL;
  Area_t* area = NULL;
  
  if (RaveIO_getObjectType(raveio) == Rave_ObjectType_PVOL) {
    PolarVolume_t* volume = RaveIO_getObject(raveio);
    scan = PolarVolume_getScan(volume, atoi(argv[2]));
    RAVE_OBJECT_RELEASE(volume);
  }
  
  area = createArea(argv[3]); 
  
  result = createPPI(scan, area);
  
  raveio.setFilename("result.h5")
  raveio.setObject(result);
  raveio.save();
  
  RAVE_OBJECT_RELEASE(raveio);
  RAVE_OBJECT_RELEASE(scan);
  RAVE_OBJECT_RELEASE(area);
  return 0;
}

The above example may leave a couple of unanswered questions, but at least it shows how the system can be used. For example, the object Transform_t supports ppi/cappi/pcappi when writing this documentation but this might change in the future and hence, I leave it up to the reader of this document to check what APIs you have at your disposal.

We have covered the basics and some of the building blocks and now it is time to take a look at a real-life example that was performed when developing the new version of RAVE, A real-life example on converting existing averaging filter functionality. This example provides a complete example of writing a C module and a Python wrapper for it.