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