/* Copyright (c) 2008-2019 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.1 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 . */ #include "jkqtplotter/jkqtpdatastorage.h" #include #include #include /************************************************************************************************************************** * JKQTPColumn **************************************************************************************************************************/ //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPColumn::JKQTPColumn() { datastore=nullptr; name=""; datastoreItem=0; datastoreOffset=0; imageColumns=1; valid=false; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPColumn::JKQTPColumn(JKQTPDatastore *datastore, const QString &name, size_t datastoreItem, size_t datastoreOffset, size_t imageColumns) { this->datastore=datastore; this->datastoreItem=datastoreItem; this->datastoreOffset=datastoreOffset; this->imageColumns=imageColumns; this->name=name; valid=true; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPColumn::setName(const QString &__value) { this->name = __value; } //////////////////////////////////////////////////////////////////////////////////////////////// QString JKQTPColumn::getName() const { return this->name; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPColumn::setImageColumns(size_t __value) { imageColumns=__value; } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPColumn::getRows() const { if (!valid || !datastore) return 0; JKQTPDatastoreItem* i=datastore->getItem(datastoreItem); if (i) return i->getRows(); else return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPColumn::copyData(QVector ©To) const { const double* d=getPointer(0); copyTo.clear(); size_t i, cnt=getRows(); if (cnt>0) { copyTo.resize(static_cast(cnt)); for (i=0; i(i)]=d[i]; } } } //////////////////////////////////////////////////////////////////////////////////////////////// QVector JKQTPColumn::copyData() { QVector d; copyData(d); return d; } //////////////////////////////////////////////////////////////////////////////////////////////// const double *JKQTPColumn::getPointer(size_t n) const { if (!datastore) return nullptr; if (!datastore->getItem(datastoreItem)) return nullptr; return datastore->getItem(datastoreItem)->getPointer(datastoreOffset, n); } //////////////////////////////////////////////////////////////////////////////////////////////// double *JKQTPColumn::getPointer(size_t n) { 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=JKQTPDatastoreItemFormat::SingleColumn; this->allocated=false; this->data=nullptr; this->columns=0; this->rows=0; this->storageType=StorageType::Internal; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPDatastoreItem::JKQTPDatastoreItem(size_t columns, size_t rows){ this->storageType=StorageType::Internal; this->allocated=false; if (columns>1) { this->dataformat=JKQTPDatastoreItemFormat::MatrixRow; this->data=static_cast(calloc(columns*rows, sizeof(double))); } else { this->dataformat=JKQTPDatastoreItemFormat::SingleColumn; this->storageType=StorageType::Vector; datavec.resize(static_cast(columns*rows)); for( double& d: datavec) { d=0.0; } this->data=datavec.data(); } this->columns=columns; this->rows=rows; this->allocated=true; } //////////////////////////////////////////////////////////////////////////////////////////////// bool JKQTPDatastoreItem::resizeColumns(size_t new_rows) { bool dataRetained=false; if (storageType==StorageType::Internal && allocated && data!=nullptr) { free(data); data=nullptr; } if (columns>1) { this->dataformat=JKQTPDatastoreItemFormat::MatrixRow; this->data=static_cast(calloc(columns*new_rows, sizeof(double))); storageType=StorageType::Internal; } else { this->dataformat=JKQTPDatastoreItemFormat::SingleColumn; this->storageType=StorageType::Vector; datavec.resize(static_cast(columns*new_rows)); if (new_rows>rows) { for( size_t i=rows; i(i)]=0.0; } } this->data=datavec.data(); dataRetained=true; } rows=new_rows; allocated=true; return dataRetained; } //////////////////////////////////////////////////////////////////////////////////////////////// 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->storageType=StorageType::External; } //////////////////////////////////////////////////////////////////////////////////////////////// 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->storageType=internal?StorageType::Internal:StorageType::External; } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPDatastoreItem::~JKQTPDatastoreItem(){ if (storageType==StorageType::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(); } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPDatastore::~JKQTPDatastore() { 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().getName()==name) { ids.append(it.key()); } } for (int i=0; i ids; QMapIterator it(columns); while (it.hasNext()) { it.next(); if (it.value().getName().startsWith(prefix)) { ids.append(it.key()); } } for (int i=0; i it(columns); while (it.hasNext()) { it.next(); if (it.value().getDatastoreItemNum()==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().getName()==name) return static_cast(it.key()); } return -1; } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::ensureColumnNum(const QString& name) { if (columns.size()<=0) return -1; QMapIterator it(columns); while (it.hasNext()) { it.next(); if (it.value().getName()==name) return static_cast(it.key()); } return static_cast(addColumn(0, name)); } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPColumn JKQTPDatastore::getColumn(size_t i) const { return columns.value(i, JKQTPColumn()); } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPColumn JKQTPDatastore::getColumn(int i) const { if (i<0) return JKQTPColumn(); return getColumn(static_cast(i)); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addCopiedItem(JKQTPDatastoreItemFormat dataformat, double* data, size_t columnsnum, size_t rows) { JKQTPDatastoreItem* it=nullptr; if ((dataformat==JKQTPDatastoreItemFormat::SingleColumn)||(columnsnum==1)) { return addCopiedItem(data, rows); } else if (dataformat==JKQTPDatastoreItemFormat::MatrixColumn) { it=new JKQTPDatastoreItem(columnsnum, rows); for (size_t c=0; cset(c, r, data[c*rows+r]); } } } else if (dataformat==JKQTPDatastoreItemFormat::MatrixColumn) { 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); }; //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addItem(size_t rows) { /*items.push_back(new JKQTPDatastoreItem(1, rows)); return items.size()-1;*/ return addItem(new JKQTPDatastoreItem(1, rows)); }; //////////////////////////////////////////////////////////////////////////////////////////////// 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(JKQTPDatastoreItemFormat::SingleColumn, data, 1, rows)); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addInternalItem(double *data, size_t rows) { JKQTPDatastoreItem* dsi=new JKQTPDatastoreItem(JKQTPDatastoreItemFormat::SingleColumn, data, 1, rows, true); return addItem(dsi); }; //////////////////////////////////////////////////////////////////////////////////////////////// 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)); }; //////////////////////////////////////////////////////////////////////////////////////////////// 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@"<(malloc(rows*sizeof(double))); double* dd=old.getPointer(0); size_t j=0; for (size_t i=start; i(rows-1); JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); for (size_t i=0; iset(0, i, start+static_cast(i) * delta); //std::cout<<"copy@"<set(0, i, pow(10.0, x0+static_cast(i)/static_cast(rows-1)*(x1-x0))); } size_t itemid= addItem(it); return addColumnForItem(itemid, 0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addDecadeLogColumn(size_t rows, double startDecade, double endDecade, const QString &name) { JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); const double x0=startDecade; const double x1=endDecade; for (size_t i=0; iset(0, i, pow(10.0, x0+static_cast(i)/static_cast(rows-1)*(x1-x0))); } size_t itemid= addItem(it); return addColumnForItem(itemid, 0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// std::pair JKQTPDatastore::addLinearGridColumns(size_t width, double startX, double endX, size_t height, double startY, double endY, const QString &nameX, const QString &nameY) { const double decX=(endX-startX)/static_cast(width-1); const double decY=(endY-startY)/static_cast(height-1); double y=startY; size_t cx=addColumn(width*height, nameX); size_t cy=addColumn(width*height, nameY); size_t i=0; for (size_t iy=0; iy &f, const QString &name) { JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); for (size_t i=0; iset(0, i, f(i, this)); } size_t itemid= addItem(it); return addColumnForItem(itemid, 0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addCalculatedColumn(size_t rows, const std::function &f, const QString &name) { JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, rows); for (size_t i=0; iset(0, i, f(i)); } size_t itemid= addItem(it); return addColumnForItem(itemid, 0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addColumnCalculatedFromColumn(size_t otherColumn, const std::function &f, const QString &name) { const JKQTPColumn& oc=columns.value(otherColumn); JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, oc.getRows()); for (size_t i=0; iset(0, i, f(oc.getValue(i))); } size_t itemid= addItem(it); return addColumnForItem(itemid, 0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::addColumnCalculatedFromColumn(size_t otherColumnX, size_t otherColumnY, const std::function &f, const QString &name) { const JKQTPColumn& ocx=columns.value(otherColumnX); const JKQTPColumn& ocy=columns.value(otherColumnY); const size_t N= qMin(ocx.getRows(), ocy.getRows()); JKQTPDatastoreItem* it=new JKQTPDatastoreItem(1, N); for (size_t i=0; iset(0, i, f(ocx.getValue(i), ocy.getValue(i))); } size_t itemid= addItem(it); return addColumnForItem(itemid, 0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// QVector JKQTPDatastore::getColumnIDsIntVec() const { QVector ks; for (const size_t& k: columns.keys()) { ks<(k); } return ks; } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextLowerIndex(size_t column, size_t row, int start, int end) const { const JKQTPColumn& col=columns[column]; if (start<0 && end>=0) return getNextLowerIndex(column, row, 0, end); else if (start>=0 && end<0) return getNextLowerIndex(column, row, start, static_cast(col.getRows())-1); else if (start<0 && end<0) return getNextLowerIndex(column, row, 0, static_cast(col.getRows())-1); else { double d=0; const double v=col.getValue(row); int res=-1; for ( int i=start; i<=end; i++) { if (i!=static_cast(row)) { const double v1=col.getValue(i); const double dd=v1-v; if ((dd<0) && ((fabs(dd)(columns[column].getRows())-1); } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextHigherIndex(size_t column, size_t row, int start, int end) const { const JKQTPColumn& col=columns[column]; if (start<0 && end>=0) return getNextHigherIndex(column, row, 0, end); else if (start>=0 && end<0) return getNextHigherIndex(column, row, start, static_cast(col.getRows())-1); else if (start<0 && end<0) return getNextHigherIndex(column, row, 0, static_cast(col.getRows())-1); else { double d=0; const double v=col.getValue(row); int res=-1; for ( int i=start; i<=end; i++) { if (i!=static_cast(row)) { const double v1=col.getValue(i); const double dd=v1-v; if ((dd>0) && ((fabs(dd)(columns[column].getRows())-1); } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextLowerIndex(int column, size_t row, int start, int end) const { return getNextLowerIndex(static_cast(column), row, start, end); } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextLowerIndex(int column, size_t row) const { return getNextLowerIndex(static_cast(column), row); } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextHigherIndex(int column, size_t row, int start, int end) const { return getNextHigherIndex(static_cast(column), row, start, end); } //////////////////////////////////////////////////////////////////////////////////////////////// size_t JKQTPDatastore::getMaxRows() const { size_t res=0; /*for (size_t i=0; ires) 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(const QString& filename, const QSet& userColumns, const QString& separator, const QString& decimal_separator, const QString& comment, const QString& aroundStrings, char floatformat) const { //std::cout<& userColumns) const { //std::cout< it(columns); int col=0; while (it.hasNext()) { it.next(); names.append(it.value().getName()); col++; } return names; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPDatastore::saveMatlab(QTextStream &txt, const QSet& userColumns) const { //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().getName().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, const QString& separator, const QString& decimal_separator, const QString& comment, const QString& aroundStrings, char floatformat) const { //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, const QString& floatformat) const { 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(static_cast(i))) { if (it.value().getRows()>i) { txt< > JKQTPDatastore::getData(QStringList *columnNames, const QSet& userColumns) const { 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, const QString& floatformat) const { 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() = default; //////////////////////////////////////////////////////////////////////////////////////////////// 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=static_cast(datastore->getColumnIDsIntVec().value(column, -1)); if (col>-1 && row>=0 && row(datastore->getRows(col))) { return datastore->get(col, static_cast(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(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 static_cast(datastore->getMaxRows()); } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastoreModel::columnCount(const QModelIndex &/*parent*/) const { if (datastore) { return static_cast(datastore->getColumnCount()); } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// void JKQTPDatastoreModel::reloadModel() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) beginResetModel(); endResetModel(); #else reset(); #endif } //////////////////////////////////////////////////////////////////////////////////////////////// int JKQTPDatastore::getNextHigherIndex(int column, size_t row) const { return getNextHigherIndex(static_cast(column), row); }