/* 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 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() = default; //////////////////////////////////////////////////////////////////////////////////////////////// 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 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=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=static_cast(calloc(columns*rows, sizeof(double))); } else { this->dataformat=JKQTPSingleColumn; this->data=static_cast(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=static_cast(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().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 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 it.key(); } return addColumn(0, name); } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPColumn JKQTPDatastore::getColumn(size_t i) const { return columns.value(i); } //////////////////////////////////////////////////////////////////////////////////////////////// JKQTPColumn JKQTPDatastore::getColumn(int 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); }; //////////////////////////////////////////////////////////////////////////////////////////////// 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(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); }; //////////////////////////////////////////////////////////////////////////////////////////////// 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); int 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@"<=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); 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)=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); 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)(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() { 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) { //std::cout<& userColumns) { //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) { //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) { //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) { 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, const 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, const 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() = 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->getColumnIDs().value(column, -1)); if (col>-1 && row>=0 && row(datastore->getColumn(col).getRows())) { 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); }