mirror of https://github.com/ecmwf/eccodes.git
4173 lines
110 KiB
C
4173 lines
110 KiB
C
/*
|
|
* Copyright 2005-2015 ECMWF.
|
|
*
|
|
* This software is licensed under the terms of the Apache Licence Version 2.0
|
|
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
|
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
|
*/
|
|
|
|
#include "grib_api_internal.h"
|
|
|
|
#ifdef HAVE_NETCDF
|
|
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
#include <netcdf.h>
|
|
|
|
#include "grib_tools.h"
|
|
|
|
char* grib_tool_description = "Convert a GRIB file to netCDF format.";
|
|
char* grib_tool_name = "grib_to_netcdf";
|
|
char* grib_tool_usage = "[options] grib_file grib_file ... ";
|
|
static char argvString[2048];
|
|
|
|
/*=====================================================================*/
|
|
|
|
static grib_context* ctx = NULL;
|
|
static double missing_value = 9999; /* TODO: Get from GRIBs */
|
|
|
|
/*===============================================================================*/
|
|
/* request from mars client */
|
|
/*===============================================================================*/
|
|
|
|
#define NUMBER(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
typedef int boolean;
|
|
typedef int err;
|
|
typedef off_t file_offset;
|
|
|
|
static int FALSE = 0;
|
|
static int TRUE = 1;
|
|
|
|
static int files = 0;
|
|
|
|
struct value {
|
|
struct value *next;
|
|
char* name;
|
|
};
|
|
typedef struct value value;
|
|
|
|
struct request {
|
|
struct request *next;
|
|
struct parameter *params;
|
|
char* name;
|
|
int order;
|
|
};
|
|
typedef struct request request;
|
|
/* request part */
|
|
/* language part */
|
|
|
|
struct parameter {
|
|
struct parameter *next;
|
|
struct value *values;
|
|
char* name;
|
|
int count;
|
|
};
|
|
typedef struct parameter parameter;
|
|
|
|
static const char* get_value(const request*, const char* name, int n);
|
|
static boolean parsedate(const char *name, long* julian, long *second, boolean* isjul);
|
|
|
|
static boolean eq_string(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return strcmp(l, r) == 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean eq_integer(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return atol(l) == atol(r);
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean eq_null(const char *l, const char *r)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean eq_coord(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return strcmp(l, r) == 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean eq_range(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return strcmp(l, r) == 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean eq_date(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return strcmp(l, r) == 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean eq_param(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return strcmp(l, r) == 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean eq_time(const char *l, const char *r)
|
|
{
|
|
if(l && r)
|
|
return strcmp(l, r) == 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static value *new_value(const char *name)
|
|
{
|
|
value *v = (value*)calloc(sizeof(value), 1);
|
|
Assert(v);
|
|
v->name = grib_context_strdup(ctx, name);
|
|
return v;
|
|
}
|
|
|
|
static parameter *find_parameter(const request *r, const char *parname)
|
|
{
|
|
if(!parname)
|
|
return 0;
|
|
if(r)
|
|
{
|
|
parameter *p = r->params;
|
|
while(p)
|
|
{
|
|
if(strcmp(p->name, parname) == 0)
|
|
return p;
|
|
p = p->next;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void _reqmerge(parameter *pa, const parameter *pb, request *a)
|
|
{
|
|
const value *vb = pb->values;
|
|
|
|
if(strcmp(pa->name, pb->name) != 0)
|
|
return;
|
|
|
|
while(vb)
|
|
{
|
|
value *va = pa->values;
|
|
value *last = 0;
|
|
const char *nb = vb->name;
|
|
boolean add = TRUE;
|
|
|
|
while(va)
|
|
{
|
|
if(strcmp(va->name, nb) == 0)
|
|
{
|
|
add = FALSE;
|
|
break;
|
|
}
|
|
|
|
last = va;
|
|
va = va->next;
|
|
}
|
|
|
|
if(add)
|
|
{
|
|
value* v = new_value(nb);
|
|
if(last)
|
|
last->next = v;
|
|
else
|
|
pa->values = v;
|
|
pa->count = 0;
|
|
}
|
|
|
|
vb = vb->next;
|
|
}
|
|
|
|
}
|
|
|
|
/* Fast version if a && b same */
|
|
static boolean _reqmerge1(request *a, const request *b)
|
|
{
|
|
parameter *pa = a->params;
|
|
const parameter *pb = b->params;
|
|
|
|
while(pa && pb)
|
|
{
|
|
if(strcmp(pa->name, pb->name) != 0)
|
|
return FALSE;
|
|
|
|
_reqmerge(pa, pb, a);
|
|
|
|
pa = pa->next;
|
|
pb = pb->next;
|
|
}
|
|
|
|
return (pa == NULL && pb == NULL);
|
|
}
|
|
|
|
static void free_one_value(value *p)
|
|
{
|
|
grib_context_free(ctx, p->name);
|
|
grib_context_free(ctx, p);
|
|
}
|
|
|
|
static void free_all_values(value *p)
|
|
{
|
|
while(p)
|
|
{
|
|
value *q = p->next;
|
|
free_one_value(p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
/* Convert the first part of the string 'p' to a number (x) and set n to its length. */
|
|
/* Return the rest of the string */
|
|
static const char *parse1(const char *p, int* x, int *n)
|
|
{
|
|
*x = *n = 0;
|
|
while(*p && isdigit(*p))
|
|
{
|
|
(*x) *= 10;
|
|
(*x) += *p - '0';
|
|
(*n)++;
|
|
p++;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static boolean is_number(const char *name)
|
|
{
|
|
const char *p = name;
|
|
int x, n;
|
|
|
|
if(p == 0 || *p == 0)
|
|
return FALSE;
|
|
|
|
if(*p == '-')
|
|
p++;
|
|
else if(*p == '+')
|
|
p++;
|
|
|
|
p = parse1(p, &x, &n);
|
|
if(n == 0 && *p != '.')
|
|
return FALSE;
|
|
|
|
if(*p == '.')
|
|
{
|
|
p++;
|
|
p = parse1(p, &x, &n);
|
|
}
|
|
|
|
if(*p == 'e' || *p == 'E')
|
|
{
|
|
p++;
|
|
if(*p == '-')
|
|
p++;
|
|
else if(*p == '+')
|
|
p++;
|
|
p = parse1(p, &x, &n);
|
|
if(n == 0)
|
|
return FALSE;
|
|
}
|
|
|
|
return *p == 0 ? TRUE : FALSE;
|
|
}
|
|
static parameter *new_parameter(char *name, value *v)
|
|
{
|
|
parameter *p = (parameter*)calloc(sizeof(parameter), 1);
|
|
Assert(p);
|
|
p->name = grib_context_strdup(ctx, name);
|
|
p->values = v;
|
|
return p;
|
|
}
|
|
|
|
static void put_value(request *r, const char *parname, const char *valname, boolean append, boolean unique, boolean ascending)
|
|
{
|
|
parameter *p;
|
|
value *v;
|
|
|
|
if(!r)
|
|
return;
|
|
|
|
if((p = find_parameter(r, parname)) != NULL)
|
|
{
|
|
if(append)
|
|
{
|
|
value *a = p->values, *b = NULL, *c = NULL;
|
|
while(a)
|
|
{
|
|
b = a;
|
|
if(unique)
|
|
{
|
|
if(is_number(a->name) && is_number(valname))
|
|
{
|
|
if(atof(a->name) == atof(valname))
|
|
return;
|
|
}
|
|
else if(strcmp(a->name, valname) == 0)
|
|
return;
|
|
}
|
|
|
|
if(ascending)
|
|
{
|
|
if(is_number(a->name))
|
|
{
|
|
if(atof(valname) < atof(a->name))
|
|
break;
|
|
}
|
|
else if(strcmp(valname, a->name) < 0)
|
|
break;
|
|
}
|
|
c = b;
|
|
a = a->next;
|
|
}
|
|
v = new_value(grib_context_strdup(ctx, valname));
|
|
if(ascending)
|
|
{
|
|
if(c)
|
|
{
|
|
if(b && b != c)
|
|
v->next = b;
|
|
c->next = v;
|
|
}
|
|
else
|
|
{
|
|
if(a)
|
|
v->next = a;
|
|
p->values = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(b)
|
|
b->next = v;
|
|
else
|
|
p->values = v;
|
|
}
|
|
/* p->count++; */
|
|
p->count = 0;
|
|
}
|
|
else
|
|
{
|
|
if(p->values)
|
|
{
|
|
free_all_values(p->values->next);
|
|
p->values->next = NULL;
|
|
/* p->count = 1; */
|
|
p->count = 0;
|
|
if(strcmp(p->values->name, valname) == 0)
|
|
return;
|
|
else
|
|
{
|
|
grib_context_free(ctx, p->values->name);
|
|
p->values->name = grib_context_strdup(ctx, valname);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
v = new_value(grib_context_strdup(ctx, valname));
|
|
p->values = v;
|
|
/* p->count = 1; */
|
|
p->count = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parameter *q = NULL;
|
|
parameter *s = r->params;
|
|
v = new_value(grib_context_strdup(ctx, valname));
|
|
p = new_parameter(grib_context_strdup(ctx, parname), v);
|
|
while(s)
|
|
{
|
|
q = s;
|
|
s = s->next;
|
|
}
|
|
if(q)
|
|
q->next = p;
|
|
else
|
|
r->params = p;
|
|
}
|
|
}
|
|
|
|
static void add_value(request *r, const char *parname, const char *fmt, ...)
|
|
{
|
|
char buffer[1024];
|
|
va_list list;
|
|
|
|
va_start(list, fmt);
|
|
vsprintf(buffer, fmt, list);
|
|
va_end(list);
|
|
|
|
put_value(r, parname, buffer, TRUE, FALSE, FALSE);
|
|
va_end(list);
|
|
}
|
|
|
|
static void _reqmerge2(request *a, const request *b)
|
|
{
|
|
const parameter *pb = b->params;
|
|
|
|
while(pb)
|
|
{
|
|
parameter* pa = find_parameter(a, pb->name);
|
|
|
|
if(pa == NULL)
|
|
{
|
|
value *v = pb->values;
|
|
while(v)
|
|
{
|
|
put_value(a, pb->name, v->name, TRUE, TRUE, FALSE);
|
|
v = v->next;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
_reqmerge(pa, pb, a);
|
|
}
|
|
|
|
pb = pb->next;
|
|
}
|
|
}
|
|
|
|
static void reqmerge(request *a, const request *b)
|
|
{
|
|
if(a && b)
|
|
{
|
|
if(!_reqmerge1(a, b))
|
|
_reqmerge2(a, b);
|
|
}
|
|
}
|
|
|
|
static void save_name(FILE *f, const char* name, int n)
|
|
{
|
|
|
|
int i = 0, cnt = 0;
|
|
|
|
if(name == NULL)
|
|
name = "(null)";
|
|
|
|
cnt = fprintf(f, "%s", name);
|
|
|
|
for(i = cnt; i < n; i++)
|
|
putc(' ', f);
|
|
}
|
|
|
|
static void save_one_value(FILE *f, value *r)
|
|
{
|
|
save_name(f, r->name, 0);
|
|
}
|
|
|
|
static void save_all_values(FILE *f, value *r)
|
|
{
|
|
while(r)
|
|
{
|
|
save_one_value(f, r);
|
|
if(r->next)
|
|
putc('/', f);
|
|
r = r->next;
|
|
}
|
|
}
|
|
|
|
static void save_all_parameters(FILE *f, parameter *r)
|
|
{
|
|
while(r)
|
|
{
|
|
|
|
if(r->values)
|
|
{
|
|
fprintf(f, ",\n ");
|
|
save_name(f, r->name, 10);
|
|
fprintf(f, " = ");
|
|
save_all_values(f, r->values);
|
|
}
|
|
|
|
r = r->next;
|
|
}
|
|
putc('\n', f);
|
|
}
|
|
|
|
static void save_one_request(FILE *f, const request *r)
|
|
{
|
|
if(r)
|
|
{
|
|
save_name(f, r->name, 0);
|
|
save_all_parameters(f, r->params);
|
|
putc('\n', f);
|
|
}
|
|
}
|
|
|
|
static void save_all_requests(FILE *f, const request *r)
|
|
{
|
|
while(r)
|
|
{
|
|
save_one_request(f, r);
|
|
r = r->next;
|
|
}
|
|
}
|
|
|
|
/* Not used for the moment
|
|
static void print_one_request(const request *r)
|
|
{
|
|
save_one_request(stdout, r);
|
|
}
|
|
*/
|
|
|
|
static void print_all_requests(const request *r)
|
|
{
|
|
save_all_requests(stdout, r);
|
|
}
|
|
|
|
static void free_one_parameter(parameter *p)
|
|
{
|
|
grib_context_free(ctx, p->name);
|
|
free_all_values(p->values);
|
|
/*free_all_requests(p->interface);*/
|
|
grib_context_free(ctx, p);
|
|
}
|
|
|
|
static void free_all_parameters(parameter *p)
|
|
{
|
|
while(p)
|
|
{
|
|
parameter *q = p->next;
|
|
free_one_parameter(p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
static void free_one_request(request *r)
|
|
{
|
|
grib_context_free(ctx, r->name);
|
|
free_all_parameters(r->params);
|
|
grib_context_free(ctx, r);
|
|
}
|
|
|
|
static void free_all_requests(request *p)
|
|
{
|
|
while(p)
|
|
{
|
|
request *q = p->next;
|
|
free_one_request(p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
static void set_value(request *r, const char *parname, const char *fmt, ...)
|
|
{
|
|
char buffer[10240];
|
|
va_list list;
|
|
|
|
va_start(list, fmt);
|
|
vsprintf(buffer, fmt, list);
|
|
va_end(list);
|
|
|
|
put_value(r, parname, buffer, FALSE, FALSE, FALSE);
|
|
}
|
|
|
|
static err handle_to_request(request *r, grib_handle* g)
|
|
{
|
|
grib_keys_iterator* ks;
|
|
char name[256];
|
|
char value[256];
|
|
size_t len = sizeof(value);
|
|
int e = 0;
|
|
|
|
if(!g)
|
|
return -1;
|
|
|
|
/* printf("------------\n"); */
|
|
ks = grib_keys_iterator_new(g, GRIB_KEYS_ITERATOR_ALL_KEYS, "mars");
|
|
|
|
while(grib_keys_iterator_next(ks))
|
|
{
|
|
|
|
strcpy(name, grib_keys_iterator_get_name(ks));
|
|
|
|
if((e = grib_keys_iterator_get_string(ks, value, &len)) != GRIB_SUCCESS)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Cannot get %s as string %d (%s)", name, e, grib_get_error_message(e));
|
|
|
|
set_value(r, name, "%s", value);
|
|
len = sizeof(value);
|
|
}
|
|
|
|
/*
|
|
Assert(grib_get_long(g, "validityDate", &l ) == 0);
|
|
set_value(r, "validityDate", "%ld", l);
|
|
|
|
Assert(grib_get_long(g, "validityTime", &l ) == 0);
|
|
set_value(r, "validityTime", "%ld", l);
|
|
*/
|
|
|
|
len = sizeof(value);
|
|
if(grib_get_string(g, "cfVarName", name, &len) == 0)
|
|
{
|
|
if(strcmp(name,"unknown") != 0)
|
|
{
|
|
set_value(r, "param", "%s", name);
|
|
}
|
|
else
|
|
{
|
|
len = sizeof(value);
|
|
if(grib_get_string(g, "shortName", name, &len) == 0)
|
|
{
|
|
set_value(r, "param", "%s", name);
|
|
}
|
|
}
|
|
}
|
|
|
|
len = sizeof(value);
|
|
if(grib_get_string(g, "name", name, &len) == 0)
|
|
{
|
|
if(strcmp(name,"unknown") != 0)
|
|
{
|
|
set_value(r, "_long_name", "%s", name);
|
|
}
|
|
}
|
|
|
|
len = sizeof(value);
|
|
if(grib_get_string(g, "units", name, &len) == 0)
|
|
{
|
|
if(strcmp(name,"unknown") != 0)
|
|
{
|
|
set_value(r, "_units", "%s", name);
|
|
}
|
|
}
|
|
|
|
len = sizeof(value);
|
|
if(grib_get_string(g, "cfName", name, &len) == 0)
|
|
{
|
|
if(strcmp(name,"unknown") != 0)
|
|
{
|
|
set_value(r, "_cf_name", "%s", name);
|
|
}
|
|
}
|
|
|
|
grib_keys_iterator_delete(ks);
|
|
return e;
|
|
}
|
|
|
|
/*===============================================================================*/
|
|
/*===============================================================================*/
|
|
|
|
typedef boolean (*namecmp)(const char *, const char *);
|
|
|
|
typedef struct hypercube {
|
|
request *cube;
|
|
request *r;
|
|
request *iterator;
|
|
char *set;
|
|
int count;
|
|
int size;
|
|
int max;
|
|
int* index_cache;
|
|
int index_cache_size;
|
|
namecmp *compare;
|
|
} hypercube;
|
|
|
|
typedef struct axis_t {
|
|
const char *name;
|
|
namecmp compare;
|
|
} axis_t;
|
|
|
|
/* This should be c++ ... */
|
|
|
|
typedef enum field_state {
|
|
unknown, packed_mem, packed_file, expand_mem
|
|
} field_state;
|
|
|
|
typedef struct {
|
|
int refcnt;
|
|
request* r;
|
|
} field_request;
|
|
|
|
/* One field .. */
|
|
|
|
typedef struct field {
|
|
int refcnt;
|
|
|
|
field_state shape;
|
|
|
|
grib_handle* handle;
|
|
double* values;
|
|
size_t value_count;
|
|
|
|
/* if on file */
|
|
|
|
file_offset offset;
|
|
size_t length;
|
|
grib_file *file;
|
|
|
|
/* missing fields/values */
|
|
|
|
boolean missing; /* field is missing */
|
|
boolean bitmap; /* field has missing values (= bitmap) */
|
|
|
|
field_request *r;
|
|
|
|
} field;
|
|
|
|
typedef struct fieldset {
|
|
|
|
int refcnt;
|
|
|
|
/* if fields */
|
|
|
|
int max;
|
|
int count;
|
|
|
|
field **fields;
|
|
|
|
} fieldset;
|
|
|
|
/*
|
|
#define MISSING_VALUE(n) ((n) == missing_value)
|
|
#define MISSING_FIELD(f) ((f)->missing)
|
|
#define FIELD_HAS_BITMAP(f) ((f)->bitmap)
|
|
|
|
#define FASTNEW(type) (type*)calloc(sizeof(type),1)
|
|
#define grib_context_free(ctx,x) grib_context_free(ctx,x)
|
|
*/
|
|
|
|
static field *get_field(fieldset *v, int n, field_state shape);
|
|
static hypercube *new_hypercube_from_mars_request(const request *r);
|
|
static void release_field(field *g);
|
|
static int count_axis(const hypercube *h);
|
|
static const char* get_axis(const hypercube *h, int pos);
|
|
static const char* get_axis(const hypercube *h, int pos);
|
|
static int cube_order(const hypercube *h, const request *r);
|
|
static void free_hypercube(hypercube *h);
|
|
static int _cube_position(const hypercube *h, const request *r, boolean remove_holes);
|
|
|
|
static value *clone_one_value(const value *p)
|
|
{
|
|
value *q = (value*)calloc(sizeof(value), 1);
|
|
Assert(q);
|
|
q->next = NULL;
|
|
q->name = grib_context_strdup(ctx, p->name);
|
|
return q;
|
|
}
|
|
|
|
static value *clone_all_values(const value *p)
|
|
{
|
|
if(p)
|
|
{
|
|
value *q = clone_one_value(p);
|
|
q->next = clone_all_values(p->next);
|
|
/* q->alias = cone_value(p->alias); */
|
|
return q;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static parameter *clone_one_parameter(const parameter *p)
|
|
{
|
|
parameter *q = (parameter*)calloc(sizeof(parameter), 1);
|
|
Assert(q);
|
|
q->next = NULL;
|
|
q->name = grib_context_strdup(ctx, p->name);
|
|
q->values = clone_all_values(p->values);
|
|
return q;
|
|
}
|
|
|
|
static parameter *clone_all_parameters(const parameter *p)
|
|
{
|
|
if(p)
|
|
{
|
|
parameter *q = clone_one_parameter(p);
|
|
q->next = clone_all_parameters(p->next);
|
|
return q;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static request *clone_one_request(const request *r)
|
|
{
|
|
if(r)
|
|
{
|
|
request *p = (request*)calloc(sizeof(request), 1);
|
|
Assert(p);
|
|
p->name = grib_context_strdup(ctx, r->name);
|
|
|
|
p->params = clone_all_parameters(r->params);
|
|
p->next = NULL;
|
|
return p;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static request *new_request(const char *name, parameter *p)
|
|
{
|
|
request *r = (request *)calloc(sizeof(request), 1);
|
|
Assert(r);
|
|
r->name = grib_context_strdup(ctx, name);
|
|
r->params = p;
|
|
return r;
|
|
}
|
|
|
|
static request *empty_request(const char *name)
|
|
{
|
|
return new_request(name ? name : "", NULL);
|
|
}
|
|
|
|
static field_request* new_field_request(request *r)
|
|
{
|
|
field_request* g = (field_request*)grib_context_malloc_clear(ctx, sizeof(field_request));
|
|
g->r = clone_one_request(r);
|
|
return g;
|
|
}
|
|
|
|
static void free_field_request(field_request* g)
|
|
{
|
|
if(!g)
|
|
return;
|
|
g->refcnt--;
|
|
if(g->refcnt <= 0)
|
|
{
|
|
free_all_requests(g->r);
|
|
grib_context_free(ctx, g);
|
|
}
|
|
}
|
|
|
|
static void free_field(field *g)
|
|
{
|
|
if(!g)
|
|
return;
|
|
g->refcnt--;
|
|
if(g->refcnt <= 0)
|
|
{
|
|
/*free_gribfile(g->file);*/
|
|
free_field_request(g->r);
|
|
if(g->values)
|
|
grib_context_free(ctx, g->values);
|
|
grib_handle_delete(g->handle);
|
|
grib_context_free(ctx, g);
|
|
}
|
|
}
|
|
|
|
static void free_fieldset(fieldset *v)
|
|
{
|
|
if(!v)
|
|
return;
|
|
v->refcnt--;
|
|
if(v->refcnt <= 0)
|
|
{
|
|
int i;
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: free_fieldset (%d fields) : ", v->count);
|
|
|
|
for(i = 0; i < v->count; i++)
|
|
free_field(v->fields[i]);
|
|
|
|
grib_context_free(ctx, v->fields);
|
|
grib_context_free(ctx, v);
|
|
}
|
|
}
|
|
|
|
static field *new_field()
|
|
{
|
|
return (field*)grib_context_malloc_clear(ctx, sizeof(field));
|
|
}
|
|
|
|
#define INIT_SIZE 1024
|
|
static void grow_fieldset(fieldset *v, int n)
|
|
{
|
|
int m = v->count;
|
|
int x = v->max;
|
|
|
|
if(n < v->count)
|
|
return;
|
|
|
|
v->count = n;
|
|
|
|
while(v->count >= v->max)
|
|
if(v->max < INIT_SIZE)
|
|
v->max = INIT_SIZE;
|
|
else
|
|
v->max += v->max / 2 + 1;
|
|
|
|
if(v->max != x)
|
|
{
|
|
int i;
|
|
if(v->fields == NULL)
|
|
{
|
|
v->fields = (field**) grib_context_malloc(ctx, sizeof(field*) * v->max);
|
|
Assert(v->fields);
|
|
}
|
|
else
|
|
{
|
|
field** f = (field**) grib_context_malloc(ctx, sizeof(field*) * v->max);
|
|
Assert(f);
|
|
for(i = 0; i < m; i++)
|
|
f[i] = v->fields[i];
|
|
grib_context_free(ctx, v->fields);
|
|
v->fields = f;
|
|
}
|
|
|
|
for(i = m; i < v->max; i++)
|
|
v->fields[i] = NULL;
|
|
}
|
|
}
|
|
|
|
static fieldset *new_fieldset(int n)
|
|
{
|
|
fieldset *f = (fieldset*)calloc(sizeof(fieldset), 1);
|
|
Assert(f);
|
|
grow_fieldset(f, n);
|
|
return f;
|
|
}
|
|
|
|
static field* read_field(grib_file *file, file_offset pos, long length)
|
|
{
|
|
field *g = new_field();
|
|
|
|
g->file = file;
|
|
g->offset = pos;
|
|
g->length = length;
|
|
g->shape = packed_file;
|
|
|
|
return g;
|
|
}
|
|
|
|
static err set_field(fieldset *v, field *g, int pos)
|
|
{
|
|
if(pos >= 0)
|
|
{
|
|
field *h;
|
|
grow_fieldset(v, pos + 1);
|
|
h = v->fields[pos];
|
|
v->fields[pos] = g;
|
|
g->refcnt++;
|
|
if(h)
|
|
free_field(h);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void count_parval(parameter *p)
|
|
{
|
|
int n = 0;
|
|
value *v = p->values;
|
|
|
|
while(v)
|
|
{
|
|
n++;
|
|
v = v->next;
|
|
}
|
|
|
|
p->count = n;
|
|
|
|
}
|
|
|
|
static int count_values(const request *r, const char *parname)
|
|
{
|
|
parameter *p = find_parameter(r, parname);
|
|
|
|
if(p == NULL)
|
|
return 0;
|
|
if(p->count)
|
|
return p->count;
|
|
|
|
count_parval(p);
|
|
|
|
return p->count;
|
|
}
|
|
static const char *get_value(const request *r, const char *parname, int nth)
|
|
{
|
|
parameter *p = find_parameter(r, parname);
|
|
value *v;
|
|
int i = 0;
|
|
|
|
if(p == NULL)
|
|
return NULL;
|
|
|
|
if(!p->count)
|
|
count_values(r, parname);
|
|
|
|
v = p->values;
|
|
|
|
while(v)
|
|
{
|
|
if(nth == i++)
|
|
return v->name;
|
|
v = v->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static err to_packed_mem(field *g)
|
|
{
|
|
|
|
if(g->shape == packed_mem)
|
|
return 0;
|
|
|
|
if(g->shape == expand_mem)
|
|
{
|
|
if(g->values)
|
|
grib_context_free(ctx, g->values);
|
|
g->values = NULL;
|
|
g->value_count = 0;
|
|
g->shape = packed_mem;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if(g->shape == packed_file)
|
|
{
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static err to_expand_mem(field *g)
|
|
{
|
|
err e = 0;
|
|
|
|
Assert(g);
|
|
if(g->shape == expand_mem)
|
|
return 0;
|
|
|
|
if(g->shape == packed_file)
|
|
{
|
|
const void* dummy = NULL;
|
|
|
|
grib_file* file = grib_file_open(g->file->name, "r", &e);
|
|
if(!file || !file->handle)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR | GRIB_LOG_PERROR, "%s", g->file->name);
|
|
return -1;
|
|
}
|
|
|
|
fseeko(file->handle, g->offset, SEEK_SET);
|
|
|
|
g->handle = grib_handle_new_from_file(ctx, file->handle, &e);
|
|
Assert(g->handle);
|
|
|
|
if(g->handle)
|
|
grib_get_message(g->handle, &dummy, &g->length);
|
|
|
|
grib_file_close(file->name, &e);
|
|
if(!g->handle)
|
|
return -1;
|
|
|
|
if(g->values)
|
|
grib_context_free(ctx, g->values);
|
|
g->values = NULL;
|
|
}
|
|
|
|
if(g->values == NULL)
|
|
{
|
|
size_t count = 0;
|
|
long bitmap = 0;
|
|
|
|
if((e = grib_get_size(g->handle, "values", &g->value_count)))
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get number of values %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
count = g->value_count;
|
|
|
|
if((e = grib_set_double(g->handle, "missingValue", missing_value)))
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot set missingValue %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
g->values = (double*) grib_context_malloc(ctx, sizeof(double) * g->value_count);
|
|
if((e = grib_get_double_array(g->handle, "values", g->values, &count)))
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get decode values %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if(count != g->value_count)
|
|
grib_context_log(ctx, GRIB_LOG_FATAL, "grib_api: value count mismatch %d %d", count, g->value_count);
|
|
|
|
if((e = grib_get_long(g->handle, "bitmapPresent", &bitmap)))
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get bitmapPresent %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
g->bitmap = (bitmap != 0);
|
|
|
|
#ifdef COMEBACK
|
|
set g->missing
|
|
#endif
|
|
}
|
|
|
|
g->shape = expand_mem;
|
|
|
|
return e;
|
|
}
|
|
|
|
static void set_field_state(field *g, field_state shape)
|
|
{
|
|
switch(shape)
|
|
{
|
|
case expand_mem:
|
|
to_expand_mem(g);
|
|
break;
|
|
|
|
case packed_mem:
|
|
to_packed_mem(g);
|
|
break;
|
|
|
|
case packed_file:
|
|
release_field(g);
|
|
break;
|
|
|
|
default:
|
|
grib_context_log(ctx, GRIB_LOG_FATAL, "Internal error %s %d", __FILE__, __LINE__);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
static field *get_field(fieldset *v, int n, field_state shape)
|
|
{
|
|
field *g = v->fields[n];
|
|
set_field_state(g, shape);
|
|
return g;
|
|
}
|
|
|
|
static void release_field(field *g)
|
|
{
|
|
if(g->file)
|
|
{
|
|
if(g->values)
|
|
grib_context_free(ctx, g->values);
|
|
g->values = NULL;
|
|
g->shape = packed_file;
|
|
grib_handle_delete(g->handle);
|
|
g->handle = NULL;
|
|
}
|
|
}
|
|
|
|
static request* field_to_request(field* f)
|
|
{
|
|
if(f->r == 0)
|
|
{
|
|
field_state state = f->shape;
|
|
request *r = empty_request(
|
|
#ifdef COMEBACK
|
|
((f->ksec1 == NULL) || (f->ksec1[2] != mars.computeflg)) ? "GRIB" : "COMPUTED");
|
|
#else
|
|
"GRIB");
|
|
#endif
|
|
|
|
set_field_state(f, packed_mem);
|
|
handle_to_request(r, f->handle);
|
|
set_field_state(f, state);
|
|
|
|
f->r = new_field_request(r);
|
|
free_all_requests(r);
|
|
}
|
|
return f->r->r;
|
|
}
|
|
|
|
static request* fieldset_to_request(fieldset *fs)
|
|
{
|
|
int i;
|
|
request* r = empty_request("GRIB");
|
|
|
|
if(!fs)
|
|
return 0;
|
|
|
|
for(i = 0; i < fs->count; i++)
|
|
{
|
|
request *s = field_to_request(fs->fields[i]);
|
|
reqmerge(r,s);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/*===============================================================================*/
|
|
/* hypercube from mars client */
|
|
/*===============================================================================*/
|
|
|
|
static boolean eq_string(const char *, const char *);
|
|
static boolean eq_integer(const char *, const char *);
|
|
static boolean eq_range(const char *, const char *);
|
|
static boolean eq_param(const char *, const char *);
|
|
static boolean eq_coord(const char *, const char *);
|
|
static boolean eq_date(const char *, const char *);
|
|
static boolean eq_time(const char *, const char *);
|
|
static boolean eq_null(const char *, const char *);
|
|
|
|
static axis_t global_axis[] =
|
|
{
|
|
|
|
/* From dhsbase.c 'check_grib' */
|
|
{ "class", eq_string, },
|
|
{ "type", eq_string, },
|
|
{ "stream", eq_string, },
|
|
{ "levtype", eq_string, },
|
|
|
|
{ "origin", eq_string, },
|
|
{ "product", eq_string, },
|
|
{ "section", eq_string, },
|
|
{ "method", eq_integer, },
|
|
{ "system", eq_integer, },
|
|
|
|
/* testing */
|
|
/* {"repres", eq_null, }, */
|
|
|
|
/* from field order */
|
|
{ "date", eq_date, },
|
|
{ "refdate", eq_date, },
|
|
{ "hdate", eq_date, },
|
|
{ "time", eq_time, },
|
|
|
|
{ "reference", eq_range, },
|
|
{ "step", eq_range, },
|
|
{ "fcmonth", eq_integer, },
|
|
{ "fcperiod", eq_range, },
|
|
|
|
{ "leadtime", eq_range, },
|
|
{ "opttime", eq_range, },
|
|
|
|
{ "expver", eq_string, },
|
|
{ "domain", eq_string, },
|
|
|
|
{ "diagnostic", eq_integer, },
|
|
{ "iteration", eq_integer, },
|
|
|
|
{ "quantile", eq_range, },
|
|
{ "number", eq_integer, },
|
|
|
|
{ "levelist", eq_coord, },
|
|
{ "latitude", eq_coord, },
|
|
{ "longitude", eq_coord, },
|
|
{ "range", eq_range, },
|
|
|
|
{ "param", eq_param, },
|
|
|
|
{ "ident", eq_integer, },
|
|
{ "obstype", eq_integer, },
|
|
{ "instrument", eq_integer, },
|
|
|
|
{ "frequency", eq_integer, },
|
|
{ "direction", eq_integer, },
|
|
|
|
{ "channel", eq_integer, }, };
|
|
|
|
static int axisindex(const char *name)
|
|
{
|
|
int i = 0;
|
|
for(i = 0; i < NUMBER(global_axis); i++)
|
|
{
|
|
if(strcmp(name, global_axis[i].name) == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static namecmp comparator(const char *name)
|
|
{
|
|
static char *dontcompare = NULL;
|
|
static boolean first = 1;
|
|
int i = 0;
|
|
|
|
if(first)
|
|
{
|
|
dontcompare = getenv("MARS_DONT_CHECK");
|
|
first = FALSE;
|
|
}
|
|
|
|
if(dontcompare != NULL)
|
|
{
|
|
if(strcmp(dontcompare, name) == 0)
|
|
return eq_null;
|
|
}
|
|
|
|
if((i = axisindex(name)) != -1)
|
|
return global_axis[i].compare;
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "No comparator for %s", name);
|
|
return eq_string;
|
|
}
|
|
|
|
/********************/
|
|
/* index accessors */
|
|
/********************/
|
|
|
|
static int count_index(const hypercube *h)
|
|
{
|
|
int i = 0, n = 0;
|
|
|
|
for(i = 0; i < h->size; ++i)
|
|
n += h->set[i];
|
|
return n;
|
|
}
|
|
|
|
static int count_holes(const hypercube *h, int cnt)
|
|
{
|
|
int i = 0, n = 0;
|
|
|
|
for(i = 0; i < cnt; ++i)
|
|
n += h->set[i];
|
|
|
|
return (n == cnt) ? 0 : (cnt - n);
|
|
}
|
|
|
|
static void reset_index(hypercube *h, int v)
|
|
{
|
|
memset(h->set, v, h->size);
|
|
}
|
|
|
|
static void set_index(hypercube *h, int index, int value)
|
|
{
|
|
if(index < 0 || index >= h->count)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error, bad hypercube index %d", index);
|
|
exit(1);
|
|
}
|
|
|
|
if(index >= h->max)
|
|
{
|
|
int old = h->max;
|
|
while(index >= h->max)
|
|
h->max += 4096;
|
|
|
|
h->set = h->set ? (char*)grib_context_realloc(ctx, h->set, h->max) : (char*)grib_context_malloc(ctx, h->max);
|
|
Assert(h->set);
|
|
memset(h->set + old, 0, h->max - old);
|
|
}
|
|
|
|
if(index >= h->size)
|
|
h->size = index + 1;
|
|
|
|
h->set[index] = value;
|
|
}
|
|
|
|
/**************************/
|
|
/* End of index accessors */
|
|
/**************************/
|
|
|
|
/*******************/
|
|
/* axis accessors */
|
|
/*******************/
|
|
|
|
static int count_axis(const hypercube *h)
|
|
{
|
|
if(h && h->cube)
|
|
return count_values(h->cube, "axis");
|
|
|
|
return -1;
|
|
}
|
|
|
|
static const char* get_axis(const hypercube *h, int pos)
|
|
{
|
|
const char *axis = NULL;
|
|
if(pos < count_axis(h))
|
|
{
|
|
axis = get_value(h->cube, "axis", pos);
|
|
}
|
|
return axis;
|
|
}
|
|
|
|
static void add_axis(hypercube *h, const char *axis)
|
|
{
|
|
add_value(h->cube, "axis", "%s", axis);
|
|
}
|
|
|
|
static void unset_value(request *r, const char *parname)
|
|
{
|
|
parameter *p, *q = NULL;
|
|
if(!r)
|
|
return;
|
|
|
|
p = r->params;
|
|
while(p)
|
|
{
|
|
if(strcmp(parname, p->name) == 0)
|
|
{
|
|
if(q)
|
|
q->next = p->next;
|
|
else
|
|
r->params = p->next;
|
|
free_one_parameter(p);
|
|
return;
|
|
}
|
|
q = p;
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
static void reset_axis(hypercube *h)
|
|
{
|
|
unset_value(h->cube, "axis");
|
|
}
|
|
|
|
static void valcpy(request *a, request *b, char *aname, char *bname)
|
|
{
|
|
parameter *p;
|
|
if(a && b && (p = find_parameter(b, bname)))
|
|
{
|
|
boolean z = FALSE;
|
|
value *v = p->values;
|
|
while(v)
|
|
{
|
|
put_value(a, aname, v->name, z, FALSE, FALSE);
|
|
z = TRUE;
|
|
v = v->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cube_values(hypercube *h, const char *p)
|
|
{
|
|
valcpy(h->cube, h->r, (char *) p, (char *) p);
|
|
}
|
|
|
|
static int count_dimensions(const hypercube *, const char *);
|
|
|
|
static int set_axis(hypercube *h)
|
|
{
|
|
int i = 0;
|
|
int count = (h && h->r) ? 1 : -1;
|
|
|
|
reset_axis(h);
|
|
for(i = (NUMBER(global_axis) - 1); i >= 0; --i)
|
|
{
|
|
int n = count_dimensions(h, global_axis[i].name);
|
|
if(n > 1)
|
|
{
|
|
add_axis(h, global_axis[i].name);
|
|
cube_values(h, global_axis[i].name);
|
|
count *= n;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*************************/
|
|
/* End of axis accessors */
|
|
/*************************/
|
|
|
|
/*******************/
|
|
/* Cube dimensions */
|
|
/*******************/
|
|
|
|
static int count_dimensions(const hypercube *h, const char *axis)
|
|
{
|
|
int dims = -1;
|
|
if(h && h->r)
|
|
dims = count_values(h->r, axis);
|
|
return dims;
|
|
}
|
|
|
|
/**************************/
|
|
/* End of cube dimensions */
|
|
/**************************/
|
|
|
|
/**************************/
|
|
/* Auxiliary functions */
|
|
/**************************/
|
|
|
|
static int count_hypercube(const request *r)
|
|
{
|
|
int i = 0, count = 1;
|
|
for(i = 0; i < NUMBER(global_axis); ++i)
|
|
{
|
|
int c = count_values(r, global_axis[i].name);
|
|
count *= c ? c : 1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int cube_order(const hypercube *h, const request *r)
|
|
{
|
|
return _cube_position(h, r, TRUE);
|
|
}
|
|
|
|
static int cube_position(const hypercube *h, const request *r)
|
|
{
|
|
return _cube_position(h, r, FALSE);
|
|
}
|
|
|
|
static void reserve_index_cache(hypercube *h, int size)
|
|
{
|
|
if(size == 0)
|
|
return;
|
|
|
|
if(h->index_cache != 0)
|
|
grib_context_free(ctx, h->index_cache);
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: Allocating hypercube index_cache: %d entries", size);
|
|
h->index_cache = (int*)calloc(sizeof(int), size);
|
|
Assert(h->index_cache);
|
|
h->index_cache_size = size;
|
|
}
|
|
|
|
static int _cube_position(const hypercube *h, const request *r, boolean remove_holes)
|
|
{
|
|
request *cube = h->cube;
|
|
int c = count_axis(h);
|
|
int index = 0;
|
|
int i = 0;
|
|
int n = 1;
|
|
int ok = 0;
|
|
|
|
if(h->index_cache == 0 || h->index_cache_size != c)
|
|
reserve_index_cache((hypercube*) h, c);
|
|
|
|
for(i = 0; i < c; ++i)
|
|
{
|
|
const char *axis = get_axis(h, i);
|
|
const char *v = get_value(r, axis, 0);
|
|
const char *w = NULL;
|
|
int dims = count_dimensions(h, axis);
|
|
int k = 0;
|
|
int count = count_values(cube, axis);
|
|
int last = h->index_cache[i];
|
|
|
|
for(k = 0; k < count; k++)
|
|
{
|
|
int j = (k + last) % count;
|
|
w = get_value(cube, axis, j);
|
|
if(h->compare ? h->compare[i](w, v) : (strcmp(w, v) == 0))
|
|
{
|
|
index += j * n;
|
|
n *= dims;
|
|
ok++;
|
|
((hypercube*) h)->index_cache[i] = j;
|
|
break;
|
|
}
|
|
else
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: _cube_position, %s, %s != %s [%scompare function available]", axis, w, v, h->compare ? "" : "no ");
|
|
}
|
|
}
|
|
|
|
if(remove_holes)
|
|
{
|
|
int holes = 0;
|
|
if(count_index(h) != h->size)
|
|
holes = count_holes(h, index);
|
|
index -= holes;
|
|
}
|
|
|
|
return (ok == c) ? index : -1;
|
|
}
|
|
|
|
static void cube_indexes(const hypercube *h, request *r, int *indexes, int size)
|
|
{
|
|
request *cube = h->cube;
|
|
int c = count_axis(h);
|
|
int i = 0;
|
|
int index = 0;
|
|
int n = 1;
|
|
int ok = 0;
|
|
|
|
if(size < c)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error in cube_indexes. size=%d < axis=%d", size, c);
|
|
}
|
|
|
|
if(h->index_cache == 0 || h->index_cache_size != c)
|
|
reserve_index_cache((hypercube*) h, c);
|
|
|
|
for(i = 0; i < c; ++i)
|
|
{
|
|
const char *axis = get_axis(h, i);
|
|
const char *v = get_value(r, axis, 0);
|
|
const char *w = NULL;
|
|
int dims = count_dimensions(h, axis);
|
|
int j = 0;
|
|
int k = 0;
|
|
int count = count_values(cube, axis);
|
|
int last = h->index_cache[i];
|
|
|
|
for(k = 0; k < count; k++)
|
|
{
|
|
j = (k + last) % count;
|
|
w = get_value(cube, axis, j);
|
|
if(h->compare ? h->compare[i](w, v) : (w == v))
|
|
{
|
|
index += j * n;
|
|
n *= dims;
|
|
ok++;
|
|
((hypercube*) h)->index_cache[i] = j;
|
|
break;
|
|
|
|
}
|
|
}
|
|
indexes[i] = j;
|
|
}
|
|
}
|
|
|
|
/*********************************/
|
|
/* End of Auxiliary functions */
|
|
/*********************************/
|
|
|
|
static hypercube *new_hypercube(const request *r)
|
|
{
|
|
hypercube *h = (hypercube*) calloc(sizeof(hypercube), 1);
|
|
int total = 0, count = 0;
|
|
int n = 0;
|
|
const char *val = 0;
|
|
|
|
h->r = clone_one_request(r);
|
|
h->cube = empty_request("CUBE");
|
|
|
|
h->count = total = count_hypercube(r);
|
|
count = set_axis(h);
|
|
|
|
h->compare = 0;
|
|
|
|
if((total != count) || (count == 0))
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error while computing hypercube fields");
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Number of fields in request %d", total);
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Number of fields in hypercube %d", count);
|
|
}
|
|
set_index(h, count - 1, 1);
|
|
memset(h->set, 1, count);
|
|
|
|
/* This is expensive, but makes the iterator with only
|
|
those parameters found as axis */
|
|
h->iterator = empty_request(0);
|
|
for(n = 0; n < NUMBER(global_axis); ++n)
|
|
if((val = get_value(h->r, global_axis[n].name, 0)) != NULL)
|
|
set_value(h->iterator, global_axis[n].name, val);
|
|
|
|
return h;
|
|
}
|
|
|
|
static void print_hypercube(const hypercube *h)
|
|
{
|
|
print_all_requests(h->r);
|
|
print_all_requests(h->cube);
|
|
grib_context_log(ctx, GRIB_LOG_INFO, "%d active out of %d fields described\n", count_index(h), h->size);
|
|
}
|
|
|
|
static void free_hypercube(hypercube *h)
|
|
{
|
|
free_all_requests(h->r);
|
|
free_all_requests(h->cube);
|
|
free_all_requests(h->iterator);
|
|
grib_context_free(ctx, h->index_cache);
|
|
grib_context_free(ctx, h->compare);
|
|
grib_context_free(ctx, h->set);
|
|
grib_context_free(ctx, h);
|
|
}
|
|
|
|
struct stuff_1 {
|
|
hypercube *c;
|
|
request *r;
|
|
};
|
|
|
|
static void reqcb_1(const request *r, int count, axis_t *names, char *vals[], void *data)
|
|
{
|
|
struct stuff_1 *s = (struct stuff_1*) data;
|
|
int i;
|
|
|
|
for(i = 0; i < count; i++)
|
|
if(vals[i])
|
|
set_value(s->r, names[i].name, vals[i]);
|
|
|
|
set_index(s->c, cube_position(s->c, s->r), 1);
|
|
}
|
|
|
|
typedef void (*loopproc)(const request*, int, axis_t*, char**, void*);
|
|
|
|
static void names_loop(const request *r, loopproc proc, void *data);
|
|
|
|
static hypercube *new_hypercube_from_mars_request(const request *r)
|
|
{
|
|
int i;
|
|
int n;
|
|
|
|
struct stuff_1 s;
|
|
|
|
#if 0
|
|
const request *lang = mars_language_from_request(r);
|
|
int count = 0;
|
|
|
|
count = init_axis(lang);
|
|
grib_context_log(ctx,GRIB_LOG_DEBUG,"cube %s",r->kind);
|
|
/* print_all_requests(mars_language_from_request(r)); */
|
|
grib_context_log(ctx,GRIB_LOG_INFO,"NUMBER(axis): %d, number axisnew: %d",NUMBER(axis),count);
|
|
#endif
|
|
|
|
s.c = new_hypercube(r);
|
|
s.r = clone_one_request(r);
|
|
|
|
reset_index(s.c, 0);
|
|
names_loop(r, reqcb_1, &s);
|
|
|
|
free_one_request(s.r);
|
|
|
|
/* add single parameters */
|
|
|
|
for(i = 0; i < NUMBER(global_axis); i++)
|
|
{
|
|
int m = count_values(r, global_axis[i].name);
|
|
if(m == 1)
|
|
{
|
|
add_value(s.c->cube, "axis", global_axis[i].name);
|
|
set_value(s.c->cube, global_axis[i].name, get_value(r, global_axis[i].name, 0));
|
|
}
|
|
}
|
|
|
|
n = count_values(s.c->cube, "axis");
|
|
if(n)
|
|
s.c->compare = (namecmp*)calloc(sizeof(namecmp), n);
|
|
|
|
for(i = 0; i < n; i++)
|
|
s.c->compare[i] = comparator(get_value(s.c->cube, "axis", i));
|
|
|
|
return s.c;
|
|
}
|
|
|
|
/* This one doesn't have single parameters in CUBE */
|
|
static hypercube *new_simple_hypercube_from_mars_request(const request *r)
|
|
{
|
|
int i;
|
|
int n;
|
|
|
|
struct stuff_1 s;
|
|
s.c = new_hypercube(r);
|
|
s.r = clone_one_request(r);
|
|
|
|
reset_index(s.c, 0);
|
|
|
|
names_loop(r, reqcb_1, &s);
|
|
|
|
free_one_request(s.r);
|
|
n = count_values(s.c->cube, "axis");
|
|
if(n)
|
|
s.c->compare = (namecmp*)calloc(sizeof(namecmp), n);
|
|
|
|
for(i = 0; i < n; i++)
|
|
s.c->compare[i] = comparator(get_value(s.c->cube, "axis", i));
|
|
|
|
return s.c;
|
|
}
|
|
|
|
/*===========================================================================================*/
|
|
|
|
/*===========================================================================================*/
|
|
|
|
/* Todo:
|
|
- BUILD A TEST SUITE !!
|
|
- Print usage in log file
|
|
- consider FCMONTH and CLimatolofy
|
|
- Build logic to create validationtime when only one of DATE or TIME or STEP have multiple values:
|
|
for example: date=-1, time=12, step=24/48
|
|
- parametrise the type of data for each axis (function fill_netcdf_dimensions & define_netcdf_dimensions)
|
|
Now, all are INT but TIME could be float
|
|
- allow user specified scale_factor
|
|
- insert 'user input request'
|
|
- ORIGIN
|
|
- Monthly means seem not to ignore STEP (data server, era40, eg retrieve 39/142)
|
|
*/
|
|
|
|
typedef struct ncatt {
|
|
char name[1024];
|
|
char *long_name;
|
|
char *units;
|
|
char *short_name;
|
|
char *standard_name;
|
|
request *metadata;
|
|
nc_type nctype;
|
|
} ncatt_t;
|
|
|
|
typedef struct filter {
|
|
fieldset *fset;
|
|
hypercube *filter;
|
|
int count;
|
|
double scale_factor;
|
|
double add_offset;
|
|
double missing;
|
|
boolean bitmap;
|
|
ncatt_t att;
|
|
request *filter_request;
|
|
boolean scale;
|
|
} dataset_t;
|
|
|
|
typedef struct ncfile {
|
|
dataset_t *filters;
|
|
int ncid;
|
|
} ncfile_t;
|
|
|
|
typedef struct ncoptions {
|
|
boolean usevalidtime; /* Whether to use valid TIME only or not */
|
|
boolean auto_refdate; /* Automatic Reference Date */
|
|
long refdate; /* Reference date */
|
|
const char *version;
|
|
|
|
char *title;
|
|
char *history;
|
|
char *unlimited;
|
|
boolean checkvalidtime;
|
|
request *mars_description;
|
|
boolean mmeans; /* Whether this dataset is Monthly Means */
|
|
boolean climatology; /* Whether this dataset is climatology */
|
|
} ncoptions_t;
|
|
|
|
ncoptions_t setup;
|
|
|
|
#define NC_TYPES 7
|
|
struct nc_types_values {
|
|
double max;
|
|
double min;
|
|
double missing;
|
|
} nc_type_values[NC_TYPES] =
|
|
{
|
|
/* In some occasions, SHRT_MIN-2 for the minimum value, makes ncview display
|
|
missing values for -32766, while NC_FILL_SHORT=-32767, and SHRT_MIN=-32768 */
|
|
{ 0, 0, 0 }, /* NC_NAT, 'Not A Type' (c.f. NaN) */
|
|
{ 0x7f, NC_FILL_BYTE +1, NC_FILL_BYTE }, /* NC_BYTE, signed 1 byte integer */
|
|
{ 0xff, NC_FILL_CHAR +1, NC_FILL_CHAR }, /* NC_CHAR, ISO/ASCII character */
|
|
{ 0x7fff, NC_FILL_SHORT+1 , NC_FILL_SHORT }, /* NC_SHORT, signed 2 byte integer */
|
|
{ 0x7ffffff, NC_FILL_INT + 1, NC_FILL_INT }, /* NC_INT, signed 4 byte integer */
|
|
{ FLT_MAX, -FLT_MAX, NC_FILL_FLOAT }, /* NC_FLOAT, single precision floating point number */
|
|
{ DBL_MAX, -DBL_MAX, NC_FILL_DOUBLE }, /* NC_DOUBLE, double precision floating point number */
|
|
};
|
|
|
|
static long fcmonth2days(long date, long months)
|
|
{
|
|
long julianfrom = grib_date_to_julian(date);
|
|
long years = (long)(months/12);
|
|
long leap = (long)(years/4) - (long)(years/100);
|
|
long to = years*365 + (months%12)*32 + 1 + leap; /* FCMONTH can't be > 28 */
|
|
long julianto = julianfrom + to;
|
|
long days = 0;
|
|
|
|
long dd = date % 100;
|
|
long dateto = grib_julian_to_date(julianto);
|
|
long nextdate = (dateto / 100) * 100 + dd;
|
|
|
|
julianto = grib_date_to_julian(nextdate);
|
|
days = julianto - julianfrom;
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: date: %ld + %ld months = %ld days", date, months, days);
|
|
|
|
return days;
|
|
}
|
|
|
|
static long request_fields(request *r)
|
|
{
|
|
long cnt = 1;
|
|
parameter *p = r->params;
|
|
while(p)
|
|
{
|
|
if(p->name[0] != '_')
|
|
{
|
|
cnt *= count_values(r,p->name);
|
|
}
|
|
p = p->next;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
/* Transform MARS FCMONTHs into number of months from base date.
|
|
For example, FCMONTH=1 is current month == 0 */
|
|
static void fcmonth2nbmonths(request *r)
|
|
{
|
|
long n = count_values(r, "fcmonth");
|
|
|
|
if(n == 0)
|
|
return;
|
|
n = atol(get_value(r, "fcmonth", 0));
|
|
set_value(r, "fcmonth", "%ld", n - 1);
|
|
}
|
|
|
|
static long monthnumber(const char *m)
|
|
{
|
|
const char *months[] =
|
|
{ "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
|
|
int i = 0;
|
|
|
|
while(i < 12)
|
|
if(strcmp(m, months[i++]) == 0)
|
|
return i;
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Error. Translation for MONTH='%s' not found", m);
|
|
return -1;
|
|
}
|
|
|
|
/* The argument represents 1 field */
|
|
static void validation_time(request *r)
|
|
{
|
|
long date = 0;
|
|
long time = 0;
|
|
long step = 0;
|
|
long fcmonthdays = 0;
|
|
long fcmonth = 0;
|
|
double v;
|
|
long julian = 0;
|
|
|
|
long nstep = count_values(r, "step");
|
|
long ndate = count_values(r, "date");
|
|
long ntime = count_values(r, "time");
|
|
long nfcmonth = count_values(r, "fcmonth");
|
|
|
|
static long julianrefdate = 0;
|
|
|
|
if(nstep > 1 || ndate > 1 || ntime > 1 || nfcmonth > 1)
|
|
{
|
|
if(nstep > 1)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error. Field has more than 1 STEP");
|
|
if(ntime > 1)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error. Field has more than 1 TIME");
|
|
if(ndate > 1)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error. Field has more than 1 DATE");
|
|
if(nfcmonth > 1)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error. Field has more than 1 FCMONTH");
|
|
print_all_requests(r);
|
|
exit(1);
|
|
}
|
|
|
|
if(nstep)
|
|
step = atol(get_value(r, "step", 0));
|
|
|
|
if(ndate)
|
|
{
|
|
const char* p = get_value(r, "date", 0);
|
|
if(is_number(p))
|
|
date = atol(p);
|
|
else
|
|
{
|
|
long julian = 0, second = 0;
|
|
boolean isjul, date_ok;
|
|
date_ok = parsedate(p, &julian, &second, &isjul);
|
|
if (!date_ok) grib_context_log(ctx, GRIB_LOG_ERROR, "Failed to parse date: '%s'", p);
|
|
date = grib_julian_to_date(julian);
|
|
}
|
|
|
|
/* Climatology */
|
|
if(strlen(p) == 3)
|
|
{
|
|
if(setup.usevalidtime)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Climatology data. Setting usevalidtime=OFF");
|
|
setup.auto_refdate = FALSE;
|
|
setup.usevalidtime = FALSE;
|
|
setup.climatology = TRUE;
|
|
}
|
|
}
|
|
|
|
if(ntime)
|
|
time = atol(get_value(r, "time", 0));
|
|
|
|
if(nfcmonth)
|
|
{
|
|
fcmonth = atol(get_value(r, "fcmonth", 0));
|
|
/* FCMONTH needs base DATE */
|
|
if(date == 0)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error: FCMONTH needs DATE");
|
|
exit(1);
|
|
}
|
|
fcmonthdays = fcmonth2days(date, fcmonth);
|
|
}
|
|
|
|
julian = grib_date_to_julian(date);
|
|
v = julian * 24.0 + fcmonthdays * 24.0 + time / 100.0 + step * 1.0;
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: date=%ld, julian=%ld, fcmonthdays=%ld, time=%ld, step=%ld, validation=%ld", date, julian, fcmonthdays, time, step, v);
|
|
set_value(r, "_validation", "%lf", v);
|
|
set_value(r, "_juliandate", "%ld", julian);
|
|
|
|
if(!julianrefdate)
|
|
julianrefdate = grib_date_to_julian(setup.refdate);
|
|
|
|
set_value(r, "_validationtime", "%lf", v - julianrefdate * 24.0);
|
|
|
|
/* Remove minutes from TIME */
|
|
if(ntime)
|
|
set_value(r, "time", "%.1lf", time / 100.0);
|
|
}
|
|
|
|
static void free_nc_options()
|
|
{
|
|
|
|
if(setup.title)
|
|
grib_context_free(ctx, setup.title);
|
|
if(setup.history)
|
|
grib_context_free(ctx, setup.history);
|
|
if(setup.unlimited)
|
|
grib_context_free(ctx, setup.unlimited);
|
|
if(setup.mars_description)
|
|
free_all_requests(setup.mars_description);
|
|
}
|
|
|
|
static void get_nc_options(const request *user_r)
|
|
{
|
|
const char *checkvalidtime_env = NULL;
|
|
const char *validtime = get_value(user_r, "usevalidtime", 0);
|
|
const char *refdate = get_value(user_r, "referencedate", 0);
|
|
|
|
const char *title = get_value(user_r, "title", 0);
|
|
const char *history = get_value(user_r, "history", 0);
|
|
const char *unlimited = get_value(user_r, "unlimited", 0);
|
|
|
|
setup.usevalidtime = validtime ? (strcmp(validtime, "true") == 0) : FALSE;
|
|
setup.refdate = refdate ? atol(refdate) : 19000101;
|
|
setup.auto_refdate = refdate ? (strcmp(get_value(user_r, "referencedate", 0), "AUTOMATIC") == 0) : FALSE;
|
|
|
|
setup.title = title ? grib_context_strdup(ctx, (title)) : NULL;
|
|
setup.history = history ? grib_context_strdup(ctx, (history)) : NULL;
|
|
setup.unlimited = unlimited ? grib_context_strdup(ctx, ((unlimited))) : NULL;
|
|
|
|
checkvalidtime_env = getenv("GRIB_TO_NETCDF_CHECKVALIDTIME");
|
|
setup.checkvalidtime = checkvalidtime_env ? atol(checkvalidtime_env) : 1;
|
|
setup.mars_description = empty_request("MARS");
|
|
}
|
|
|
|
static nc_type translate_nctype(const char *name)
|
|
{
|
|
|
|
if(!name)
|
|
return NC_SHORT;
|
|
|
|
if(strcmp(name, "NC_BYTE") == 0)
|
|
return NC_BYTE;
|
|
|
|
if(strcmp(name, "NC_SHORT") == 0)
|
|
return NC_SHORT;
|
|
|
|
if(strcmp(name, "NC_INT") == 0)
|
|
return NC_INT;
|
|
|
|
if(strcmp(name, "NC_FLOAT") == 0)
|
|
return NC_FLOAT;
|
|
|
|
if(strcmp(name, "NC_DOUBLE") == 0)
|
|
return NC_DOUBLE;
|
|
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Unknown Netcdf type '%s'. Using NC_SHORT");
|
|
return NC_SHORT;
|
|
}
|
|
|
|
static void check_err(const int stat, const int line, const char *file)
|
|
{
|
|
if(stat != NC_NOERR)
|
|
{
|
|
(void) fprintf(stderr, "line %d of %s: %s\n", line, file, nc_strerror(stat));
|
|
if (stat == NC_EVARSIZE) {
|
|
(void) fprintf(stderr, "\nCannot create netCDF classic format, dataset is too large!\n"
|
|
"Try splitting the input GRIB(s).\n");
|
|
}
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
#define DIM_ID 1
|
|
static int set_dimension(int ncid, const char *name, int n, int xtype, const char *units, const char *long_name)
|
|
{
|
|
|
|
int var_id = 0;
|
|
int stat = 0;
|
|
int dim_id = DIM_ID;
|
|
int dim_vec[DIM_ID];
|
|
|
|
if(setup.unlimited && (strcmp(name, setup.unlimited) == 0))
|
|
n = NC_UNLIMITED;
|
|
|
|
stat = nc_def_dim(ncid, name, n, &dim_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
dim_vec[0] = dim_id;
|
|
stat = nc_def_var(ncid, name, (nc_type) xtype, 1, dim_vec, &var_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
if(units != NULL)
|
|
{
|
|
stat = nc_put_att_text(ncid, var_id, "units", strlen(units), units);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
if(long_name != NULL)
|
|
{
|
|
stat = nc_put_att_text(ncid, var_id, "long_name", strlen(long_name), long_name);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
return var_id;
|
|
}
|
|
|
|
static int def_latlon(int ncid, fieldset *fs)
|
|
{
|
|
int n = 0;
|
|
size_t l = 0;
|
|
int var_id = 0;
|
|
err e = 0;
|
|
|
|
field *g = get_field(fs, 0, expand_mem);
|
|
|
|
char grid_type[80];
|
|
size_t size;
|
|
size = sizeof(grid_type);
|
|
if((e = grib_get_string(g->handle, "typeOfGrid", grid_type, &size)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get typeOfGrid %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if(strcmp(grid_type, "regular_ll") != 0 && (strcmp(grid_type, "regular_gg") != 0) )
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "First GRIB is not on a regular lat/lon grid or on a regular Gaussian grid. Exiting.\n");
|
|
return GRIB_GEOCALCULUS_PROBLEM;
|
|
}
|
|
|
|
/* Define longitude */
|
|
if((e = grib_get_size(g->handle, "distinctLongitudes", &l)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get distinctLongitudes %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
n = l;
|
|
var_id = set_dimension(ncid, "longitude", n, NC_FLOAT, "degrees_east", "longitude");
|
|
|
|
/* Define latitude */
|
|
if((e = grib_get_size(g->handle, "distinctLatitudes", &l)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get distinctLatitudes %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
n = l;
|
|
var_id = set_dimension(ncid, "latitude", n, NC_FLOAT, "degrees_north", "latitude");
|
|
|
|
/* g->purge_header = TRUE; */
|
|
release_field(g);
|
|
|
|
return e;
|
|
}
|
|
|
|
static int put_latlon(int ncid, fieldset *fs)
|
|
{
|
|
int var_id = 0;
|
|
int i = 0;
|
|
size_t n = 0;
|
|
int stat = 0;
|
|
err e = 0;
|
|
|
|
field *g = get_field(fs, 0, expand_mem);
|
|
|
|
double *dvalues = NULL;
|
|
float *fvalues = NULL;
|
|
long nv = 0;
|
|
|
|
size_t ni;
|
|
size_t nj;
|
|
|
|
#if 0
|
|
/* Get info in degrees */
|
|
if((e = grib_get_double(g->handle, "iDirectionIncrementInDegrees", &ew_stride)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get iDirectionIncrementInDegrees %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if((e = grib_get_double(g->handle, "jDirectionIncrementInDegrees", &ns_stride)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get jDirectionIncrementInDegrees %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
/* Define longitude */
|
|
if((e = grib_get_long(g->handle, "Ni", &ni)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get Ni %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
/* Define latitude */
|
|
if((e = grib_get_long(g->handle, "Nj", &nj)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get Nj %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
#endif
|
|
|
|
if((e = grib_get_size(g->handle, "distinctLatitudes", &nj)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get distinctLatitudes %s", grib_get_error_message(e));
|
|
return e;
|
|
|
|
}
|
|
|
|
if((e = grib_get_size(g->handle, "distinctLongitudes", &ni)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get distinctLongitudes %s", grib_get_error_message(e));
|
|
return e;
|
|
|
|
}
|
|
/* Compute max. # values and allocate */
|
|
nv = ni;
|
|
if(nv < nj)
|
|
nv = nj;
|
|
|
|
fvalues = (float *) grib_context_malloc(ctx, sizeof(float) * nv);
|
|
dvalues = (double *) grib_context_malloc(ctx, sizeof(double) * nv);
|
|
|
|
/* longitude */
|
|
n = ni;
|
|
stat = nc_inq_varid(ncid, "longitude", &var_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
if((e = grib_get_double_array(g->handle, "distinctLongitudes", dvalues, &n)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get distinctLongitudes %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
Assert(n == ni);
|
|
|
|
for(i=0; i< n; i++) { fvalues[i] = dvalues[i]; }
|
|
|
|
stat = nc_put_var_float(ncid, var_id, fvalues);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* latitude */
|
|
n = nj;
|
|
stat = nc_inq_varid(ncid, "latitude", &var_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
if((e = grib_get_double_array(g->handle, "distinctLatitudes", dvalues, &n)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get distinctLatitudes %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
Assert(n == nj);
|
|
|
|
for(i=0; i< n; i++) { fvalues[i] = dvalues[i]; }
|
|
stat = nc_put_var_float(ncid, var_id, fvalues);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* g->purge_header = TRUE; */
|
|
release_field(g);
|
|
grib_context_free(ctx, fvalues);
|
|
grib_context_free(ctx, dvalues);
|
|
|
|
return e;
|
|
}
|
|
|
|
static int compute_scale(dataset_t *subset)
|
|
{
|
|
double max = -DBL_MAX;
|
|
double min = DBL_MAX;
|
|
double median = 0;
|
|
long i = 0;
|
|
long j = 0;
|
|
int64_t scaled_max = 0;
|
|
int64_t scaled_min = 0;
|
|
int64_t scaled_median = 0;
|
|
double ao = 0.0, sf = 0.0;
|
|
double x;
|
|
|
|
char test_scaled_max = 0;
|
|
char test_scaled_min = 0;
|
|
char test_scaled_median = 0;
|
|
|
|
err e = 0;
|
|
|
|
fieldset *fs = subset->fset;
|
|
int idx = subset->att.nctype;
|
|
|
|
for(i = 0; i < fs->count; i++)
|
|
{
|
|
field *g = get_field(fs, i, expand_mem);
|
|
size_t len;
|
|
static double *vals = NULL;
|
|
static size_t vals_len = 0;
|
|
|
|
if((e = grib_get_size(g->handle, "values", &len)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get size of values %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if(len > vals_len)
|
|
{
|
|
if(vals)
|
|
grib_context_free(ctx, vals);
|
|
vals = (double*) grib_context_malloc(ctx, sizeof(double) * len);
|
|
vals_len = len;
|
|
}
|
|
if((e = grib_get_double_array(g->handle, "values", vals, &len)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get values %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if(g->bitmap)
|
|
{
|
|
subset->bitmap = TRUE;
|
|
for(j = 0; j < len; ++j)
|
|
{
|
|
if(vals[j] != (double) missing_value)
|
|
{
|
|
if(vals[j] > max)
|
|
max = vals[j];
|
|
if(vals[j] < min)
|
|
min = vals[j];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(j = 0; j < len; ++j)
|
|
{
|
|
if(vals[j] > max)
|
|
max = vals[j];
|
|
if(vals[j] < min)
|
|
min = vals[j];
|
|
}
|
|
}
|
|
/* g->purge_header = TRUE; */
|
|
release_field(g);
|
|
}
|
|
|
|
median = (max + min) / 2.0;
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: max_int: %lf, min_int: %lf", nc_type_values[idx].max, nc_type_values[idx].min);
|
|
|
|
sf = (double) ((max - min) / (double) (nc_type_values[idx].max - nc_type_values[idx].min));
|
|
ao = ((max + min) - sf * (nc_type_values[idx].min + nc_type_values[idx].max)) / 2;
|
|
|
|
if (min == max) {
|
|
sf = 1.0; /* Prevent divide by zero later. Constant field grib has max == min */
|
|
}
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: idx is: %d", idx);
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: max: %lf, min: %lf, median: %lf, scale factor: %lf, add_offset: %lf", max, min, median, sf, ao);
|
|
|
|
x = ((median - ao));
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: x=%lf", x);
|
|
x /= sf;
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: x=%lf", x);
|
|
|
|
scaled_max = rint((max - ao) / sf);
|
|
scaled_min = rint((min - ao) / sf);
|
|
scaled_median = rint((median - ao) / sf);
|
|
|
|
test_scaled_max = (char) scaled_max;
|
|
test_scaled_min = (char) scaled_min;
|
|
test_scaled_median = (char) scaled_median;
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: scaled_max: %lld, scaled_min: %lld, scaled_median: %lld, x: %lf", scaled_max, scaled_min, scaled_median, x);
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: test_scaled_max: %x, test_scaled_min: %x, test_scaled_median: %x", test_scaled_max, test_scaled_min, test_scaled_median, x);
|
|
|
|
max = scaled_max * sf + ao;
|
|
min = scaled_min * sf + ao;
|
|
median = scaled_median * sf + ao;
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: max: %lf, min: %lf, median: %lf", max, min, median);
|
|
|
|
subset->scale_factor = sf;
|
|
subset->add_offset = ao;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nc_put_att_type(int ncid, int varid, const char *name, nc_type nctype, int n, double value)
|
|
{
|
|
int r = 0;
|
|
unsigned char val_uchar = (unsigned char) value;
|
|
short int val_short = (short int) value;
|
|
int val_int = (int) value;
|
|
float val_flt = (float) value;
|
|
double val_dbl = (double) value;
|
|
|
|
switch(nctype)
|
|
{
|
|
case NC_BYTE:
|
|
r = nc_put_att_uchar(ncid, varid, name, nctype, n, &val_uchar);
|
|
break;
|
|
case NC_SHORT:
|
|
r = nc_put_att_short(ncid, varid, name, nctype, n, &val_short);
|
|
break;
|
|
case NC_INT:
|
|
r = nc_put_att_int(ncid, varid, name, nctype, n, &val_int);
|
|
break;
|
|
case NC_FLOAT:
|
|
r = nc_put_att_float(ncid, varid, name, nctype, n, &val_flt);
|
|
break;
|
|
case NC_DOUBLE:
|
|
r = nc_put_att_double(ncid, varid, name, nctype, n, &val_dbl);
|
|
break;
|
|
default:
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "nc_put_att_type(...): Unknown netcdf type '%d'", nctype);
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int nc_put_vara_type(int ncid, int varid, const size_t start[], const size_t count[], void *valuesp, nc_type nctype)
|
|
{
|
|
int r = 0;
|
|
switch(nctype)
|
|
{
|
|
case NC_BYTE:
|
|
r = nc_put_vara_uchar(ncid, varid, start, count, (unsigned char *) valuesp);
|
|
break;
|
|
case NC_SHORT:
|
|
r = nc_put_vara_short(ncid, varid, start, count, (short int *) valuesp);
|
|
break;
|
|
case NC_INT:
|
|
r = nc_put_vara_int(ncid, varid, start, count, (int *) valuesp);
|
|
break;
|
|
case NC_FLOAT:
|
|
r = nc_put_vara_float(ncid, varid, start, count, (float *) valuesp);
|
|
break;
|
|
case NC_DOUBLE:
|
|
r = nc_put_vara_double(ncid, varid, start, count, (double *) valuesp);
|
|
break;
|
|
default:
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "nc_put_vara_type(...): Unknown netcdf type '%d'", nctype);
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static void scale_bitmap(double *vals, long n, void *data, dataset_t *subset)
|
|
{
|
|
int i = 0;
|
|
nc_type nctype = subset->att.nctype;
|
|
|
|
/*
|
|
if(!subset->bitmap)
|
|
{
|
|
grib_context_log(ctx,GRIB_LOG_DEBUG,"No scale of bitmap required");
|
|
return;
|
|
}
|
|
*/
|
|
|
|
switch(nctype)
|
|
{
|
|
case NC_BYTE:
|
|
{
|
|
unsigned char *vscaled = (unsigned char *) data;
|
|
for(i = 0; i < n; ++i)
|
|
if(vals[i] == missing_value)
|
|
vscaled[i] = (unsigned char) subset->missing;
|
|
break;
|
|
}
|
|
|
|
case NC_SHORT:
|
|
{
|
|
short int *vscaled = (short int *) data;
|
|
for(i = 0; i < n; ++i)
|
|
if(vals[i] == missing_value)
|
|
vscaled[i] = (short int) subset->missing;
|
|
break;
|
|
}
|
|
|
|
case NC_INT:
|
|
{
|
|
int *vscaled = (int *) data;
|
|
for(i = 0; i < n; ++i)
|
|
if(vals[i] == missing_value)
|
|
vscaled[i] = (int) subset->missing;
|
|
break;
|
|
}
|
|
|
|
case NC_FLOAT:
|
|
{
|
|
float *vscaled = (float *) data;
|
|
for(i = 0; i < n; ++i)
|
|
if(vals[i] == missing_value)
|
|
vscaled[i] = (float) subset->missing;
|
|
break;
|
|
}
|
|
|
|
case NC_DOUBLE:
|
|
{
|
|
double *vscaled = (double *) data;
|
|
for(i = 0; i < n; ++i)
|
|
if(vals[i] == missing_value)
|
|
vscaled[i] = (double) subset->missing;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "scale(...): Unknown netcdf type %d", nctype);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void scale(double *vals, long n, void *data, dataset_t *g)
|
|
{
|
|
int i = 0;
|
|
nc_type nctype = g->att.nctype;
|
|
double scale_factor = g->scale_factor;
|
|
double add_offset = g->add_offset;
|
|
|
|
/*
|
|
if(!subset->scale)
|
|
{
|
|
grib_context_log(ctx,GRIB_LOG_DEBUG,"No scale required");
|
|
return;
|
|
}
|
|
*/
|
|
|
|
switch(nctype)
|
|
{
|
|
case NC_BYTE:
|
|
{
|
|
unsigned char *vscaled = (unsigned char *) data;
|
|
for(i = 0; i < n; ++i)
|
|
{
|
|
if(!g->bitmap || (vals[i] != missing_value))
|
|
{
|
|
double d = rint((vals[i] - add_offset) / scale_factor);
|
|
Assert(d >= nc_type_values[nctype].min && d <= nc_type_values[nctype].max);
|
|
vscaled[i] = d;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NC_SHORT:
|
|
{
|
|
short int *vscaled = (short int *) data;
|
|
for(i = 0; i < n; ++i)
|
|
{
|
|
if(!g->bitmap || (vals[i] != missing_value))
|
|
{
|
|
double d = 0;
|
|
Assert(scale_factor>0);
|
|
d = rint((vals[i] - add_offset) / scale_factor);
|
|
Assert(d >= nc_type_values[nctype].min && d <= nc_type_values[nctype].max);
|
|
vscaled[i] = d;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NC_INT:
|
|
{
|
|
int *vscaled = (int *) data;
|
|
for(i = 0; i < n; ++i)
|
|
{
|
|
if(!g->bitmap || (vals[i] != missing_value))
|
|
{
|
|
double d = rint((vals[i] - add_offset) / scale_factor);
|
|
Assert(d >= nc_type_values[nctype].min && d <= nc_type_values[nctype].max);
|
|
vscaled[i] = d;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NC_FLOAT:
|
|
{
|
|
float *vscaled = (float *) data;
|
|
for(i = 0; i < n; ++i)
|
|
{
|
|
if(!g->bitmap || (vals[i] != missing_value))
|
|
{
|
|
double d = vals[i];
|
|
Assert(d >= nc_type_values[nctype].min && d <= nc_type_values[nctype].max);
|
|
vscaled[i] = d;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NC_DOUBLE:
|
|
{
|
|
double *vscaled = (double *) data;
|
|
for(i = 0; i < n; ++i)
|
|
{
|
|
if(!g->bitmap || (vals[i] != missing_value))
|
|
{
|
|
double d = vals[i];
|
|
Assert(d >= nc_type_values[nctype].min && d <= nc_type_values[nctype].max);
|
|
vscaled[i] = d;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "scale(...): Unknown netcdf type %d", nctype);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int put_data(hypercube *h, int ncid, const char *name, dataset_t *subset)
|
|
{
|
|
int i = 0;
|
|
int stat = 0;
|
|
int dataid = 0;
|
|
int naxis = count_axis(h);
|
|
size_t start[NC_MAX_DIMS];
|
|
size_t count[NC_MAX_DIMS];
|
|
fieldset *fs = subset->fset;
|
|
field *f = get_field(fs, 0, expand_mem);
|
|
|
|
void *vscaled = NULL;
|
|
long vscaled_length = 0;
|
|
|
|
long ni;
|
|
long nj;
|
|
err e = 0;
|
|
|
|
/* Define longitude */
|
|
if((e = grib_get_long(f->handle, "Ni", &ni)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get Ni %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
/* Define latitude */
|
|
if((e = grib_get_long(f->handle, "Nj", &nj)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get Nj %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
/* Start filling dimensions at first value */
|
|
for(i = 0; i < 2 + naxis; ++i)
|
|
start[i] = 0;
|
|
|
|
/* Count dimensions per axis */
|
|
for(i = 0; i < naxis; ++i)
|
|
count[naxis - i - 1] = 1;
|
|
|
|
count[naxis] = nj; /* latitude */
|
|
count[naxis + 1] = ni; /* longitude */
|
|
|
|
/* f->purge_header = TRUE; */
|
|
release_field(f);
|
|
|
|
stat = nc_inq_varid(ncid, name, &dataid);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
for(i = 0; i < fs->count; i++)
|
|
{
|
|
field *g = get_field(fs, i, expand_mem);
|
|
size_t len;
|
|
static double *vals = NULL;
|
|
static size_t vals_len = 0;
|
|
boolean missing = 0;
|
|
|
|
request *r;
|
|
int j = 0;
|
|
int idx[1024];
|
|
int idxsize = 1024;
|
|
|
|
if((e = grib_get_size(g->handle, "values", &len)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get size of values %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if(len > vals_len)
|
|
{
|
|
if(vals)
|
|
grib_context_free(ctx, vals);
|
|
vals = (double*) grib_context_malloc(ctx, sizeof(double) * len);
|
|
vals_len = len;
|
|
}
|
|
if((e = grib_get_double_array(g->handle, "values", vals, &len)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get values %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
#if 0
|
|
boolean missing = (g->ksec4[0] < 0); /* If negative number of values, field is missing */
|
|
#endif
|
|
|
|
r = field_to_request(g);
|
|
if(!missing)
|
|
{
|
|
/* Reserved the maximum memory needed */
|
|
/* This should only be done once, as all fields have the same geometry */
|
|
if((vscaled_length == 0) || (vscaled_length < sizeof(double) * len))
|
|
{
|
|
if(vscaled)
|
|
grib_context_free(ctx, vscaled);
|
|
vscaled = (void *) grib_context_malloc(ctx, sizeof(double) * len);
|
|
vscaled_length = sizeof(double) * len;
|
|
}
|
|
|
|
scale(vals, len, vscaled, subset);
|
|
if(subset->bitmap)
|
|
scale_bitmap(vals, len, vscaled, subset);
|
|
|
|
if((e = grib_get_long(g->handle, "Ni", &ni)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get Ni %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
/* Define latitude */
|
|
if((e = grib_get_long(g->handle, "Nj", &nj)) != GRIB_SUCCESS)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "grib_api: cannot get Nj %s", grib_get_error_message(e));
|
|
return e;
|
|
}
|
|
|
|
if(nj != count[naxis] || ni != count[naxis + 1])
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Grib %d has different resolution\n", i + 1);
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "lat=%d, long=%d instead of lat=%d, long=%d\n", nj, ni, count[naxis], count[naxis + 1]);
|
|
exit(1);
|
|
}
|
|
|
|
cube_indexes(h, r, idx, idxsize);
|
|
for(j = 0; j < naxis; ++j)
|
|
start[naxis - j - 1] = idx[j];
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: Put data from field %d", i);
|
|
|
|
stat = nc_put_vara_type(ncid, dataid, start, count, vscaled, subset->att.nctype);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
/* g->purge_header = TRUE; */
|
|
release_field(g);
|
|
}
|
|
grib_context_free(ctx, vscaled);
|
|
return 0;
|
|
}
|
|
|
|
static void set_always_a_time(hypercube *h, request *data_r)
|
|
{
|
|
if (setup.usevalidtime && count_values(data_r, "time") == 1)
|
|
{
|
|
set_value(h->cube, "time", "%.2lf", atof(get_value(data_r, "_validationtime", 0)));
|
|
add_value(h->cube, "axis", "time");
|
|
|
|
{
|
|
int i = 0;
|
|
int n = count_values(h->cube, "axis");
|
|
if(n)
|
|
{
|
|
h->compare = (namecmp*)calloc(sizeof(namecmp), n);
|
|
Assert(h->compare);
|
|
}
|
|
|
|
for(i = 0; i < n; i++)
|
|
h->compare[i] = comparator(get_value(h->cube, "axis", i));
|
|
}
|
|
}
|
|
}
|
|
|
|
static int define_netcdf_dimensions(hypercube *h, fieldset *fs, int ncid, dataset_t *subsets, int subsetcnt, const request *data_r)
|
|
{
|
|
const request *cube = h->cube;
|
|
int naxis = count_axis(h);
|
|
int i = 0;
|
|
int stat = 0;
|
|
int n = 0;
|
|
int var_id = 0; /* Variable ID */
|
|
int dims[1024];
|
|
|
|
/* START DEFINITIONS */
|
|
|
|
/* Define latitude/longitude dimensions */
|
|
err e = def_latlon(ncid, fs);
|
|
if (e != GRIB_SUCCESS)
|
|
return e;
|
|
|
|
/* Define netcdf dimensions */
|
|
for(i = 0; i < naxis; ++i)
|
|
{
|
|
int nctype = NC_INT;
|
|
const char *axis = get_axis(h, i);
|
|
char *units = NULL;
|
|
char u[10240];
|
|
const char *lowaxis = (axis);
|
|
char *longname = (char *) lowaxis;
|
|
n = count_values(cube, axis);
|
|
|
|
if(count_values(data_r,"levtype") > 1)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Cannot handle fields for different levtypes.\n");
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Please split input data into different files. Exiting!\n");
|
|
exit(1);
|
|
}
|
|
|
|
if(strcmp(axis, "levelist") == 0)
|
|
{
|
|
const char *levtype = get_value(data_r, "levtype", 0);
|
|
if(levtype)
|
|
{
|
|
if(strcmp("pl", levtype) == 0)
|
|
{
|
|
units = "millibars";
|
|
longname = "pressure_level";
|
|
}
|
|
|
|
if(strcmp("ml", levtype) == 0)
|
|
{
|
|
longname = "model_level_number";
|
|
}
|
|
}
|
|
|
|
lowaxis = "level";
|
|
}
|
|
|
|
if(strcmp(axis, "number") == 0)
|
|
{
|
|
longname = "ensemble_member";
|
|
}
|
|
|
|
if(strcmp(axis, "date") == 0)
|
|
{
|
|
sprintf(u, "days since %ld-%02ld-%02ld 00:00:0.0", setup.refdate / 10000, (setup.refdate % 10000) / 100, (setup.refdate % 100));
|
|
units = u;
|
|
longname = "Base_date";
|
|
if(setup.climatology)
|
|
{
|
|
sprintf(u, "months");
|
|
units = u;
|
|
}
|
|
}
|
|
|
|
if(strcmp(axis, "time") == 0)
|
|
{
|
|
boolean onedtime = (count_values(cube, "date") == 0 && count_values(cube, "step") == 0);
|
|
sprintf(u, "hours since 0000-00-00 00:00:0.0");
|
|
longname = "reference_time";
|
|
if(setup.usevalidtime || onedtime)
|
|
{
|
|
sprintf(u, "hours since %ld-%02ld-%02ld 00:00:0.0", setup.refdate / 10000, (setup.refdate % 10000) / 100, (setup.refdate % 100));
|
|
longname = "time";
|
|
}
|
|
if(setup.climatology)
|
|
{
|
|
sprintf(u, "hours");
|
|
}
|
|
units = u;
|
|
/* nctype = NC_FLOAT; */
|
|
}
|
|
|
|
if(strcmp(axis, "step") == 0)
|
|
{
|
|
units = "hours";
|
|
longname = "time_step";
|
|
if(count_values(cube, "date") == 0 && count_values(cube, "time") == 0)
|
|
{
|
|
const char *d = get_value(data_r, "date", 0);
|
|
const char *t = get_value(data_r, "time", 0);
|
|
long date = d ? atol(d) : 0;
|
|
long hour = t ? atol(t) : 0;
|
|
long min = t ? 60 * (atof(t) - hour) : 0;
|
|
sprintf(u, "hours since %ld-%02ld-%02ld %02ld:%02ld:0.0", date / 10000, (date % 10000) / 100, (date % 100), hour, min);
|
|
units = u;
|
|
}
|
|
}
|
|
|
|
if((strcmp(axis, "fcmonth") == 0))
|
|
{
|
|
const char *date = get_value(data_r, "date", 0);
|
|
char ymd[32] = "";
|
|
|
|
if(date)
|
|
{
|
|
strncat(ymd, date, 4);
|
|
strcat(ymd, "-");
|
|
strncat(ymd, date + 4, 2);
|
|
strcat(ymd, "-");
|
|
/* udunits is a bit tricky with month being 30.4 days */
|
|
/* ncview doesn't display properly */
|
|
strcat(ymd, "01");
|
|
}
|
|
else
|
|
{
|
|
sprintf(ymd, "00-00-00");
|
|
}
|
|
sprintf(u, "months since %s 00:00:00.0", ymd);
|
|
units = u;
|
|
longname = "time";
|
|
}
|
|
|
|
var_id = set_dimension(ncid, lowaxis, n, nctype, units, longname);
|
|
|
|
if(strcmp(axis, "time") == 0)
|
|
{
|
|
if(setup.usevalidtime)
|
|
{
|
|
const char *cal = "gregorian";
|
|
|
|
if(setup.mmeans)
|
|
{
|
|
char *period = "0000-01-00 00:00:00";
|
|
stat = nc_put_att_text(ncid, var_id, "avg_period", strlen(period), period);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
stat = nc_put_att_text(ncid, var_id, "calendar", strlen(cal), cal);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Define data dimension */
|
|
n = 1 + 1 + naxis; /* longitude + latitude + # axis */
|
|
for(i = 0; i < n; ++i)
|
|
dims[i] = n - i - 1;
|
|
|
|
for(i = 0; i < subsetcnt; ++i)
|
|
{
|
|
|
|
printf("%s: Defining variable '%s'.\n", grib_tool_name, subsets[i].att.name);
|
|
|
|
stat = nc_def_var(ncid, subsets[i].att.name, subsets[i].att.nctype, n, dims, &var_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
if(subsets[i].scale)
|
|
{
|
|
compute_scale(&subsets[i]);
|
|
stat = nc_put_att_double(ncid, var_id, "scale_factor", NC_DOUBLE, 1, &subsets[i].scale_factor);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
stat = nc_put_att_double(ncid, var_id, "add_offset", NC_DOUBLE, 1, &subsets[i].add_offset);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
stat = nc_put_att_type(ncid, var_id, "_FillValue", subsets[i].att.nctype, 1, nc_type_values[subsets[i].att.nctype].missing);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
stat = nc_put_att_type(ncid, var_id, "missing_value", subsets[i].att.nctype, 1, nc_type_values[subsets[i].att.nctype].missing);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
if(subsets[i].att.units)
|
|
{
|
|
const char *txt = subsets[i].att.units;
|
|
stat = nc_put_att_text(ncid, var_id, "units", strlen(txt), txt);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
if(subsets[i].att.long_name)
|
|
{
|
|
const char *txt = subsets[i].att.long_name;
|
|
stat = nc_put_att_text(ncid, var_id, "long_name", strlen(txt), txt);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
if(subsets[i].att.short_name)
|
|
{
|
|
const char *txt = subsets[i].att.short_name;
|
|
stat = nc_put_att_text(ncid, var_id, "short_name", strlen(txt), txt);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
if(subsets[i].att.standard_name)
|
|
{
|
|
const char *txt = subsets[i].att.standard_name;
|
|
stat = nc_put_att_text(ncid, var_id, "standard_name", strlen(txt), txt);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
#if 0
|
|
if(subsets[i].att.other)
|
|
{
|
|
const char *txt = subsets[i].att.long_name;
|
|
stat = nc_put_att_text(ncid, var_id, "other",strlen(txt),txt);
|
|
check_err(stat,__LINE__,__FILE__);
|
|
}
|
|
#endif
|
|
|
|
if(subsets[i].att.metadata)
|
|
{
|
|
parameter *p = subsets[i].att.metadata->params;
|
|
|
|
while(p)
|
|
{
|
|
const char *txt = p->values->name;
|
|
stat = nc_put_att_text(ncid, var_id, p->name, strlen(txt), txt);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Dimension-less variable for MARS request */
|
|
if(0) /* reset when we have proper & fast mars_description */
|
|
{
|
|
/* parameter *p = data_r->params; */
|
|
parameter *p = setup.mars_description->params;
|
|
stat = nc_def_var(ncid, "MARS", NC_CHAR, 0, 0, &var_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* Store request for those parameters with single value */
|
|
while(p)
|
|
{
|
|
/* if((p->name[0] != '_') && (p->count == 1)) */
|
|
if(p->name[0] != '_')
|
|
{
|
|
char par[1024];
|
|
char val[1024000] = "";
|
|
sprintf(par, "%s", (p->name));
|
|
#if 0
|
|
value2string(p->values,val);
|
|
#else
|
|
sprintf(val, "%s", (p->values->name));
|
|
#endif
|
|
stat = nc_put_att_text(ncid, var_id, par, strlen(val), (val));
|
|
if(stat != NC_NOERR)
|
|
{
|
|
printf("Error setting request for %s = %s\n", par, val);
|
|
}
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
/* Global attributes */
|
|
{
|
|
char timestamp[80];
|
|
time_t now;
|
|
/* char *convention = "MARS;CF"; */
|
|
char *convention = "CF-1.6";
|
|
char history[10240];
|
|
/* char *institution = "ECMWF Meteorological Archival and Retrieval System"; */
|
|
|
|
/* Convention */
|
|
stat = nc_put_att_text(ncid, NC_GLOBAL, "Conventions", strlen(convention), convention);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* Use history provided or Timestamp */
|
|
if(setup.history)
|
|
{
|
|
sprintf(history, "%s", setup.history);
|
|
}
|
|
else
|
|
{
|
|
int major = GRIB_API_MAJOR_VERSION;
|
|
int minor = GRIB_API_MINOR_VERSION;
|
|
int revision = GRIB_API_REVISION_VERSION;
|
|
|
|
time(&now);
|
|
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S GMT", gmtime(&now));
|
|
sprintf(history, "%s by grib_to_netcdf-%d.%d.%d: %s", timestamp, major, minor, revision, argvString);
|
|
}
|
|
stat = nc_put_att_text(ncid, NC_GLOBAL, "history", strlen(history), history);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
#if 0
|
|
stat = nc_put_att_text(ncid, NC_GLOBAL, "source",strlen(setup.source),setup.source);
|
|
check_err(stat,__LINE__,__FILE__);
|
|
|
|
stat = nc_put_att_text(ncid, NC_GLOBAL, "institution",strlen(institution),institution);
|
|
check_err(stat,__LINE__,__FILE__);
|
|
#endif
|
|
|
|
if(setup.title)
|
|
{
|
|
stat = nc_put_att_text(ncid, NC_GLOBAL, "title", strlen(setup.title), setup.title);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static int fill_netcdf_dimensions(hypercube *h, fieldset *fs, int ncid)
|
|
{
|
|
const request *cube = h->cube;
|
|
int naxis = count_axis(h);
|
|
int i = 0;
|
|
int var_id = 0;
|
|
int stat = 0;
|
|
|
|
/* Put latitude/longitude values */
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: Put latitude/longitude values");
|
|
put_latlon(ncid, fs);
|
|
|
|
/* Put netcdf axis values */
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: Put netcdf axis values");
|
|
for(i = 0; i < naxis; ++i)
|
|
{
|
|
int j = 0;
|
|
const char *axis = get_axis(h, i);
|
|
int n = count_values(cube, axis);
|
|
int *values = (int *) grib_context_malloc(ctx, sizeof(int) * n);
|
|
const char *lowaxis = (axis);
|
|
|
|
if(!values)
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "fill_netcdf_dimensions: cannot allocate %ld bytes", sizeof(int) * n);
|
|
|
|
if(strcmp("levelist", axis) == 0)
|
|
lowaxis = "level";
|
|
|
|
if(strcmp("date", axis) == 0)
|
|
{
|
|
if(setup.climatology)
|
|
for(j = 0; j < n; ++j)
|
|
values[j] = monthnumber(get_value(cube, axis, j));
|
|
else
|
|
for(j = 0; j < n; ++j)
|
|
values[j] = grib_date_to_julian(atol(get_value(cube, axis, j))) - grib_date_to_julian(setup.refdate);
|
|
}
|
|
else
|
|
{
|
|
for(j = 0; j < n; ++j)
|
|
values[j] = atol(get_value(cube, axis, j));
|
|
}
|
|
|
|
stat = nc_inq_varid(ncid, (lowaxis), &var_id);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* if ( strcmp("time", axis) == 0 && setup.unlimited != NULL && strcmp(setup.unlimited, "time") == 0 && setup.usevalidtime) */
|
|
/* GRIB-437, GRIB-625 Special treatment of RECORD (unlimited) dimension */
|
|
/* See "The NetCDF C Interface Guide" Section 6.23 */
|
|
if ( setup.unlimited != NULL && strcmp(setup.unlimited, axis) == 0 )
|
|
{
|
|
/* This is tricky. I'm not sure it works when this dimension is not outer dimension */
|
|
size_t start[NC_MAX_DIMS];
|
|
size_t count[NC_MAX_DIMS];
|
|
nc_type dim_type = 0;
|
|
|
|
start[0] = 0;
|
|
count[0] = n;
|
|
|
|
stat = nc_inq_vartype(ncid, var_id, &dim_type); /* get the type of this dimension */
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
stat = nc_put_vara_type(ncid, var_id, start, count, values, dim_type);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
else
|
|
{
|
|
stat = nc_put_var_int(ncid, var_id, values);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
}
|
|
|
|
grib_context_free(ctx, values);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void remove_param(request *r, void *data, const char *p)
|
|
{
|
|
request *config = (request *) data;
|
|
const char *ignore;
|
|
int i = 0;
|
|
|
|
while((ignore = get_value(config, p, i++)) != NULL) {
|
|
unset_value(r, ignore);
|
|
}
|
|
}
|
|
|
|
static void print_ignored_keys(FILE* f, request* data)
|
|
{
|
|
const char *ignore = NULL;
|
|
int i = 0;
|
|
while ((ignore = get_value(data, "ignore", i)) != NULL) {
|
|
if (i==0) {
|
|
fprintf(f, "%s: Ignoring key(s): %s", grib_tool_name, ignore);
|
|
} else {
|
|
fprintf(f, ", %s", ignore);
|
|
}
|
|
++i;
|
|
}
|
|
if (i>0) fprintf(f, "\n");
|
|
}
|
|
|
|
#define NO_TABLE -1
|
|
#define NO_PARAM 0
|
|
|
|
static void paramtable(const char *p, long *param, long *table, boolean paramIdMode)
|
|
{
|
|
const char *q = p;
|
|
int len = strlen(p);
|
|
|
|
*param = atol(p);
|
|
|
|
while(p && (*p != '.') && ((p - q) < len))
|
|
++p;
|
|
|
|
if((*p == '.'))
|
|
*table = atol(++p);
|
|
|
|
/* This version is grib_api... It should rely on what grib_api returns,
|
|
either param.table or paramId
|
|
*/
|
|
if(paramIdMode)
|
|
{
|
|
/* Special case for param=228015 => 15.228 */
|
|
if((*param != NO_PARAM) && (*table == NO_TABLE) && (len == 6))
|
|
{
|
|
char tbl[4];
|
|
char par[4];
|
|
p = q;
|
|
strncpy(tbl, p, 3);
|
|
tbl[3] = '\0';
|
|
strncpy(par, p + 3, 3);
|
|
par[3] = '\0';
|
|
*param = atol(par);
|
|
*table = atol(tbl);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void find_nc_attributes(const request *subset_r, const request *user_r, ncatt_t *att, const request *config_r, const request *data_r)
|
|
{
|
|
const char *split = NULL;
|
|
int j = 0;
|
|
boolean set_param_as_name = TRUE;
|
|
long datatable = 0; /* = atol(get_value(data_r,"_CODETABLE2",0)); */
|
|
|
|
if(count_values(user_r, "split") == 0)
|
|
strcat(att->name, "data");
|
|
|
|
while((split = get_value(user_r, "split", j++)) != NULL)
|
|
{
|
|
if(strcmp(split, "param") != 0)
|
|
{
|
|
if(count_values(data_r, split) > 1)
|
|
set_param_as_name = FALSE;
|
|
}
|
|
}
|
|
|
|
j = 0;
|
|
while((split = get_value(user_r, "split", j++)) != NULL)
|
|
{
|
|
boolean found = FALSE;
|
|
request *cfg = (request *) config_r;
|
|
boolean is_param = strcmp(split, "param") == 0;
|
|
/* Only use this parameter in the name if there is more
|
|
than one value in the original request or if param */
|
|
boolean setname = ((count_values(data_r, split) > 1) || (is_param && set_param_as_name));
|
|
|
|
while(cfg)
|
|
{
|
|
const char *cfgname = get_value(cfg, "NAME", 0);
|
|
const char *cfgval = get_value(cfg, "VALUE", 0);
|
|
const char *dataval = NULL;
|
|
int i = 0;
|
|
|
|
if(strcmp(split, cfgname) == 0)
|
|
{
|
|
while((dataval = get_value(subset_r, cfgname, i++)) != NULL)
|
|
{
|
|
const char *tablestr = get_value(cfg, "TABLE2", 0);
|
|
long cfgtable = (is_param && tablestr != NULL) ? atol(get_value(cfg, "TABLE2", 0)) : -1;
|
|
|
|
long cfgparam = atol(cfgval);
|
|
long dataparam = atol(dataval);
|
|
paramtable(dataval, &dataparam, &datatable, FALSE);
|
|
|
|
/* If it is not param and they're EXACTLY equal or
|
|
being param, they're the same parameter and table */
|
|
if((!is_param && (strcmp(dataval, cfgval) == 0)) || (is_param && (dataparam == cfgparam) && (datatable == cfgtable || (datatable == 0 && (cfgtable == 128)))))
|
|
{
|
|
const char *val = NULL;
|
|
const char *metafile = NULL;
|
|
|
|
if((val = get_value(cfg, "accuracy", 0)) != NULL)
|
|
att->nctype = translate_nctype(val);
|
|
att->long_name = grib_context_strdup(ctx, (get_value(cfg, "LONG_NAME", 0)));
|
|
att->short_name = grib_context_strdup(ctx, (get_value(cfg, "SHORT_NAME", 0)));
|
|
att->units = grib_context_strdup(ctx, (get_value(cfg, "UNITS", 0)));
|
|
|
|
/* Check if there is more metadata for this variable */
|
|
if((metafile = get_value(cfg, "METADATA", 0)) != NULL)
|
|
{
|
|
static const char *metadata_dir = NULL;
|
|
char metapath[1024];
|
|
|
|
if(!metadata_dir)
|
|
metadata_dir = getenv("METADATA_DIR");
|
|
|
|
sprintf(metapath, "%s/%s", metadata_dir ? metadata_dir : ".", metafile);
|
|
att->metadata = 0;/* read_request_file(metapath); */
|
|
|
|
}
|
|
|
|
if(setname)
|
|
{
|
|
const char *pname = get_value(cfg, "DATA", 0);
|
|
if(strlen(att->name))
|
|
strcat(att->name, "_");
|
|
strcat(att->name, pname);
|
|
}
|
|
found = TRUE;
|
|
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: Var. name found: '%s'", att->name);
|
|
}
|
|
}
|
|
}
|
|
cfg = cfg->next;
|
|
}
|
|
|
|
/* We have not found configuration for this attribute */
|
|
if(!found)
|
|
{
|
|
const char *val = get_value(subset_r, split, 0);
|
|
if(val && (setname))
|
|
{
|
|
if(strlen(att->name))
|
|
strcat(att->name, "_");
|
|
strcat(att->name, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* NetCDF does not allow variable names to start with a digit */
|
|
if(!isalpha(att->name[0]))
|
|
{
|
|
char buf[1024];
|
|
sprintf(buf,"p%s",att->name);
|
|
strcpy(att->name,buf);
|
|
}
|
|
}
|
|
|
|
static request *first;
|
|
static request *last;
|
|
|
|
static void reqcb(const request *r, int count, axis_t *names, char *vals[], void *data)
|
|
{
|
|
request *w = clone_one_request(r);
|
|
int i;
|
|
/*request **d = (request**)data;*/
|
|
|
|
int *n = (int*) data;
|
|
|
|
w->order = (*n)++;
|
|
for(i = 0; i < count; i++)
|
|
{
|
|
|
|
if(vals[i])
|
|
put_value(w, names[i].name, vals[i], FALSE, FALSE, FALSE);
|
|
}
|
|
|
|
if(first == NULL)
|
|
first = w;
|
|
else
|
|
last->next = w;
|
|
last = w;
|
|
}
|
|
|
|
static boolean chk_152(int count, axis_t *names, char *vals[])
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void loop(const request *r, boolean ml, int index, int count, axis_t* strings, char *values[], loopproc callback, void *data)
|
|
{
|
|
if(index < count)
|
|
{
|
|
parameter *p = find_parameter(r, strings[index].name);
|
|
|
|
(void) count_values(r, strings[index].name); /* force list expansion */
|
|
|
|
if(p)
|
|
{
|
|
value *v = p->values;
|
|
|
|
while(v)
|
|
{
|
|
values[index] = v->name;
|
|
loop(r, ml, index + 1, count, strings, values, callback, data);
|
|
v = v->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
values[index] = NULL;
|
|
loop(r, ml, index + 1, count, strings, values, callback, data);
|
|
}
|
|
|
|
}
|
|
else if(!ml || chk_152(count, strings, values))
|
|
callback(r, count, strings, values, data);
|
|
}
|
|
|
|
static void values_loop(const request *r, int count, axis_t *parnames, loopproc callback, void *data)
|
|
{
|
|
char **values = (char**) grib_context_malloc(ctx, sizeof(char*) * count);
|
|
const char * p = get_value(r, "levtype", 0);
|
|
boolean ml = (boolean) (p && (strcmp(p, "ml") == 0));
|
|
|
|
if(ml)
|
|
{
|
|
p = get_value(r, "expect", 0);
|
|
if(p && atol(p) != 0)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "EXPECT provided, special treatment of LNSP");
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "and other single level parameters disabled");
|
|
ml = FALSE;
|
|
}
|
|
}
|
|
|
|
loop(r, ml, 0, count, parnames, values, callback, data);
|
|
|
|
grib_context_free(ctx, values);
|
|
}
|
|
|
|
static void names_loop(const request *r, loopproc proc, void *data)
|
|
{
|
|
values_loop(r, NUMBER(global_axis), global_axis, proc, data);
|
|
}
|
|
|
|
static request *unwind_one_request(const request *r)
|
|
{
|
|
int n = 0;
|
|
first = last = NULL;
|
|
names_loop(r, reqcb, &n);
|
|
return first;
|
|
}
|
|
|
|
static int split_fieldset(fieldset *fs, request *data_r, dataset_t **subsets, const request *user_r, const request *config_r)
|
|
{
|
|
const char *split = NULL;
|
|
int count = 1;
|
|
int i = 0;
|
|
request *s = NULL;
|
|
request *u = NULL;
|
|
dataset_t *filters = NULL;
|
|
nc_type nctype = translate_nctype(get_value(user_r, "accuracy", 0));
|
|
|
|
s = empty_request("filter");
|
|
while((split = get_value(user_r, "split", i++)) != NULL)
|
|
{
|
|
int cnt = count_values(data_r, split);
|
|
if(cnt)
|
|
{
|
|
count *= count_values(data_r, split);
|
|
valcpy(s, data_r, (char *) split, (char *) split);
|
|
}
|
|
}
|
|
|
|
u = unwind_one_request(s);
|
|
|
|
free_all_requests(s);
|
|
|
|
filters = (dataset_t*)calloc(sizeof(dataset_t), count);
|
|
Assert(filters);
|
|
|
|
s = u;
|
|
for(i = 0; i < count; ++i)
|
|
{
|
|
Assert(s);
|
|
filters[i].filter = new_hypercube_from_mars_request(s);
|
|
filters[i].fset = new_fieldset(1);
|
|
filters[i].count = 0;
|
|
filters[i].filter_request = clone_one_request(s);
|
|
filters[i].bitmap = FALSE;
|
|
/* filters[i].mmeans = FALSE; */
|
|
|
|
s = s->next;
|
|
}
|
|
|
|
for(i = 0; i < fs->count; ++i)
|
|
{
|
|
boolean ok = FALSE;
|
|
field *f = get_field(fs, i, packed_mem);
|
|
request *g = field_to_request(f);
|
|
int j = 0;
|
|
|
|
while(!ok && (j < count))
|
|
{
|
|
ok = (cube_order(filters[j].filter, g) != -1);
|
|
if(ok)
|
|
{
|
|
const char *p;
|
|
set_field(filters[j].fset, f, filters[j].count++);
|
|
filters[j].bitmap |= f->bitmap;
|
|
if((p = get_value(f->r->r, "_units", 0)) != NULL)
|
|
{
|
|
filters[j].att.units = grib_context_strdup(ctx, p);
|
|
}
|
|
if((p = get_value(f->r->r, "_long_name", 0)) != NULL)
|
|
{
|
|
filters[j].att.long_name = grib_context_strdup(ctx, p);
|
|
}
|
|
if((p = get_value(f->r->r, "_cf_name", 0)) != NULL)
|
|
{
|
|
filters[j].att.standard_name = grib_context_strdup(ctx, p);
|
|
}
|
|
}
|
|
j++;
|
|
}
|
|
if(!ok)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal error. Lost field %d while splitting fieldset", i);
|
|
print_all_requests(g);
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "count is %d", count);
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "First cube is:");
|
|
print_hypercube(filters[0].filter);
|
|
exit(1);
|
|
}
|
|
/* f->purge_header = TRUE; */
|
|
release_field(f);
|
|
}
|
|
|
|
for(i = 0; i < count; ++i)
|
|
{
|
|
filters[i].att.nctype = nctype;
|
|
filters[i].scale = TRUE;
|
|
filters[i].missing = nc_type_values[nctype].missing;
|
|
find_nc_attributes(filters[i].filter_request, user_r, &(filters[i].att), config_r, data_r);
|
|
grib_context_log(ctx, GRIB_LOG_DEBUG, "grib_to_netcdf: filter[%d] found.- Var. name '%s', nctype: %d, found nctype: %d", i, filters[i].att.name, nctype, filters[i].att.nctype);
|
|
|
|
if(strlen(filters[i].att.name) == 0)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Variable name not found");
|
|
exit(1);
|
|
}
|
|
|
|
/* Check if we need scaling */
|
|
switch(filters[i].att.nctype)
|
|
{
|
|
case NC_FLOAT:
|
|
case NC_DOUBLE:
|
|
filters[i].scale = FALSE;
|
|
break;
|
|
default:
|
|
filters[i].scale = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free_all_requests(u);
|
|
*subsets = filters;
|
|
return count;
|
|
}
|
|
|
|
static void free_subsets(dataset_t *subsets, int count)
|
|
{
|
|
int i = 0;
|
|
|
|
for(i = 0; i < count; ++i)
|
|
{
|
|
grib_context_free(ctx, subsets[i].att.units);
|
|
grib_context_free(ctx, subsets[i].att.long_name);
|
|
grib_context_free(ctx, subsets[i].att.short_name);
|
|
grib_context_free(ctx, subsets[i].att.standard_name);
|
|
free_all_requests(subsets[i].att.metadata);
|
|
free_hypercube(subsets[i].filter);
|
|
free_fieldset(subsets[i].fset);
|
|
free_all_requests(subsets[i].filter_request);
|
|
}
|
|
grib_context_free(ctx, subsets);
|
|
}
|
|
|
|
/* Return the number of the given month or -1 if it fails to match */
|
|
static int convert_month(const char* pMonthString)
|
|
{
|
|
if (strcmp(pMonthString, "jan")==0) return 1;
|
|
if (strcmp(pMonthString, "feb")==0) return 2;
|
|
if (strcmp(pMonthString, "mar")==0) return 3;
|
|
if (strcmp(pMonthString, "apr")==0) return 4;
|
|
if (strcmp(pMonthString, "may")==0) return 5;
|
|
if (strcmp(pMonthString, "jun")==0) return 6;
|
|
if (strcmp(pMonthString, "jul")==0) return 7;
|
|
if (strcmp(pMonthString, "aug")==0) return 8;
|
|
if (strcmp(pMonthString, "sep")==0) return 9;
|
|
if (strcmp(pMonthString, "oct")==0) return 10;
|
|
if (strcmp(pMonthString, "nov")==0) return 11;
|
|
if (strcmp(pMonthString, "dec")==0) return 12;
|
|
return -1; /*Failed*/
|
|
}
|
|
|
|
static boolean parsedate(const char *name, long* julian, long *second, boolean* isjul)
|
|
{
|
|
const char *p = name;
|
|
int n;
|
|
int y = 0, m = 0, d = 0, H = 0, M = 0, S = 0;
|
|
|
|
*julian = *second = 0;
|
|
*isjul = FALSE;
|
|
|
|
if(p == 0 || *p == 0)
|
|
return FALSE;
|
|
|
|
/* Special ERA Interim grib1 date format: jul-21, sep-02 etc
|
|
* See GRIB-416
|
|
*/
|
|
if (isalpha(*p))
|
|
{
|
|
char month[32];
|
|
int day = 0;
|
|
int n = sscanf(p, "%[^-]-%d", month, &day);
|
|
/* Matched two items (month and day) and month is 3 letters */
|
|
if (n == 2 && strlen(month) == 3) {
|
|
y = 1900; /* no year specified */
|
|
m = convert_month(month);
|
|
if (m == -1) return FALSE;
|
|
*julian = grib_date_to_julian(y * 10000 + m * 100 + day);
|
|
*second = 0;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* year */
|
|
p = parse1(p, &y, &n);
|
|
if(n != 2 && n != 4) /* year string must be 2 or 4 characters long: 93 or 1993 */
|
|
return FALSE;
|
|
if(*p++ != '-')
|
|
return FALSE;
|
|
|
|
/* month */
|
|
p = parse1(p, &m, &n);
|
|
if(n == 2)
|
|
{
|
|
/* day */
|
|
if(*p++ != '-')
|
|
return FALSE;
|
|
p = parse1(p, &d, &n);
|
|
if(n != 2)
|
|
return FALSE;
|
|
}
|
|
else if(n == 3)
|
|
{
|
|
long j = grib_date_to_julian(y * 10000 + 101) + m - 1;
|
|
j = grib_julian_to_date(j);
|
|
/* julian day */;
|
|
d = j % 100;
|
|
m = (j % 10000) / 100;
|
|
*isjul = TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
if (m == 0 || m > 12) {
|
|
return FALSE; /* month out of range */
|
|
}
|
|
|
|
while(*p && isspace(*p))
|
|
p++;
|
|
|
|
/* hour */
|
|
p = parse1(p, &H, &n);
|
|
if(n != 0)
|
|
{
|
|
if(n != 2)
|
|
return FALSE;
|
|
|
|
/* minute */
|
|
if(*p++ != ':')
|
|
return FALSE;
|
|
|
|
p = parse1(p, &M, &n);
|
|
if(n != 2)
|
|
return FALSE;
|
|
|
|
if(*p != 0)
|
|
{
|
|
/* second */
|
|
if(*p++ != ':')
|
|
return FALSE;
|
|
p = parse1(p, &S, &n);
|
|
if(n != 2)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*julian = grib_date_to_julian(y * 10000 + m * 100 + d);
|
|
*second = H * 3600 + M * 60 + S;
|
|
|
|
return *p == 0 ? TRUE : FALSE;
|
|
}
|
|
|
|
static boolean check_dimension_name(const char* dim)
|
|
{
|
|
/* Dimension name must begin with an alphabetic character, followed by zero
|
|
* or more alphanumeric characters including the underscore */
|
|
int i = 0, len = 0;
|
|
if (!dim) return FALSE;
|
|
|
|
len = strlen(dim);
|
|
if (len==0) return FALSE;
|
|
if (!isalpha(dim[0])) return FALSE;
|
|
|
|
for (i=1; i<len; ++i) {
|
|
const char c = dim[i];
|
|
const int ok = isalnum(c) || c == '_';
|
|
if (!ok) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static int get_creation_mode(int option_kind)
|
|
{
|
|
/* Return the mode flag for nc_create based */
|
|
/* on the kind of netcdf user wants to create */
|
|
int creation_mode = NC_CLOBBER;
|
|
switch(option_kind) {
|
|
case NC_FORMAT_CLASSIC:
|
|
printf("%s: Creating classic file format.\n", grib_tool_name);
|
|
break;
|
|
case NC_FORMAT_64BIT:
|
|
creation_mode |= NC_64BIT_OFFSET;
|
|
printf("%s: Creating large (64 bit) file format.\n", grib_tool_name);
|
|
break;
|
|
#ifdef NC_NETCDF4
|
|
case NC_FORMAT_NETCDF4:
|
|
creation_mode |= NC_NETCDF4;
|
|
printf("%s: Creating netCDF-4/HDF5 format.\n", grib_tool_name);
|
|
break;
|
|
case NC_FORMAT_NETCDF4_CLASSIC:
|
|
creation_mode |= NC_NETCDF4 | NC_CLASSIC_MODEL;
|
|
printf("%s: Creating netCDF-4 classic model file format.\n", grib_tool_name);
|
|
break;
|
|
#else
|
|
case NC_FORMAT_NETCDF4:
|
|
case NC_FORMAT_NETCDF4_CLASSIC:
|
|
fprintf(stderr,"%s not built with netcdf4, cannot create netCDF-4 files.\n", grib_tool_name);
|
|
exit(1);
|
|
break;
|
|
#endif
|
|
default:
|
|
fprintf(stderr, "Bad value (%d) for -k option\n", option_kind);
|
|
exit(1);
|
|
break;
|
|
}
|
|
return creation_mode;
|
|
}
|
|
/*=====================================================================*/
|
|
|
|
grib_option grib_options[] = {
|
|
{ "I:", "key1,key2,...", "\n\t\tIgnore keys. Default method,type,stream,refdate,hdate\n", 0, 1, "method,type,stream,refdate,hdate" },
|
|
{ "S:", "key1,key2,...", "\n\t\tSplit according to keys. Default param,expver\n", 0, 1, "param,expver" },
|
|
{ "R:", "date", "\n\t\tReference date in the format YYYYMMDD. Default value 19000101.\n", 0, 1, "19000101" },
|
|
{ "D:", "NC_DATATYPE", "\n\t\tType of data. Possible values NC_BYTE, NC_SHORT, NC_INT, NC_FLOAT, NC_DOUBLE."
|
|
"\n\t\tDefault NC_SHORT\n", 0, 1, "NC_SHORT" },
|
|
{ "T", 0, "Don't use time of validity.\n", 0, 1, 0 },
|
|
{ "f", 0, 0, 0, 1, 0 },
|
|
{ "o:", "output file", "\n\t\tThe name of the netcdf file.\n", 1, 1, 0 },
|
|
{ "V", 0, 0, 0, 1, 0 },
|
|
{ "M", 0, 0, 0, 1, 0 },
|
|
{ "k:", "kind", "\n\t\tSpecifies the kind of file to be created. Possible values are:"
|
|
"\n\t\t1 -> netCDF classic file format"
|
|
"\n\t\t2 -> netCDF 64 bit classic file format (Default)"
|
|
"\n\t\t3 -> netCDF-4 file format"
|
|
"\n\t\t4 -> netCDF-4 classic model file format\n"
|
|
, 0, 1, "2" },
|
|
{ "u:", "dimension", "\n\t\tSet dimension to be an unlimited dimension.\n", 0, 1, "time" }
|
|
};
|
|
|
|
int grib_options_count = sizeof(grib_options) / sizeof(grib_option);
|
|
static fieldset *fs = NULL;
|
|
static request* data_r = NULL;
|
|
request *user_r = NULL;
|
|
static int option_kind = 2; /* By default NetCDF3, 64-bit offset */
|
|
|
|
/* Table of formats for legal -k values. Inspired by nccopy */
|
|
struct KindValue {
|
|
char* name;
|
|
int kind;
|
|
} legalkinds[] = {
|
|
{"1", NC_FORMAT_CLASSIC},
|
|
{"classic", NC_FORMAT_CLASSIC},
|
|
|
|
/* The 64-bit offset kind */
|
|
{"2", NC_FORMAT_64BIT},
|
|
{"64-bit-offset", NC_FORMAT_64BIT},
|
|
{"64-bit offset", NC_FORMAT_64BIT},
|
|
|
|
/* NetCDF-4 HDF5 format */
|
|
{"3", NC_FORMAT_NETCDF4},
|
|
{"hdf5", NC_FORMAT_NETCDF4},
|
|
{"netCDF-4", NC_FORMAT_NETCDF4},
|
|
{"netCDF4", NC_FORMAT_NETCDF4},
|
|
{"enhanced", NC_FORMAT_NETCDF4},
|
|
|
|
/* NetCDF-4 HDF5 format, but using only nc3 data model */
|
|
{"4", NC_FORMAT_NETCDF4_CLASSIC},
|
|
{"hdf5-nc3", NC_FORMAT_NETCDF4_CLASSIC},
|
|
{"netCDF-4 classic model", NC_FORMAT_NETCDF4_CLASSIC},
|
|
{"netCDF4_classic", NC_FORMAT_NETCDF4_CLASSIC},
|
|
{"enhanced-nc3", NC_FORMAT_NETCDF4_CLASSIC},
|
|
|
|
/* null terminate*/
|
|
{NULL,0}
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i, ret = 0;
|
|
/* GRIB-413: Collect all program arguments into a string */
|
|
for (i=0; i<argc; ++i) {
|
|
strcat(argvString, argv[i]);
|
|
if (i != argc-1) strcat(argvString, " ");
|
|
}
|
|
|
|
ret = grib_tool(argc, argv);
|
|
return ret;
|
|
}
|
|
|
|
int grib_tool_before_getopt(grib_runtime_options* options)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int grib_tool_init(grib_runtime_options* options)
|
|
{
|
|
char* p = NULL;
|
|
char* list = NULL;
|
|
ctx = grib_context_get_default();
|
|
options->onlyfiles = 1;
|
|
fs = new_fieldset(0);
|
|
data_r = empty_request(0);
|
|
user_r = empty_request(0);
|
|
|
|
printf("%s: Version ", grib_tool_name);
|
|
grib_print_api_version(stdout);
|
|
printf("\n");
|
|
|
|
if(grib_options_on("D:"))
|
|
{
|
|
set_value(user_r, "accuracy", grib_options_get_option("D:"));
|
|
}
|
|
else
|
|
{
|
|
set_value(user_r, "accuracy", "NC_SHORT");
|
|
}
|
|
|
|
/* Option -S: Split according to keys */
|
|
if(grib_options_on("S:"))
|
|
{
|
|
list = grib_options_get_option("S:");
|
|
p = strtok(list, ",");
|
|
set_value(user_r, "split", p);
|
|
p = strtok(NULL, ",");
|
|
while(p != NULL)
|
|
{
|
|
add_value(user_r, "split", p);
|
|
p = strtok(NULL, ",");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
set_value(user_r, "split", "param");
|
|
add_value(user_r, "split", "expver");
|
|
}
|
|
|
|
/* Option -I: Ignore keys */
|
|
if (grib_options_on("I:"))
|
|
{
|
|
list = grib_options_get_option("I:");
|
|
p = strtok(list, ",");
|
|
set_value(user_r, "ignore", p);
|
|
p = strtok(NULL, ",");
|
|
while(p != NULL) {
|
|
add_value(user_r, "ignore", p);
|
|
p = strtok(NULL, ",");
|
|
}
|
|
}
|
|
else {
|
|
set_value(user_r, "ignore", "method");
|
|
add_value(user_r, "ignore", "type");
|
|
add_value(user_r, "ignore", "stream");
|
|
add_value(user_r, "ignore", "refdate");
|
|
add_value(user_r, "ignore", "hdate");
|
|
}
|
|
|
|
if(grib_options_on("T"))
|
|
set_value(user_r, "usevalidtime", "false");
|
|
else
|
|
set_value(user_r, "usevalidtime", "true");
|
|
|
|
if (grib_options_on("k:"))
|
|
{
|
|
struct KindValue* kvalue = NULL;
|
|
char* kind_name = grib_options_get_option("k:");
|
|
for (kvalue=legalkinds; kvalue->name; kvalue++) {
|
|
if (strcmp(kind_name, kvalue->name) == 0) {
|
|
option_kind = kvalue->kind;/* Found the right kind */
|
|
break;
|
|
}
|
|
}
|
|
if (kvalue->name == NULL) {
|
|
fprintf(stderr, "Invalid format: %s", kind_name);
|
|
usage();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if(grib_options_on("R:"))
|
|
{
|
|
char* theArg = grib_options_get_option("R:");
|
|
if (!is_number(theArg)) {
|
|
fprintf(stderr, "Invalid reference date: %s\n", theArg);
|
|
usage();
|
|
exit(1);
|
|
}
|
|
set_value(user_r, "referencedate", theArg);
|
|
}
|
|
else
|
|
{
|
|
set_value(user_r, "referencedate", "19000101");
|
|
}
|
|
|
|
if (grib_options_on("u:")) {
|
|
char* theDimension = grib_options_get_option("u:");
|
|
if ( !check_dimension_name(theDimension) ) {
|
|
fprintf(stderr, "Invalid dimension: \"%s\"\n", theDimension);
|
|
exit(1);
|
|
}
|
|
set_value(user_r, "unlimited", theDimension);
|
|
}
|
|
|
|
get_nc_options(user_r);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int grib_tool_new_filename_action(grib_runtime_options* options, const char* filename)
|
|
{
|
|
char buf[1024] = { 0, };
|
|
int e = 0;
|
|
int i = 0;
|
|
grib_handle* h = NULL;
|
|
grib_file* file = NULL;
|
|
|
|
printf("%s: Processing input file '%s'.\n", grib_tool_name, filename);
|
|
|
|
file = grib_file_open(filename, "r", &e);
|
|
if(!file || !file->handle)
|
|
return e;
|
|
|
|
fseeko(file->handle, 0, SEEK_SET);
|
|
|
|
files++;
|
|
|
|
while((h = grib_handle_new_from_file(ctx, file->handle, &e)) != NULL)
|
|
{
|
|
long length;
|
|
field* g;
|
|
request *r;
|
|
|
|
/* process only GRIB for the moment*/
|
|
size_t size = sizeof(buf);
|
|
Assert(grib_get_string(h, "identifier", buf, &size) == 0);
|
|
if(strcmp(buf, "GRIB"))
|
|
{
|
|
grib_handle_delete(h);
|
|
continue;
|
|
}
|
|
Assert(grib_get_long(h,"totalLength",&length) == 0);
|
|
|
|
g = read_field(file, h->offset, length);
|
|
|
|
r = empty_request("");
|
|
Assert(handle_to_request(r,h) == 0);
|
|
|
|
/* Keep full MARS description */
|
|
/* copy = clone_one_request(r); */
|
|
/* reqmerge(setup.mars_description,copy); */
|
|
/* reqmerge(setup.mars_description,r); */
|
|
|
|
if(i == 1)
|
|
{
|
|
const char *mmeans = get_value(r, "_MONTHLY_MEANS", 0);
|
|
setup.mmeans = mmeans ? (atol(mmeans) == 1) : FALSE;
|
|
}
|
|
fcmonth2nbmonths(r);
|
|
|
|
if(!setup.refdate)
|
|
{
|
|
if(setup.auto_refdate)
|
|
setup.refdate = atol(get_value(r, "date", 0));
|
|
else
|
|
{
|
|
const char *p = get_value(user_r, "referencedate", 0);
|
|
if(is_number(p))
|
|
setup.refdate = atol(p);
|
|
else
|
|
{
|
|
long julian = 0, second = 0;
|
|
boolean isjul;
|
|
parsedate(p, &julian, &second, &isjul);
|
|
setup.refdate = grib_julian_to_date(julian);
|
|
}
|
|
}
|
|
}
|
|
|
|
validation_time(r);
|
|
if(setup.usevalidtime)
|
|
{
|
|
unset_value(r, "date");
|
|
unset_value(r, "time");
|
|
unset_value(r, "step");
|
|
unset_value(r, "fcmonth");
|
|
|
|
set_value(r, "time", "%.2lf", atof(get_value(r, "_validationtime", 0)));
|
|
}
|
|
|
|
g->r = new_field_request(r);
|
|
|
|
set_field(fs, g, i++);
|
|
|
|
reqmerge(data_r, r);
|
|
free_all_requests(r);
|
|
|
|
grib_handle_delete(h);
|
|
}
|
|
|
|
grib_file_close(file->name, &e);
|
|
|
|
{
|
|
/* Now do some checks */
|
|
request* temp_data_r = fieldset_to_request(fs);
|
|
if(setup.checkvalidtime)
|
|
{
|
|
int cnt = request_fields(temp_data_r);
|
|
if(fs->count != i || (cnt < i))
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Wrong number of fields");
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "File contains %d GRIBs, %d left in internal description, %d in request", i, fs->count, cnt);
|
|
/*grib_context_log(ctx, GRIB_LOG_ERROR, "MARS description");*/
|
|
/*print_all_requests(setup.mars_description);*/
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Internal description");
|
|
print_all_requests(temp_data_r);
|
|
exit(1);
|
|
}
|
|
}
|
|
free_all_requests(temp_data_r);
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
int grib_tool_new_file_action(grib_runtime_options* options, grib_tools_file* file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int grib_tool_new_handle_action(grib_runtime_options* options, grib_handle* h)
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
int grib_tool_skip_handle(grib_runtime_options* options, grib_handle* h)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void grib_tool_print_key_values(grib_runtime_options* options, grib_handle* h)
|
|
{
|
|
}
|
|
|
|
int grib_tool_finalise_action(grib_runtime_options* options)
|
|
{
|
|
request *config_r = NULL;
|
|
hypercube *dims = NULL;
|
|
dataset_t *subsets = NULL;
|
|
int count;
|
|
int i;
|
|
int ncid;
|
|
int stat;
|
|
int err = 0;
|
|
int creation_mode = NC_CLOBBER;
|
|
|
|
if(options->outfile==NULL || options->outfile->name==NULL)
|
|
{
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
if(fs->count == 0)
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Input does not contain any field. Exiting!");
|
|
return -1;
|
|
}
|
|
|
|
printf("%s: Found %d GRIB field%s in %d file%s.\n", grib_tool_name, fs->count, fs->count>1?"s":"", files, files > 1 ? "s" : "");
|
|
|
|
/*
|
|
grib_context_log(ctx, GRIB_LOG_INFO, "Request representing %d fields ", fs->count);
|
|
print_all_requests(data_r);
|
|
*/
|
|
|
|
/* Split the SOURCE from request into as many datasets as specified */
|
|
count = split_fieldset(fs, data_r, &subsets, user_r, config_r);
|
|
remove_param(data_r, (void *) user_r, "ignore");
|
|
remove_param(data_r, (void *) user_r, "split");
|
|
print_ignored_keys(stdout, user_r);
|
|
dims = new_simple_hypercube_from_mars_request(data_r);
|
|
|
|
/* In case there is only 1 DATE+TIME+STEP, set at least 1 time as axis */
|
|
set_always_a_time(dims, data_r);
|
|
|
|
/* Create netcdf file */
|
|
|
|
printf("%s: Creating netcdf file '%s'\n", grib_tool_name, options->outfile->name);
|
|
printf("%s: NetCDF library version: %s\n", grib_tool_name, nc_inq_libvers());
|
|
|
|
creation_mode = get_creation_mode(option_kind);
|
|
stat = nc_create(options->outfile->name, creation_mode, &ncid);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* Define netcdf dataset */
|
|
err = define_netcdf_dimensions(dims, fs, ncid, subsets, count, data_r);
|
|
if (err != GRIB_SUCCESS) {
|
|
stat = nc_close(ncid);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
stat = nc_delete(options->outfile->name);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
/* End definitions */
|
|
stat = nc_enddef(ncid);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
/* Fill dimensions */
|
|
fill_netcdf_dimensions(dims, fs, ncid);
|
|
|
|
/* Put data values */
|
|
for(i = 0; i < count; ++i)
|
|
{
|
|
if(subsets[i].fset)
|
|
{
|
|
char dataset[100];
|
|
|
|
sprintf(dataset, subsets[i].att.name, i + 1);
|
|
put_data(dims, ncid, dataset, &subsets[i]);
|
|
}
|
|
else
|
|
{
|
|
grib_context_log(ctx, GRIB_LOG_ERROR, "Fieldset %d is empty!!", i + 1);
|
|
}
|
|
}
|
|
|
|
stat = nc_close(ncid);
|
|
check_err(stat, __LINE__, __FILE__);
|
|
|
|
free_all_requests(data_r);
|
|
free_hypercube(dims);
|
|
free_fieldset(fs);
|
|
free_subsets(subsets, count);
|
|
|
|
free_nc_options();
|
|
printf("%s: Done.\n", grib_tool_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
int main(int argc, char** argv)
|
|
{
|
|
printf("\n");
|
|
printf("grib_to_netcdf:\n");
|
|
printf("\n");
|
|
printf(" GRIB API was not compiled with NETCDF enabled\n");
|
|
printf(" Please rerun configure with --with-netcdf=PATH_TO_NETCDF_INSTALLATION\n");
|
|
printf(" and re-install the software.\n");
|
|
printf("\n");
|
|
exit(1);
|
|
}
|
|
#endif
|