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 }