##// END OF EJS Templates
Adds params for fillDataHoles() method to handle data holes at the beginning/end of the data series
Alexandre Leroux -
r1024:fa3d932b9e7e
parent child
Show More
@@ -1,49 +1,80
1 1 #ifndef SCIQLOP_DATASERIESUTILS_H
2 2 #define SCIQLOP_DATASERIESUTILS_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <QLoggingCategory>
7 7
8 8 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSeriesUtils)
9 9
10 10 /**
11 11 * Utility class with methods for data series
12 12 */
13 13 struct SCIQLOP_CORE_EXPORT DataSeriesUtils {
14 14 /**
15 15 * Processes data from a data series to complete the data holes with a fill value.
16 16 *
17 17 * A data hole is determined by the resolution passed in parameter: if, between two continuous
18 18 * data on the x-axis, the difference between these data is greater than the resolution, then
19 19 * there is one or more holes between them. The holes are filled by adding:
20 20 * - for the x-axis, new data corresponding to the 'step resolution' starting from the first
21 21 * data;
22 22 * - for values, a default value (fill value) for each new data added on the x-axis.
23 23 *
24 24 * For example, with :
25 25 * - xAxisData = [0, 1, 5, 7, 14 ]
26 26 * - valuesData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (two components per x-axis data)
27 27 * - fillValue = NaN
28 28 * - and resolution = 2;
29 29 *
30 30 * For the x axis, we calculate as data holes: [3, 9, 11, 13]. These holes are added to the
31 31 * x-axis data, and NaNs (two per x-axis data) are added to the values:
32 32 * => xAxisData = [0, 1, 3, 5, 7, 9, 11, 13, 14 ]
33 33 * => valuesData = [0, 1, 2, 3, NaN, NaN, 4, 5, 6, 7, NaN, NaN, NaN, NaN, NaN, NaN, 8, 9]
34 34 *
35 * It is also possible to set bounds for the data series. If these bounds are defined and exceed
36 * the limits of the data series, data holes are added to the series at the beginning and/or the
37 * end.
38 *
39 * The generation of data holes at the beginning/end of the data series is performed starting
40 * from the x-axis series limit and adding data holes at each 'resolution step' as long as the
41 * new bound is not reached.
42 *
43 * For example, with :
44 * - xAxisData = [3, 4, 5, 6, 7 ]
45 * - valuesData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
46 * - fillValue = NaN
47 * - minBound = 0
48 * - maxBound = 12
49 * - and resolution = 2;
50 *
51 * => Starting from 3 and decreasing 2 by 2 until reaching 0 : a data hole at value 1 will be
52 * added to the beginning of the series
53 * => Starting from 7 and increasing 2 by 2 until reaching 12 : data holes at values 9 and 11
54 * will be added to the end of the series
55 *
56 * So :
57 * => xAxisData = [1, 3, 4, 5, 6, 7, 9, 11 ]
58 * => valuesData = [NaN, NaN, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NaN, NaN, NaN, NaN]
59 *
35 60 * @param xAxisData the x-axis data of the data series
36 61 * @param valuesData the values data of the data series
37 62 * @param resolution the resoultion (on x-axis) used to determinate data holes
38 63 * @param fillValue the fill value used for data holes in the values data
64 * @param minBound the limit at which to start filling data holes for the series. If set to NaN,
65 * the limit is not used
66 * @param maxBound the limit at which to end filling data holes for the series. If set to NaN,
67 * the limit is not used
39 68 *
40 69 * @remarks There is no control over the consistency between x-axis data and values data. The
41 70 * method considers that the data is well formed (the total number of values data is a multiple
42 71 * of the number of x-axis data)
43 72 */
44 73 static void fillDataHoles(std::vector<double> &xAxisData, std::vector<double> &valuesData,
45 74 double resolution,
46 double fillValue = std::numeric_limits<double>::quiet_NaN());
75 double fillValue = std::numeric_limits<double>::quiet_NaN(),
76 double minBound = std::numeric_limits<double>::quiet_NaN(),
77 double maxBound = std::numeric_limits<double>::quiet_NaN());
47 78 };
48 79
49 80 #endif // SCIQLOP_DATASERIESUTILS_H
@@ -1,59 +1,81
1 1 #include "Data/DataSeriesUtils.h"
2 2
3 3 Q_LOGGING_CATEGORY(LOG_DataSeriesUtils, "DataSeriesUtils")
4 4
5 5 void DataSeriesUtils::fillDataHoles(std::vector<double> &xAxisData, std::vector<double> &valuesData,
6 double resolution, double fillValue)
6 double resolution, double fillValue, double minBound,
7 double maxBound)
7 8 {
8 9 if (resolution == 0. || std::isnan(resolution)) {
9 10 qCWarning(LOG_DataSeriesUtils())
10 11 << "Can't fill data holes with a null resolution, no changes will be made";
11 12 return;
12 13 }
13 14
14 15 if (xAxisData.empty()) {
15 16 qCWarning(LOG_DataSeriesUtils())
16 17 << "Can't fill data holes for empty data, no changes will be made";
17 18 return;
18 19 }
19 20
20 21 // Gets the number of values per x-axis data
21 22 auto nbComponents = valuesData.size() / xAxisData.size();
22 23
23 24 // Generates fill values that will be used to complete values data
24 25 std::vector<double> fillValues(nbComponents, fillValue);
25 26
26 // Traverses the x-axis data
27 // Checks if there are data holes on the beginning of the data and generates the hole at the
28 // extremity if it's the case
29 auto minXAxisData = xAxisData.front();
30 if (!std::isnan(minBound) && minBound < minXAxisData) {
31 auto holeSize = static_cast<int>((minXAxisData - minBound) / resolution);
32 if (holeSize > 0) {
33 xAxisData.insert(xAxisData.begin(), minXAxisData - holeSize * resolution);
34 valuesData.insert(valuesData.begin(), fillValues.begin(), fillValues.end());
35 }
36 }
37
38 // Same for the end of the data
39 auto maxXAxisData = xAxisData.back();
40 if (!std::isnan(maxBound) && maxBound > maxXAxisData) {
41 auto holeSize = static_cast<int>((maxBound - maxXAxisData) / resolution);
42 if (holeSize > 0) {
43 xAxisData.insert(xAxisData.end(), maxXAxisData + holeSize * resolution);
44 valuesData.insert(valuesData.end(), fillValues.begin(), fillValues.end());
45 }
46 }
47
48 // Generates other data holes
27 49 auto xAxisIt = xAxisData.begin();
28 50 while (xAxisIt != xAxisData.end()) {
29 51 // Stops at first value which has a gap greater than resolution with the value next to it
30 52 xAxisIt = std::adjacent_find(
31 53 xAxisIt, xAxisData.end(),
32 54 [resolution](const auto &a, const auto &b) { return (b - a) > resolution; });
33 55
34 56 if (xAxisIt != xAxisData.end()) {
35 57 auto nextXAxisIt = xAxisIt + 1;
36 58
37 59 // Gets the values that has a gap greater than resolution between them
38 60 auto lowValue = *xAxisIt;
39 61 auto highValue = *nextXAxisIt;
40 62
41 63 // Completes holes between the two values by creating new values (according to the
42 64 // resolution)
43 65 for (auto i = lowValue + resolution; i < highValue; i += resolution) {
44 66 // Gets the iterator of values data from which to insert fill values
45 67 auto nextValuesIt = valuesData.begin()
46 68 + std::distance(xAxisData.begin(), nextXAxisIt) * nbComponents;
47 69
48 70 // New value is inserted before nextXAxisIt
49 71 nextXAxisIt = xAxisData.insert(nextXAxisIt, i) + 1;
50 72
51 73 // New values are inserted before nextValuesIt
52 74 valuesData.insert(nextValuesIt, fillValues.begin(), fillValues.end());
53 75 }
54 76
55 77 // Moves to the next value to continue loop on the x-axis data
56 78 xAxisIt = nextXAxisIt;
57 79 }
58 80 }
59 81 }
General Comments 0
You need to be logged in to leave comments. Login now