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(" ")); 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 }