/* Copyright (c) 2008-2018 Jan W. Krieger () This software is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License (LGPL) for more details. You should have received a copy of the GNU Lesser General Public License (LGPL) along with this program. If not, see . */ /** \file jkqtpdatastorage.cpp * \ingroup jkqtpdatastorage */ #include "jkqtplotter/jkqtpdatastorage.h" #include #include /************************************************************************************************************************** * JKQTPcolumn **************************************************************************************************************************/ //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPcolumn::JKQTPcolumn() { datastore=nullptr; name=""; datastoreItem=0; datastoreOffset=0; valid=false; } JKQTPcolumn::JKQTPcolumn(JKQTPdatastore *datastore, const QString &name, size_t datastoreItem, size_t datastoreOffset) { this->datastore=datastore; this->datastoreItem=datastoreItem; this->datastoreOffset=datastoreOffset; this->name=name; valid=true; } JKQTPcolumn::~JKQTPcolumn() { } size_t JKQTPcolumn::getRows() const { if (!valid || !datastore) return 0; JKQTPdatastoreItem* i=datastore->getItem(datastoreItem); if (i) return i->get_rows(); else return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPcolumn::copyData(QVector ©To) const { double* d=getPointer(0); copyTo.clear(); size_t i, cnt=getRows(); if (cnt>0) { copyTo.resize(cnt); for (i=0; i JKQTPcolumn::copyData() { QVector d; copyData(d); return d; } //////////////////////////////////////////////////////////////////////////////////////////////// double *JKQTPcolumn::getPointer(size_t n) const { if (!datastore) return nullptr; if (!datastore->getItem(datastoreItem)) return nullptr; return datastore->getItem(datastoreItem)->getPointer(datastoreOffset, n); } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPcolumn::copy(double* data, size_t N, size_t offset) { if (!datastore) return ; JKQTPdatastoreItem* it=datastore->getItem(datastoreItem); if (!it) return; for (size_t i=0; iset(datastoreOffset, i+offset, data[i]); } } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPcolumn::exchange(double value, double replace) { if (!datastore) return ; for (size_t i=0; idataformat=JKQTPsingleColumn; this->allocated=false; this->data=nullptr; this->columns=0; this->rows=0; this->internal=true; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPdatastoreItem::JKQTPdatastoreItem(size_t columns, size_t rows){ this->internal=true; this->allocated=false; if (columns>1) { this->dataformat=JKQTPmatrixRow; this->data=(double*)calloc(columns*rows, sizeof(double)); } else { this->dataformat=JKQTPsingleColumn; this->data=(double*)calloc(rows, sizeof(double)); } this->columns=columns; this->rows=rows; this->allocated=true; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPdatastoreItem::resizeColumns(size_t new_rows) { if (internal && allocated && data!=nullptr) { free(data); data=nullptr; } data=(double*)calloc(columns*new_rows, sizeof(double)); rows=new_rows; internal=true; allocated=true; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPdatastoreItem::JKQTPdatastoreItem(JKQTPdatastoreItemFormat dataformat, double* data, size_t columns, size_t rows){ this->dataformat=dataformat; this->allocated=true; this->data=data; this->columns=columns; this->rows=rows; this->internal=false; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPdatastoreItem::JKQTPdatastoreItem(JKQTPdatastoreItemFormat dataformat, double *data, size_t columns, size_t rows, bool internal) { this->dataformat=dataformat; this->allocated=true; this->data=data; this->columns=columns; this->rows=rows; this->internal=internal; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPdatastoreItem::~JKQTPdatastoreItem(){ if (internal && allocated && data!=nullptr) { free(data); data=nullptr; } } /************************************************************************************************************************** * JKQTPdatastore **************************************************************************************************************************/ //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPdatastore::addItem(JKQTPdatastoreItem* item) { /*items.push_back(item); return items.size()-1;*/ items.insert(maxItemID, item); maxItemID++; return maxItemID-1; } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPdatastore::addColumn(JKQTPcolumn col) { columns.insert(maxColumnsID, col); maxColumnsID++; return maxColumnsID-1; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPdatastore::JKQTPdatastore() { maxItemID=0; maxColumnsID=0; clear(); } void JKQTPdatastore::clear(){ maxItemID=0; maxColumnsID=0; /*if (items.size()>0) { for (size_t i=0; i it(items); while (it.hasNext()) { it.next(); delete it.value(); } items.clear(); columns.clear(); } void JKQTPdatastore::deleteAllColumns(const QString& name, bool removeItems) { QList ids; QMapIterator it(columns); while (it.hasNext()) { it.next(); if (it.value().get_name()==name) { ids.append(it.key()); } } for (int i=0; i ids; QMapIterator it(columns); while (it.hasNext()) { it.next(); if (it.value().get_name().startsWith(prefix)) { ids.append(it.key()); } } for (int i=0; i it(columns); while (it.hasNext()) { it.next(); if (it.value().get_datastoreItem()==dsitem) { cnt++; } } if (cnt<=1) { delete items[dsitem]; items.remove(dsitem); } } columns.remove(column); } int JKQTPdatastore::getColumnNum(const QString& name) { if (columns.size()<=0) return -1; QMapIterator it(columns); while (it.hasNext()) { it.next(); if (it.value().get_name()==name) return it.key(); } return -1; } size_t JKQTPdatastore::ensureColumnNum(const QString& name) { if (columns.size()<=0) return -1; QMapIterator it(columns); while (it.hasNext()) { it.next(); if (it.value().get_name()==name) return it.key(); } return addColumn(0, name); } JKQTPcolumn JKQTPdatastore::getColumn(size_t i) const { return columns.value(i); } size_t JKQTPdatastore::addCopiedItem(JKQTPdatastoreItemFormat dataformat, double* data, size_t columnsnum, size_t rows) { JKQTPdatastoreItem* it=nullptr; if ((dataformat==JKQTPsingleColumn)||(columnsnum==1)) { return addCopiedItem(data, rows); } else if (dataformat==JKQTPmatrixColumn) { it=new JKQTPdatastoreItem(columnsnum, rows); for (size_t c=0; cset(c, r, data[c*rows+r]); } } } else if (dataformat==JKQTPmatrixColumn) { it=new JKQTPdatastoreItem(columnsnum, rows); for (size_t r=0; rset(c, r, data[r*columnsnum+c]); } } } /*items.push_back(it); return items.size()-1;*/ return addItem(it); }; /** \brief add a new columns/item with \a rows rows to the datastore and return its ID. The item uses internal memory management. */ size_t JKQTPdatastore::addItem(size_t rows) { /*items.push_back(new JKQTPdatastoreItem(1, rows)); return items.size()-1;*/ return addItem(new JKQTPdatastoreItem(1, rows)); }; /** \brief add a new item with \a rows rows and \a columns columns to the datastore and return its ID. The item uses internal memory management. */ size_t JKQTPdatastore::addItem(size_t columnsnum, size_t rows) { /*items.push_back(new JKQTPdatastoreItem(columnsnum, rows)); return items.size()-1;*/ return addItem(new JKQTPdatastoreItem(columnsnum, rows)); }; /** \brief add one external column to the datastore. It contains \a rows rows.*/ size_t JKQTPdatastore::addItem(double* data, size_t rows) { /*items.push_back(new JKQTPdatastoreItem(JKQTPsingleColumn, data, 1, rows)); return items.size()-1;*/ return addItem(new JKQTPdatastoreItem(JKQTPsingleColumn, data, 1, rows)); } size_t JKQTPdatastore::addInternalItem(double *data, size_t rows) { JKQTPdatastoreItem* dsi=new JKQTPdatastoreItem(JKQTPsingleColumn, data, 1, rows, true); return addItem(dsi); }; /** \brief add an external memory block to the datastore. It contains \a rows rows and \a columns columns. \a dataformat determined the memory layout*/ size_t JKQTPdatastore::addItem(JKQTPdatastoreItemFormat dataformat, double* data, size_t columnsnum, size_t rows) { /*items.push_back(new JKQTPdatastoreItem(dataformat, data, columnsnum, rows)); return items.size()-1;*/ return addItem(new JKQTPdatastoreItem(dataformat, data, columnsnum, rows)); }; /** \brief add one external array to the datastore. It contains \a rows rows. The data is copied and the copy managed internally */ size_t JKQTPdatastore::addCopiedItem(const double* data, size_t rows) { JKQTPdatastoreItem* it=new JKQTPdatastoreItem(1, rows); if (data) { for (size_t i=0; iset(0, i, data[i]); //std::cout<<"copy@"< void copyDataFromMat(double* data, const cv::Mat& mat, int channel) { size_t r=0; const int channels=mat.channels(); for (int iy=0; iy(iy); for (int ix=0; ix(&(row[ix*channels+channel]))); r++; } } } } size_t JKQTPdatastore::copyCvMatToColumn(const cv::Mat& mat, const QString &name, int channel) { const size_t N=static_cast(mat.cols*mat.rows); double* d=static_cast(malloc(static_cast(N)*sizeof(double))); if (CV_MAT_DEPTH(mat.type())==CV_64F) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else if (CV_MAT_DEPTH(mat.type())==CV_32F) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else if (CV_MAT_DEPTH(mat.type())==CV_32S) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else if (CV_MAT_DEPTH(mat.type())==CV_16S) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else if (CV_MAT_DEPTH(mat.type())==CV_16U) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else if (CV_MAT_DEPTH(mat.type())==CV_8S) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else if (CV_MAT_DEPTH(mat.type())==CV_8U) JKQTPdatastore_Helper::copyDataFromMat(d, mat, channel); else throw std::runtime_error("datatype of cv::Mat not supported by JKQTPdatastore::copyImageToColumn()"); size_t itemid=addInternalItem(d, N); return addColumnForItem(itemid, 0, name); } #endif size_t JKQTPdatastore::addLinearColumn(size_t rows, double start, double end, const QString& name) { double delta=(end-start)/(double)(rows-1); JKQTPdatastoreItem* it=new JKQTPdatastoreItem(1, rows); for (size_t i=0; iset(0, i, start+(double)i * delta); //std::cout<<"copy@"<=0) return getNextLowerIndex(column, row, 0, end); else if (start>=0 && end<0) return getNextLowerIndex(column, row, start, col.getRows()-1); else if (start<0 && end<0) return getNextLowerIndex(column, row, 0, col.getRows()-1); else { double d=0; const double v=col.getValue(row); long long res=-1; for ( long long i=start; i<=end; i++) { if (i!=(long long)row) { const double v1=col.getValue(i); const double dd=v1-v; if ((dd<0) && ((fabs(dd)=0) return getNextHigherIndex(column, row, 0, end); else if (start>=0 && end<0) return getNextHigherIndex(column, row, start, col.getRows()-1); else if (start<0 && end<0) return getNextHigherIndex(column, row, 0, col.getRows()-1); else { double d=0; const double v=col.getValue(row); long long res=-1; for ( long long i=start; i<=end; i++) { if (i!=(long long)row) { const double v1=col.getValue(i); const double dd=v1-v; if ((dd>0) && ((fabs(dd)res) res=r; }*/ QMapIterator it(columns); while (it.hasNext()) { it.next(); size_t r=it.value().getRows(); if (r>res) res=r; } return res; } void JKQTPdatastore::saveCSV(QString filename, QSet userColumns, QString separator, QString decimal_separator, QString comment, QString aroundStrings, char floatformat) { //std::cout< userColumns) { //std::cout< it(columns); int col=0; while (it.hasNext()) { it.next(); names.append(it.value().get_name()); col++; } return names; } void JKQTPdatastore::saveMatlab(QTextStream &txt, QSet userColumns) { //std::cout< varnames; txt.setLocale(loc); /* if (comment.size()>0) { txt< it(columns); while (it.hasNext()) { it.next(); if (!first) txt< it(columns); int col=0; int i=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(i)) { // convert the column name into a variable name (name cleanup!) If the column name is empty, we use a default name ("column") QString newvarbase=jkqtp_to_valid_variable_name(it.value().get_name().toStdString()).c_str(); if (newvarbase.isEmpty()) newvarbase="column"; int cnt=1; QString newvar=newvarbase; // now we make sure that the name is not already used, i.e. we compare it to all variable names used so far and if it has already been used, // an increasing number is added to the name. while (varnames.contains(newvar)) { newvar=newvarbase+QString::number(cnt); cnt++; } varnames.insert(newvar); txt< userColumns, QString separator, QString decimal_separator, QString comment, QString aroundStrings, char floatformat) { //std::cout<0) { txt< it(columns); int i=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(i)) { if (!first) txt< it(columns); int j=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(j)) { if (!first) txt<i) { QString num=QString("%1").arg(get(it.key(), i), 0, floatformat); num=num.replace(dsep, decimal_separator); txt< userColumns, QString floatformat) { Q_UNUSED(floatformat) // find out the decimal and the thousand separator QLocale loc=QLocale::c(); loc.setNumberOptions(QLocale::OmitGroupSeparator); //QChar dsep=loc.decimalPoint(); QFile f(filename); if (!f.open(QIODevice::WriteOnly|QIODevice::Text)) return; QTextStream txt(&f); txt.setLocale(loc); // write SYLK header txt<<"ID;P\n"; // write column headers QMapIterator it(columns); size_t c=1; int i=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(i)) { txt<<"C;Y1;X"< it(columns); c=1; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(i)) { if (it.value().getRows()>i) { txt< > JKQTPdatastore::getData(QStringList *columnNames, QSet userColumns) { QStringList cl; QList > res; // write column headers QMapIterator it(columns); size_t c=1; int i=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(i)) { QVector dat; for (size_t i=0; i userColumns, QString floatformat) { Q_UNUSED(floatformat) // find out the decimal and the thousand separator QLocale loc=QLocale::c(); loc.setNumberOptions(QLocale::OmitGroupSeparator); //QChar dsep=loc.decimalPoint(); QFile f(filename); if (!f.open(QIODevice::WriteOnly|QIODevice::Text)) return; QTextStream txt(&f); txt.setLocale(loc); size_t rows=getMaxRows(); // write DIF header txt< it(columns); size_t c=1; int i=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(i)) { txt< it(columns); c=1; int j=0; while (it.hasNext()) { it.next(); if (userColumns.isEmpty() || userColumns.contains(j)) { //qDebug()<datastore=datastore; reloadModel(); } JKQTPdatastoreModel::~JKQTPdatastoreModel() { } QVariant JKQTPdatastoreModel::data(const QModelIndex &index, int role) const { int row=index.row(); int column=index.column(); if (datastore) { if (role==Qt::DisplayRole || role==Qt::EditRole) { int col=datastore->getColumnIDs().value(column, -1); if (col>-1 && row>=0 && row<(int64_t)datastore->getColumn(col).getRows()) { return datastore->get(col, row); } } } return QVariant(); } Qt::ItemFlags JKQTPdatastoreModel::flags(const QModelIndex &/*index*/) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant JKQTPdatastoreModel::headerData(int section, Qt::Orientation orientation, int role) const { if (datastore) { if (role==Qt::DisplayRole) { if (orientation==Qt::Horizontal) { if (section>=0 && section<(int64_t)datastore->getColumnCount()) { //qDebug()<<"columns: "<getColumnNames().size()<getColumnIDs().size()<<": "<getColumnNames(); return datastore->getColumnNames().value(section); } } else { return QString::number(section+1); } } } return QVariant(); } int JKQTPdatastoreModel::rowCount(const QModelIndex &/*parent*/) const { if (datastore) { return datastore->getMaxRows(); } return 0; } int JKQTPdatastoreModel::columnCount(const QModelIndex &/*parent*/) const { if (datastore) { return datastore->getColumnCount(); } return 0; } void JKQTPdatastoreModel::reloadModel() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) beginResetModel(); endResetModel(); #else reset(); #endif }