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