/*************************************************************************** * Copyright (C) 2011 by Nicolas PASCAL * * nicolas.pascal@icare.univ-lille1.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "gribfiledata.h" const string GribFileData::var_name_id = "shortName"; const string GribFileData::parameter_name_id = "parameterName"; const string GribFileData::parameter_code_id = "paramId"; const string GribFileData::fill_value_id = "missingValue"; const string GribFileData::scale_factor_id = "scaleValuesBy"; const string GribFileData::add_offset_id = "offsetValuesBy"; const string GribFileData::x_dim_id = "Ni"; const string GribFileData::y_dim_id = "Nj"; const string GribFileData::level_type_id = "typeOfLevel"; const string GribFileData::level_id = "level"; const string GribFileData::data_flag_id = "dataFlag"; const string GribFileData::data_width_id = "bitsPerValue"; const string GribFileData::data_id = "values"; GribFileData::GribFileData (const string & name, const string & mode, const bool open_file) : FileData (name,mode) { fd = NULL; var_table.clear(); open_data_file (); init_param_table (); n_msg = -1; init_n_msg(); if (!open_file) close_data_file(); } GribFileData::~ GribFileData() { close_data_file (); var_table.clear(); } GribFileData::GribFileData(const GribFileData & fdata) : FileData(fdata) { if ( &fdata != NULL ) { fd = fdata.fd; var_table = fdata.var_table; } } GribFileData &GribFileData::operator= (const GribFileData &fdata) { if (&fdata == this) return *this; // protection against self assignation if ( &fdata != NULL ) { fd = fdata.fd; var_table = fdata.var_table; } return *this; } void GribFileData::open_data_file() { grib_multi_support_on(0); if (! is_file_loaded() ) { fd = fopen(name.c_str(), mode.c_str()); if (fd == NULL) { string msg ( "Invalid file " + name ) ; g_exception e( __FILE__ , __LINE__ , msg ); throw e; } } } void GribFileData::close_data_file() { if (fd != NULL) { fclose(fd); fd = NULL; } } const bool GribFileData::is_file_loaded() { return (fd != NULL); } void GribFileData::init_selector (const char * ds_name, int *& start, int *& stride, int *& edges, int & rank, bool is_selector_allocated[]) { fill_n (is_selector_allocated, 3, false); vector dims = get_dataset_dimension(ds_name); int ndims = dims.size(); // if rank is not defined, set it if (rank == -1) rank = ndims; // if start is not defined, allocate it with a rank size, and set its values to 0 if ( start == NULL ) { is_selector_allocated [0] = true; start = new int [rank]; fill_n (start, rank, 0); } // if edges is not defined, allocate it with a rank size, and set its values using the sds dimensions if ( edges == NULL ) { is_selector_allocated [2] = true; edges = new int [rank]; copy (dims.begin(), dims.end(), edges); } } void GribFileData::check_selector(const char * ds_name, int * start, int * stride, int * edges, const int rank) { if (start == NULL) { g_exception e( __FILE__ , __LINE__ , "Start selector not set" ); throw e; } if (edges == NULL) { g_exception e( __FILE__ , __LINE__ , "Edges selector not set" ); throw e; } vector dims = get_dataset_dimension(ds_name); int ndims = dims.size(); string msg (""); if (rank != ndims) { msg = "Rank (" + MyTools::to_string (rank) + ") is different than number of dimensions of the dataset (" + MyTools::to_string (ndims) + ")"; g_exception e( __FILE__ , __LINE__ , msg ); throw e; } // maximum allowed indice int imax; int stride_val; for (int i = 0 ; i < rank ; ++i) { imax = dims[i] - 1; if (start[i] < 0 || start[i] > imax) { msg = "Invalid start index " + MyTools::to_string (start[i]) + ". Must be in Range [0, " + MyTools::to_string (imax) + "]"; g_exception e( __FILE__ , __LINE__ , msg ); throw e; } // test the selection length (stride != NULL)?stride_val = stride[i]:stride_val = 1; if ( (edges[i] * stride_val) > (dims[i] - start[i]) ) { msg = "Invalid number of values to read " + MyTools::to_string (edges[i]) + ". Must be in Range [0, " + MyTools::to_string (dims[i] - start[i]) + "]"; g_exception e( __FILE__ , __LINE__ , msg ); throw e; } } } void GribFileData::free_selector(const bool * is_selector_allocated, int * start, int * stride, int * edges) { if (is_selector_allocated [0]) delete[] start; if (is_selector_allocated [1]) delete[] stride; if (is_selector_allocated [2]) delete[] edges; } void * GribFileData::read_data(void * data, const char * ds_name, int * start, int * stride, int * edges, int rank) { // --- check if file is ready for reading if (! is_file_loaded()) { g_exception e( __FILE__ , __LINE__ , "Data file not opened" ); throw e; } // --- check args validity // Dataset name if (!is_existing_dataset(ds_name)) { g_exception e( __FILE__ , __LINE__ , "Dataset " + string(ds_name) + " does not exist" ); throw e; } // check if start, stride or edges need to be initialized in this method bool is_selector_allocated[3]; // tells if start, stride or edges (Region Of Interest) is initialized in this method // --- initialize NULL input parametres --- init_selector (ds_name, start, stride, edges, rank, is_selector_allocated); // printf ("start = "); // for (int irank = 0 ; irank < rank ; ++irank) { // printf ("%ld ", start[irank]); // } // printf ("\n"); // printf ("edges = "); // for (int irank = 0 ; irank < rank ; ++irank) { // printf ("%ld ", edges[irank]); // } // printf ("\n"); // check the selection limits. Raise exception in case of mismatch check_selector (ds_name, start, stride, edges, rank); // --- read buffer management // size requested for the buffer unsigned int sz_data = 1; for (int i = 0 ; i < rank ; ++i) sz_data *= edges[i]; size_t sz_dtype = sizeof(double); // 4 bytes // allocate output buffer if needed if (data == NULL) data = static_cast(malloc(sz_dtype * sz_data)); // --- read the data : if parametre is 3D, append 2D subsets along Z // - set data subset indices int iz_start = 0, iz_end = 1; if (rank > 2) { iz_start = start [0]; iz_end = iz_start + edges [0]; } int y_dim = rank - 2; // indice of Y dimension in start, stride, edges int iy_start = start [y_dim], iy_end = iy_start + edges [y_dim], iy; int x_dim = y_dim + 1; int ix_start = start [x_dim]; // --- read all variable's messages // list of messages related to the parametre vector & v_msg = var_table[ds_name].v_msg; vector ::const_iterator it_msg, it_msg_start = v_msg.begin() + iz_start, it_msg_end = v_msg.begin() + iz_end; vector v_dim_msg; // dimensions of 2D message dataset // an inner buffer with the size the data of a message is requested : no "on the fly" subsetting is possible and all the data have to be extracted before slicing double * inner_buf = NULL; double * inbuf_start = inner_buf; double * outbuf_start = (double*)(data); for (it_msg = it_msg_start ; it_msg != it_msg_end ; ++it_msg) { // load message buffer inner_buf = get_msg_data ((*it_msg), v_dim_msg, inner_buf); // copy message 2D data subset row by row in output buffer inbuf_start = inner_buf + iy_start * v_dim_msg [1] + ix_start; for (iy = iy_start ; iy < iy_end ; ++iy) { copy (inbuf_start, inbuf_start + edges [x_dim], outbuf_start); // printf ("cell [%d, %ld, %ld:%ld]\n", distance((vector ::const_iterator)(v_msg.begin()), it_msg), iy, ix_start, ix_start + edges [x_dim]); // printf ("\tInput : "); // for (int ix = 0 ; ix < edges [x_dim] ; ++ix) // printf ("%f ", inbuf_start[ix]); // printf ("\n\tOutput : "); // for (int ix = 0 ; ix < edges [x_dim] ; ++ix) // printf ("%f ", outbuf_start[ix]); // printf ("\n"); // next row inbuf_start += v_dim_msg [1]; outbuf_start += edges [x_dim]; } } // free the ressources allocated by this method delete [] inner_buf; free_selector (is_selector_allocated, start, stride, edges); return data; } void GribFileData::init_n_msg () { int err=0; int grib_count=0; n_msg = -1; // check if file is ready for reading if (! is_file_loaded()) { g_exception e( __FILE__ , __LINE__ , "Data file not opened" ); throw e; } // read count rewind(fd); err = grib_count_in_file (0, fd, &grib_count); if (err != 0) { string msg = "Can't read number of messages in file " + name; g_exception e(__FILE__ , __LINE__ , msg); throw e; } n_msg = grib_count; } vector< int > GribFileData::get_dataset_dimension(const string & ds_name) { // check dataset_name valiity if (!is_existing_dataset(ds_name)) { g_exception e( __FILE__ , __LINE__ , "Dataset " + string(ds_name) + " dos not exists" ); throw e; } return var_table[ds_name].get_sz_dims (); } void GribFileData::get_dataset_fill_value(const string & dataset_name, void * fill_value) { UnimplementedMethod e(__FILE__,__LINE__,__PRETTY_FUNCTION__); throw e; } void GribFileData::print_keys(unsigned int keys_filter) { // check if file is ready for reading if (! is_file_loaded()) { g_exception e(__FILE__ , __LINE__ , "Data file not opened"); throw e; } // iterate over messages grib_handle* h=NULL; char value [MAX_VAL_LEN]; size_t vlen = MAX_VAL_LEN; int err=0; int grib_count=0; grib_keys_iterator* kiter = NULL; // // // // void * values = NULL; // // // // size_t values_len; // reset to start of file rewind(fd); while((h = grib_handle_new_from_file(0, fd, &err)) != NULL) { printf("-- GRIB N. %d --\n",grib_count); if(!h) { g_exception e(__FILE__ , __LINE__ , "Unable to create grib handle\n"); throw e; } // --- iterate over message keys and print them kiter = grib_keys_iterator_new(h, keys_filter, NULL); if (!kiter) { g_exception e(__FILE__ , __LINE__ , "Unable to create keys iterator\n"); throw e; } while(grib_keys_iterator_next(kiter)) { const char* name = grib_keys_iterator_get_name(kiter); vlen=MAX_VAL_LEN; bzero(value,vlen); GRIB_CHECK(grib_get_string(h,name,value,&vlen),name); printf("%s = %s\n",name,value); } grib_keys_iterator_delete(kiter); // --- print values // // // GRIB_CHECK(grib_get_size(h,"values",&values_len),0); // // // values_len *= 4; // // // values = malloc(values_len); // // // // GRIB_CHECK(grib_get_double_array(h,"values", values, &values_len),0); // // // GRIB_CHECK(grib_get_bytes(h,"values", (unsigned char *)values, &values_len),0); // // // printf ("data="); // // // for(int i = 0; i < 20; i++) // // // printf("%x",((unsigned char *)(values))[i]); // // // printf("\n"); // // // free(values), values = NULL; grib_count++; grib_handle_delete (h); } // end of file reached : needs a rewind release_handle(h); } void GribFileData::print_msg_keys(int i_msg, unsigned int keys_filter) { vector > v_keys (0); get_msg_keys(i_msg, v_keys, keys_filter); printf ("--- Message # %d ---\n", i_msg); typedef vector >::iterator key_val_iter; for (key_val_iter it = v_keys.begin() ; it != v_keys.end() ; ++it) { cout << it->first << " = " << it->second << endl; } } grib_handle * GribFileData::get_handle (int i_msg) { // check if file is ready for reading if (! is_file_loaded()) { g_exception e(__FILE__ , __LINE__ , "Data file not opened"); throw e; } // validate message indice if (i_msg < 0 || i_msg > n_msg) { string msg = "Invalid message indice " + MyTools::to_string(i_msg) + " Must be i, [0, " + MyTools::to_string(n_msg) + "]\n"; g_exception e( __FILE__ , __LINE__ , msg ); throw e; } // iterate over messages int err = 0; grib_handle * handle = NULL; // - The file handle processing is sequential. So depending of what is the last processed message, the search needs to restart from the begining, is not needed or can be continued from the last position // if (i_msg == icur_msg) // handle points already on the requested message : nothing to do // return true; // else /*if (i_msg < icur_msg)*/ { // rewind handle // release_handle(); // } /*else if (icur_msg != -1){ // ++icur_msg; // ++icur_msg; // }*/ // release_handle(); // seek desired message int icur_msg = 0; rewind (fd); while ( ((handle = grib_handle_new_from_file(0, fd, &err)) != NULL) && (icur_msg != i_msg)) { ++icur_msg; grib_handle_delete (handle); // handle = NULL; } if (icur_msg != i_msg) { release_handle(handle); handle = NULL; } // release_handle(); return handle; } void GribFileData::release_handle(grib_handle * handle) { if (handle != NULL) grib_handle_delete(handle); handle = NULL; if (fd != NULL) rewind(fd); } string GribFileData::get_msg_key(int i_msg, const string & searched_key) { // list of (key, val) for a message vector > v_keys (0); get_msg_keys(i_msg, v_keys); vector >::iterator it; // search for the key in the message it = find_if (v_keys.begin(), v_keys.end(), MyTools::KeyComparator(searched_key)); if (it == v_keys.end()) { string msg = "Key " + searched_key + " not found in message " + MyTools::to_string(i_msg) + "\n"; g_exception e( __FILE__ , __LINE__ , msg ); throw e; } return it->second; } void GribFileData::get_msg_keys(int i_msg, vector > & v_keys, unsigned int keys_filter) { // go to message # i_msg grib_handle * handle = get_handle (i_msg); if (handle == NULL) { string msg = "Fail to seek GRIB message number " + MyTools::to_string(i_msg); g_exception e( __FILE__ , __LINE__ , msg ); throw e; } // init keys iterator grib_keys_iterator * kiter = grib_keys_iterator_new (handle, GRIB_KEYS_ITERATOR_ALL_KEYS, NULL); if (kiter == NULL) { g_exception e( __FILE__ , __LINE__ , "Unable to create keys iterator" ); throw e; } // iterate on message's key=value pairs and decode them string key (""); string value (""); char buffer [MAX_VAL_LEN]; size_t vlen = MAX_VAL_LEN; v_keys.clear(); pair < string, string > item; while(grib_keys_iterator_next(kiter)) { // read key/value item.first = grib_keys_iterator_get_name(kiter); vlen = MAX_VAL_LEN; bzero(buffer, vlen); GRIB_CHECK(grib_get_string(handle, item.first.c_str(), buffer, &vlen), item.first.c_str()); item.second = buffer; // store key/value v_keys.push_back (item); } grib_keys_iterator_delete(kiter); release_handle (handle); } void GribFileData::init_param_table(void ) { // check if file is ready for reading if (! is_file_loaded()) { g_exception e(__FILE__ , __LINE__ , "Data file not opened"); throw e; } // reset variables descriptor table var_table.clear(); // iterate over messages grib_handle* h=NULL; char value [MAX_VAL_LEN]; size_t vlen = MAX_VAL_LEN; int err=0; int grib_count=0; // int param_id, sz_y, sz_x, level, data_flag, data_width; int param_id, sz_y, sz_x, level; double fill_value, scale, offset; string name, long_name, z_level_type, type_id; long long_int; while((h = grib_handle_new_from_file(0, fd, &err)) != NULL) { if(!h) { g_exception e(__FILE__ , __LINE__ , "Unable to create grib handle\n"); throw e; } GRIB_CHECK(err,0); // --- read parameter characteristics // name vlen = MAX_VAL_LEN; bzero(value, vlen); GRIB_CHECK(grib_get_string(h, GribFileData::var_name_id.c_str(), value, &vlen),0); // bad termination character in excess need to be NULLified if string and not if integer // if (! MyTools::is_int(value, vlen)) // memset (value + vlen - 2, NULL, 1); // name = value; name = MyTools::strip (value); // cout << "-" << name << "- " << vlen << endl; // read vertical level value GRIB_CHECK(grib_get_long(h, GribFileData::level_id.c_str(), &long_int),0); level = long_int; // --- create parameter entry if first time met if (!is_existing_dataset(name)) { // parameter ID GRIB_CHECK(grib_get_long(h, GribFileData::parameter_code_id.c_str(), &long_int),0); param_id = long_int; // extended name of the parametre vlen = MAX_VAL_LEN; bzero(value,vlen); GRIB_CHECK(grib_get_string(h, GribFileData::parameter_name_id.c_str(), value, &vlen),0); long_name = MyTools::strip (value); // data type and flags // GRIB_CHECK(grib_get_long(h, GribFileData::data_flag_id.c_str(), &data_flag),0); // GRIB_CHECK(grib_get_long(h, GribFileData::data_width_id.c_str(), &data_width),0); // type_id = GribFileData::parse_type_id (data_flag, data_width); type_id = "float32"; // for compatibility with GRIB1 // grid dimensions GRIB_CHECK(grib_get_long(h, GribFileData::x_dim_id.c_str(), &long_int),0); sz_x = long_int; GRIB_CHECK(grib_get_long(h, GribFileData::y_dim_id.c_str(), &long_int),0); sz_y = long_int; // type of pressure level vlen = MAX_VAL_LEN; bzero(value,vlen); GRIB_CHECK(grib_get_string(h, GribFileData::level_type_id.c_str(), value, &vlen),0); z_level_type = value; // fill value GRIB_CHECK(grib_get_double(h, GribFileData::fill_value_id.c_str(), &fill_value),0); // scaling GRIB_CHECK(grib_get_double(h, GribFileData::scale_factor_id.c_str(), &scale),0); GRIB_CHECK(grib_get_double(h, GribFileData::add_offset_id.c_str(), &offset),0); // create descriptor entry if first time var_table [name] = t_grib_var_desc(name, long_name, param_id, type_id, sz_x, sz_y, z_level_type, fill_value, scale, offset); } // push back message indice var_table [name].add_msg (grib_count, level); grib_count++; } grib_handle_delete(h); } void GribFileData::print_param_table(void ) { // cout << var_table.size() << endl; for_each (var_table.begin(), var_table.end(), GribFileData::print_var_desc); } const void GribFileData::print_msg_data(const int i_msg) { vector sz_dims; double * data = get_msg_data(i_msg, sz_dims); printf ("--- message %d [%dx%d] ---\n", i_msg, sz_dims[0], sz_dims[1]); for (int j = 0 ; j < sz_dims[0] ; ++j) { cout << "j=" << j << "\t"; for (int i = 0 ; i < sz_dims[1] ; ++i) cout << data [j * sz_dims[1] + i] << " "; cout << endl; } delete [] data; }