#include "FileWriterNetCDF.h" #include #include "../Common/Toolbox.h" #include "../TimeManager/TimeManager.h" #define TREPS_NC_UNLIMITED_DIM_ID "treps_unlimited_dim" using namespace TREPS::Common; using namespace TREPS::TimeManager; namespace TREPS { namespace File { FileWriterNetCDFClass::FileWriterNetCDFClass(void):FileWriterAbstractClass(), ncid(0) { } FileWriterNetCDFClass::~FileWriterNetCDFClass(void) { this->close(); } bool FileWriterNetCDFClass::init(const char *file_path, const map *attributes) { if (!this->close()) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot close previous document"); return false; } int status = nc_create(file_path, 0, &this->ncid); if (status != NC_NOERR) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create file " << file_path); return false; } //write attributes for (map::const_iterator att = attributes->begin(); att != attributes->end(); ++att) { status = nc_put_att_text(this->ncid, NC_GLOBAL, att->first.c_str(), att->second.size(), att->second.c_str()); if (status != NC_NOERR) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot write attrubute"); return false; } } return true; } bool FileWriterNetCDFClass::writeData(const DataRecordListClass *data, t_TimeFormat timeFormat, const char *timePattern, const t_FieldList *fields) { if (!this->isOpened()) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot write data"); return false; } int unlimitedDimId; if (!this->writeUnlimitedDim(timeFormat, timePattern, unlimitedDimId)) return false; if (timeFormat != TF_NONE) { t_Field timeField; timeField.id = "TREPS_TIME"; timeField.name = timeField.id; timeField.type = FT_TIME; timeField.timeformat = timeFormat; timeField.timepattern = timePattern; timeField.dims.clear(); if (!this->writeField(&timeField, data, unlimitedDimId)) return false; } //write fields for (t_FieldList::const_iterator field = fields->begin(); field != fields->end(); ++field) { if (!this->writeField(&(*field), data, unlimitedDimId)) return false; } return true; } bool FileWriterNetCDFClass::getNetCDFVarTypeDef(const t_Field *field, const DataRecordListClass *data, nc_type &ncType, long &numElts) { //get time manager instance TimeManagerClass *timeMgr = TimeManagerClass::getInstance(); numElts = 1; //compute nb val by record int nbValByRec = 1; for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) nbValByRec *= (*dim).size; switch (field->type) { case FT_UNKNOWN : { ncType = NC_CHAR; if (nbValByRec == 1) numElts = data->getMaxStringValueSize(field->id.c_str()); else { for (int i = 0; i < nbValByRec; ++i) { string crtId = field->id; crtId += "_TREPSID"; crtId += intToStr(i); int crtNumElts = data->getMaxStringValueSize(crtId.c_str()); numElts = ((crtNumElts > numElts) ? crtNumElts : numElts); } } break; } case FT_TIME : { switch (field->timeformat) { case TF_NONE : return false; case TF_PATTERN : ncType = NC_CHAR; timeMgr->setCurrentPattern(field->timepattern.c_str()); numElts = timeMgr->getCurrentPatternSize(); break; case TF_TIMESTAMP : ncType = NC_LONG; break; case TF_DECIMAL : case TF_EPOCH : ncType = NC_DOUBLE; break; case TF_TT2000 : //force to double (long long don't exist in netCDF format) ncType = NC_DOUBLE; break; case TF_DDTIME : ncType = NC_CHAR; numElts = 16; break; default : return false; } break; } case FT_FLOAT : ncType = NC_FLOAT; break; case FT_DOUBLE : ncType = NC_DOUBLE; break; case FT_SHORT : ncType = NC_SHORT; break; case FT_INT : ncType = NC_INT; break; case FT_LONG : ncType = NC_LONG; break; default : return false; } return true; } bool FileWriterNetCDFClass::writeDim(const t_Dimension *dim, int &dimid) { //check if the dim is already defined if (nc_inq_dimid(this->ncid, dim->id.c_str(), &dimid) != NC_NOERR) { //create dimension int status = nc_def_dim(this->ncid, dim->id.c_str(), dim->size, &dimid); if (status != NC_NOERR) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create dimension " << dim->id); return false; } } return true; } bool FileWriterNetCDFClass::writeUnlimitedDim(t_TimeFormat timeFormat, const char *timePattern, int &unlimitedDimId) { //create unlimited dim t_Dimension dim; dim.id = TREPS_NC_UNLIMITED_DIM_ID; dim.size = NC_UNLIMITED; if (!this->writeDim(&dim, unlimitedDimId)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create unlimited dimension"); return false; } return true; } bool FileWriterNetCDFClass::writeField(const t_Field *field, const DataRecordListClass *data, int unlimitedDimId) { int status; //enter define mode nc_redef(this->ncid); //get field info nc_type ncType; long numElts; if (!this->getNetCDFVarTypeDef(field, data, ncType, numElts)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot get field info"); return false; } int nbDims = field->dims.size() + 1; //+1 for unlimited dim int stringDimId; if (numElts > 1) { t_Dimension dim; dim.id = "treps_string_dim_"; dim.id += intToStr(numElts); dim.size = numElts; if (!this->writeDim(&dim, stringDimId)) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to create string dim"); return false; } ++nbDims; //+1 for string dim } int dimids[nbDims]; int index = 0; //inlimited dim dimids[index] = unlimitedDimId; ++index; //create other dims for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) { if (!this->writeDim(&(*dim), dimids[index])) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create dim"); return false; } ++index; } //string dim if (numElts > 1) { dimids[index] = stringDimId; ++index; } //create var int varid; status = nc_def_var(this->ncid, field->id.c_str(), ncType, nbDims, dimids, &varid); if (status != NC_NOERR) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot create variable " << field->name); return false; } //write var data if (!this->writeFieldData(field, data, ncType, numElts, varid)) return false; //write attributes nc_redef(this->ncid); for (map::const_iterator att = field->attributes.begin(); att != field->attributes.end(); ++att) { if (att->second.empty()) continue; status = nc_put_att_text(this->ncid, varid, att->first.c_str(), att->second.size(), att->second.c_str()); if (status != NC_NOERR) { LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot write attribute " << nc_strerror(status)); return false; } } return true; } bool FileWriterNetCDFClass::writeFieldData(const t_Field *field, const DataRecordListClass *data, nc_type ncType, long numElts, int varId) { nc_enddef(this->ncid); DataRecordClass *crtRecord = data->getFirstRecord(); int nbValByRec = 1; for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) nbValByRec *= (*dim).size; int nbDims = field->dims.size() + 1; if (numElts > 1) ++nbDims; size_t start[nbDims]; size_t count[nbDims]; int index = 0; //unlimited dim start[index] = 0; count[index] = 1; ++index; //others dims for (t_DimensionList::const_iterator dim = field->dims.begin(); dim != field->dims.end(); ++dim) { start[index] = 0; count[index] = (*dim).size; ++index; } //string dim if (numElts > 1) { start[index] = 0; count[index] = numElts; ++index; } //get one value size int valSize; switch (ncType) { case NC_INT : valSize = sizeof(int); break; case NC_FLOAT : valSize = sizeof(float); break; case NC_DOUBLE : valSize = sizeof(double); break; case NC_SHORT : valSize = sizeof(short); break; case NC_CHAR : valSize = sizeof(char); break; default : LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Cannot get value size"); return false; } //create buffer void *buffer = malloc(numElts * valSize * nbValByRec); while (crtRecord != NULL) { memset(buffer,0,numElts * valSize * nbValByRec); if (field->dims.empty()) { if (!this->convertData(field, crtRecord, field->id.c_str(), ncType, numElts, buffer)) { free(buffer); LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to convert data"); return false; } } else { void *p = buffer; for (int i = 0; i < nbValByRec; ++i) { string key = field->id; key += "_TREPSID"; key += intToStr(i); if (!this->convertData(field, crtRecord, key.c_str(), ncType, numElts, p)) return false; p = (void*)((long)p + (long)(numElts*valSize)); } } int status = nc_put_vara (this->ncid, varId, start, count, buffer); if (status != NC_NOERR) { free(buffer); LOG4CXX_ERROR(this->app->getLog()->getPtr(),"Error to put data"); return false; } crtRecord = crtRecord->getNextRecord(); ++start[0]; } free(buffer); return true; } bool FileWriterNetCDFClass::convertData(const t_Field *field, DataRecordClass *record, const char *key, nc_type ncType, long numElts, void *buffer) { //get time manager instance TimeManagerClass *timeMgr = TimeManagerClass::getInstance(); switch (ncType) { case NC_INT : { int i; if (field->type != FT_TIME) i = record->getIntValue(key); else { t_Time time = record->getTime(); switch (field->timeformat) { case TF_TIMESTAMP : i = timeMgr->to_TIMESTAMP(time); break; default : //error i = 0; } } memcpy(buffer,&i,sizeof(int)); break; } case NC_FLOAT : { float f; if (field->type != FT_TIME) f = record->getFloatValue(key); else { //error f = 0; } memcpy(buffer,&f,sizeof(float)); break; } case NC_DOUBLE : { double d; if (field->type != FT_TIME) d = record->getDoubleValue(key); else { t_Time time = record->getTime(); switch (field->timeformat) { case TF_DECIMAL : d = timeMgr->to_DECIMAL(time); break; case TF_EPOCH : d = timeMgr->to_EPOCH(time); break; case TF_TT2000 : d = timeMgr->to_TT2000(time); break; default : //error d = 0; } } memcpy(buffer,&d,sizeof(double)); break; } case NC_SHORT : { short int s; if (field->type != FT_TIME) s = record->getShortValue(key); else //error s = 0; memcpy(buffer,&s,sizeof(short int)); break; } case NC_CHAR : { string val; if (field->type != FT_TIME) val = record->getValue(key); else { t_Time time = record->getTime(); val = timeMgr->to_string(time, field->timeformat, field->timepattern.c_str()); } if (val.size() > 0) memcpy(buffer,val.c_str(),val.size()*sizeof(char)); //((char *)buffer)[val.size()] = '\0'; break; } default : return false; } return true; } bool FileWriterNetCDFClass::save(void) { return (this->close()); } bool FileWriterNetCDFClass::close(void) { if (this->isOpened()) { int res = nc_close(this->ncid); if (res == NC_NOERR) this->ncid = 0; return (res == NC_NOERR); } return !this->isOpened(); } bool FileWriterNetCDFClass::isOpened(void) { return (this->ncid > 0); } } }