File indexing completed on 2024-12-22 04:17:16
0001 /*************************************************************************** 0002 netcdf.cpp - netCDF file data source reader 0003 ------------------- 0004 begin : 17/06/2004 0005 copyright : (C) 2004 Nicolas Brisset <nicodev@users.sourceforge.net> 0006 email : kst@kde.org 0007 modified : 03/14/05 by K. Scott 0008 ***************************************************************************/ 0009 0010 /*************************************************************************** 0011 * * 0012 * This program is free software; you can redistribute it and/or modify * 0013 * it under the terms of the GNU General Public License as published by * 0014 * the Free Software Foundation; either version 2 of the License, or * 0015 * (at your option) any later version. * 0016 * * 0017 ***************************************************************************/ 0018 0019 // TODO move 0020 #include "sharedptr.h" 0021 0022 #include "netcdfsource.h" 0023 0024 #include "debug.h" 0025 0026 #include <QFile> 0027 #include <QFileInfo> 0028 0029 #include <ctype.h> 0030 #include <stdlib.h> 0031 0032 //#define NETCDF_DEBUG_SHARED 0033 #ifdef NETCDF_DEBUG_SHARED 0034 #define NETCDF_DBG if (true) 0035 #else 0036 #define NETCDF_DBG if (false) 0037 #endif 0038 0039 0040 using namespace Kst; 0041 0042 static const QString netCdfTypeString = "netCDF Files"; 0043 0044 0045 // 0046 // Scalar interface 0047 // 0048 0049 class DataInterfaceNetCdfScalar : public DataSource::DataInterface<DataScalar> 0050 { 0051 public: 0052 DataInterfaceNetCdfScalar(NetcdfSource& s) : netcdf(s) {} 0053 0054 // read one element 0055 int read(const QString&, DataScalar::ReadInfo&); 0056 0057 // named elements 0058 QStringList list() const { return netcdf._scalarList; } 0059 bool isListComplete() const { return true; } 0060 bool isValid(const QString&) const; 0061 0062 // T specific 0063 const DataScalar::DataInfo dataInfo(const QString&, int frame = 0) const { Q_UNUSED(frame) return DataScalar::DataInfo(); } 0064 void setDataInfo(const QString&, const DataScalar::DataInfo&) {} 0065 0066 // meta data 0067 QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); } 0068 QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); } 0069 0070 0071 private: 0072 NetcdfSource& netcdf; 0073 }; 0074 0075 0076 int DataInterfaceNetCdfScalar::read(const QString& scalar, DataScalar::ReadInfo& p) 0077 { 0078 return netcdf.readScalar(p.value, scalar); 0079 } 0080 0081 0082 bool DataInterfaceNetCdfScalar::isValid(const QString& scalar) const 0083 { 0084 return netcdf._scalarList.contains( scalar ); 0085 } 0086 0087 0088 // 0089 // String interface 0090 // 0091 0092 class DataInterfaceNetCdfString : public DataSource::DataInterface<DataString> 0093 { 0094 public: 0095 DataInterfaceNetCdfString(NetcdfSource& s) : netcdf(s) {} 0096 0097 // read one element 0098 int read(const QString&, DataString::ReadInfo&); 0099 0100 // named elements 0101 QStringList list() const { return netcdf._strings.keys(); } 0102 bool isListComplete() const { return true; } 0103 bool isValid(const QString&) const; 0104 0105 // T specific 0106 const DataString::DataInfo dataInfo(const QString&, int frame=0) const { Q_UNUSED(frame) return DataString::DataInfo(); } 0107 void setDataInfo(const QString&, const DataString::DataInfo&) {} 0108 0109 // meta data 0110 QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); } 0111 QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); } 0112 0113 0114 private: 0115 NetcdfSource& netcdf; 0116 }; 0117 0118 0119 //------------------------------------------------------------------------------------------- 0120 int DataInterfaceNetCdfString::read(const QString& string, DataString::ReadInfo& p) 0121 { 0122 //return netcdf.readString(p.value, string); 0123 if (isValid(string) && p.value) { 0124 *p.value = netcdf._strings[string]; 0125 return 1; 0126 } 0127 return 0; 0128 } 0129 0130 0131 bool DataInterfaceNetCdfString::isValid(const QString& string) const 0132 { 0133 return netcdf._strings.contains( string ); 0134 } 0135 0136 0137 0138 0139 0140 // 0141 // Vector interface 0142 // 0143 0144 class DataInterfaceNetCdfVector : public DataSource::DataInterface<DataVector> 0145 { 0146 public: 0147 DataInterfaceNetCdfVector(NetcdfSource& s) : netcdf(s) {} 0148 0149 // read one element 0150 int read(const QString&, DataVector::ReadInfo&); 0151 0152 // named elements 0153 QStringList list() const { return netcdf._fieldList; } 0154 bool isListComplete() const { return true; } 0155 bool isValid(const QString&) const; 0156 0157 // T specific 0158 const DataVector::DataInfo dataInfo(const QString&, int frame=0) const; 0159 void setDataInfo(const QString&, const DataVector::DataInfo&) {} 0160 0161 // meta data 0162 QMap<QString, double> metaScalars(const QString&); 0163 QMap<QString, QString> metaStrings(const QString&); 0164 0165 0166 private: 0167 NetcdfSource& netcdf; 0168 }; 0169 0170 0171 const DataVector::DataInfo DataInterfaceNetCdfVector::dataInfo(const QString &field, int frame) const 0172 { 0173 Q_UNUSED(frame) 0174 if (!netcdf._fieldList.contains(field)) 0175 return DataVector::DataInfo(); 0176 0177 return DataVector::DataInfo(netcdf.frameCount(field), netcdf.samplesPerFrame(field)); 0178 } 0179 0180 0181 0182 int DataInterfaceNetCdfVector::read(const QString& field, DataVector::ReadInfo& p) 0183 { 0184 return netcdf.readField(p.data, field, p.startingFrame, p.numberOfFrames); 0185 } 0186 0187 0188 bool DataInterfaceNetCdfVector::isValid(const QString& field) const 0189 { 0190 return netcdf._fieldList.contains( field ); 0191 } 0192 0193 QMap<QString, double> DataInterfaceNetCdfVector::metaScalars(const QString& field) 0194 { 0195 NcVar *var = 0; 0196 if (field != "INDEX") { 0197 var = netcdf._ncfile->get_var((NcToken) field.toLatin1().constData()); 0198 } 0199 if (!var) { 0200 NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl; 0201 return QMap<QString, double>(); 0202 } 0203 QMap<QString, double> fieldScalars; 0204 fieldScalars["NbAttributes"] = var->num_atts(); 0205 for (int i=0; i<var->num_atts(); ++i) { 0206 NcAtt *att = var->get_att(i); 0207 // Only handle char attributes as fieldStrings, the others as fieldScalars 0208 if (att->type() == ncByte || att->type() == ncShort || att->type() == ncInt 0209 || att->type() == ncLong || att->type() == ncFloat || att->type() == ncDouble) { 0210 // Some attributes may have multiple values => load the first as is, and for the others 0211 // add a -2, -3, etc... suffix as obviously we can have only one value per scalar. 0212 // Do it in two steps to avoid a test in the loop while keeping a "clean" name for the first one 0213 fieldScalars[QString(att->name())] = att->values()->as_double(0); 0214 for (int j=1; j<att->values()->num(); ++j) { 0215 fieldScalars[QString(att->name()) + QString("-") + QString::number(j+1)] = att->values()->as_double(j); 0216 } 0217 } 0218 } 0219 return fieldScalars; 0220 } 0221 0222 QMap<QString, QString> DataInterfaceNetCdfVector::metaStrings(const QString& field) 0223 { 0224 NcVar *var = 0; 0225 if (field != "INDEX") { 0226 var = netcdf._ncfile->get_var(field.toLatin1().constData()); 0227 } 0228 if (!var) { 0229 NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl; 0230 return QMap<QString, QString>(); 0231 } 0232 QMap<QString, QString> fieldStrings; 0233 QString tmpString; 0234 for (int i=0; i<var->num_atts(); ++i) { 0235 NcAtt *att = var->get_att(i); 0236 // Only handle char/unspecified attributes as fieldStrings, the others as fieldScalars 0237 if (att->type() == ncChar || att->type() == ncNoType) { 0238 fieldStrings[att->name()] = QString(att->values()->as_string(0)); 0239 } 0240 // qDebug() << att->name() << ": " << att->values()->num() << endl; 0241 } 0242 return fieldStrings; 0243 } 0244 0245 0246 // 0247 // Matrix interface 0248 // 0249 0250 class DataInterfaceNetCdfMatrix : public DataSource::DataInterface<DataMatrix> 0251 { 0252 public: 0253 0254 DataInterfaceNetCdfMatrix(NetcdfSource& s) : netcdf(s) {} 0255 0256 // read one element 0257 int read(const QString&, DataMatrix::ReadInfo&); 0258 0259 // named elements 0260 QStringList list() const { return netcdf._matrixList; } 0261 bool isListComplete() const { return true; } 0262 bool isValid(const QString&) const; 0263 0264 // T specific 0265 const DataMatrix::DataInfo dataInfo (const QString&, int frame) const; 0266 void setDataInfo(const QString&, const DataMatrix::DataInfo&) {} 0267 0268 // meta data 0269 QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); } 0270 QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); } 0271 0272 0273 private: 0274 NetcdfSource& netcdf; 0275 }; 0276 0277 0278 const DataMatrix::DataInfo DataInterfaceNetCdfMatrix::dataInfo(const QString& matrix, int frame) const 0279 { 0280 Q_UNUSED(frame) 0281 if (!netcdf._matrixList.contains( matrix ) ) { 0282 return DataMatrix::DataInfo(); 0283 } 0284 0285 QByteArray bytes = matrix.toLatin1(); 0286 NcVar *var = netcdf._ncfile->get_var(bytes.constData()); // var is owned by _ncfile 0287 if (!var) { 0288 return DataMatrix::DataInfo(); 0289 } 0290 0291 if (var->num_dims() != 2) { 0292 return DataMatrix::DataInfo(); 0293 } 0294 0295 DataMatrix::DataInfo info; 0296 // TODO is this right? 0297 info.xSize = var->get_dim(0)->size(); 0298 info.ySize = var->get_dim(1)->size(); 0299 0300 return info; 0301 } 0302 0303 0304 int DataInterfaceNetCdfMatrix::read(const QString& field, DataMatrix::ReadInfo& p) 0305 { 0306 int count = netcdf.readMatrix(p.data->z, field); 0307 0308 p.data->xMin = 0; 0309 p.data->yMin = 0; 0310 p.data->xStepSize = 1; 0311 p.data->yStepSize = 1; 0312 0313 return count; 0314 } 0315 0316 0317 bool DataInterfaceNetCdfMatrix::isValid(const QString& field) const { 0318 return netcdf._matrixList.contains( field ); 0319 } 0320 0321 0322 // 0323 // NetcdfSource 0324 // 0325 0326 NetcdfSource::NetcdfSource(Kst::ObjectStore *store, QSettings *cfg, const QString& filename, const QString& type, const QDomElement &element) : 0327 Kst::DataSource(store, cfg, filename, type), 0328 _ncfile(0L), 0329 _ncErr(NcError::silent_nonfatal), 0330 is(new DataInterfaceNetCdfScalar(*this)), 0331 it(new DataInterfaceNetCdfString(*this)), 0332 iv(new DataInterfaceNetCdfVector(*this)), 0333 im(new DataInterfaceNetCdfMatrix(*this)) 0334 { 0335 setInterface(is); 0336 setInterface(it); 0337 setInterface(iv); 0338 setInterface(im); 0339 0340 setUpdateType(None); 0341 0342 if (!type.isEmpty() && type != "netCDF") { 0343 return; 0344 } 0345 0346 _valid = false; 0347 _maxFrameCount = 0; 0348 0349 _filename = filename; 0350 _strings = fileMetas(); 0351 _valid = initFile(); 0352 } 0353 0354 0355 NetcdfSource::~NetcdfSource() { 0356 delete _ncfile; 0357 _ncfile = 0L; 0358 } 0359 0360 0361 void NetcdfSource::reset() { 0362 delete _ncfile; 0363 _ncfile = 0L; 0364 _maxFrameCount = 0; 0365 _valid = initFile(); 0366 } 0367 0368 0369 bool NetcdfSource::initFile() { 0370 _ncfile = new NcFile(_filename.toUtf8().data(), NcFile::ReadOnly); 0371 if (!_ncfile->is_valid()) { 0372 qDebug() << _filename << ": failed to open in initFile()" << endl; 0373 return false; 0374 } 0375 0376 NETCDF_DBG qDebug() << _filename << ": building field list" << endl; 0377 _fieldList.clear(); 0378 _fieldList += "INDEX"; 0379 0380 int nb_vars = _ncfile->num_vars(); 0381 NETCDF_DBG qDebug() << nb_vars << " vars found in total" << endl; 0382 0383 _maxFrameCount = 0; 0384 0385 for (int i = 0; i < nb_vars; i++) { 0386 NcVar *var = _ncfile->get_var(i); 0387 if (!var) { 0388 continue; 0389 } 0390 if (var->num_dims() == 0) { 0391 _scalarList += var->name(); 0392 } else if (var->num_dims() == 1) { 0393 _fieldList += var->name(); 0394 int fc = var->num_vals() / var->rec_size(); 0395 _maxFrameCount = qMax(_maxFrameCount, fc); 0396 _frameCounts[var->name()] = fc; 0397 } else if (var->num_dims() == 2) { 0398 _matrixList += var->name(); 0399 } 0400 } 0401 0402 // Get strings 0403 int globalAttributesNb = _ncfile->num_atts(); 0404 for (int i = 0; i < globalAttributesNb; ++i) { 0405 // Get only first value, should be enough for a start especially as strings are complete 0406 NcAtt *att = _ncfile->get_att(i); 0407 if (att) { 0408 QString attrName = QString(att->name()); 0409 char *attString = att->as_string(0); 0410 QString attrValue = QString(att->as_string(0)); 0411 delete[] attString; 0412 //TODO port 0413 //KstString *ms = new KstString(KstObjectTag(attrName, tag()), this, attrValue); 0414 _strings[attrName] = attrValue; 0415 } 0416 delete att; 0417 } 0418 setUpdateType(Timer); 0419 0420 NETCDF_DBG qDebug() << "netcdf file initialized"; 0421 // TODO update(); // necessary? slows down initial loading 0422 return true; 0423 } 0424 0425 0426 0427 Kst::Object::UpdateType NetcdfSource::internalDataSourceUpdate() { 0428 //TODO port 0429 /* 0430 if (KstObject::checkUpdateCounter(u)) { 0431 return lastUpdateResult(); 0432 } 0433 */ 0434 0435 _ncfile->sync(); 0436 0437 bool updated = false; 0438 /* Update member variables _ncfile, _maxFrameCount, and _frameCounts 0439 and indicate that an update is needed */ 0440 int nb_vars = _ncfile->num_vars(); 0441 for (int j = 0; j < nb_vars; j++) { 0442 NcVar *var = _ncfile->get_var(j); 0443 if (!var) { 0444 continue; 0445 } 0446 int fc = var->num_vals() / var->rec_size(); 0447 _maxFrameCount = qMax(_maxFrameCount, fc); 0448 updated = updated || (_frameCounts[var->name()] != fc); 0449 _frameCounts[var->name()] = fc; 0450 } 0451 return updated ? Object::Updated : Object::NoChange; 0452 } 0453 0454 0455 int NetcdfSource::readScalar(double *v, const QString& field) 0456 { 0457 // TODO error handling 0458 QByteArray bytes = field.toLatin1(); 0459 NcVar *var = _ncfile->get_var(bytes.constData()); // var is owned by _ncfile 0460 if (var) { 0461 var->get(v); 0462 return 1; 0463 } 0464 return 0; 0465 } 0466 0467 int NetcdfSource::readString(QString *stringValue, const QString& stringName) 0468 { 0469 // TODO more error handling? 0470 NcAtt *att = _ncfile->get_att((NcToken) stringName.toLatin1().data()); 0471 if (att) { 0472 *stringValue = QString(att->as_string(0)); 0473 delete att; 0474 return 1; 0475 } 0476 return 0; 0477 } 0478 0479 int NetcdfSource::readField(double *v, const QString& field, int s, int n) { 0480 NcType dataType = ncNoType; /* netCDF data type */ 0481 /* Values for one record */ 0482 NcValues *record = 0;// = new NcValues(dataType,numFrameVals); 0483 0484 NETCDF_DBG qDebug() << "Entering NetcdfSource::readField with params: " << field << ", from " << s << " for " << n << " frames" << endl; 0485 0486 /* For INDEX field */ 0487 if (field.toLower() == "index") { 0488 if (n < 0) { 0489 v[0] = double(s); 0490 return 1; 0491 } 0492 for (int i = 0; i < n; ++i) { 0493 v[i] = double(s + i); 0494 } 0495 return n; 0496 } 0497 0498 /* For a variable from the netCDF file */ 0499 QByteArray bytes = field.toLatin1(); 0500 NcVar *var = _ncfile->get_var(bytes.constData()); // var is owned by _ncfile 0501 if (!var) { 0502 NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl; 0503 return -1; 0504 } 0505 0506 dataType = var->type(); 0507 0508 if (s >= var->num_vals() / var->rec_size()) { 0509 return 0; 0510 } 0511 0512 bool oneSample = n < 0; 0513 int recSize = var->rec_size(); 0514 double add_offset = 1.0, scale_factor = 1.0; 0515 switch (dataType) { 0516 case ncShort: 0517 { 0518 // Check for special attributes add_offset and scale_factor indicating the use of the convention described in 0519 // <http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Attribute-Conventions.html> 0520 bool packed = iv->metaScalars(field).contains("add_offset") && iv->metaScalars(field).contains("scale_factor"); 0521 if (packed) { 0522 // Get the values into local vars 0523 add_offset = iv->metaScalars(field)["add_offset"]; 0524 scale_factor = iv->metaScalars(field)["scale_factor"]; 0525 } 0526 if (oneSample) { 0527 record = var->get_rec(s); 0528 v[0] = packed ? record->as_short(0)*scale_factor+add_offset : record->as_short(0); 0529 delete record; 0530 } else { 0531 for (int i = 0; i < n; i++) { 0532 record = var->get_rec(i+s); 0533 if (packed) { 0534 for (int j = 0; j < recSize; j++) { 0535 v[i*recSize + j] = record->as_short(j)*scale_factor+add_offset; 0536 } 0537 } 0538 else { 0539 for (int j = 0; j < recSize; j++) { 0540 v[i*recSize + j] = record->as_short(j); 0541 } 0542 } 0543 delete record; 0544 } 0545 } 0546 } 0547 break; 0548 0549 case ncInt: 0550 { 0551 if (oneSample) { 0552 record = var->get_rec(s); 0553 v[0] = record->as_int(0); 0554 delete record; 0555 } else { 0556 for (int i = 0; i < n; i++) { 0557 record = var->get_rec(i+s); 0558 NETCDF_DBG qDebug() << "Read record " << i+s << endl; 0559 for (int j = 0; j < recSize; j++) { 0560 v[i*recSize + j] = record->as_int(j); 0561 } 0562 delete record; 0563 } 0564 } 0565 } 0566 break; 0567 0568 case ncFloat: 0569 { 0570 if (oneSample) { 0571 record = var->get_rec(s); 0572 v[0] = record->as_float(0); 0573 delete record; 0574 } else { 0575 for (int i = 0; i < n; i++) { 0576 record = var->get_rec(i+s); 0577 for (int j = 0; j < recSize; j++) { 0578 v[i*recSize + j] = record->as_float(j); 0579 } 0580 delete record; 0581 } 0582 } 0583 } 0584 break; 0585 0586 case ncDouble: 0587 { 0588 if (oneSample) { 0589 record = var->get_rec(s); 0590 v[0] = record->as_double(0); 0591 delete record; 0592 } else { 0593 for (int i = 0; i < n; i++) { 0594 record = var->get_rec(i+s); 0595 for (int j = 0; j < recSize; j++) { 0596 v[i*recSize + j] = record->as_double(j); 0597 } 0598 delete record; 0599 } 0600 } 0601 } 0602 break; 0603 0604 default: 0605 NETCDF_DBG qDebug() << field << ": wrong datatype for kst, no values read" << endl; 0606 return -1; 0607 break; 0608 0609 } 0610 0611 NETCDF_DBG qDebug() << "Finished reading " << field << endl; 0612 0613 return oneSample ? 1 : n * recSize; 0614 } 0615 0616 0617 0618 0619 0620 int NetcdfSource::readMatrix(double *v, const QString& field) 0621 { 0622 /* For a variable from the netCDF file */ 0623 QByteArray bytes = field.toLatin1(); 0624 NcVar *var = _ncfile->get_var(bytes.constData()); // var is owned by _ncfile 0625 if (!var) { 0626 NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl; 0627 return -1; 0628 } 0629 0630 int xSize = var->get_dim(0)->size(); 0631 int ySize = var->get_dim(1)->size(); 0632 0633 var->get(v, xSize, ySize); 0634 0635 0636 return xSize * ySize; 0637 } 0638 0639 0640 0641 0642 0643 0644 0645 0646 int NetcdfSource::samplesPerFrame(const QString& field) { 0647 if (field.toLower() == "index") { 0648 return 1; 0649 } 0650 QByteArray bytes = field.toLatin1(); 0651 NcVar *var = _ncfile->get_var(bytes.constData()); 0652 if (!var) { 0653 NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl; 0654 return 0; 0655 } 0656 return var->rec_size(); 0657 } 0658 0659 0660 0661 int NetcdfSource::frameCount(const QString& field) const { 0662 if (field.isEmpty() || field.toLower() == "index") { 0663 return _maxFrameCount; 0664 } else { 0665 return _frameCounts[field]; 0666 } 0667 } 0668 0669 0670 0671 QString NetcdfSource::fileType() const { 0672 return "netCDF"; 0673 } 0674 0675 0676 0677 bool NetcdfSource::isEmpty() const { 0678 return frameCount() < 1; 0679 } 0680 0681 0682 0683 const QString& NetcdfSource::typeString() const 0684 { 0685 return netCdfTypeString; 0686 } 0687 0688 0689 const QString NetcdfSource::netcdfTypeKey() 0690 { 0691 return ::netCdfTypeString; 0692 }