File indexing completed on 2025-01-26 03:34:18

0001 /*
0002     File                 : XYDataReductionCurve.cpp
0003     Project              : LabPlot
0004     Description          : A xy-curve defined by a data reduction
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016 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 
0011 /*!
0012   \class XYDataReductionCurve
0013   \brief A xy-curve defined by a data reduction
0014 
0015   \ingroup worksheet
0016 */
0017 
0018 #include "XYDataReductionCurve.h"
0019 #include "CartesianCoordinateSystem.h"
0020 #include "XYDataReductionCurvePrivate.h"
0021 #include "backend/core/column/Column.h"
0022 #include "backend/lib/XmlStreamReader.h"
0023 #include "backend/lib/commandtemplates.h"
0024 #include "backend/lib/macros.h"
0025 
0026 #include <KLocalizedString>
0027 #include <QElapsedTimer>
0028 #include <QIcon>
0029 #include <QThreadPool>
0030 
0031 XYDataReductionCurve::XYDataReductionCurve(const QString& name)
0032     : XYAnalysisCurve(name, new XYDataReductionCurvePrivate(this), AspectType::XYDataReductionCurve) {
0033 }
0034 
0035 XYDataReductionCurve::XYDataReductionCurve(const QString& name, XYDataReductionCurvePrivate* dd)
0036     : XYAnalysisCurve(name, dd, AspectType::XYDataReductionCurve) {
0037 }
0038 
0039 // no need to delete the d-pointer here - it inherits from QGraphicsItem
0040 // and is deleted during the cleanup in QGraphicsScene
0041 XYDataReductionCurve::~XYDataReductionCurve() = default;
0042 
0043 void XYDataReductionCurve::recalculate() {
0044     Q_D(XYDataReductionCurve);
0045     d->recalculate();
0046 }
0047 
0048 const XYAnalysisCurve::Result& XYDataReductionCurve::result() const {
0049     Q_D(const XYDataReductionCurve);
0050     return d->dataReductionResult;
0051 }
0052 /*!
0053     Returns an icon to be used in the project explorer.
0054 */
0055 QIcon XYDataReductionCurve::icon() const {
0056     return QIcon::fromTheme(QStringLiteral("labplot-xy-data-reduction-curve"));
0057 }
0058 
0059 // ##############################################################################
0060 // ##########################  getter methods  ##################################
0061 // ##############################################################################
0062 BASIC_SHARED_D_READER_IMPL(XYDataReductionCurve, XYDataReductionCurve::DataReductionData, dataReductionData, dataReductionData)
0063 
0064 const XYDataReductionCurve::DataReductionResult& XYDataReductionCurve::dataReductionResult() const {
0065     Q_D(const XYDataReductionCurve);
0066     return d->dataReductionResult;
0067 }
0068 
0069 // ##############################################################################
0070 // #################  setter methods and undo commands ##########################
0071 // ##############################################################################
0072 STD_SETTER_CMD_IMPL_F_S(XYDataReductionCurve, SetDataReductionData, XYDataReductionCurve::DataReductionData, dataReductionData, recalculate)
0073 void XYDataReductionCurve::setDataReductionData(const XYDataReductionCurve::DataReductionData& reductionData) {
0074     Q_D(XYDataReductionCurve);
0075     exec(new XYDataReductionCurveSetDataReductionDataCmd(d, reductionData, ki18n("%1: set options and perform the data reduction")));
0076 }
0077 
0078 // ##############################################################################
0079 // ######################### Private implementation #############################
0080 // ##############################################################################
0081 XYDataReductionCurvePrivate::XYDataReductionCurvePrivate(XYDataReductionCurve* owner)
0082     : XYAnalysisCurvePrivate(owner)
0083     , q(owner) {
0084 }
0085 
0086 // no need to delete xColumn and yColumn, they are deleted
0087 // when the parent aspect is removed
0088 XYDataReductionCurvePrivate::~XYDataReductionCurvePrivate() = default;
0089 
0090 void XYDataReductionCurvePrivate::resetResults() {
0091     dataReductionResult = XYDataReductionCurve::DataReductionResult();
0092 }
0093 
0094 const XYAnalysisCurve::Result& XYDataReductionCurvePrivate::result() const {
0095     return dataReductionResult;
0096 }
0097 
0098 bool XYDataReductionCurvePrivate::recalculateSpecific(const AbstractColumn* tmpXDataColumn, const AbstractColumn* tmpYDataColumn) {
0099     QElapsedTimer timer;
0100     timer.start();
0101 
0102     // copy all valid data point for the data reduction to temporary vectors
0103     QVector<double> xdataVector;
0104     QVector<double> ydataVector;
0105 
0106     double xmin;
0107     double xmax;
0108     if (dataReductionData.autoRange) {
0109         xmin = tmpXDataColumn->minimum();
0110         xmax = tmpXDataColumn->maximum();
0111     } else {
0112         xmin = dataReductionData.xRange.first();
0113         xmax = dataReductionData.xRange.last();
0114     }
0115 
0116     XYAnalysisCurve::copyData(xdataVector, ydataVector, tmpXDataColumn, tmpYDataColumn, xmin, xmax);
0117 
0118     // number of data points to use
0119     const size_t n = (size_t)xdataVector.size();
0120     if (n < 2) {
0121         dataReductionResult.available = true;
0122         dataReductionResult.valid = false;
0123         dataReductionResult.status = i18n("Not enough data points available.");
0124         return true;
0125     }
0126 
0127     double* xdata = xdataVector.data();
0128     double* ydata = ydataVector.data();
0129 
0130     // dataReduction settings
0131     const nsl_geom_linesim_type type = dataReductionData.type;
0132     const double tol = dataReductionData.tolerance;
0133     const double tol2 = dataReductionData.tolerance2;
0134 
0135     DEBUG("n =" << n);
0136     DEBUG("type:" << nsl_geom_linesim_type_name[type]);
0137     DEBUG("tolerance/step:" << tol);
0138     DEBUG("tolerance2/repeat/maxtol/region:" << tol2);
0139 
0140     ///////////////////////////////////////////////////////////
0141     Q_EMIT q->completed(10);
0142 
0143     size_t npoints = 0;
0144     double calcTolerance = 0; // calculated tolerance from Douglas-Peucker variant
0145     size_t* index = (size_t*)malloc(n * sizeof(size_t));
0146     switch (type) {
0147     case nsl_geom_linesim_type_douglas_peucker_variant: // tol used as number of points
0148         npoints = tol;
0149         calcTolerance = nsl_geom_linesim_douglas_peucker_variant(xdata, ydata, n, npoints, index);
0150         break;
0151     case nsl_geom_linesim_type_douglas_peucker:
0152         npoints = nsl_geom_linesim_douglas_peucker(xdata, ydata, n, tol, index);
0153         break;
0154     case nsl_geom_linesim_type_nthpoint: // tol used as step
0155         npoints = nsl_geom_linesim_nthpoint(n, (int)tol, index);
0156         break;
0157     case nsl_geom_linesim_type_raddist:
0158         npoints = nsl_geom_linesim_raddist(xdata, ydata, n, tol, index);
0159         break;
0160     case nsl_geom_linesim_type_perpdist: // tol2 used as repeat
0161         npoints = nsl_geom_linesim_perpdist_repeat(xdata, ydata, n, tol, tol2, index);
0162         break;
0163     case nsl_geom_linesim_type_interp:
0164         npoints = nsl_geom_linesim_interp(xdata, ydata, n, tol, index);
0165         break;
0166     case nsl_geom_linesim_type_visvalingam_whyatt:
0167         npoints = nsl_geom_linesim_visvalingam_whyatt(xdata, ydata, n, tol, index);
0168         break;
0169     case nsl_geom_linesim_type_reumann_witkam:
0170         npoints = nsl_geom_linesim_reumann_witkam(xdata, ydata, n, tol, index);
0171         break;
0172     case nsl_geom_linesim_type_opheim:
0173         npoints = nsl_geom_linesim_opheim(xdata, ydata, n, tol, tol2, index);
0174         break;
0175     case nsl_geom_linesim_type_lang: // tol2 used as region
0176         npoints = nsl_geom_linesim_opheim(xdata, ydata, n, tol, tol2, index);
0177         break;
0178     }
0179 
0180     DEBUG("npoints =" << npoints);
0181     if (type == nsl_geom_linesim_type_douglas_peucker_variant) {
0182         DEBUG("calculated tolerance =" << calcTolerance)
0183     } else
0184         Q_UNUSED(calcTolerance);
0185 
0186     Q_EMIT q->completed(80);
0187 
0188     xVector->resize((int)npoints);
0189     yVector->resize((int)npoints);
0190     for (int i = 0; i < (int)npoints; i++) {
0191         (*xVector)[i] = xdata[index[i]];
0192         (*yVector)[i] = ydata[index[i]];
0193     }
0194 
0195     Q_EMIT q->completed(90);
0196 
0197     const double posError = nsl_geom_linesim_positional_squared_error(xdata, ydata, n, index);
0198     const double areaError = nsl_geom_linesim_area_error(xdata, ydata, n, index);
0199 
0200     free(index);
0201 
0202     ///////////////////////////////////////////////////////////
0203 
0204     // write the result
0205     dataReductionResult.available = true;
0206     dataReductionResult.valid = npoints > 0;
0207     if (npoints > 0)
0208         dataReductionResult.status = QStringLiteral("OK");
0209     else
0210         dataReductionResult.status = QStringLiteral("FAILURE");
0211     dataReductionResult.elapsedTime = timer.elapsed();
0212     dataReductionResult.npoints = npoints;
0213     dataReductionResult.posError = posError;
0214     dataReductionResult.areaError = areaError;
0215 
0216     Q_EMIT q->completed(100);
0217     return true;
0218 }
0219 
0220 // ##############################################################################
0221 // ##################  Serialization/Deserialization  ###########################
0222 // ##############################################################################
0223 //! Save as XML
0224 void XYDataReductionCurve::save(QXmlStreamWriter* writer) const {
0225     Q_D(const XYDataReductionCurve);
0226 
0227     writer->writeStartElement(QStringLiteral("xyDataReductionCurve"));
0228 
0229     // write the base class
0230     XYAnalysisCurve::save(writer);
0231 
0232     // write xy-dataReduction-curve specific information
0233     //  dataReduction data
0234     writer->writeStartElement(QStringLiteral("dataReductionData"));
0235     writer->writeAttribute(QStringLiteral("autoRange"), QString::number(d->dataReductionData.autoRange));
0236     writer->writeAttribute(QStringLiteral("xRangeMin"), QString::number(d->dataReductionData.xRange.first()));
0237     writer->writeAttribute(QStringLiteral("xRangeMax"), QString::number(d->dataReductionData.xRange.last()));
0238     writer->writeAttribute(QStringLiteral("type"), QString::number(d->dataReductionData.type));
0239     writer->writeAttribute(QStringLiteral("autoTolerance"), QString::number(d->dataReductionData.autoTolerance));
0240     writer->writeAttribute(QStringLiteral("tolerance"), QString::number(d->dataReductionData.tolerance));
0241     writer->writeAttribute(QStringLiteral("autoTolerance2"), QString::number(d->dataReductionData.autoTolerance2));
0242     writer->writeAttribute(QStringLiteral("tolerance2"), QString::number(d->dataReductionData.tolerance2));
0243     writer->writeEndElement(); // dataReductionData
0244 
0245     // dataReduction results (generated columns)
0246     writer->writeStartElement(QStringLiteral("dataReductionResult"));
0247     writer->writeAttribute(QStringLiteral("available"), QString::number(d->dataReductionResult.available));
0248     writer->writeAttribute(QStringLiteral("valid"), QString::number(d->dataReductionResult.valid));
0249     writer->writeAttribute(QStringLiteral("status"), d->dataReductionResult.status);
0250     writer->writeAttribute(QStringLiteral("time"), QString::number(d->dataReductionResult.elapsedTime));
0251     writer->writeAttribute(QStringLiteral("npoints"), QString::number(d->dataReductionResult.npoints));
0252     writer->writeAttribute(QStringLiteral("posError"), QString::number(d->dataReductionResult.posError));
0253     writer->writeAttribute(QStringLiteral("areaError"), QString::number(d->dataReductionResult.areaError));
0254 
0255     // save calculated columns if available
0256     if (saveCalculations() && d->xColumn) {
0257         d->xColumn->save(writer);
0258         d->yColumn->save(writer);
0259     }
0260     writer->writeEndElement(); //"dataReductionResult"
0261 
0262     writer->writeEndElement(); //"xyDataReductionCurve"
0263 }
0264 
0265 //! Load from XML
0266 bool XYDataReductionCurve::load(XmlStreamReader* reader, bool preview) {
0267     Q_D(XYDataReductionCurve);
0268 
0269     QXmlStreamAttributes attribs;
0270     QString str;
0271 
0272     while (!reader->atEnd()) {
0273         reader->readNext();
0274         if (reader->isEndElement() && reader->name() == QLatin1String("xyDataReductionCurve"))
0275             break;
0276 
0277         if (!reader->isStartElement())
0278             continue;
0279 
0280         if (reader->name() == QLatin1String("xyAnalysisCurve")) {
0281             if (!XYAnalysisCurve::load(reader, preview))
0282                 return false;
0283         } else if (!preview && reader->name() == QLatin1String("dataReductionData")) {
0284             attribs = reader->attributes();
0285             READ_INT_VALUE("autoRange", dataReductionData.autoRange, bool);
0286             READ_DOUBLE_VALUE("xRangeMin", dataReductionData.xRange.first());
0287             READ_DOUBLE_VALUE("xRangeMax", dataReductionData.xRange.last());
0288             READ_INT_VALUE("type", dataReductionData.type, nsl_geom_linesim_type);
0289             READ_INT_VALUE("autoTolerance", dataReductionData.autoTolerance, int);
0290             READ_DOUBLE_VALUE("tolerance", dataReductionData.tolerance);
0291             READ_INT_VALUE("autoTolerance2", dataReductionData.autoTolerance2, int);
0292             READ_DOUBLE_VALUE("tolerance2", dataReductionData.tolerance2);
0293         } else if (!preview && reader->name() == QLatin1String("dataReductionResult")) {
0294             attribs = reader->attributes();
0295             READ_INT_VALUE("available", dataReductionResult.available, int);
0296             READ_INT_VALUE("valid", dataReductionResult.valid, int);
0297             READ_STRING_VALUE("status", dataReductionResult.status);
0298             READ_INT_VALUE("time", dataReductionResult.elapsedTime, int);
0299             READ_INT_VALUE("npoints", dataReductionResult.npoints, size_t);
0300             READ_DOUBLE_VALUE("posError", dataReductionResult.posError);
0301             READ_DOUBLE_VALUE("areaError", dataReductionResult.areaError);
0302         } else if (reader->name() == QLatin1String("column")) {
0303             Column* column = new Column(QString(), AbstractColumn::ColumnMode::Double);
0304             if (!column->load(reader, preview)) {
0305                 delete column;
0306                 return false;
0307             }
0308             if (column->name() == QLatin1String("x"))
0309                 d->xColumn = column;
0310             else if (column->name() == QLatin1String("y"))
0311                 d->yColumn = column;
0312         } else { // unknown element
0313             reader->raiseUnknownElementWarning();
0314             if (!reader->skipToEndElement())
0315                 return false;
0316         }
0317     }
0318 
0319     if (preview)
0320         return true;
0321 
0322     // wait for data to be read before using the pointers
0323     QThreadPool::globalInstance()->waitForDone();
0324 
0325     if (d->xColumn && d->yColumn) {
0326         d->xColumn->setHidden(true);
0327         addChild(d->xColumn);
0328 
0329         d->yColumn->setHidden(true);
0330         addChild(d->yColumn);
0331 
0332         d->xVector = static_cast<QVector<double>*>(d->xColumn->data());
0333         d->yVector = static_cast<QVector<double>*>(d->yColumn->data());
0334 
0335         static_cast<XYCurvePrivate*>(d_ptr)->xColumn = d->xColumn;
0336         static_cast<XYCurvePrivate*>(d_ptr)->yColumn = d->yColumn;
0337 
0338         recalcLogicalPoints();
0339     }
0340 
0341     return true;
0342 }