1 /***************************************************************************
2                 netcdf.cpp  -  netCDF file data source reader
3                              -------------------
4     begin                : 17/06/2004
5     copyright            : (C) 2004 Nicolas Brisset <nicodev@users.sourceforge.net>
6     email                : kst@kde.org
7     modified             : 03/14/05 by K. Scott
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 // TODO move
20 #include "sharedptr.h"
21 
22 #include "netcdfsource.h"
23 
24 #include "debug.h"
25 
26 #include <QFile>
27 #include <QFileInfo>
28 
29 #include <ctype.h>
30 #include <stdlib.h>
31 
32 //#define NETCDF_DEBUG_SHARED
33 #ifdef NETCDF_DEBUG_SHARED
34 #define NETCDF_DBG if (true)
35 #else
36 #define NETCDF_DBG if (false)
37 #endif
38 
39 
40 using namespace Kst;
41 
42 static const QString netCdfTypeString = "netCDF Files";
43 
44 
45 //
46 // Scalar interface
47 //
48 
49 class DataInterfaceNetCdfScalar : public DataSource::DataInterface<DataScalar>
50 {
51 public:
DataInterfaceNetCdfScalar(NetcdfSource & s)52   DataInterfaceNetCdfScalar(NetcdfSource& s) : netcdf(s) {}
53 
54   // read one element
55   int read(const QString&, DataScalar::ReadInfo&);
56 
57   // named elements
list() const58   QStringList list() const { return netcdf._scalarList; }
isListComplete() const59   bool isListComplete() const { return true; }
60   bool isValid(const QString&) const;
61 
62   // T specific
dataInfo(const QString &,int frame=0) const63   const DataScalar::DataInfo dataInfo(const QString&, int frame = 0) const { Q_UNUSED(frame) return DataScalar::DataInfo(); }
setDataInfo(const QString &,const DataScalar::DataInfo &)64   void setDataInfo(const QString&, const DataScalar::DataInfo&) {}
65 
66   // meta data
metaScalars(const QString &)67   QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
metaStrings(const QString &)68   QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
69 
70 
71 private:
72   NetcdfSource& netcdf;
73 };
74 
75 
read(const QString & scalar,DataScalar::ReadInfo & p)76 int DataInterfaceNetCdfScalar::read(const QString& scalar, DataScalar::ReadInfo& p)
77 {
78   return netcdf.readScalar(p.value, scalar);
79 }
80 
81 
isValid(const QString & scalar) const82 bool DataInterfaceNetCdfScalar::isValid(const QString& scalar) const
83 {
84   return  netcdf._scalarList.contains( scalar );
85 }
86 
87 
88 //
89 // String interface
90 //
91 
92 class DataInterfaceNetCdfString : public DataSource::DataInterface<DataString>
93 {
94 public:
DataInterfaceNetCdfString(NetcdfSource & s)95   DataInterfaceNetCdfString(NetcdfSource& s) : netcdf(s) {}
96 
97   // read one element
98   int read(const QString&, DataString::ReadInfo&);
99 
100   // named elements
list() const101   QStringList list() const { return netcdf._strings.keys(); }
isListComplete() const102   bool isListComplete() const { return true; }
103   bool isValid(const QString&) const;
104 
105   // T specific
dataInfo(const QString &,int frame=0) const106   const DataString::DataInfo dataInfo(const QString&, int frame=0) const { Q_UNUSED(frame) return DataString::DataInfo(); }
setDataInfo(const QString &,const DataString::DataInfo &)107   void setDataInfo(const QString&, const DataString::DataInfo&) {}
108 
109   // meta data
metaScalars(const QString &)110   QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
metaStrings(const QString &)111   QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
112 
113 
114 private:
115   NetcdfSource& netcdf;
116 };
117 
118 
119 //-------------------------------------------------------------------------------------------
read(const QString & string,DataString::ReadInfo & p)120 int DataInterfaceNetCdfString::read(const QString& string, DataString::ReadInfo& p)
121 {
122   //return netcdf.readString(p.value, string);
123   if (isValid(string) && p.value) {
124     *p.value = netcdf._strings[string];
125     return 1;
126   }
127   return 0;
128 }
129 
130 
isValid(const QString & string) const131 bool DataInterfaceNetCdfString::isValid(const QString& string) const
132 {
133   return netcdf._strings.contains( string );
134 }
135 
136 
137 
138 
139 
140 //
141 // Vector interface
142 //
143 
144 class DataInterfaceNetCdfVector : public DataSource::DataInterface<DataVector>
145 {
146 public:
DataInterfaceNetCdfVector(NetcdfSource & s)147   DataInterfaceNetCdfVector(NetcdfSource& s) : netcdf(s) {}
148 
149   // read one element
150   int read(const QString&, DataVector::ReadInfo&);
151 
152   // named elements
list() const153   QStringList list() const { return netcdf._fieldList; }
isListComplete() const154   bool isListComplete() const { return true; }
155   bool isValid(const QString&) const;
156 
157   // T specific
158   const DataVector::DataInfo dataInfo(const QString&, int frame=0) const;
setDataInfo(const QString &,const DataVector::DataInfo &)159   void setDataInfo(const QString&, const DataVector::DataInfo&) {}
160 
161   // meta data
162   QMap<QString, double> metaScalars(const QString&);
163   QMap<QString, QString> metaStrings(const QString&);
164 
165 
166 private:
167   NetcdfSource& netcdf;
168 };
169 
170 
dataInfo(const QString & field,int frame) const171 const DataVector::DataInfo DataInterfaceNetCdfVector::dataInfo(const QString &field, int frame) const
172 {
173   Q_UNUSED(frame)
174   if (!netcdf._fieldList.contains(field))
175     return DataVector::DataInfo();
176 
177   return DataVector::DataInfo(netcdf.frameCount(field), netcdf.samplesPerFrame(field));
178 }
179 
180 
181 
read(const QString & field,DataVector::ReadInfo & p)182 int DataInterfaceNetCdfVector::read(const QString& field, DataVector::ReadInfo& p)
183 {
184   return netcdf.readField(p.data, field, p.startingFrame, p.numberOfFrames);
185 }
186 
187 
isValid(const QString & field) const188 bool DataInterfaceNetCdfVector::isValid(const QString& field) const
189 {
190   return  netcdf._fieldList.contains( field );
191 }
192 
metaScalars(const QString & field)193 QMap<QString, double> DataInterfaceNetCdfVector::metaScalars(const QString& field)
194 {
195   NcVar *var = 0;
196   if (field != "INDEX") {
197     var = netcdf._ncfile->get_var((NcToken) field.toLatin1().constData());
198   }
199   if (!var) {
200     NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl;
201     return QMap<QString, double>();
202   }
203   QMap<QString, double> fieldScalars;
204   fieldScalars["NbAttributes"] = var->num_atts();
205   for (int i=0; i<var->num_atts(); ++i) {
206     NcAtt *att = var->get_att(i);
207     // Only handle char attributes as fieldStrings, the others as fieldScalars
208     if (att->type() == ncByte || att->type() == ncShort || att->type() == ncInt
209         || att->type() == ncLong || att->type() == ncFloat || att->type() == ncDouble) {
210       // Some attributes may have multiple values => load the first as is, and for the others
211       // add a -2, -3, etc... suffix as obviously we can have only one value per scalar.
212       // Do it in two steps to avoid a test in the loop while keeping a "clean" name for the first one
213       fieldScalars[QString(att->name())] = att->values()->as_double(0);
214       for (int j=1; j<att->values()->num(); ++j) {
215         fieldScalars[QString(att->name()) + QString("-") + QString::number(j+1)] = att->values()->as_double(j);
216       }
217     }
218   }
219   return fieldScalars;
220 }
221 
metaStrings(const QString & field)222 QMap<QString, QString> DataInterfaceNetCdfVector::metaStrings(const QString& field)
223 {
224   NcVar *var = 0;
225   if (field != "INDEX") {
226     var = netcdf._ncfile->get_var(field.toLatin1().constData());
227   }
228   if (!var) {
229     NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl;
230     return QMap<QString, QString>();
231   }
232   QMap<QString, QString> fieldStrings;
233   QString tmpString;
234   for (int i=0; i<var->num_atts(); ++i) {
235     NcAtt *att = var->get_att(i);
236     // Only handle char/unspecified attributes as fieldStrings, the others as fieldScalars
237     if (att->type() == ncChar || att->type() == ncNoType) {
238       fieldStrings[att->name()] = QString(att->values()->as_string(0));
239     }
240     // qDebug() << att->name() << ": " << att->values()->num() << endl;
241   }
242   return fieldStrings;
243 }
244 
245 
246 //
247 // Matrix interface
248 //
249 
250 class DataInterfaceNetCdfMatrix : public DataSource::DataInterface<DataMatrix>
251 {
252 public:
253 
DataInterfaceNetCdfMatrix(NetcdfSource & s)254   DataInterfaceNetCdfMatrix(NetcdfSource& s) : netcdf(s) {}
255 
256   // read one element
257   int read(const QString&, DataMatrix::ReadInfo&);
258 
259   // named elements
list() const260   QStringList list() const { return netcdf._matrixList; }
isListComplete() const261   bool isListComplete() const { return true; }
262   bool isValid(const QString&) const;
263 
264   // T specific
265   const DataMatrix::DataInfo dataInfo	(const QString&, int frame) const;
setDataInfo(const QString &,const DataMatrix::DataInfo &)266   void setDataInfo(const QString&, const DataMatrix::DataInfo&) {}
267 
268   // meta data
metaScalars(const QString &)269   QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
metaStrings(const QString &)270   QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
271 
272 
273 private:
274   NetcdfSource& netcdf;
275 };
276 
277 
dataInfo(const QString & matrix,int frame) const278 const DataMatrix::DataInfo DataInterfaceNetCdfMatrix::dataInfo(const QString& matrix, int frame) const
279 {
280   Q_UNUSED(frame)
281   if (!netcdf._matrixList.contains( matrix ) ) {
282     return DataMatrix::DataInfo();
283   }
284 
285   QByteArray bytes = matrix.toLatin1();
286   NcVar *var = netcdf._ncfile->get_var(bytes.constData());  // var is owned by _ncfile
287   if (!var) {
288     return DataMatrix::DataInfo();
289   }
290 
291   if (var->num_dims() != 2) {
292     return DataMatrix::DataInfo();
293   }
294 
295   DataMatrix::DataInfo info;
296   // TODO is this right?
297   info.xSize = var->get_dim(0)->size();
298   info.ySize = var->get_dim(1)->size();
299 
300   return info;
301 }
302 
303 
read(const QString & field,DataMatrix::ReadInfo & p)304 int DataInterfaceNetCdfMatrix::read(const QString& field, DataMatrix::ReadInfo& p)
305 {
306   int count = netcdf.readMatrix(p.data->z, field);
307 
308   p.data->xMin = 0;
309   p.data->yMin = 0;
310   p.data->xStepSize = 1;
311   p.data->yStepSize = 1;
312 
313   return count;
314 }
315 
316 
isValid(const QString & field) const317 bool DataInterfaceNetCdfMatrix::isValid(const QString& field) const {
318   return  netcdf._matrixList.contains( field );
319 }
320 
321 
322 //
323 // NetcdfSource
324 //
325 
NetcdfSource(Kst::ObjectStore * store,QSettings * cfg,const QString & filename,const QString & type,const QDomElement & element)326 NetcdfSource::NetcdfSource(Kst::ObjectStore *store, QSettings *cfg, const QString& filename, const QString& type, const QDomElement &element) :
327   Kst::DataSource(store, cfg, filename, type),
328   _ncfile(0L),
329   _ncErr(NcError::silent_nonfatal),
330   is(new DataInterfaceNetCdfScalar(*this)),
331   it(new DataInterfaceNetCdfString(*this)),
332   iv(new DataInterfaceNetCdfVector(*this)),
333   im(new DataInterfaceNetCdfMatrix(*this))
334   {
335   setInterface(is);
336   setInterface(it);
337   setInterface(iv);
338   setInterface(im);
339 
340   setUpdateType(None);
341 
342   if (!type.isEmpty() && type != "netCDF") {
343     return;
344   }
345 
346   _valid = false;
347   _maxFrameCount = 0;
348 
349   _filename = filename;
350   _strings = fileMetas();
351   _valid = initFile();
352 }
353 
354 
~NetcdfSource()355 NetcdfSource::~NetcdfSource() {
356   delete _ncfile;
357   _ncfile = 0L;
358 }
359 
360 
reset()361 void NetcdfSource::reset() {
362   delete _ncfile;
363   _ncfile = 0L;
364   _maxFrameCount = 0;
365   _valid = initFile();
366 }
367 
368 
initFile()369 bool NetcdfSource::initFile() {
370   _ncfile = new NcFile(_filename.toUtf8().data(), NcFile::ReadOnly);
371   if (!_ncfile->is_valid()) {
372       qDebug() << _filename << ": failed to open in initFile()" << endl;
373       return false;
374     }
375 
376   NETCDF_DBG qDebug() << _filename << ": building field list" << endl;
377   _fieldList.clear();
378   _fieldList += "INDEX";
379 
380   int nb_vars = _ncfile->num_vars();
381   NETCDF_DBG qDebug() << nb_vars << " vars found in total" << endl;
382 
383   _maxFrameCount = 0;
384 
385   for (int i = 0; i < nb_vars; i++) {
386     NcVar *var = _ncfile->get_var(i);
387     if (!var) {
388       continue;
389     }
390     if (var->num_dims() == 0) {
391       _scalarList += var->name();
392     } else if (var->num_dims() == 1) {
393       _fieldList += var->name();
394       int fc = var->num_vals() / var->rec_size();
395       _maxFrameCount = qMax(_maxFrameCount, fc);
396       _frameCounts[var->name()] = fc;
397     } else if (var->num_dims() == 2) {
398       _matrixList += var->name();
399     }
400   }
401 
402   // Get strings
403   int globalAttributesNb = _ncfile->num_atts();
404   for (int i = 0; i < globalAttributesNb; ++i) {
405     // Get only first value, should be enough for a start especially as strings are complete
406     NcAtt *att = _ncfile->get_att(i);
407     if (att) {
408       QString attrName = QString(att->name());
409       char *attString = att->as_string(0);
410       QString attrValue = QString(att->as_string(0));
411       delete[] attString;
412       //TODO port
413       //KstString *ms = new KstString(KstObjectTag(attrName, tag()), this, attrValue);
414       _strings[attrName] = attrValue;
415     }
416     delete att;
417   }
418   setUpdateType(Timer);
419 
420   NETCDF_DBG qDebug() << "netcdf file initialized";
421   // TODO update(); // necessary?  slows down initial loading
422   return true;
423 }
424 
425 
426 
internalDataSourceUpdate()427 Kst::Object::UpdateType NetcdfSource::internalDataSourceUpdate() {
428   //TODO port
429   /*
430   if (KstObject::checkUpdateCounter(u)) {
431     return lastUpdateResult();
432   }
433   */
434 
435   _ncfile->sync();
436 
437   bool updated = false;
438   /* Update member variables _ncfile, _maxFrameCount, and _frameCounts
439      and indicate that an update is needed */
440   int nb_vars = _ncfile->num_vars();
441   for (int j = 0; j < nb_vars; j++) {
442     NcVar *var = _ncfile->get_var(j);
443     if (!var) {
444       continue;
445     }
446     int fc = var->num_vals() / var->rec_size();
447     _maxFrameCount = qMax(_maxFrameCount, fc);
448     updated = updated || (_frameCounts[var->name()] != fc);
449     _frameCounts[var->name()] = fc;
450   }
451   return updated ? Object::Updated : Object::NoChange;
452 }
453 
454 
readScalar(double * v,const QString & field)455 int NetcdfSource::readScalar(double *v, const QString& field)
456 {
457   // TODO error handling
458   QByteArray bytes = field.toLatin1();
459   NcVar *var = _ncfile->get_var(bytes.constData());  // var is owned by _ncfile
460   if (var) {
461     var->get(v);
462     return 1;
463   }
464   return 0;
465 }
466 
readString(QString * stringValue,const QString & stringName)467 int NetcdfSource::readString(QString *stringValue, const QString& stringName)
468 {
469   // TODO more error handling?
470   NcAtt *att = _ncfile->get_att((NcToken) stringName.toLatin1().data());
471   if (att) {
472     *stringValue = QString(att->as_string(0));
473     delete att;
474     return 1;
475   }
476   return 0;
477 }
478 
readField(double * v,const QString & field,int s,int n)479 int NetcdfSource::readField(double *v, const QString& field, int s, int n) {
480   NcType dataType = ncNoType; /* netCDF data type */
481   /* Values for one record */
482   NcValues *record = 0;// = new NcValues(dataType,numFrameVals);
483 
484   NETCDF_DBG qDebug() << "Entering NetcdfSource::readField with params: " << field << ", from " << s << " for " << n << " frames" << endl;
485 
486   /* For INDEX field */
487   if (field.toLower() == "index") {
488     if (n < 0) {
489       v[0] = double(s);
490       return 1;
491     }
492     for (int i = 0; i < n; ++i) {
493       v[i] = double(s + i);
494     }
495     return n;
496   }
497 
498   /* For a variable from the netCDF file */
499   QByteArray bytes = field.toLatin1();
500   NcVar *var = _ncfile->get_var(bytes.constData());  // var is owned by _ncfile
501   if (!var) {
502     NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl;
503     return -1;
504   }
505 
506   dataType = var->type();
507 
508   if (s >= var->num_vals() / var->rec_size()) {
509     return 0;
510   }
511 
512   bool oneSample = n < 0;
513   int recSize = var->rec_size();
514   double add_offset = 1.0, scale_factor = 1.0;
515   switch (dataType) {
516     case ncShort:
517       {
518         // Check for special attributes add_offset and scale_factor indicating the use of the convention described in
519         // <http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Attribute-Conventions.html>
520         bool packed = iv->metaScalars(field).contains("add_offset") && iv->metaScalars(field).contains("scale_factor");
521         if (packed) {
522           // Get the values into local vars
523             add_offset = iv->metaScalars(field)["add_offset"];
524             scale_factor = iv->metaScalars(field)["scale_factor"];
525         }
526         if (oneSample) {
527           record = var->get_rec(s);
528           v[0] = packed ? record->as_short(0)*scale_factor+add_offset : record->as_short(0);
529           delete record;
530         } else {
531             for (int i = 0; i < n; i++) {
532               record = var->get_rec(i+s);
533               if (packed) {
534                 for (int j = 0; j < recSize; j++) {
535                   v[i*recSize + j] = record->as_short(j)*scale_factor+add_offset;
536                 }
537               }
538               else {
539                 for (int j = 0; j < recSize; j++) {
540                   v[i*recSize + j] = record->as_short(j);
541                 }
542               }
543             delete record;
544           }
545         }
546       }
547       break;
548 
549     case ncInt:
550       {
551         if (oneSample) {
552           record = var->get_rec(s);
553           v[0] = record->as_int(0);
554           delete record;
555         } else {
556           for (int i = 0; i < n; i++) {
557             record = var->get_rec(i+s);
558             NETCDF_DBG qDebug() << "Read record " << i+s << endl;
559             for (int j = 0; j < recSize; j++) {
560               v[i*recSize + j] = record->as_int(j);
561             }
562             delete record;
563           }
564         }
565       }
566       break;
567 
568     case ncFloat:
569       {
570         if (oneSample) {
571           record = var->get_rec(s);
572           v[0] = record->as_float(0);
573           delete record;
574         } else {
575           for (int i = 0; i < n; i++) {
576             record = var->get_rec(i+s);
577             for (int j = 0; j < recSize; j++) {
578               v[i*recSize + j] = record->as_float(j);
579             }
580             delete record;
581           }
582         }
583       }
584       break;
585 
586     case ncDouble:
587       {
588         if (oneSample) {
589           record = var->get_rec(s);
590           v[0] = record->as_double(0);
591           delete record;
592         } else {
593           for (int i = 0; i < n; i++) {
594             record = var->get_rec(i+s);
595             for (int j = 0; j < recSize; j++) {
596               v[i*recSize + j] = record->as_double(j);
597             }
598             delete record;
599           }
600         }
601       }
602       break;
603 
604     default:
605       NETCDF_DBG qDebug() << field << ": wrong datatype for kst, no values read" << endl;
606       return -1;
607       break;
608 
609   }
610 
611   NETCDF_DBG qDebug() << "Finished reading " << field << endl;
612 
613   return oneSample ? 1 : n * recSize;
614 }
615 
616 
617 
618 
619 
readMatrix(double * v,const QString & field)620 int NetcdfSource::readMatrix(double *v, const QString& field)
621 {
622   /* For a variable from the netCDF file */
623   QByteArray bytes = field.toLatin1();
624   NcVar *var = _ncfile->get_var(bytes.constData());  // var is owned by _ncfile
625   if (!var) {
626     NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl;
627     return -1;
628   }
629 
630   int xSize = var->get_dim(0)->size();
631   int ySize = var->get_dim(1)->size();
632 
633   var->get(v, xSize, ySize);
634 
635 
636   return  xSize * ySize;
637 }
638 
639 
640 
641 
642 
643 
644 
645 
samplesPerFrame(const QString & field)646 int NetcdfSource::samplesPerFrame(const QString& field) {
647   if (field.toLower() == "index") {
648     return 1;
649   }
650   QByteArray bytes = field.toLatin1();
651   NcVar *var = _ncfile->get_var(bytes.constData());
652   if (!var) {
653     NETCDF_DBG qDebug() << "Queried field " << field << " which can't be read" << endl;
654     return 0;
655   }
656   return var->rec_size();
657 }
658 
659 
660 
frameCount(const QString & field) const661 int NetcdfSource::frameCount(const QString& field) const {
662   if (field.isEmpty() || field.toLower() == "index") {
663     return _maxFrameCount;
664   } else {
665     return _frameCounts[field];
666   }
667 }
668 
669 
670 
fileType() const671 QString NetcdfSource::fileType() const {
672   return "netCDF";
673 }
674 
675 
676 
isEmpty() const677 bool NetcdfSource::isEmpty() const {
678   return frameCount() < 1;
679 }
680 
681 
682 
typeString() const683 const QString& NetcdfSource::typeString() const
684 {
685   return netCdfTypeString;
686 }
687 
688 
netcdfTypeKey()689 const QString NetcdfSource::netcdfTypeKey()
690 {
691   return ::netCdfTypeString;
692 }
693