File indexing completed on 2024-06-16 03:42:48

0001 /*
0002     File                 : NetCDFFilter.cpp
0003     Project              : LabPlot
0004     Description          : NetCDF I/O-filter
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2015-2019 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-FileCopyrightText: 2017 Alexander Semke <alexander.semke@web.de>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "NetCDFFilter.h"
0011 #include "NetCDFFilterPrivate.h"
0012 #include "backend/core/column/Column.h"
0013 #include "backend/lib/XmlStreamReader.h"
0014 #include "backend/lib/macros.h"
0015 #include "backend/spreadsheet/Spreadsheet.h"
0016 
0017 #include <KLocalizedString>
0018 #include <QProcess>
0019 #include <QStandardPaths>
0020 
0021 ///////////// macros ///////////////////////////////////////////////
0022 
0023 #define NC_GET_ATT(type, ftype)                                                                                                                                \
0024     auto* value = (type*)malloc(len * sizeof(type));                                                                                                           \
0025     m_status = nc_get_att_##ftype(ncid, varid, name, value);                                                                                                   \
0026     handleError(m_status, QStringLiteral("nc_get_att_" #ftype));                                                                                               \
0027     for (unsigned int l = 0; l < len; l++)                                                                                                                     \
0028         valueString << QString::number(value[l]);                                                                                                              \
0029     free(value);
0030 
0031 #define NC_SCAN_VAR(type, ftype)                                                                                                                               \
0032     type data;                                                                                                                                                 \
0033     m_status = nc_get_var_##ftype(ncid, i, &data);                                                                                                             \
0034     handleError(m_status, QStringLiteral("nc_get_var_" #ftype));                                                                                               \
0035     rowStrings << QString::number(data);
0036 
0037 #define NC_READ_VAR(type, ftype, dtype)                                                                                                                        \
0038     type data;                                                                                                                                                 \
0039     m_status = nc_get_var_##ftype(ncid, varid, &data);                                                                                                         \
0040     handleError(m_status, QStringLiteral("nc_get_var_" #ftype));                                                                                               \
0041                                                                                                                                                                \
0042     if (dataSource) {                                                                                                                                          \
0043         dtype* sourceData = static_cast<QVector<dtype>*>(dataContainer[0])->data();                                                                            \
0044         sourceData[0] = (dtype)data;                                                                                                                           \
0045     } else { /* preview */                                                                                                                                     \
0046         dataStrings << (QStringList() << QString::number(data));                                                                                               \
0047     }
0048 
0049 #define NC_READ_AVAR(type, ftype, dtype)                                                                                                                       \
0050     auto* data = new type[(unsigned int)actualRows];                                                                                                           \
0051                                                                                                                                                                \
0052     size_t start = (size_t)(startRow - 1), count = (size_t)actualRows;                                                                                         \
0053     m_status = nc_get_vara_##ftype(ncid, varid, &start, &count, data);                                                                                         \
0054     handleError(m_status, QStringLiteral("nc_get_vara_" #ftype));                                                                                              \
0055                                                                                                                                                                \
0056     if (dataSource) {                                                                                                                                          \
0057         dtype* sourceData = static_cast<QVector<dtype>*>(dataContainer[0])->data();                                                                            \
0058         for (int i = 0; i < actualRows; i++)                                                                                                                   \
0059             sourceData[i] = (dtype)data[i];                                                                                                                    \
0060     } else { /* preview */                                                                                                                                     \
0061         for (int i = 0; i < std::min(actualRows, lines); i++)                                                                                                  \
0062             dataStrings << (QStringList() << QString::number(data[i]));                                                                                        \
0063     }                                                                                                                                                          \
0064     delete[] data;
0065 
0066 // for native types (atm: int, double)
0067 #define NC_READ_AVAR_NATIVE(type)                                                                                                                              \
0068     type* data = nullptr;                                                                                                                                      \
0069     if (dataSource)                                                                                                                                            \
0070         data = static_cast<QVector<type>*>(dataContainer[0])->data();                                                                                          \
0071     else                                                                                                                                                       \
0072         data = new type[(unsigned int)actualRows];                                                                                                             \
0073                                                                                                                                                                \
0074     size_t start = (size_t)(startRow - 1), count = (size_t)actualRows;                                                                                         \
0075     m_status = nc_get_vara_##type(ncid, varid, &start, &count, data);                                                                                          \
0076     handleError(m_status, QStringLiteral("nc_get_vara_" #type));                                                                                               \
0077                                                                                                                                                                \
0078     if (!dataSource) { /* preview */                                                                                                                           \
0079         for (int i = 0; i < std::min(actualRows, lines); i++)                                                                                                  \
0080             dataStrings << (QStringList() << QString::number(data[i]));                                                                                        \
0081         delete[] data;                                                                                                                                         \
0082     }
0083 
0084 #define NC_READ_VAR2(type, ftype, dtype)                                                                                                                       \
0085     auto** data = (type**)malloc(rows * sizeof(type*));                                                                                                        \
0086     data[0] = (type*)malloc(cols * rows * sizeof(type));                                                                                                       \
0087     for (unsigned int i = 1; i < rows; i++)                                                                                                                    \
0088         data[i] = data[0] + i * cols;                                                                                                                          \
0089                                                                                                                                                                \
0090     m_status = nc_get_var_##ftype(ncid, varid, &data[0][0]);                                                                                                   \
0091     handleError(m_status, QStringLiteral("nc_get_var_" #ftype));                                                                                               \
0092                                                                                                                                                                \
0093     if (m_status == NC_NOERR) {                                                                                                                                \
0094         for (int i = 0; i < std::min((int)rows, lines); i++) {                                                                                                 \
0095             QStringList line;                                                                                                                                  \
0096             for (size_t j = 0; j < cols; j++) {                                                                                                                \
0097                 if (dataSource && dataContainer[0])                                                                                                            \
0098                     static_cast<QVector<dtype>*>(dataContainer[(int)(j - (size_t)startColumn + 1)])->operator[](i - startRow + 1) = data[i][(int)j];           \
0099                 else                                                                                                                                           \
0100                     line << QString::number(data[i][j]);                                                                                                       \
0101             }                                                                                                                                                  \
0102             dataStrings << line;                                                                                                                               \
0103             Q_EMIT q->completed(100 * i / actualRows);                                                                                                         \
0104         }                                                                                                                                                      \
0105     }                                                                                                                                                          \
0106     free(data[0]);                                                                                                                                             \
0107     free(data);
0108 
0109 //////////////////////////////////////////////////////////////////////
0110 
0111 /*!
0112     \class NetCDFFilter
0113     \brief Manages the import/export of data from/to a NetCDF file.
0114 
0115     \ingroup datasources
0116 */
0117 NetCDFFilter::NetCDFFilter()
0118     : AbstractFileFilter(FileType::NETCDF)
0119     , d(new NetCDFFilterPrivate(this)) {
0120 }
0121 
0122 NetCDFFilter::~NetCDFFilter() = default;
0123 
0124 /*!
0125   parses the content of the file \c ileName.
0126 */
0127 void NetCDFFilter::parse(const QString& fileName, QTreeWidgetItem* rootItem) {
0128     d->parse(fileName, rootItem);
0129 }
0130 
0131 /*!
0132   reads the content of the selected attribute from file \c fileName.
0133 */
0134 QString NetCDFFilter::readAttribute(const QString& fileName, const QString& name, const QString& varName) {
0135     return d->readAttribute(fileName, name, varName);
0136 }
0137 
0138 /*!
0139   reads the content of the current variable from file \c fileName.
0140 */
0141 QVector<QStringList>
0142 NetCDFFilter::readCurrentVar(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) {
0143     return d->readCurrentVar(fileName, dataSource, importMode, lines);
0144 }
0145 
0146 /*!
0147   reads the content of the file \c fileName to the data source \c dataSource.
0148 */
0149 void NetCDFFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode) {
0150     d->readDataFromFile(fileName, dataSource, mode);
0151 }
0152 
0153 /*!
0154 writes the content of the data source \c dataSource to the file \c fileName.
0155 */
0156 void NetCDFFilter::write(const QString& fileName, AbstractDataSource* dataSource) {
0157     d->write(fileName, dataSource);
0158     //  emit()
0159 }
0160 
0161 ///////////////////////////////////////////////////////////////////////
0162 
0163 void NetCDFFilter::setCurrentVarName(const QString& ds) {
0164     d->currentVarName = ds;
0165 }
0166 
0167 const QString NetCDFFilter::currentVarName() const {
0168     return d->currentVarName;
0169 }
0170 
0171 void NetCDFFilter::setStartRow(const int s) {
0172     d->startRow = s;
0173 }
0174 
0175 int NetCDFFilter::startRow() const {
0176     return d->startRow;
0177 }
0178 
0179 void NetCDFFilter::setEndRow(const int e) {
0180     d->endRow = e;
0181 }
0182 
0183 int NetCDFFilter::endRow() const {
0184     return d->endRow;
0185 }
0186 
0187 void NetCDFFilter::setStartColumn(const int c) {
0188     d->startColumn = c;
0189 }
0190 
0191 int NetCDFFilter::startColumn() const {
0192     return d->startColumn;
0193 }
0194 
0195 void NetCDFFilter::setEndColumn(const int c) {
0196     d->endColumn = c;
0197 }
0198 
0199 int NetCDFFilter::endColumn() const {
0200     return d->endColumn;
0201 }
0202 
0203 QString NetCDFFilter::fileInfoString(const QString& fileName) {
0204     DEBUG(Q_FUNC_INFO << ", fileName = " << qPrintable(fileName))
0205 
0206     QString info;
0207 #ifdef HAVE_NETCDF
0208     int ncid, status;
0209     status = nc_open(qPrintable(fileName), NC_NOWRITE, &ncid);
0210     NetCDFFilterPrivate::handleError(status, QStringLiteral("nc_open"));
0211     if (status != NC_NOERR) {
0212         DEBUG(" File error. Giving up");
0213         return i18n("Error opening file");
0214     }
0215 
0216     int ndims, nvars, nattr, uldid;
0217     status = nc_inq(ncid, &ndims, &nvars, &nattr, &uldid);
0218     NetCDFFilterPrivate::handleError(status, QStringLiteral("nc_inq"));
0219     DEBUG(" nattr/ndims/nvars = " << nattr << ' ' << ndims << ' ' << nvars);
0220 
0221     if (status == NC_NOERR) {
0222         info += i18n("Number of global attributes: %1", QString::number(nattr));
0223         info += QStringLiteral("<br>");
0224         info += i18n("Number of dimensions: %1", QString::number(ndims));
0225         info += QStringLiteral("<br>");
0226         info += i18n("Number of variables: %1", QString::number(nvars));
0227         info += QStringLiteral("<br>");
0228 
0229         int format;
0230         status = nc_inq_format(ncid, &format);
0231         if (status == NC_NOERR)
0232             info += i18n("Format version: %1", NetCDFFilterPrivate::translateFormat(format));
0233         info += QStringLiteral("<br>");
0234 
0235         info += i18n("Using library version %1", QLatin1String(nc_inq_libvers()));
0236     } else {
0237         info += i18n("Error getting file info");
0238     }
0239 
0240     status = ncclose(ncid);
0241     NetCDFFilterPrivate::handleError(status, QStringLiteral("nc_close"));
0242 #else
0243     Q_UNUSED(fileName)
0244 #endif
0245 
0246     return info;
0247 }
0248 
0249 /*!
0250  * Get file content in CDL (Common Data form Language) format
0251  * uses "ncdump"
0252  */
0253 QString NetCDFFilter::fileCDLString(const QString& fileName) {
0254     DEBUG(Q_FUNC_INFO);
0255 
0256     QString CDLString;
0257 #ifdef Q_OS_LINUX
0258     const QString ncdumpFullPath = QStandardPaths::findExecutable(QLatin1String("ncdump"));
0259     if (ncdumpFullPath.isEmpty())
0260         return i18n("ncdump not found.");
0261 
0262     QProcess proc;
0263     QStringList args;
0264     args << QStringLiteral("-cs") << fileName;
0265     proc.start(ncdumpFullPath, args);
0266 
0267     if (proc.waitForReadyRead(1000) == false)
0268         CDLString += i18n("Reading from file %1 failed.", fileName);
0269     else {
0270         CDLString += QLatin1String(proc.readAll());
0271         CDLString.replace(QLatin1Char('\n'), QStringLiteral("<br>\n"));
0272         CDLString.replace(QLatin1Char('\t'), QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;"));
0273         // DEBUG("  CDL string: " << STDSTRING(CDLString));
0274     }
0275 #else // TODO: ncdump on Win, Mac
0276     Q_UNUSED(fileName)
0277 #endif
0278 
0279     return CDLString;
0280 }
0281 
0282 // #####################################################################
0283 // ################### Private implementation ##########################
0284 // #####################################################################
0285 
0286 NetCDFFilterPrivate::NetCDFFilterPrivate(NetCDFFilter* owner)
0287     : q(owner) {
0288 #ifdef HAVE_NETCDF
0289     m_status = 0;
0290 #endif
0291 }
0292 
0293 #ifdef HAVE_NETCDF
0294 void NetCDFFilterPrivate::handleError(int err, const QString& function) {
0295     Q_UNUSED(function);
0296     if (err != NC_NOERR) {
0297         DEBUG("NETCDF ERROR:" << STDSTRING(function) << "() - " << nc_strerror(err));
0298         return;
0299     }
0300 }
0301 
0302 QString NetCDFFilterPrivate::translateDataType(nc_type type) {
0303     QString typeString;
0304 
0305     switch (type) {
0306     case NC_BYTE:
0307         typeString = QStringLiteral("BYTE");
0308         break;
0309     case NC_UBYTE:
0310         typeString = QStringLiteral("UBYTE");
0311         break;
0312     case NC_CHAR:
0313         typeString = QStringLiteral("CHAR");
0314         break;
0315     case NC_SHORT:
0316         typeString = QStringLiteral("SHORT");
0317         break;
0318     case NC_USHORT:
0319         typeString = QStringLiteral("USHORT");
0320         break;
0321     case NC_INT:
0322         typeString = QStringLiteral("INT");
0323         break;
0324     case NC_UINT:
0325         typeString = QStringLiteral("UINT");
0326         break;
0327     case NC_INT64:
0328         typeString = QStringLiteral("INT64");
0329         break;
0330     case NC_UINT64:
0331         typeString = QStringLiteral("UINT64");
0332         break;
0333     case NC_FLOAT:
0334         typeString = QStringLiteral("FLOAT");
0335         break;
0336     case NC_DOUBLE:
0337         typeString = QStringLiteral("DOUBLE");
0338         break;
0339     case NC_STRING:
0340         typeString = QStringLiteral("STRING");
0341         break;
0342     default:
0343         typeString = QStringLiteral("UNKNOWN");
0344     }
0345 
0346     return typeString;
0347 }
0348 
0349 QString NetCDFFilterPrivate::translateFormat(int format) {
0350     QString formatString;
0351 
0352     switch (format) {
0353     case NC_FORMAT_CLASSIC:
0354         formatString = QStringLiteral("NC_FORMAT_CLASSIC");
0355         break;
0356     case NC_FORMAT_64BIT:
0357         formatString = QStringLiteral("NC_FORMAT_64BIT");
0358         break;
0359     case NC_FORMAT_NETCDF4:
0360         formatString = QStringLiteral("NC_FORMAT_NETCDF4");
0361         break;
0362     case NC_FORMAT_NETCDF4_CLASSIC:
0363         formatString = QStringLiteral("NC_FORMAT_NETCDF4_CLASSIC");
0364         break;
0365     }
0366 
0367     return formatString;
0368 }
0369 
0370 QString NetCDFFilterPrivate::scanAttrs(int ncid, int varid, int attid, QTreeWidgetItem* parentItem) {
0371     char name[NC_MAX_NAME + 1];
0372 
0373     int nattr, nstart = 0;
0374     if (attid == -1) {
0375         m_status = nc_inq_varnatts(ncid, varid, &nattr);
0376         handleError(m_status, QStringLiteral("nc_inq_varnatts"));
0377     } else {
0378         nstart = attid;
0379         nattr = attid + 1;
0380     }
0381 
0382     nc_type type;
0383     size_t len;
0384     QStringList valueString;
0385     for (int i = nstart; i < nattr; i++) {
0386         valueString.clear();
0387         m_status = nc_inq_attname(ncid, varid, i, name);
0388         handleError(m_status, QStringLiteral("nc_inq_attname"));
0389 
0390         m_status = nc_inq_att(ncid, varid, name, &type, &len);
0391         handleError(m_status, QStringLiteral("nc_inq_att"));
0392         QDEBUG("    attr" << i + 1 << "name/type/len =" << name << translateDataType(type) << len);
0393 
0394         // read attribute
0395         switch (type) {
0396         case NC_BYTE: {
0397             NC_GET_ATT(signed char, schar);
0398             break;
0399         }
0400         case NC_UBYTE: {
0401             NC_GET_ATT(unsigned char, uchar);
0402             break;
0403         }
0404         case NC_CHAR: { // not number
0405             char* value = (char*)malloc((len + 1) * sizeof(char));
0406             m_status = nc_get_att_text(ncid, varid, name, value);
0407             handleError(m_status, QStringLiteral("nc_get_att_text"));
0408             value[len] = 0;
0409             valueString << QLatin1String(value);
0410             free(value);
0411             break;
0412         }
0413         case NC_SHORT: {
0414             NC_GET_ATT(short, short);
0415             break;
0416         }
0417         case NC_USHORT: {
0418             NC_GET_ATT(unsigned short, ushort);
0419             break;
0420         }
0421         case NC_INT: {
0422             NC_GET_ATT(int, int);
0423             break;
0424         }
0425         case NC_UINT: {
0426             NC_GET_ATT(unsigned int, uint);
0427             break;
0428         }
0429         case NC_INT64: {
0430             NC_GET_ATT(long long, longlong);
0431             break;
0432         }
0433         case NC_UINT64: {
0434             NC_GET_ATT(unsigned long long, ulonglong);
0435             break;
0436         }
0437         case NC_FLOAT: {
0438             NC_GET_ATT(float, float);
0439             break;
0440         }
0441         case NC_DOUBLE: {
0442             NC_GET_ATT(double, double);
0443             break;
0444         }
0445         // TODO: NC_STRING
0446         default:
0447             valueString << QStringLiteral("not supported");
0448         }
0449 
0450         if (parentItem != nullptr) {
0451             QString typeName;
0452             if (varid == NC_GLOBAL)
0453                 typeName = i18n("global attribute");
0454             else {
0455                 char varName[NC_MAX_NAME + 1];
0456                 m_status = nc_inq_varname(ncid, varid, varName);
0457                 typeName = i18n("%1 attribute", QLatin1String(varName));
0458             }
0459             QStringList props;
0460             props << translateDataType(type) << QStringLiteral(" (") << QString::number(len) << QStringLiteral(")");
0461             auto* attrItem =
0462                 new QTreeWidgetItem(QStringList() << QLatin1String(name) << typeName << props.join(QString()) << valueString.join(QStringLiteral(", ")));
0463             attrItem->setIcon(0, QIcon::fromTheme(QStringLiteral("accessories-calculator")));
0464             attrItem->setFlags(Qt::ItemIsEnabled);
0465             parentItem->addChild(attrItem);
0466         }
0467     }
0468 
0469     return valueString.join(QLatin1Char('\n'));
0470 }
0471 
0472 void NetCDFFilterPrivate::scanDims(int ncid, int ndims, QTreeWidgetItem* parentItem) {
0473     int ulid;
0474     m_status = nc_inq_unlimdim(ncid, &ulid);
0475     handleError(m_status, QStringLiteral("nc_inq_att"));
0476 
0477     char name[NC_MAX_NAME + 1];
0478     size_t len;
0479     for (int i = 0; i < ndims; i++) {
0480         m_status = nc_inq_dim(ncid, i, name, &len);
0481         handleError(m_status, QStringLiteral("nc_inq_att"));
0482         DEBUG(" dim" << i + 1 << ": name/len =" << name << len);
0483 
0484         QStringList props;
0485         props << i18n("length") << QLatin1String(" = ") << QString::number(len);
0486         QString value;
0487         if (i == ulid)
0488             value = i18n("unlimited");
0489         auto* attrItem = new QTreeWidgetItem(QStringList() << QLatin1String(name) << i18n("dimension") << props.join(QString()) << value);
0490         attrItem->setIcon(0, QIcon::fromTheme(QStringLiteral("accessories-calculator")));
0491         attrItem->setFlags(Qt::ItemIsEnabled);
0492         parentItem->addChild(attrItem);
0493     }
0494 }
0495 
0496 void NetCDFFilterPrivate::scanVars(int ncid, int nvars, QTreeWidgetItem* parentItem) {
0497     char name[NC_MAX_NAME + 1];
0498     nc_type type;
0499     int ndims, nattrs;
0500     int dimids[NC_MAX_VAR_DIMS];
0501 
0502     for (int i = 0; i < nvars; i++) {
0503         m_status = nc_inq_var(ncid, i, name, &type, &ndims, dimids, &nattrs);
0504         handleError(m_status, QStringLiteral("nc_inq_att"));
0505 
0506         QDEBUG("    var" << i + 1 << ": name/type=" << name << translateDataType(type));
0507         DEBUG("     ndims/nattr" << ndims << nattrs);
0508 
0509         QStringList props; // properties column
0510         props << translateDataType(type);
0511         char dname[NC_MAX_NAME + 1];
0512         size_t dlen;
0513         props << QStringLiteral("(");
0514         if (ndims == 0)
0515             props << QString::number(0);
0516         for (int j = 0; j < ndims; j++) {
0517             m_status = nc_inq_dim(ncid, dimids[j], dname, &dlen);
0518             if (j != 0)
0519                 props << QStringLiteral("x");
0520             props << QString::number(dlen);
0521         }
0522         props << QStringLiteral(")");
0523 
0524         QStringList rowStrings;
0525         rowStrings << QLatin1String(name) << i18n("variable") << props.join(QString());
0526         if (ndims == 0) { // get value of zero dim var
0527             switch (type) {
0528             case NC_BYTE: {
0529                 NC_SCAN_VAR(signed char, schar);
0530                 break;
0531             }
0532             case NC_UBYTE: {
0533                 NC_SCAN_VAR(unsigned char, uchar);
0534                 break;
0535             }
0536             case NC_CHAR: { // not number
0537                 char data;
0538                 m_status = nc_get_var_text(ncid, i, &data);
0539                 handleError(m_status, QStringLiteral("nc_get_var_text"));
0540 
0541                 rowStrings << QChar::fromLatin1(data);
0542                 break;
0543             }
0544             case NC_SHORT: {
0545                 NC_SCAN_VAR(short, short);
0546                 break;
0547             }
0548             case NC_USHORT: {
0549                 NC_SCAN_VAR(unsigned short, ushort);
0550                 break;
0551             }
0552             case NC_INT: {
0553                 NC_SCAN_VAR(int, int);
0554                 break;
0555             }
0556             case NC_UINT: {
0557                 NC_SCAN_VAR(unsigned int, uint);
0558                 break;
0559             }
0560             case NC_INT64: {
0561                 NC_SCAN_VAR(long long, longlong);
0562                 break;
0563             }
0564             case NC_UINT64: {
0565                 NC_SCAN_VAR(unsigned long long, ulonglong);
0566                 break;
0567             }
0568             case NC_DOUBLE: {
0569                 NC_SCAN_VAR(double, double);
0570                 break;
0571             }
0572             case NC_FLOAT: {
0573                 NC_SCAN_VAR(float, float);
0574                 break;
0575             }
0576             }
0577 
0578         } else {
0579             rowStrings << QString();
0580         }
0581 
0582         auto* varItem = new QTreeWidgetItem(rowStrings);
0583         varItem->setIcon(0, QIcon::fromTheme(QStringLiteral("x-office-spreadsheet")));
0584         varItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
0585         // highlight item
0586         for (int c = 0; c < varItem->columnCount(); c++) {
0587             varItem->setBackground(c, QColor(192, 255, 192));
0588             varItem->setForeground(c, Qt::black);
0589         }
0590         parentItem->addChild(varItem);
0591 
0592         scanAttrs(ncid, i, -1, varItem);
0593     }
0594 }
0595 #endif
0596 
0597 /*!
0598     parses the content of the file \c fileName and fill the tree using rootItem.
0599 */
0600 void NetCDFFilterPrivate::parse(const QString& fileName, QTreeWidgetItem* rootItem) {
0601     DEBUG(Q_FUNC_INFO);
0602 #ifdef HAVE_NETCDF
0603     DEBUG("fileName = " << qPrintable(fileName));
0604 
0605     int ncid;
0606     m_status = nc_open(qPrintable(fileName), NC_NOWRITE, &ncid);
0607     handleError(m_status, QStringLiteral("nc_open"));
0608     if (m_status != NC_NOERR) {
0609         DEBUG(" Giving up");
0610         return;
0611     }
0612 
0613     int ndims, nvars, nattr, uldid;
0614     m_status = nc_inq(ncid, &ndims, &nvars, &nattr, &uldid);
0615     handleError(m_status, QStringLiteral("nc_inq"));
0616     DEBUG(" nattr/ndims/nvars = " << nattr << ' ' << ndims << ' ' << nvars);
0617 
0618     auto* attrItem = new QTreeWidgetItem(QStringList() << QString(i18n("Attributes")));
0619     attrItem->setIcon(0, QIcon::fromTheme(QStringLiteral("folder")));
0620     attrItem->setFlags(Qt::ItemIsEnabled);
0621     rootItem->addChild(attrItem);
0622     scanAttrs(ncid, NC_GLOBAL, -1, attrItem);
0623 
0624     auto* dimItem = new QTreeWidgetItem(QStringList() << QString(i18n("Dimensions")));
0625     dimItem->setIcon(0, QIcon::fromTheme(QStringLiteral("folder")));
0626     dimItem->setFlags(Qt::ItemIsEnabled);
0627     rootItem->addChild(dimItem);
0628     scanDims(ncid, ndims, dimItem);
0629 
0630     auto* varItem = new QTreeWidgetItem(QStringList() << QString(i18n("Variables")));
0631     varItem->setIcon(0, QIcon::fromTheme(QStringLiteral("folder")));
0632     varItem->setFlags(Qt::ItemIsEnabled);
0633     rootItem->addChild(varItem);
0634     scanVars(ncid, nvars, varItem);
0635 
0636     m_status = ncclose(ncid);
0637     handleError(m_status, QStringLiteral("nc_close"));
0638 #else
0639     Q_UNUSED(fileName)
0640     Q_UNUSED(rootItem)
0641 #endif
0642 }
0643 
0644 QString NetCDFFilterPrivate::readAttribute(const QString& fileName, const QString& name, const QString& varName) {
0645 #ifdef HAVE_NETCDF
0646     int ncid;
0647     m_status = nc_open(qPrintable(fileName), NC_NOWRITE, &ncid);
0648     handleError(m_status, QStringLiteral("nc_open"));
0649     if (m_status != NC_NOERR) {
0650         DEBUG(" Giving up");
0651         return {};
0652     }
0653 
0654     // get varid
0655     int varid;
0656     if (varName == QStringLiteral("global"))
0657         varid = NC_GLOBAL;
0658     else {
0659         m_status = nc_inq_varid(ncid, qPrintable(varName), &varid);
0660         handleError(m_status, QStringLiteral("nc_inq_varid"));
0661     }
0662 
0663     // attribute 'name'
0664     int attid;
0665     m_status = nc_inq_attid(ncid, varid, qPrintable(name), &attid);
0666     handleError(m_status, QStringLiteral("nc_inq_attid"));
0667 
0668     QString nameString = scanAttrs(ncid, varid, attid);
0669 
0670     m_status = ncclose(ncid);
0671     handleError(m_status, QStringLiteral("nc_close"));
0672 
0673     return nameString;
0674 #else
0675     Q_UNUSED(fileName)
0676     Q_UNUSED(name)
0677     Q_UNUSED(varName)
0678     return {};
0679 #endif
0680 }
0681 
0682 /*!
0683     reads the content of the variable in the file \c fileName to a string (for preview) or to the data source.
0684 */
0685 QVector<QStringList>
0686 NetCDFFilterPrivate::readCurrentVar(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) {
0687     QVector<QStringList> dataStrings;
0688 
0689     if (currentVarName.isEmpty()) {
0690         DEBUG(Q_FUNC_INFO << ", WARNING: current var name is empty!")
0691         return dataStrings << (QStringList() << i18n("No variable selected"));
0692     }
0693     DEBUG(" current variable = " << STDSTRING(currentVarName));
0694 
0695 #ifdef HAVE_NETCDF
0696     int ncid;
0697     m_status = nc_open(qPrintable(fileName), NC_NOWRITE, &ncid);
0698     handleError(m_status, QStringLiteral("nc_open"));
0699     if (m_status != NC_NOERR) {
0700         DEBUG(" Giving up");
0701         return dataStrings;
0702     }
0703 
0704     int varid;
0705     m_status = nc_inq_varid(ncid, qPrintable(currentVarName), &varid);
0706     handleError(m_status, QStringLiteral("nc_inq_varid"));
0707 
0708     int ndims;
0709     nc_type type;
0710     m_status = nc_inq_varndims(ncid, varid, &ndims);
0711     handleError(m_status, QStringLiteral("nc_inq_varndims"));
0712     m_status = nc_inq_vartype(ncid, varid, &type);
0713     handleError(m_status, QStringLiteral("nc_inq_type"));
0714 
0715     int* dimids = (int*)malloc(ndims * sizeof(int));
0716     m_status = nc_inq_vardimid(ncid, varid, dimids);
0717     handleError(m_status, QStringLiteral("nc_inq_vardimid"));
0718 
0719     int actualRows = 0, actualCols = 0;
0720     int columnOffset = 0;
0721     std::vector<void*> dataContainer;
0722     switch (ndims) {
0723     case 0: {
0724         DEBUG(" zero dimensions");
0725         actualRows = actualCols = 1; // only one value
0726         QVector<AbstractColumn::ColumnMode> columnModes;
0727         columnModes.resize(actualCols);
0728         switch (type) {
0729         case NC_BYTE:
0730         case NC_UBYTE:
0731         case NC_SHORT:
0732         case NC_USHORT:
0733         case NC_INT:
0734             columnModes[0] = AbstractColumn::ColumnMode::Integer;
0735             break;
0736         case NC_UINT: // converted to double (int is too small)
0737         case NC_INT64:
0738             columnModes[0] = AbstractColumn::ColumnMode::BigInt;
0739             break;
0740         case NC_UINT64: // converted to double (int is too small)
0741         case NC_DOUBLE:
0742         case NC_FLOAT:
0743             columnModes[0] = AbstractColumn::ColumnMode::Double;
0744             break;
0745         case NC_CHAR:
0746             columnModes[0] = AbstractColumn::ColumnMode::Text;
0747             break;
0748             // TODO: NC_STRING
0749         }
0750 
0751         // TODO: use given names?
0752         QStringList vectorNames;
0753 
0754         if (dataSource)
0755             columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes);
0756 
0757         DEBUG(" Reading data of type " << STDSTRING(translateDataType(type)));
0758         switch (type) {
0759         case NC_BYTE: {
0760             NC_READ_VAR(signed char, schar, int);
0761             break;
0762         }
0763         case NC_UBYTE: {
0764             NC_READ_VAR(unsigned char, uchar, int);
0765             break;
0766         }
0767         case NC_CHAR: { // no number
0768             char data;
0769 
0770             m_status = nc_get_var_text(ncid, varid, &data);
0771             handleError(m_status, QStringLiteral("nc_get_var_text"));
0772 
0773             if (dataSource) {
0774                 QString* sourceData = static_cast<QVector<QString>*>(dataContainer[0])->data();
0775                 sourceData[0] = QChar::fromLatin1(data);
0776             } else { // preview
0777                 dataStrings << (QStringList() << QChar::fromLatin1(data));
0778             }
0779             break;
0780         }
0781         case NC_SHORT: {
0782             NC_READ_VAR(short, short, int);
0783             break;
0784         }
0785         case NC_USHORT: {
0786             NC_READ_VAR(unsigned short, ushort, int);
0787             break;
0788         }
0789         case NC_INT: {
0790             NC_READ_VAR(int, int, int);
0791             break;
0792         }
0793         case NC_UINT: {
0794             NC_READ_VAR(unsigned int, uint, double);
0795             break;
0796         } // converted to double (int is too small)
0797         case NC_INT64: {
0798             NC_READ_VAR(long long, longlong, double);
0799             break;
0800         } // converted to double (int is too small)
0801         case NC_UINT64: {
0802             NC_READ_VAR(unsigned long long, ulonglong, double);
0803             break;
0804         } // converted to double (int is too small)
0805         case NC_DOUBLE: {
0806             NC_READ_VAR(double, double, double);
0807             break;
0808         }
0809         case NC_FLOAT: {
0810             NC_READ_VAR(float, float, double);
0811             break;
0812         }
0813         }
0814         break;
0815     }
0816     case 1: {
0817         size_t size;
0818         m_status = nc_inq_dimlen(ncid, dimids[0], &size);
0819         handleError(m_status, QStringLiteral("nc_inq_dimlen"));
0820 
0821         if (endRow == -1)
0822             endRow = (int)size;
0823         if (lines == -1)
0824             lines = endRow;
0825         actualRows = endRow - startRow + 1;
0826         actualCols = 1; // only one column
0827 
0828         DEBUG("start/end row: " << startRow << ' ' << endRow);
0829         DEBUG("act rows/cols: " << actualRows << ' ' << actualCols);
0830 
0831         QVector<AbstractColumn::ColumnMode> columnModes;
0832         columnModes.resize(actualCols);
0833         switch (type) {
0834         case NC_BYTE:
0835         case NC_UBYTE:
0836         case NC_SHORT:
0837         case NC_USHORT:
0838         case NC_INT:
0839             columnModes[0] = AbstractColumn::ColumnMode::Integer;
0840             break;
0841         case NC_UINT: // converted to double (int is too small)
0842         case NC_INT64:
0843             columnModes[0] = AbstractColumn::ColumnMode::BigInt;
0844             break;
0845         case NC_UINT64: // converted to double (int is too small)
0846         case NC_DOUBLE:
0847         case NC_FLOAT:
0848             columnModes[0] = AbstractColumn::ColumnMode::Double;
0849             break;
0850         case NC_CHAR:
0851             columnModes[0] = AbstractColumn::ColumnMode::Text;
0852             break;
0853             // TODO: NC_STRING
0854         }
0855 
0856         // TODO: use given names?
0857         QStringList vectorNames;
0858 
0859         if (dataSource)
0860             columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes);
0861 
0862         DEBUG(" Reading data of type " << STDSTRING(translateDataType(type)));
0863         switch (type) {
0864         case NC_BYTE: {
0865             NC_READ_AVAR(signed char, schar, int);
0866             break;
0867         }
0868         case NC_UBYTE: {
0869             NC_READ_AVAR(unsigned char, uchar, int);
0870             break;
0871         }
0872         case NC_CHAR: { // not number
0873             char* data = new char[(unsigned int)actualRows];
0874 
0875             size_t start = (size_t)(startRow - 1), count = (size_t)actualRows;
0876             m_status = nc_get_vara_text(ncid, varid, &start, &count, data);
0877             handleError(m_status, QStringLiteral("nc_get_vara_text"));
0878 
0879             if (dataSource) {
0880                 QString* sourceData = static_cast<QVector<QString>*>(dataContainer[0])->data();
0881                 for (int i = 0; i < actualRows; i++)
0882                     sourceData[i] = QChar::fromLatin1(data[i]);
0883             } else { // preview
0884                 for (int i = 0; i < std::min(actualRows, lines); i++)
0885                     dataStrings << (QStringList() << QChar::fromLatin1(data[i]));
0886             }
0887             delete[] data;
0888 
0889             break;
0890         }
0891         case NC_SHORT: {
0892             NC_READ_AVAR(short, short, int);
0893             break;
0894         }
0895         case NC_USHORT: {
0896             NC_READ_AVAR(unsigned short, ushort, int);
0897             break;
0898         }
0899         case NC_INT: {
0900             NC_READ_AVAR_NATIVE(int);
0901             break;
0902         }
0903         case NC_UINT: {
0904             NC_READ_AVAR(unsigned int, uint, double);
0905             break;
0906         } // converted to double (int is too small)
0907         case NC_INT64: {
0908             NC_READ_AVAR(long long, longlong, double);
0909             break;
0910         } // converted to double (int is too small)
0911         case NC_UINT64: {
0912             NC_READ_AVAR(unsigned long long, ulonglong, double);
0913             break;
0914         } // converted to double (int is too small)
0915         case NC_DOUBLE: {
0916             NC_READ_AVAR_NATIVE(double);
0917             break;
0918         }
0919         case NC_FLOAT: {
0920             NC_READ_AVAR(float, float, double);
0921             break;
0922         }
0923         // TODO: NC_STRING
0924         default:
0925             DEBUG(" data type not supported yet");
0926         }
0927 
0928         break;
0929     }
0930     case 2: {
0931         size_t rows, cols;
0932         m_status = nc_inq_dimlen(ncid, dimids[0], &rows);
0933         handleError(m_status, QStringLiteral("nc_inq_dimlen"));
0934         m_status = nc_inq_dimlen(ncid, dimids[1], &cols);
0935         handleError(m_status, QStringLiteral("nc_inq_dimlen"));
0936 
0937         if (endRow == -1)
0938             endRow = (int)rows;
0939         if (lines == -1)
0940             lines = endRow;
0941         if (endColumn == -1)
0942             endColumn = (int)cols;
0943         actualRows = endRow - startRow + 1;
0944         actualCols = endColumn - startColumn + 1;
0945 
0946         DEBUG("dim = " << rows << "x" << cols);
0947         DEBUG("startRow/endRow: " << startRow << ' ' << endRow);
0948         DEBUG("startColumn/endColumn: " << startColumn << ' ' << endColumn);
0949         DEBUG("actual rows/cols: " << actualRows << ' ' << actualCols);
0950         DEBUG("lines: " << lines);
0951 
0952         QVector<AbstractColumn::ColumnMode> columnModes;
0953         columnModes.resize(actualCols);
0954         switch (type) {
0955         case NC_BYTE:
0956         case NC_UBYTE:
0957         case NC_SHORT:
0958         case NC_USHORT:
0959         case NC_INT:
0960             for (int i = 0; i < actualCols; i++)
0961                 columnModes[i] = AbstractColumn::ColumnMode::Integer;
0962             break;
0963         case NC_UINT: // converted to double (int is too small)
0964         case NC_INT64:
0965             for (int i = 0; i < actualCols; i++)
0966                 columnModes[i] = AbstractColumn::ColumnMode::BigInt;
0967             break;
0968         case NC_UINT64: // converted to double (int is too small)
0969         case NC_DOUBLE:
0970         case NC_FLOAT:
0971             for (int i = 0; i < actualCols; i++)
0972                 columnModes[i] = AbstractColumn::ColumnMode::Double;
0973             break;
0974         case NC_CHAR:
0975             for (int i = 0; i < actualCols; i++)
0976                 columnModes[i] = AbstractColumn::ColumnMode::Text;
0977             break;
0978             // TODO: NC_STRING
0979         }
0980 
0981         // TODO: use given names?
0982         QStringList vectorNames;
0983 
0984         if (dataSource)
0985             columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes);
0986 
0987         switch (type) {
0988         case NC_BYTE: {
0989             NC_READ_VAR2(signed char, schar, int);
0990             break;
0991         }
0992         case NC_UBYTE: {
0993             NC_READ_VAR2(unsigned char, uchar, int);
0994             break;
0995         }
0996         case NC_CHAR: { // no number
0997             char** data = (char**)malloc(rows * sizeof(char*));
0998             data[0] = (char*)malloc(cols * rows * sizeof(char));
0999             for (unsigned int i = 1; i < rows; i++)
1000                 data[i] = data[0] + i * cols;
1001 
1002             m_status = nc_get_var_text(ncid, varid, &data[0][0]);
1003             handleError(m_status, QStringLiteral("nc_get_var_text"));
1004 
1005             if (m_status == NC_NOERR) {
1006                 for (int i = 0; i < std::min((int)rows, lines); i++) {
1007                     QStringList line;
1008                     for (size_t j = 0; j < cols; j++) {
1009                         if (dataSource && dataContainer[0])
1010                             static_cast<QVector<QString>*>(dataContainer[(int)(j - (size_t)startColumn + 1)])->operator[](i - startRow + 1) =
1011                                 QChar::fromLatin1(data[i][(int)j]);
1012                         else
1013                             line << QChar::fromLatin1(data[i][j]);
1014                     }
1015                     dataStrings << line;
1016                     Q_EMIT q->completed(100 * i / actualRows);
1017                 }
1018             }
1019             free(data[0]);
1020             free(data);
1021 
1022             break;
1023         }
1024         case NC_SHORT: {
1025             NC_READ_VAR2(short, short, int);
1026             break;
1027         }
1028         case NC_USHORT: {
1029             NC_READ_VAR2(unsigned short, ushort, int);
1030             break;
1031         }
1032         case NC_INT: {
1033             NC_READ_VAR2(int, int, int);
1034             break;
1035         }
1036         case NC_UINT: {
1037             NC_READ_VAR2(unsigned int, uint, double);
1038             break;
1039         } // converted to double (int is too small)
1040         case NC_INT64: {
1041             NC_READ_VAR2(long long, longlong, double);
1042             break;
1043         } // converted to double (int is too small)
1044         case NC_UINT64: {
1045             NC_READ_VAR2(unsigned long long, ulonglong, double);
1046             break;
1047         } // converted to double (int is too small)
1048         case NC_FLOAT: {
1049             NC_READ_VAR2(float, float, double);
1050             break;
1051         }
1052         case NC_DOUBLE: {
1053             NC_READ_VAR2(double, double, double);
1054             break;
1055         }
1056         // TODO: NC_STRING
1057         default:
1058             DEBUG(" data type not supported yet");
1059         }
1060         break;
1061     }
1062     default:
1063         dataStrings << (QStringList() << i18n("%1 dimensional data of type %2 not supported yet", ndims, translateDataType(type)));
1064         QDEBUG(dataStrings);
1065     }
1066 
1067     free(dimids);
1068 
1069     m_status = ncclose(ncid);
1070     handleError(m_status, QStringLiteral("nc_close"));
1071 
1072     // TODO: why 1 row?
1073     if (dataSource)
1074         dataSource->finalizeImport(columnOffset, 1, actualCols, QString(), mode);
1075 #else
1076     Q_UNUSED(fileName)
1077     Q_UNUSED(dataSource)
1078     Q_UNUSED(mode)
1079     Q_UNUSED(lines)
1080 #endif
1081 
1082     return dataStrings;
1083 }
1084 
1085 /*!
1086     reads the content of the current selected variable from file \c fileName to the data source \c dataSource.
1087     Uses the settings defined in the data source.
1088 */
1089 QVector<QStringList> NetCDFFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode) {
1090     DEBUG(Q_FUNC_INFO)
1091     QVector<QStringList> dataStrings;
1092 
1093     if (currentVarName.isEmpty()) {
1094         DEBUG(Q_FUNC_INFO << ", no variable selected");
1095         return dataStrings;
1096     }
1097 
1098     return readCurrentVar(fileName, dataSource, mode);
1099 }
1100 
1101 /*!
1102     writes the content of \c dataSource to the file \c fileName.
1103 */
1104 void NetCDFFilterPrivate::write(const QString& /*fileName*/, AbstractDataSource* /*dataSource*/) {
1105     // TODO: writing NetCDF files not implemented yet
1106 }
1107 
1108 // ##############################################################################
1109 // ##################  Serialization/Deserialization  ###########################
1110 // ##############################################################################
1111 
1112 /*!
1113   Saves as XML.
1114  */
1115 void NetCDFFilter::save(QXmlStreamWriter* writer) const {
1116     writer->writeStartElement(QStringLiteral("netcdfFilter"));
1117     writer->writeEndElement();
1118 }
1119 
1120 /*!
1121   Loads from XML.
1122 */
1123 bool NetCDFFilter::load(XmlStreamReader*) {
1124     return true;
1125 }