##// END OF EJS Templates
Merge branch 'feature/SpectroMeshes' into develop
Alexandre Leroux -
r999:bd6284620dee merge
parent child
Show More
@@ -0,0 +1,228
1 #ifndef SCIQLOP_DATASERIESUTILS_H
2 #define SCIQLOP_DATASERIESUTILS_H
3
4 #include "CoreGlobal.h"
5
6 #include <Data/DataSeriesIterator.h>
7
8 #include <cmath>
9 #include <QLoggingCategory>
10
11 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSeriesUtils)
12
13 /**
14 * Utility class with methods for data series
15 */
16 struct SCIQLOP_CORE_EXPORT DataSeriesUtils {
17 /**
18 * Define a meshs.
19 *
20 * A mesh is a regular grid representing cells of the same width (in x) and of the same height
21 * (in y). At each mesh point is associated a value.
22 *
23 * Each axis of the mesh is defined by a minimum value, a number of values is a mesh step.
24 * For example: if min = 1, nbValues = 5 and step = 2 => the axis of the mesh will be [1, 3, 5,
25 * 7, 9].
26 *
27 * The values are defined in an array of size {nbX * nbY}. The data is stored along the X axis.
28 *
29 * For example, the mesh:
30 * Y = 2 [ 7 ; 8 ; 9
31 * Y = 1 4 ; 5 ; 6
32 * Y = 0 1 ; 2 ; 3 ]
33 * X = 0 X = 1 X = 2
34 *
35 * will be represented by data [1, 2, 3, 4, 5, 6, 7, 8, 9]
36 */
37 struct Mesh {
38 explicit Mesh() = default;
39 explicit Mesh(int nbX, double xMin, double xStep, int nbY, double yMin, double yStep)
40 : m_NbX{nbX},
41 m_XMin{xMin},
42 m_XStep{xStep},
43 m_NbY{nbY},
44 m_YMin{yMin},
45 m_YStep{yStep},
46 m_Data(nbX * nbY)
47 {
48 }
49
50 inline bool isEmpty() const { return m_Data.size() == 0; }
51 inline double xMax() const { return m_XMin + (m_NbX - 1) * m_XStep; }
52 inline double yMax() const { return m_YMin + (m_NbY - 1) * m_YStep; }
53
54 int m_NbX{0};
55 double m_XMin{};
56 double m_XStep{};
57 int m_NbY{0};
58 double m_YMin{};
59 double m_YStep{};
60 std::vector<double> m_Data{};
61 };
62
63 /**
64 * Represents a resolution used to generate the data of a mesh on the x-axis or in Y.
65 *
66 * A resolution is represented by a value and flag indicating if it's in the logarithmic scale
67 * @sa Mesh
68 */
69 struct Resolution {
70 double m_Val{std::numeric_limits<double>::quiet_NaN()};
71 bool m_Logarithmic{false};
72 };
73
74 /**
75 * Processes data from a data series to complete the data holes with a fill value.
76 *
77 * A data hole is determined by the resolution passed in parameter: if, between two continuous
78 * data on the x-axis, the difference between these data is greater than the resolution, then
79 * there is one or more holes between them. The holes are filled by adding:
80 * - for the x-axis, new data corresponding to the 'step resolution' starting from the first
81 * data;
82 * - for values, a default value (fill value) for each new data added on the x-axis.
83 *
84 * For example, with :
85 * - xAxisData = [0, 1, 5, 7, 14 ]
86 * - valuesData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (two components per x-axis data)
87 * - fillValue = NaN
88 * - and resolution = 2;
89 *
90 * For the x axis, we calculate as data holes: [3, 9, 11, 13]. These holes are added to the
91 * x-axis data, and NaNs (two per x-axis data) are added to the values:
92 * => xAxisData = [0, 1, 3, 5, 7, 9, 11, 13, 14 ]
93 * => valuesData = [0, 1, 2, 3, NaN, NaN, 4, 5, 6, 7, NaN, NaN, NaN, NaN, NaN, NaN, 8, 9]
94 *
95 * It is also possible to set bounds for the data series. If these bounds are defined and exceed
96 * the limits of the data series, data holes are added to the series at the beginning and/or the
97 * end.
98 *
99 * The generation of data holes at the beginning/end of the data series is performed starting
100 * from the x-axis series limit and adding data holes at each 'resolution step' as long as the
101 * new bound is not reached.
102 *
103 * For example, with :
104 * - xAxisData = [3, 4, 5, 6, 7 ]
105 * - valuesData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
106 * - fillValue = NaN
107 * - minBound = 0
108 * - maxBound = 12
109 * - and resolution = 2;
110 *
111 * => Starting from 3 and decreasing 2 by 2 until reaching 0 : a data hole at value 1 will be
112 * added to the beginning of the series
113 * => Starting from 7 and increasing 2 by 2 until reaching 12 : data holes at values 9 and 11
114 * will be added to the end of the series
115 *
116 * So :
117 * => xAxisData = [1, 3, 4, 5, 6, 7, 9, 11 ]
118 * => valuesData = [NaN, NaN, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NaN, NaN, NaN, NaN]
119 *
120 * @param xAxisData the x-axis data of the data series
121 * @param valuesData the values data of the data series
122 * @param resolution the resoultion (on x-axis) used to determinate data holes
123 * @param fillValue the fill value used for data holes in the values data
124 * @param minBound the limit at which to start filling data holes for the series. If set to NaN,
125 * the limit is not used
126 * @param maxBound the limit at which to end filling data holes for the series. If set to NaN,
127 * the limit is not used
128 *
129 * @remarks There is no control over the consistency between x-axis data and values data. The
130 * method considers that the data is well formed (the total number of values data is a multiple
131 * of the number of x-axis data)
132 */
133 static void fillDataHoles(std::vector<double> &xAxisData, std::vector<double> &valuesData,
134 double resolution,
135 double fillValue = std::numeric_limits<double>::quiet_NaN(),
136 double minBound = std::numeric_limits<double>::quiet_NaN(),
137 double maxBound = std::numeric_limits<double>::quiet_NaN());
138 /**
139 * Computes the resolution of a dataset passed as a parameter.
140 *
141 * The resolution of a dataset is the minimum difference between two values that follow in the
142 * set.
143 * For example:
144 * - for the set [0, 2, 4, 8, 10, 11, 13] => the resolution is 1 (difference between 10 and 11).
145 *
146 * A resolution can be calculated on the logarithmic scale (base of 10). In this case, the
147 * dataset is first converted to logarithmic values.
148 * For example:
149 * - for the set [10, 100, 10000, 1000000], the values are converted to [1, 2, 4, 6] => the
150 * logarithmic resolution is 1 (difference between 1 and 2).
151 *
152 * @param begin the iterator pointing to the beginning of the dataset
153 * @param end the iterator pointing to the end of the dataset
154 * @param logarithmic computes a logarithmic resolution or not
155 * @return the resolution computed
156 * @warning the method considers the dataset as sorted and doesn't control it.
157 */
158 template <typename Iterator>
159 static Resolution resolution(Iterator begin, Iterator end, bool logarithmic = false);
160
161 /**
162 * Computes a regular mesh for a data series, according to resolutions for x-axis and y-axis
163 * passed as parameters.
164 *
165 * The mesh is created from the resolutions in x and y and the boundaries delimiting the data
166 * series. If the resolutions do not allow to obtain a regular mesh, they are recalculated.
167 *
168 * For example :
169 * Let x-axis data = [0, 1, 3, 5, 9], its associated values ​​= [0, 10, 30, 50, 90] and
170 * xResolution = 2.
171 * Based on the resolution, the mesh would be [0, 2, 4, 6, 8, 10] and would be invalid because
172 * it exceeds the maximum bound of the data. The resolution is thus recalculated so that the
173 * mesh holds between the data terminals.
174 * So => resolution is 1.8 and the mesh is [0, 1.8, 3.6, 5.4, 7.2, 9].
175 *
176 * Once the mesh is generated in x and y, the values ​​are associated with each mesh point,
177 * based on the data in the series, finding the existing data at which the mesh point would be
178 * or would be closest to, without exceeding it.
179 *
180 * In the example, we determine the value of each mesh point:
181 * - x = 0 => value = 0 (existing x in the data series)
182 * - x = 1.8 => value = 10 (the closest existing x: 1)
183 * - x = 3.6 => value = 30 (the closest existing x: 3)
184 * - x = 5.4 => value = 50 (the closest existing x: 5)
185 * - x = 7.2 => value = 50 (the closest existing x: 5)
186 * - x = 9 => value = 90 (existing x in the data series)
187 *
188 * Same algorithm is applied for y-axis.
189 *
190 * @param begin the iterator pointing to the beginning of the data series
191 * @param end the iterator pointing to the end of the data series
192 * @param xResolution the resolution expected for the mesh's x-axis
193 * @param yResolution the resolution expected for the mesh's y-axis
194 * @return the mesh created, an empty mesh if the input data do not allow to generate a regular
195 * mesh (empty data, null resolutions, logarithmic x-axis)
196 * @warning the method considers the dataset as sorted and doesn't control it.
197 */
198 static Mesh regularMesh(DataSeriesIterator begin, DataSeriesIterator end,
199 Resolution xResolution, Resolution yResolution);
200 };
201
202 template <typename Iterator>
203 DataSeriesUtils::Resolution DataSeriesUtils::resolution(Iterator begin, Iterator end,
204 bool logarithmic)
205 {
206 // Retrieves data into a work dataset
207 using ValueType = typename Iterator::value_type;
208 std::vector<ValueType> values{};
209 std::copy(begin, end, std::back_inserter(values));
210
211 // Converts data if logarithmic flag is activated
212 if (logarithmic) {
213 std::for_each(values.begin(), values.end(),
214 [logarithmic](auto &val) { val = std::log10(val); });
215 }
216
217 // Computes the differences between the values in the dataset
218 std::adjacent_difference(values.begin(), values.end(), values.begin());
219
220 // Retrieves the smallest difference
221 auto resolutionIt = std::min_element(values.begin(), values.end());
222 auto resolution
223 = resolutionIt != values.end() ? *resolutionIt : std::numeric_limits<double>::quiet_NaN();
224
225 return Resolution{resolution, logarithmic};
226 }
227
228 #endif // SCIQLOP_DATASERIESUTILS_H
@@ -0,0 +1,194
1 #include "Data/DataSeriesUtils.h"
2
3 Q_LOGGING_CATEGORY(LOG_DataSeriesUtils, "DataSeriesUtils")
4
5 void DataSeriesUtils::fillDataHoles(std::vector<double> &xAxisData, std::vector<double> &valuesData,
6 double resolution, double fillValue, double minBound,
7 double maxBound)
8 {
9 if (resolution == 0. || std::isnan(resolution)) {
10 qCWarning(LOG_DataSeriesUtils())
11 << "Can't fill data holes with a null resolution, no changes will be made";
12 return;
13 }
14
15 if (xAxisData.empty()) {
16 qCWarning(LOG_DataSeriesUtils())
17 << "Can't fill data holes for empty data, no changes will be made";
18 return;
19 }
20
21 // Gets the number of values per x-axis data
22 auto nbComponents = valuesData.size() / xAxisData.size();
23
24 // Generates fill values that will be used to complete values data
25 std::vector<double> fillValues(nbComponents, fillValue);
26
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
49 auto xAxisIt = xAxisData.begin();
50 while (xAxisIt != xAxisData.end()) {
51 // Stops at first value which has a gap greater than resolution with the value next to it
52 xAxisIt = std::adjacent_find(
53 xAxisIt, xAxisData.end(),
54 [resolution](const auto &a, const auto &b) { return (b - a) > resolution; });
55
56 if (xAxisIt != xAxisData.end()) {
57 auto nextXAxisIt = xAxisIt + 1;
58
59 // Gets the values that has a gap greater than resolution between them
60 auto lowValue = *xAxisIt;
61 auto highValue = *nextXAxisIt;
62
63 // Completes holes between the two values by creating new values (according to the
64 // resolution)
65 for (auto i = lowValue + resolution; i < highValue; i += resolution) {
66 // Gets the iterator of values data from which to insert fill values
67 auto nextValuesIt = valuesData.begin()
68 + std::distance(xAxisData.begin(), nextXAxisIt) * nbComponents;
69
70 // New value is inserted before nextXAxisIt
71 nextXAxisIt = xAxisData.insert(nextXAxisIt, i) + 1;
72
73 // New values are inserted before nextValuesIt
74 valuesData.insert(nextValuesIt, fillValues.begin(), fillValues.end());
75 }
76
77 // Moves to the next value to continue loop on the x-axis data
78 xAxisIt = nextXAxisIt;
79 }
80 }
81 }
82
83 namespace {
84
85 /**
86 * Generates axis's mesh properties according to data and resolution
87 * @param begin the iterator pointing to the beginning of the data
88 * @param end the iterator pointing to the end of the data
89 * @param fun the function to retrieve data from the data iterators
90 * @param resolution the resolution to use for the axis' mesh
91 * @return a tuple representing the mesh properties : <nb values, min value, value step>
92 */
93 template <typename Iterator, typename IteratorFun>
94 std::tuple<int, double, double> meshProperties(Iterator begin, Iterator end, IteratorFun fun,
95 double resolution)
96 {
97 // Computes the gap between min and max data. This will be used to determinate the step between
98 // each data of the mesh
99 auto min = fun(begin);
100 auto max = fun(end - 1);
101 auto gap = max - min;
102
103 // Computes the step trying to use the fixed resolution. If the resolution doesn't separate the
104 // values evenly , it is recalculated.
105 // For example, for a resolution of 2.0:
106 // - for interval [0; 8] => resolution is valid, the generated mesh will be [0, 2, 4, 6, 8]
107 // - for interval [0; 9] => it's impossible to create a regular mesh with this resolution
108 // The resolution is recalculated and is worth 1.8. The generated mesh will be [0, 1.8, 3.6,
109 // 5.4, 7.2, 9]
110 auto nbVal = static_cast<int>(std::ceil(gap / resolution));
111 auto step = gap / nbVal;
112
113 // last data is included in the total number of values
114 return std::make_tuple(nbVal + 1, min, step);
115 }
116
117 } // namespace
118
119 DataSeriesUtils::Mesh DataSeriesUtils::regularMesh(DataSeriesIterator begin, DataSeriesIterator end,
120 Resolution xResolution, Resolution yResolution)
121 {
122 // Checks preconditions
123 if (xResolution.m_Val == 0. || std::isnan(xResolution.m_Val) || yResolution.m_Val == 0.
124 || std::isnan(yResolution.m_Val)) {
125 qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh with a null resolution";
126 return Mesh{};
127 }
128
129 if (xResolution.m_Logarithmic) {
130 qCWarning(LOG_DataSeriesUtils())
131 << "Can't generate mesh with a logarithmic x-axis resolution";
132 return Mesh{};
133 }
134
135 if (std::distance(begin, end) == 0) {
136 qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh for empty data";
137 return Mesh{};
138 }
139
140 auto yData = begin->y();
141 if (yData.empty()) {
142 qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh for data with no y-axis";
143 return Mesh{};
144 }
145
146 // Converts y-axis and its resolution to logarithmic values
147 if (yResolution.m_Logarithmic) {
148 std::for_each(yData.begin(), yData.end(), [](auto &val) { val = std::log10(val); });
149 }
150
151 // Computes mesh properties
152 int nbX, nbY;
153 double xMin, xStep, yMin, yStep;
154 std::tie(nbX, xMin, xStep)
155 = meshProperties(begin, end, [](const auto &it) { return it->x(); }, xResolution.m_Val);
156 std::tie(nbY, yMin, yStep) = meshProperties(
157 yData.begin(), yData.end(), [](const auto &it) { return *it; }, yResolution.m_Val);
158
159 // Generates mesh according to the x-axis and y-axis steps
160 Mesh result{nbX, xMin, xStep, nbY, yMin, yStep};
161
162 for (auto meshXIndex = 0; meshXIndex < nbX; ++meshXIndex) {
163 auto meshX = xMin + meshXIndex * xStep;
164 // According to current x-axis of the mesh, finds in the data series the interval in which
165 // the data is or gets closer (without exceeding it).
166 // An interval is defined by a value and extends to +/- 50% of the resolution. For example,
167 // for a value of 3 and a resolution of 1, the associated interval is [2.5, 3.5].
168 auto xIt = std::lower_bound(begin, end, meshX,
169 [xResolution](const auto &it, const auto &val) {
170 return it.x() - xResolution.m_Val / 2. < val;
171 })
172 - 1;
173
174 // When the corresponding entry of the data series is found, generates the values of the
175 // mesh by retrieving the values of the entry, for each y-axis value of the mesh
176 auto values = xIt->values();
177
178 for (auto meshYIndex = 0; meshYIndex < nbY; ++meshYIndex) {
179 auto meshY = yMin + meshYIndex * yStep;
180
181 auto yBegin = yData.begin();
182 auto yIt = std::lower_bound(yBegin, yData.end(), meshY,
183 [yResolution](const auto &it, const auto &val) {
184 return it - yResolution.m_Val / 2. < val;
185 })
186 - 1;
187
188 auto valueIndex = std::distance(yBegin, yIt);
189 result.m_Data[result.m_NbX * meshYIndex + meshXIndex] = values.at(valueIndex);
190 }
191 }
192
193 return result;
194 }
@@ -0,0 +1,108
1 #include <Data/DataSeriesUtils.h>
2
3 #include <QObject>
4 #include <QtTest>
5
6 class TestDataSeriesUtils : public QObject {
7 Q_OBJECT
8
9 private slots:
10 /// Tests @sa DataSeriesUtils::fillDataHoles() method
11 void testFillDataHoles_data();
12 void testFillDataHoles();
13 };
14
15 void TestDataSeriesUtils::testFillDataHoles_data()
16 {
17 QTest::addColumn<std::vector<double> >("xAxisData");
18 QTest::addColumn<std::vector<double> >("valuesData");
19 QTest::addColumn<double>("resolution");
20 QTest::addColumn<double>("fillValue");
21 QTest::addColumn<double>("minBound");
22 QTest::addColumn<double>("maxBound");
23 QTest::addColumn<std::vector<double> >(
24 "expectedXAxisData"); // expected x-axis data after filling holes
25 QTest::addColumn<std::vector<double> >(
26 "expectedValuesData"); // expected values data after filling holes
27
28 auto nan = std::numeric_limits<double>::quiet_NaN();
29
30 QTest::newRow("fillDataHoles (basic case)")
31 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 2.
32 << nan << nan << nan << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
33 << std::vector<double>{0., 1., nan, 2., 3., nan, nan, nan, 4.};
34
35 QTest::newRow("fillDataHoles (change nb components)")
36 << std::vector<double>{0., 1., 5., 7., 14.}
37 << std::vector<double>{0., 1., 2., 3., 4., 5., 6., 7., 8., 9.} << 2. << nan << nan << nan
38 << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
39 << std::vector<double>{0., 1., 2., 3., nan, nan, 4., 5., 6.,
40 7., nan, nan, nan, nan, nan, nan, 8., 9.};
41
42 QTest::newRow("fillDataHoles (change resolution)")
43 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.}
44 << 1.5 << nan << nan << nan
45 << std::vector<double>{0., 1., 2.5, 4., 5., 6.5, 7., 8.5, 10., 11.5, 13., 14.}
46 << std::vector<double>{0., 1., nan, nan, 2., nan, 3., nan, nan, nan, nan, 4.};
47
48 QTest::newRow("fillDataHoles (with no data (no changes made))")
49 << std::vector<double>{} << std::vector<double>{} << 2. << nan << nan << nan
50 << std::vector<double>{} << std::vector<double>{};
51
52 QTest::newRow("fillDataHoles (with no resolution (no changes made))")
53 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 0.
54 << nan << nan << nan << std::vector<double>{0., 1., 5., 7., 14.}
55 << std::vector<double>{0., 1., 2., 3., 4.};
56
57 QTest::newRow("fillDataHoles (change fill value)")
58 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 2.
59 << -1. << nan << nan << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
60 << std::vector<double>{0., 1., -1., 2., 3., -1., -1., -1., 4.};
61
62 QTest::newRow("fillDataHoles (add data holes to the beginning)")
63 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
64 << 2. << nan << 0. << nan << std::vector<double>{1., 3., 5., 7., 9., 11., 13.}
65 << std::vector<double>{nan, nan, 0., 1., 2., 3., 4.};
66
67 QTest::newRow("fillDataHoles (add data holes to the end)")
68 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
69 << 2. << nan << nan << 21. << std::vector<double>{5., 7., 9., 11., 13., 15., 17., 19., 21.}
70 << std::vector<double>{0., 1., 2., 3., 4., nan, nan, nan, nan};
71
72 QTest::newRow("fillDataHoles (invalid min/max bounds (no changes made))")
73 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
74 << 2. << nan << 8. << 13. << std::vector<double>{5., 7., 9., 11., 13.}
75 << std::vector<double>{0., 1., 2., 3., 4.};
76 }
77
78 void TestDataSeriesUtils::testFillDataHoles()
79 {
80 QFETCH(std::vector<double>, xAxisData);
81 QFETCH(std::vector<double>, valuesData);
82 QFETCH(double, resolution);
83 QFETCH(double, fillValue);
84 QFETCH(double, minBound);
85 QFETCH(double, maxBound);
86
87 QFETCH(std::vector<double>, expectedXAxisData);
88 QFETCH(std::vector<double>, expectedValuesData);
89
90 // Executes method (xAxisData and valuesData are modified)
91 DataSeriesUtils::fillDataHoles(xAxisData, valuesData, resolution, fillValue, minBound,
92 maxBound);
93
94 // Checks results
95 auto equal = [](const auto &data, const auto &expectedData) {
96 // Compares with NaN values
97 return std::equal(data.begin(), data.end(), expectedData.begin(), expectedData.end(),
98 [](const auto &val, const auto &expectedVal) {
99 return (std::isnan(val) && std::isnan(expectedVal))
100 || val == expectedVal;
101 });
102 };
103 QVERIFY(equal(xAxisData, expectedXAxisData));
104 QVERIFY(equal(valuesData, expectedValuesData));
105 }
106
107 QTEST_MAIN(TestDataSeriesUtils)
108 #include "TestDataSeriesUtils.moc"
@@ -0,0 +1,62
1 # -----------
2 # AMDA INFO :
3 # -----------
4 # AMDA_ABOUT : Created by CDPP/AMDA(c)
5 # AMDA_VERSION : 3.5.0
6 # AMDA_ACKNOWLEDGEMENT : CDPP/AMDA Team
7 #
8 # --------------
9 # REQUEST INFO :
10 # --------------
11 # REQUEST_STRUCTURE : one-file-per-parameter-per-interval
12 # REQUEST_TIME_FORMAT : ISO 8601
13 # REQUEST_OUTPUT_PARAMS : tha_ion_sp
14 #
15 # -----------------
16 # BASE PARAMETERS :
17 # -----------------
18 #
19 # MISSION_ID : NONE
20 #
21 # INSTRUMENT_ID : NONE
22 #
23 # DATASET_ID : tha-esa-l2i
24 # DATASET_NAME : ion full mode
25 # DATASET_SOURCE : CDPP/DDServer
26 # DATASET_GLOBAL_START : 2007-03-07T18:53:59.134
27 # DATASET_GLOBAL_STOP : 2017-11-04T18:35:25.907
28 # DATASET_MIN_SAMPLING : 96
29 # DATASET_MAX_SAMPLING : 180
30 #
31 # PARAMETER_ID : tha_ion_sp
32 # PARAMETER_NAME : tha_ion_sp
33 # PARAMETER_SHORT_NAME : spectra
34 # PARAMETER_UNITS : eV/(cm^2-s-sr-eV)
35 # PARAMETER_TENSOR_ORDER : 0
36 # PARAMETER_TABLE[0] : energy
37 # PARAMETER_TABLE_UNITS[0] : eV
38 # PARAMETER_TABLE_MIN_VALUES[0] : 14234.4,18737.3
39 # PARAMETER_TABLE_MAX_VALUES[0] : 18737.3,23254.9
40 # PARAMETER_FILL_VALUE : nan
41 # PARAMETER_UCD : phys.flux.density;phys.energy;phys.atmol.ionStage
42 #
43 #
44 # ---------------
45 # INTERVAL INFO :
46 # ---------------
47 # INTERVAL_START : 2011-12-10T12:10:00.000
48 # INTERVAL_STOP : 2011-12-10T12:40:00.000
49 #
50 # ------
51 # DATA :
52 # ------
53 # DATA_COLUMNS : AMDA_TIME, tha_ion_sp[0], tha_ion_sp[1]
54 #
55 2011-12-10T12:10:54.000 2577578.000 2336016.000
56 2011-12-10T12:17:23.000 2314121.500 1712093.125
57 2011-12-10T12:23:51.000 2063608.750 1614491.625
58 2011-12-10T12:30:19.000 2234525.500 1764516.500
59 2011-12-10T12:35:04.000 1670215.250 1688078.500
60 2011-12-10T12:36:41.000 1689243.250 1743183.500
61 2011-12-10T12:38:18.000 1654617.125 1733603.250
62 2011-12-10T12:39:55.000 1504983.750 1708356.500 No newline at end of file
@@ -0,0 +1,62
1 # -----------
2 # AMDA INFO :
3 # -----------
4 # AMDA_ABOUT : Created by CDPP/AMDA(c)
5 # AMDA_VERSION : 3.5.0
6 # AMDA_ACKNOWLEDGEMENT : CDPP/AMDA Team
7 #
8 # --------------
9 # REQUEST INFO :
10 # --------------
11 # REQUEST_STRUCTURE : one-file-per-parameter-per-interval
12 # REQUEST_TIME_FORMAT : ISO 8601
13 # REQUEST_OUTPUT_PARAMS : tha_ion_sp
14 #
15 # -----------------
16 # BASE PARAMETERS :
17 # -----------------
18 #
19 # MISSION_ID : NONE
20 #
21 # INSTRUMENT_ID : NONE
22 #
23 # DATASET_ID : tha-esa-l2i
24 # DATASET_NAME : ion full mode
25 # DATASET_SOURCE : CDPP/DDServer
26 # DATASET_GLOBAL_START : 2007-03-07T18:53:59.134
27 # DATASET_GLOBAL_STOP : 2017-11-04T18:35:25.907
28 # DATASET_MIN_SAMPLING : 96
29 # DATASET_MAX_SAMPLING : 240
30 #
31 # PARAMETER_ID : tha_ion_sp
32 # PARAMETER_NAME : tha_ion_sp
33 # PARAMETER_SHORT_NAME : spectra
34 # PARAMETER_UNITS : eV/(cm^2-s-sr-eV)
35 # PARAMETER_TENSOR_ORDER : 0
36 # PARAMETER_TABLE[0] : energy
37 # PARAMETER_TABLE_UNITS[0] : eV
38 # PARAMETER_TABLE_MIN_VALUES[0] : 14234.4,18737.3
39 # PARAMETER_TABLE_MAX_VALUES[0] : 18737.3,23254.9
40 # PARAMETER_FILL_VALUE : nan
41 # PARAMETER_UCD : phys.flux.density;phys.energy;phys.atmol.ionStage
42 #
43 #
44 # ---------------
45 # INTERVAL INFO :
46 # ---------------
47 # INTERVAL_START : 2011-12-10T12:00:00.000
48 # INTERVAL_STOP : 2011-12-10T13:00:00.000
49 #
50 # ------
51 # DATA :
52 # ------
53 # DATA_COLUMNS : AMDA_TIME, tha_ion_sp[0], tha_ion_sp[1]
54 #
55 2011-12-10T12:10:54.000 2577578.000 2336016.000
56 2011-12-10T12:17:23.000 2314121.500 1712093.125
57 2011-12-10T12:23:51.000 2063608.750 1614491.625
58 2011-12-10T12:30:19.000 2234525.500 1764516.500
59 2011-12-10T12:35:04.000 1670215.250 1688078.500
60 2011-12-10T12:36:41.000 1689243.250 1743183.500
61 2011-12-10T12:38:18.000 1654617.125 1733603.250
62 2011-12-10T12:39:55.000 1504983.750 1708356.500 No newline at end of file
@@ -1,483 +1,507
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11 #include <Data/OptionalAxis.h>
12
12
13 #include <QLoggingCategory>
13 #include <QLoggingCategory>
14 #include <QReadLocker>
14 #include <QReadLocker>
15 #include <QReadWriteLock>
15 #include <QReadWriteLock>
16 #include <memory>
16 #include <memory>
17
17
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
19 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
20 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
21 {
21 {
22 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
23 return category;
23 return category;
24 }
24 }
25
25
26 template <int Dim>
26 template <int Dim>
27 class DataSeries;
27 class DataSeries;
28
28
29 namespace dataseries_detail {
29 namespace dataseries_detail {
30
30
31 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
33 public:
33 public:
34 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
35
35
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end()),
40 m_YItBegin{dataSeries.yAxis().begin()},
41 m_YItEnd{dataSeries.yAxis().end()}
40 {
42 {
41 }
43 }
42
44
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
45 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
46 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
47 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
48 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
47 : dataSeries.valuesData()->cend())
49 : dataSeries.valuesData()->cend()),
50 m_YItBegin{dataSeries.yAxis().cbegin()},
51 m_YItEnd{dataSeries.yAxis().cend()}
48 {
52 {
49 }
53 }
50
54
51 IteratorValue(const IteratorValue &other) = default;
55 IteratorValue(const IteratorValue &other) = default;
52
56
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
57 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
54 {
58 {
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
59 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
56 }
60 }
57
61
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
62 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
63 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
60 return m_XIt->distance(*otherImpl.m_XIt);
64 return m_XIt->distance(*otherImpl.m_XIt);
61 }
65 }
62 catch (const std::bad_cast &) {
66 catch (const std::bad_cast &) {
63 return 0;
67 return 0;
64 }
68 }
65
69
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
70 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
71 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
72 return std::tie(m_XIt, m_ValuesIt, m_YItBegin, m_YItEnd)
73 == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt, otherImpl.m_YItBegin,
74 otherImpl.m_YItEnd);
69 }
75 }
70 catch (const std::bad_cast &) {
76 catch (const std::bad_cast &) {
71 return false;
77 return false;
72 }
78 }
73
79
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
80 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
81 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
82 return m_XIt->lowerThan(*otherImpl.m_XIt);
77 }
83 }
78 catch (const std::bad_cast &) {
84 catch (const std::bad_cast &) {
79 return false;
85 return false;
80 }
86 }
81
87
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
88 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
83 {
89 {
84 auto result = clone();
90 auto result = clone();
85 result->next(offset);
91 result->next(offset);
86 return result;
92 return result;
87 }
93 }
88
94
89 void next(int offset) override
95 void next(int offset) override
90 {
96 {
91 m_XIt->next(offset);
97 m_XIt->next(offset);
92 m_ValuesIt->next(offset);
98 m_ValuesIt->next(offset);
93 }
99 }
94
100
95 void prev() override
101 void prev() override
96 {
102 {
97 --m_XIt;
103 --m_XIt;
98 --m_ValuesIt;
104 --m_ValuesIt;
99 }
105 }
100
106
101 double x() const override { return m_XIt->at(0); }
107 double x() const override { return m_XIt->at(0); }
108 std::vector<double> y() const override
109 {
110 std::vector<double> result{};
111 std::transform(m_YItBegin, m_YItEnd, std::back_inserter(result),
112 [](const auto &it) { return it.first(); });
113
114 return result;
115 }
116
102 double value() const override { return m_ValuesIt->at(0); }
117 double value() const override { return m_ValuesIt->at(0); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
118 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
104 double minValue() const override { return m_ValuesIt->min(); }
119 double minValue() const override { return m_ValuesIt->min(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
120 double maxValue() const override { return m_ValuesIt->max(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
121 QVector<double> values() const override { return m_ValuesIt->values(); }
107
122
108 void swap(DataSeriesIteratorValue::Impl &other) override
123 void swap(DataSeriesIteratorValue::Impl &other) override
109 {
124 {
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
125 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
126 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
127 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
128 m_YItBegin->impl()->swap(*otherImpl.m_YItBegin->impl());
129 m_YItEnd->impl()->swap(*otherImpl.m_YItEnd->impl());
113 }
130 }
114
131
115 private:
132 private:
116 ArrayDataIterator m_XIt;
133 ArrayDataIterator m_XIt;
117 ArrayDataIterator m_ValuesIt;
134 ArrayDataIterator m_ValuesIt;
135 ArrayDataIterator m_YItBegin;
136 ArrayDataIterator m_YItEnd;
118 };
137 };
119 } // namespace dataseries_detail
138 } // namespace dataseries_detail
120
139
121 /**
140 /**
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
141 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
123 *
142 *
124 * The DataSeries represents values on one or two axes, according to these rules:
143 * The DataSeries represents values on one or two axes, according to these rules:
125 * - the x-axis is always defined
144 * - the x-axis is always defined
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
145 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
127 * below)
146 * below)
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
147 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
129 * distributed into components (for example, a vector defines three components)
148 * distributed into components (for example, a vector defines three components)
130 * - New values can be added to the series, on the x-axis.
149 * - New values can be added to the series, on the x-axis.
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
150 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
132 * - Data representing values and axes are associated with a unit
151 * - Data representing values and axes are associated with a unit
133 * - The data series is always sorted in ascending order on the x-axis.
152 * - The data series is always sorted in ascending order on the x-axis.
134 *
153 *
135 * Consistency checks are carried out between the axes and the values. These controls are provided
154 * Consistency checks are carried out between the axes and the values. These controls are provided
136 * throughout the DataSeries lifecycle:
155 * throughout the DataSeries lifecycle:
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
156 * - the number of data on the x-axis must be equal to the number of values (in the case of
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
157 * 2-dim ArrayData for values, the test is performed on the number of values per component)
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
158 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
140 * number of data on the y-axis.
159 * number of data on the y-axis.
141 *
160 *
142 * Examples:
161 * Examples:
143 * 1)
162 * 1)
144 * - x-axis: [1 ; 2 ; 3]
163 * - x-axis: [1 ; 2 ; 3]
145 * - y-axis: not defined
164 * - y-axis: not defined
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
165 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
147 * => the DataSeries is valid, as x-axis and values have the same number of data
166 * => the DataSeries is valid, as x-axis and values have the same number of data
148 *
167 *
149 * 2)
168 * 2)
150 * - x-axis: [1 ; 2 ; 3]
169 * - x-axis: [1 ; 2 ; 3]
151 * - y-axis: not defined
170 * - y-axis: not defined
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
171 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
172 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
154 *
173 *
155 * 3)
174 * 3)
156 * - x-axis: [1 ; 2 ; 3]
175 * - x-axis: [1 ; 2 ; 3]
157 * - y-axis: not defined
176 * - y-axis: not defined
158 * - values: [10 ; 20 ; 30
177 * - values: [10 ; 20 ; 30
159 * 40 ; 50 ; 60] (2-dim ArrayData)
178 * 40 ; 50 ; 60] (2-dim ArrayData)
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
179 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
161 * data each
180 * data each
162 *
181 *
163 * 4)
182 * 4)
164 * - x-axis: [1 ; 2 ; 3]
183 * - x-axis: [1 ; 2 ; 3]
165 * - y-axis: [1 ; 2]
184 * - y-axis: [1 ; 2]
166 * - values: [10 ; 20 ; 30
185 * - values: [10 ; 20 ; 30
167 * 40 ; 50 ; 60] (2-dim ArrayData)
186 * 40 ; 50 ; 60] (2-dim ArrayData)
168 * => the DataSeries is valid, as:
187 * => the DataSeries is valid, as:
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
188 * - x-axis has 3 data and values contains 2 components with 3 data each AND
170 * - y-axis has 2 data and values contains 2 components
189 * - y-axis has 2 data and values contains 2 components
171 *
190 *
172 * 5)
191 * 5)
173 * - x-axis: [1 ; 2 ; 3]
192 * - x-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
193 * - y-axis: [1 ; 2 ; 3]
175 * - values: [10 ; 20 ; 30
194 * - values: [10 ; 20 ; 30
176 * 40 ; 50 ; 60] (2-dim ArrayData)
195 * 40 ; 50 ; 60] (2-dim ArrayData)
177 * => the DataSeries is invalid, as:
196 * => the DataSeries is invalid, as:
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
197 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
179 * - y-axis has 3 data and values contains only 2 components
198 * - y-axis has 3 data and values contains only 2 components
180 *
199 *
181 * @tparam Dim The dimension of the values data
200 * @tparam Dim The dimension of the values data
182 *
201 *
183 */
202 */
184 template <int Dim>
203 template <int Dim>
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
204 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
186 friend class DataSeriesMergeHelper;
205 friend class DataSeriesMergeHelper;
187
206
188 public:
207 public:
189 /// @sa IDataSeries::xAxisData()
208 /// @sa IDataSeries::xAxisData()
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
209 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
210 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
192
211
193 /// @sa IDataSeries::xAxisUnit()
212 /// @sa IDataSeries::xAxisUnit()
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
213 Unit xAxisUnit() const override { return m_XAxisUnit; }
195
214
215 /// @sa IDataSeries::yAxisUnit()
216 Unit yAxisUnit() const override { return m_YAxis.unit(); }
217
196 /// @return the values dataset
218 /// @return the values dataset
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
219 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
220 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
199
221
200 /// @sa IDataSeries::valuesUnit()
222 /// @sa IDataSeries::valuesUnit()
201 Unit valuesUnit() const override { return m_ValuesUnit; }
223 Unit valuesUnit() const override { return m_ValuesUnit; }
202
224
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
225 int nbPoints() const override { return m_ValuesData->totalSize(); }
204
226
227 std::pair<double, double> yBounds() const override { return m_YAxis.bounds(); }
228
205 void clear()
229 void clear()
206 {
230 {
207 m_XAxisData->clear();
231 m_XAxisData->clear();
208 m_ValuesData->clear();
232 m_ValuesData->clear();
209 }
233 }
210
234
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
235 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
212
236
213 /// Merges into the data series an other data series.
237 /// Merges into the data series an other data series.
214 ///
238 ///
215 /// The two dataseries:
239 /// The two dataseries:
216 /// - must be of the same dimension
240 /// - must be of the same dimension
217 /// - must have the same y-axis (if defined)
241 /// - must have the same y-axis (if defined)
218 ///
242 ///
219 /// If the prerequisites are not valid, the method does nothing
243 /// If the prerequisites are not valid, the method does nothing
220 ///
244 ///
221 /// @remarks the data series to merge with is cleared after the operation
245 /// @remarks the data series to merge with is cleared after the operation
222 void merge(IDataSeries *dataSeries) override
246 void merge(IDataSeries *dataSeries) override
223 {
247 {
224 dataSeries->lockWrite();
248 dataSeries->lockWrite();
225 lockWrite();
249 lockWrite();
226
250
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
251 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
228 if (m_YAxis == other->m_YAxis) {
252 if (m_YAxis == other->m_YAxis) {
229 DataSeriesMergeHelper::merge(*other, *this);
253 DataSeriesMergeHelper::merge(*other, *this);
230 }
254 }
231 else {
255 else {
232 qCWarning(LOG_DataSeries())
256 qCWarning(LOG_DataSeries())
233 << QObject::tr("Can't merge data series that have not the same y-axis");
257 << QObject::tr("Can't merge data series that have not the same y-axis");
234 }
258 }
235 }
259 }
236 else {
260 else {
237 qCWarning(LOG_DataSeries())
261 qCWarning(LOG_DataSeries())
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
262 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
239 }
263 }
240 unlock();
264 unlock();
241 dataSeries->unlock();
265 dataSeries->unlock();
242 }
266 }
243
267
244 void purge(double min, double max) override
268 void purge(double min, double max) override
245 {
269 {
246 // Nothing to purge if series is empty
270 // Nothing to purge if series is empty
247 if (isEmpty()) {
271 if (isEmpty()) {
248 return;
272 return;
249 }
273 }
250
274
251 if (min > max) {
275 if (min > max) {
252 std::swap(min, max);
276 std::swap(min, max);
253 }
277 }
254
278
255 // Nothing to purge if series min/max are inside purge range
279 // Nothing to purge if series min/max are inside purge range
256 auto xMin = cbegin()->x();
280 auto xMin = cbegin()->x();
257 auto xMax = (--cend())->x();
281 auto xMax = (--cend())->x();
258 if (xMin >= min && xMax <= max) {
282 if (xMin >= min && xMax <= max) {
259 return;
283 return;
260 }
284 }
261
285
262 auto lowerIt = std::lower_bound(
286 auto lowerIt = std::lower_bound(
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
287 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
264 erase(begin(), lowerIt);
288 erase(begin(), lowerIt);
265 auto upperIt = std::upper_bound(
289 auto upperIt = std::upper_bound(
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
290 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
267 erase(upperIt, end());
291 erase(upperIt, end());
268 }
292 }
269
293
270 // ///////// //
294 // ///////// //
271 // Iterators //
295 // Iterators //
272 // ///////// //
296 // ///////// //
273
297
274 DataSeriesIterator begin() override
298 DataSeriesIterator begin() override
275 {
299 {
276 return DataSeriesIterator{DataSeriesIteratorValue{
300 return DataSeriesIterator{DataSeriesIteratorValue{
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
301 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
278 }
302 }
279
303
280 DataSeriesIterator end() override
304 DataSeriesIterator end() override
281 {
305 {
282 return DataSeriesIterator{DataSeriesIteratorValue{
306 return DataSeriesIterator{DataSeriesIteratorValue{
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
307 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
284 }
308 }
285
309
286 DataSeriesIterator cbegin() const override
310 DataSeriesIterator cbegin() const override
287 {
311 {
288 return DataSeriesIterator{DataSeriesIteratorValue{
312 return DataSeriesIterator{DataSeriesIteratorValue{
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
313 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
290 }
314 }
291
315
292 DataSeriesIterator cend() const override
316 DataSeriesIterator cend() const override
293 {
317 {
294 return DataSeriesIterator{DataSeriesIteratorValue{
318 return DataSeriesIterator{DataSeriesIteratorValue{
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
319 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
296 }
320 }
297
321
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
322 void erase(DataSeriesIterator first, DataSeriesIterator last)
299 {
323 {
300 auto firstImpl
324 auto firstImpl
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
325 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
326 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
303
327
304 if (firstImpl && lastImpl) {
328 if (firstImpl && lastImpl) {
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
329 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
330 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
307 }
331 }
308 }
332 }
309
333
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
334 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
311 {
335 {
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
336 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
337 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
314
338
315 if (firstImpl && lastImpl) {
339 if (firstImpl && lastImpl) {
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
340 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
341 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
318 }
342 }
319 }
343 }
320
344
321 /// @sa IDataSeries::minXAxisData()
345 /// @sa IDataSeries::minXAxisData()
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
346 DataSeriesIterator minXAxisData(double minXAxisData) const override
323 {
347 {
324 return std::lower_bound(
348 return std::lower_bound(
325 cbegin(), cend(), minXAxisData,
349 cbegin(), cend(), minXAxisData,
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
350 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
327 }
351 }
328
352
329 /// @sa IDataSeries::maxXAxisData()
353 /// @sa IDataSeries::maxXAxisData()
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
354 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
331 {
355 {
332 // Gets the first element that greater than max value
356 // Gets the first element that greater than max value
333 auto it = std::upper_bound(
357 auto it = std::upper_bound(
334 cbegin(), cend(), maxXAxisData,
358 cbegin(), cend(), maxXAxisData,
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
359 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
336
360
337 return it == cbegin() ? cend() : --it;
361 return it == cbegin() ? cend() : --it;
338 }
362 }
339
363
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
364 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
341 double maxXAxisData) const override
365 double maxXAxisData) const override
342 {
366 {
343 if (minXAxisData > maxXAxisData) {
367 if (minXAxisData > maxXAxisData) {
344 std::swap(minXAxisData, maxXAxisData);
368 std::swap(minXAxisData, maxXAxisData);
345 }
369 }
346
370
347 auto begin = cbegin();
371 auto begin = cbegin();
348 auto end = cend();
372 auto end = cend();
349
373
350 auto lowerIt = std::lower_bound(
374 auto lowerIt = std::lower_bound(
351 begin, end, minXAxisData,
375 begin, end, minXAxisData,
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
376 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
353 auto upperIt = std::upper_bound(
377 auto upperIt = std::upper_bound(
354 lowerIt, end, maxXAxisData,
378 lowerIt, end, maxXAxisData,
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
379 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
356
380
357 return std::make_pair(lowerIt, upperIt);
381 return std::make_pair(lowerIt, upperIt);
358 }
382 }
359
383
360 std::pair<DataSeriesIterator, DataSeriesIterator>
384 std::pair<DataSeriesIterator, DataSeriesIterator>
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
385 valuesBounds(double minXAxisData, double maxXAxisData) const override
362 {
386 {
363 // Places iterators to the correct x-axis range
387 // Places iterators to the correct x-axis range
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
388 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
365
389
366 // Returns end iterators if the range is empty
390 // Returns end iterators if the range is empty
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
391 if (xAxisRangeIts.first == xAxisRangeIts.second) {
368 return std::make_pair(cend(), cend());
392 return std::make_pair(cend(), cend());
369 }
393 }
370
394
371 // Gets the iterator on the min of all values data
395 // Gets the iterator on the min of all values data
372 auto minIt = std::min_element(
396 auto minIt = std::min_element(
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
397 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
398 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
375 });
399 });
376
400
377 // Gets the iterator on the max of all values data
401 // Gets the iterator on the max of all values data
378 auto maxIt = std::max_element(
402 auto maxIt = std::max_element(
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
403 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
404 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
381 });
405 });
382
406
383 return std::make_pair(minIt, maxIt);
407 return std::make_pair(minIt, maxIt);
384 }
408 }
385
409
386 /// @return the y-axis associated to the data series
410 /// @return the y-axis associated to the data series
387 /// @todo pass getter as protected and use iterators to access the y-axis data
411 const OptionalAxis &yAxis() const { return m_YAxis; }
388 OptionalAxis yAxis() const { return m_YAxis; }
412 OptionalAxis &yAxis() { return m_YAxis; }
389
413
390 // /////// //
414 // /////// //
391 // Mutexes //
415 // Mutexes //
392 // /////// //
416 // /////// //
393
417
394 virtual void lockRead() { m_Lock.lockForRead(); }
418 virtual void lockRead() { m_Lock.lockForRead(); }
395 virtual void lockWrite() { m_Lock.lockForWrite(); }
419 virtual void lockWrite() { m_Lock.lockForWrite(); }
396 virtual void unlock() { m_Lock.unlock(); }
420 virtual void unlock() { m_Lock.unlock(); }
397
421
398 protected:
422 protected:
399 /// Protected ctor (DataSeries is abstract).
423 /// Protected ctor (DataSeries is abstract).
400 ///
424 ///
401 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
425 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
402 /// class description for consistent rules)
426 /// class description for consistent rules)
403 /// @remarks data series is automatically sorted on its x-axis data
427 /// @remarks data series is automatically sorted on its x-axis data
404 /// @throws std::invalid_argument if the data are inconsistent with each other
428 /// @throws std::invalid_argument if the data are inconsistent with each other
405 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
429 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
406 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
430 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
407 OptionalAxis yAxis = OptionalAxis{})
431 OptionalAxis yAxis = OptionalAxis{})
408 : m_XAxisData{xAxisData},
432 : m_XAxisData{xAxisData},
409 m_XAxisUnit{xAxisUnit},
433 m_XAxisUnit{xAxisUnit},
410 m_ValuesData{valuesData},
434 m_ValuesData{valuesData},
411 m_ValuesUnit{valuesUnit},
435 m_ValuesUnit{valuesUnit},
412 m_YAxis{std::move(yAxis)}
436 m_YAxis{std::move(yAxis)}
413 {
437 {
414 if (m_XAxisData->size() != m_ValuesData->size()) {
438 if (m_XAxisData->size() != m_ValuesData->size()) {
415 throw std::invalid_argument{
439 throw std::invalid_argument{
416 "The number of values by component must be equal to the number of x-axis data"};
440 "The number of values by component must be equal to the number of x-axis data"};
417 }
441 }
418
442
419 // Validates y-axis (if defined)
443 // Validates y-axis (if defined)
420 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
444 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
421 throw std::invalid_argument{
445 throw std::invalid_argument{
422 "As the y-axis is defined, the number of value components must be equal to the "
446 "As the y-axis is defined, the number of value components must be equal to the "
423 "number of y-axis data"};
447 "number of y-axis data"};
424 }
448 }
425
449
426 // Sorts data if it's not the case
450 // Sorts data if it's not the case
427 const auto &xAxisCData = m_XAxisData->cdata();
451 const auto &xAxisCData = m_XAxisData->cdata();
428 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
452 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
429 sort();
453 sort();
430 }
454 }
431 }
455 }
432
456
433 /// Copy ctor
457 /// Copy ctor
434 explicit DataSeries(const DataSeries<Dim> &other)
458 explicit DataSeries(const DataSeries<Dim> &other)
435 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
459 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
436 m_XAxisUnit{other.m_XAxisUnit},
460 m_XAxisUnit{other.m_XAxisUnit},
437 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
461 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
438 m_ValuesUnit{other.m_ValuesUnit},
462 m_ValuesUnit{other.m_ValuesUnit},
439 m_YAxis{other.m_YAxis}
463 m_YAxis{other.m_YAxis}
440 {
464 {
441 // Since a series is ordered from its construction and is always ordered, it is not
465 // Since a series is ordered from its construction and is always ordered, it is not
442 // necessary to call the sort method here ('other' is sorted)
466 // necessary to call the sort method here ('other' is sorted)
443 }
467 }
444
468
445 /// Assignment operator
469 /// Assignment operator
446 template <int D>
470 template <int D>
447 DataSeries &operator=(DataSeries<D> other)
471 DataSeries &operator=(DataSeries<D> other)
448 {
472 {
449 std::swap(m_XAxisData, other.m_XAxisData);
473 std::swap(m_XAxisData, other.m_XAxisData);
450 std::swap(m_XAxisUnit, other.m_XAxisUnit);
474 std::swap(m_XAxisUnit, other.m_XAxisUnit);
451 std::swap(m_ValuesData, other.m_ValuesData);
475 std::swap(m_ValuesData, other.m_ValuesData);
452 std::swap(m_ValuesUnit, other.m_ValuesUnit);
476 std::swap(m_ValuesUnit, other.m_ValuesUnit);
453 std::swap(m_YAxis, other.m_YAxis);
477 std::swap(m_YAxis, other.m_YAxis);
454
478
455 return *this;
479 return *this;
456 }
480 }
457
481
458 private:
482 private:
459 /**
483 /**
460 * Sorts data series on its x-axis data
484 * Sorts data series on its x-axis data
461 */
485 */
462 void sort() noexcept
486 void sort() noexcept
463 {
487 {
464 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
488 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
465 m_XAxisData = m_XAxisData->sort(permutation);
489 m_XAxisData = m_XAxisData->sort(permutation);
466 m_ValuesData = m_ValuesData->sort(permutation);
490 m_ValuesData = m_ValuesData->sort(permutation);
467 }
491 }
468
492
469 // x-axis
493 // x-axis
470 std::shared_ptr<ArrayData<1> > m_XAxisData;
494 std::shared_ptr<ArrayData<1> > m_XAxisData;
471 Unit m_XAxisUnit;
495 Unit m_XAxisUnit;
472
496
473 // values
497 // values
474 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
498 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
475 Unit m_ValuesUnit;
499 Unit m_ValuesUnit;
476
500
477 // y-axis (optional)
501 // y-axis (optional)
478 OptionalAxis m_YAxis;
502 OptionalAxis m_YAxis;
479
503
480 QReadWriteLock m_Lock;
504 QReadWriteLock m_Lock;
481 };
505 };
482
506
483 #endif // SCIQLOP_DATASERIES_H
507 #endif // SCIQLOP_DATASERIES_H
@@ -1,78 +1,81
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
5 #include "Data/SqpIterator.h"
6
6
7 #include <QVector>
7 #include <QVector>
8 #include <memory>
8 #include <memory>
9
9
10 /**
10 /**
11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
13 * each series to define its own implementation of how to retrieve this data, by implementing the
13 * each series to define its own implementation of how to retrieve this data, by implementing the
14 * DataSeriesIteratorValue::Impl interface
14 * DataSeriesIteratorValue::Impl interface
15 *
15 *
16 * @sa DataSeriesIterator
16 * @sa DataSeriesIterator
17 */
17 */
18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
19 public:
19 public:
20 struct Impl {
20 struct Impl {
21 virtual ~Impl() noexcept = default;
21 virtual ~Impl() noexcept = default;
22 virtual std::unique_ptr<Impl> clone() const = 0;
22 virtual std::unique_ptr<Impl> clone() const = 0;
23 virtual int distance(const Impl &other) const = 0;
23 virtual int distance(const Impl &other) const = 0;
24 virtual bool equals(const Impl &other) const = 0;
24 virtual bool equals(const Impl &other) const = 0;
25 virtual bool lowerThan(const Impl &other) const = 0;
25 virtual bool lowerThan(const Impl &other) const = 0;
26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
27 virtual void next(int offset) = 0;
27 virtual void next(int offset) = 0;
28 virtual void prev() = 0;
28 virtual void prev() = 0;
29 virtual double x() const = 0;
29 virtual double x() const = 0;
30 virtual std::vector<double> y() const = 0;
30 virtual double value() const = 0;
31 virtual double value() const = 0;
31 virtual double value(int componentIndex) const = 0;
32 virtual double value(int componentIndex) const = 0;
32 virtual double minValue() const = 0;
33 virtual double minValue() const = 0;
33 virtual double maxValue() const = 0;
34 virtual double maxValue() const = 0;
34 virtual QVector<double> values() const = 0;
35 virtual QVector<double> values() const = 0;
35
36
36 virtual void swap(Impl &other) = 0;
37 virtual void swap(Impl &other) = 0;
37 };
38 };
38
39
39 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
40 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
40 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
41 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
41 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
42 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
42
43
43 int distance(const DataSeriesIteratorValue &other) const;
44 int distance(const DataSeriesIteratorValue &other) const;
44 bool equals(const DataSeriesIteratorValue &other) const;
45 bool equals(const DataSeriesIteratorValue &other) const;
45 bool lowerThan(const DataSeriesIteratorValue &other) const;
46 bool lowerThan(const DataSeriesIteratorValue &other) const;
46
47
47 DataSeriesIteratorValue advance(int offset) const;
48 DataSeriesIteratorValue advance(int offset) const;
48 /// Advances to the next value
49 /// Advances to the next value
49 void next(int offset = 1);
50 void next(int offset = 1);
50 /// Moves back to the previous value
51 /// Moves back to the previous value
51 void prev();
52 void prev();
52 /// Gets x-axis data
53 /// Gets x-axis data
53 double x() const;
54 double x() const;
55 /// Gets y-axis data
56 std::vector<double> y() const;
54 /// Gets value data
57 /// Gets value data
55 double value() const;
58 double value() const;
56 /// Gets value data depending on an index
59 /// Gets value data depending on an index
57 double value(int componentIndex) const;
60 double value(int componentIndex) const;
58 /// Gets min of all values data
61 /// Gets min of all values data
59 double minValue() const;
62 double minValue() const;
60 /// Gets max of all values data
63 /// Gets max of all values data
61 double maxValue() const;
64 double maxValue() const;
62 /// Gets all values data
65 /// Gets all values data
63 QVector<double> values() const;
66 QVector<double> values() const;
64
67
65 Impl *impl();
68 Impl *impl();
66
69
67 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
70 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
68 {
71 {
69 std::swap(lhs.m_Impl, rhs.m_Impl);
72 std::swap(lhs.m_Impl, rhs.m_Impl);
70 }
73 }
71
74
72 private:
75 private:
73 std::unique_ptr<Impl> m_Impl;
76 std::unique_ptr<Impl> m_Impl;
74 };
77 };
75
78
76 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
79 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
77
80
78 #endif // SCIQLOP_DATASERIESITERATOR_H
81 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -1,96 +1,102
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
4 #include <Common/MetaTypes.h>
4 #include <Common/MetaTypes.h>
5 #include <Data/DataSeriesIterator.h>
5 #include <Data/DataSeriesIterator.h>
6 #include <Data/SqpRange.h>
6 #include <Data/SqpRange.h>
7 #include <Data/Unit.h>
7 #include <Data/Unit.h>
8
8
9 #include <memory>
9 #include <memory>
10
10
11 #include <QString>
11 #include <QString>
12
12
13 template <int Dim>
13 template <int Dim>
14 class ArrayData;
14 class ArrayData;
15
15
16 /**
16 /**
17 * @brief The IDataSeries aims to declare a data series.
17 * @brief The IDataSeries aims to declare a data series.
18 *
18 *
19 * A data series is an entity that contains at least :
19 * A data series is an entity that contains at least :
20 * - one dataset representing the x-axis
20 * - one dataset representing the x-axis
21 * - one dataset representing the values
21 * - one dataset representing the values
22 *
22 *
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
24 *
24 *
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
26 * IDataSeries. The x-axis dataset is always unidimensional.
26 * IDataSeries. The x-axis dataset is always unidimensional.
27 *
27 *
28 * @sa ArrayData
28 * @sa ArrayData
29 */
29 */
30 class IDataSeries {
30 class IDataSeries {
31 public:
31 public:
32 virtual ~IDataSeries() noexcept = default;
32 virtual ~IDataSeries() noexcept = default;
33
33
34 /// Returns the x-axis dataset
34 /// Returns the x-axis dataset
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
36
36
37 /// Returns the x-axis dataset (as const)
37 /// Returns the x-axis dataset (as const)
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
39
39
40 virtual Unit xAxisUnit() const = 0;
40 virtual Unit xAxisUnit() const = 0;
41
41
42 /// @return the y-axis unit, if axis is defined, default unit otherwise
43 virtual Unit yAxisUnit() const = 0;
44
42 virtual Unit valuesUnit() const = 0;
45 virtual Unit valuesUnit() const = 0;
43
46
44 virtual void merge(IDataSeries *dataSeries) = 0;
47 virtual void merge(IDataSeries *dataSeries) = 0;
45 /// Removes from data series all entries whose value on the x-axis is not between min and max
48 /// Removes from data series all entries whose value on the x-axis is not between min and max
46 virtual void purge(double min, double max) = 0;
49 virtual void purge(double min, double max) = 0;
47
50
48 /// @todo Review the name and signature of this method
51 /// @todo Review the name and signature of this method
49 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
52 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
50
53
51 virtual std::unique_ptr<IDataSeries> clone() const = 0;
54 virtual std::unique_ptr<IDataSeries> clone() const = 0;
52
55
53 /// @return the total number of points contained in the data series
56 /// @return the total number of points contained in the data series
54 virtual int nbPoints() const = 0;
57 virtual int nbPoints() const = 0;
55
58
59 /// @return the bounds of the y-axis axis (if defined)
60 virtual std::pair<double, double> yBounds() const = 0;
61
56 // ///////// //
62 // ///////// //
57 // Iterators //
63 // Iterators //
58 // ///////// //
64 // ///////// //
59
65
60 virtual DataSeriesIterator cbegin() const = 0;
66 virtual DataSeriesIterator cbegin() const = 0;
61 virtual DataSeriesIterator cend() const = 0;
67 virtual DataSeriesIterator cend() const = 0;
62 virtual DataSeriesIterator begin() = 0;
68 virtual DataSeriesIterator begin() = 0;
63 virtual DataSeriesIterator end() = 0;
69 virtual DataSeriesIterator end() = 0;
64
70
65 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
71 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
66 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
72 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
67 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
73 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
68
74
69 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
75 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
70 /// equal to the value passed in parameter, or the end iterator if there is no matching value
76 /// equal to the value passed in parameter, or the end iterator if there is no matching value
71 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
77 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
72
78
73 /// @return the iterators pointing to the range of data whose x-axis values are between min and
79 /// @return the iterators pointing to the range of data whose x-axis values are between min and
74 /// max passed in parameters
80 /// max passed in parameters
75 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
76 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
82 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
77
83
78 /// @return two iterators pointing to the data that have respectively the min and the max value
84 /// @return two iterators pointing to the data that have respectively the min and the max value
79 /// data of a data series' range. The search is performed for a given x-axis range.
85 /// data of a data series' range. The search is performed for a given x-axis range.
80 /// @sa xAxisRange()
86 /// @sa xAxisRange()
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
87 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
82 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
88 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
83
89
84 // /////// //
90 // /////// //
85 // Mutexes //
91 // Mutexes //
86 // /////// //
92 // /////// //
87
93
88 virtual void lockRead() = 0;
94 virtual void lockRead() = 0;
89 virtual void lockWrite() = 0;
95 virtual void lockWrite() = 0;
90 virtual void unlock() = 0;
96 virtual void unlock() = 0;
91 };
97 };
92
98
93 // Required for using shared_ptr in signals/slots
99 // Required for using shared_ptr in signals/slots
94 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
100 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
95
101
96 #endif // SCIQLOP_IDATASERIES_H
102 #endif // SCIQLOP_IDATASERIES_H
@@ -1,62 +1,66
1 #ifndef SCIQLOP_OPTIONALAXIS_H
1 #ifndef SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
3
3
4 #include <Data/ArrayDataIterator.h>
5
4 #include "CoreGlobal.h"
6 #include "CoreGlobal.h"
5 #include "Unit.h"
7 #include "Unit.h"
6
8
7 #include <memory>
9 #include <memory>
8
10
9 template <int Dim>
11 template <int Dim>
10 class ArrayData;
12 class ArrayData;
11
13
12 /**
14 /**
13 * @brief The OptionalAxis class defines an optional data axis for a series of data.
15 * @brief The OptionalAxis class defines an optional data axis for a series of data.
14 *
16 *
15 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
17 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
16 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
18 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
17 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
19 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
18 * data or the unit are always callable but will return undefined values.
20 * data or the unit are always callable but will return undefined values.
19 *
21 *
20 * @sa DataSeries
22 * @sa DataSeries
21 * @sa ArrayData
23 * @sa ArrayData
22 */
24 */
23 class SCIQLOP_CORE_EXPORT OptionalAxis {
25 class SCIQLOP_CORE_EXPORT OptionalAxis {
24 public:
26 public:
25 /// Ctor for an undefined axis
27 /// Ctor for an undefined axis
26 explicit OptionalAxis();
28 explicit OptionalAxis();
27 /// Ctor for a defined axis
29 /// Ctor for a defined axis
28 /// @param data the axis' data
30 /// @param data the axis' data
29 /// @param unit the axis' unit
31 /// @param unit the axis' unit
30 /// @throws std::invalid_argument if no data is associated to the axis
32 /// @throws std::invalid_argument if no data is associated to the axis
31 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
33 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
32
34
33 /// Copy ctor
35 /// Copy ctor
34 OptionalAxis(const OptionalAxis &other);
36 OptionalAxis(const OptionalAxis &other);
35 /// Assignment operator
37 /// Assignment operator
36 OptionalAxis &operator=(OptionalAxis other);
38 OptionalAxis &operator=(OptionalAxis other);
37
39
38 /// @return the flag that indicates if the axis is defined or not
40 /// @return the flag that indicates if the axis is defined or not
39 bool isDefined() const;
41 bool isDefined() const;
40
42
41 /// @return gets the data at the index passed in parameter, NaN if the index is outside the
42 /// bounds of the axis, or if the axis is undefined
43 double at(int index) const;
44
45 ///@return the min and max values of the data on the axis, NaN values if there is no data
43 ///@return the min and max values of the data on the axis, NaN values if there is no data
46 std::pair<double, double> bounds() const;
44 std::pair<double, double> bounds() const;
47
45
48 /// @return the number of data on the axis, 0 if the axis is not defined
46 /// @return the number of data on the axis, 0 if the axis is not defined
49 int size() const;
47 int size() const;
50 /// @return the unit of the axis, an empty unit if the axis is not defined
48 /// @return the unit of the axis, an empty unit if the axis is not defined
51 Unit unit() const;
49 Unit unit() const;
52
50
53 bool operator==(const OptionalAxis &other);
51 bool operator==(const OptionalAxis &other);
54 bool operator!=(const OptionalAxis &other);
52 bool operator!=(const OptionalAxis &other);
55
53
54 // Iterators on data
55 ArrayDataIterator begin();
56 ArrayDataIterator end();
57 ArrayDataIterator cbegin() const;
58 ArrayDataIterator cend() const;
59
56 private:
60 private:
57 bool m_Defined; ///< Axis is defined or not
61 bool m_Defined; ///< Axis is defined or not
58 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
62 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
59 Unit m_Unit; ///< Axis' unit
63 Unit m_Unit; ///< Axis' unit
60 };
64 };
61
65
62 #endif // SCIQLOP_OPTIONALAXIS_H
66 #endif // SCIQLOP_OPTIONALAXIS_H
@@ -1,33 +1,40
1 #ifndef SCIQLOP_SPECTROGRAMSERIES_H
1 #ifndef SCIQLOP_SPECTROGRAMSERIES_H
2 #define SCIQLOP_SPECTROGRAMSERIES_H
2 #define SCIQLOP_SPECTROGRAMSERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Data/DataSeries.h>
6 #include <Data/DataSeries.h>
7
7
8 /**
8 /**
9 * @brief The SpectrogramSeries class is the implementation for a data series representing a
9 * @brief The SpectrogramSeries class is the implementation for a data series representing a
10 * spectrogram.
10 * spectrogram.
11 *
11 *
12 * It defines values on a x-axis and a y-axis.
12 * It defines values on a x-axis and a y-axis.
13 */
13 */
14 class SCIQLOP_CORE_EXPORT SpectrogramSeries : public DataSeries<2> {
14 class SCIQLOP_CORE_EXPORT SpectrogramSeries : public DataSeries<2> {
15 public:
15 public:
16 /// Ctor
16 /// Ctor
17 explicit SpectrogramSeries(std::vector<double> xAxisData, std::vector<double> yAxisData,
17 explicit SpectrogramSeries(std::vector<double> xAxisData, std::vector<double> yAxisData,
18 std::vector<double> valuesData, const Unit &xAxisUnit,
18 std::vector<double> valuesData, const Unit &xAxisUnit,
19 const Unit &yAxisUnit, const Unit &valuesUnit);
19 const Unit &yAxisUnit, const Unit &valuesUnit,
20 double xResolution = std::numeric_limits<double>::quiet_NaN());
20
21
21 /// Ctor directly with the y-axis
22 /// Ctor directly with the y-axis
22 explicit SpectrogramSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
23 explicit SpectrogramSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
23 std::shared_ptr<ArrayData<2> > valuesData, const Unit &valuesUnit,
24 std::shared_ptr<ArrayData<2> > valuesData, const Unit &valuesUnit,
24 OptionalAxis yAxis);
25 OptionalAxis yAxis,
26 double xResolution = std::numeric_limits<double>::quiet_NaN());
25
27
26 /// @sa DataSeries::clone()
28 /// @sa DataSeries::clone()
27 std::unique_ptr<IDataSeries> clone() const override;
29 std::unique_ptr<IDataSeries> clone() const override;
28
30
29 /// @sa DataSeries::subDataSeries()
31 /// @sa DataSeries::subDataSeries()
30 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
32 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
33
34 inline double xResolution() const noexcept { return m_XResolution; }
35
36 private:
37 double m_XResolution; ///< Resolution used on x-axis to build the spectrogram
31 };
38 };
32
39
33 #endif // SCIQLOP_SPECTROGRAMSERIES_H
40 #endif // SCIQLOP_SPECTROGRAMSERIES_H
@@ -1,64 +1,65
1
1
2 core_moc_headers = [
2 core_moc_headers = [
3 'include/Data/IDataProvider.h',
3 'include/Data/IDataProvider.h',
4 'include/DataSource/DataSourceController.h',
4 'include/DataSource/DataSourceController.h',
5 'include/DataSource/DataSourceItemAction.h',
5 'include/DataSource/DataSourceItemAction.h',
6 'include/Network/NetworkController.h',
6 'include/Network/NetworkController.h',
7 'include/Time/TimeController.h',
7 'include/Time/TimeController.h',
8 'include/Variable/Variable.h',
8 'include/Variable/Variable.h',
9 'include/Variable/VariableCacheController.h',
9 'include/Variable/VariableCacheController.h',
10 'include/Variable/VariableController.h',
10 'include/Variable/VariableController.h',
11 'include/Variable/VariableAcquisitionWorker.h',
11 'include/Variable/VariableAcquisitionWorker.h',
12 'include/Variable/VariableSynchronizationGroup.h',
12 'include/Variable/VariableSynchronizationGroup.h',
13 'include/Variable/VariableModel.h',
13 'include/Variable/VariableModel.h',
14 'include/Visualization/VisualizationController.h'
14 'include/Visualization/VisualizationController.h'
15 ]
15 ]
16
16
17
17
18 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
18 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
19
19
20 core_sources = [
20 core_sources = [
21 'src/Common/DateUtils.cpp',
21 'src/Common/DateUtils.cpp',
22 'src/Common/StringUtils.cpp',
22 'src/Common/StringUtils.cpp',
23 'src/Common/MimeTypesDef.cpp',
23 'src/Common/MimeTypesDef.cpp',
24 'src/Data/ScalarSeries.cpp',
24 'src/Data/ScalarSeries.cpp',
25 'src/Data/SpectrogramSeries.cpp',
25 'src/Data/SpectrogramSeries.cpp',
26 'src/Data/DataSeriesIterator.cpp',
26 'src/Data/DataSeriesIterator.cpp',
27 'src/Data/ArrayDataIterator.cpp',
27 'src/Data/ArrayDataIterator.cpp',
28 'src/Data/VectorSeries.cpp',
28 'src/Data/VectorSeries.cpp',
29 'src/Data/OptionalAxis.cpp',
29 'src/Data/OptionalAxis.cpp',
30 'src/Data/DataSeriesUtils.cpp',
30 'src/DataSource/DataSourceController.cpp',
31 'src/DataSource/DataSourceController.cpp',
31 'src/DataSource/DataSourceItem.cpp',
32 'src/DataSource/DataSourceItem.cpp',
32 'src/DataSource/DataSourceItemAction.cpp',
33 'src/DataSource/DataSourceItemAction.cpp',
33 'src/Network/NetworkController.cpp',
34 'src/Network/NetworkController.cpp',
34 'src/Plugin/PluginManager.cpp',
35 'src/Plugin/PluginManager.cpp',
35 'src/Settings/SqpSettingsDefs.cpp',
36 'src/Settings/SqpSettingsDefs.cpp',
36 'src/Time/TimeController.cpp',
37 'src/Time/TimeController.cpp',
37 'src/Variable/Variable.cpp',
38 'src/Variable/Variable.cpp',
38 'src/Variable/VariableCacheController.cpp',
39 'src/Variable/VariableCacheController.cpp',
39 'src/Variable/VariableController.cpp',
40 'src/Variable/VariableController.cpp',
40 'src/Variable/VariableAcquisitionWorker.cpp',
41 'src/Variable/VariableAcquisitionWorker.cpp',
41 'src/Variable/VariableSynchronizationGroup.cpp',
42 'src/Variable/VariableSynchronizationGroup.cpp',
42 'src/Variable/VariableModel.cpp',
43 'src/Variable/VariableModel.cpp',
43 'src/Visualization/VisualizationController.cpp'
44 'src/Visualization/VisualizationController.cpp'
44 ]
45 ]
45
46
46 core_inc = include_directories(['include', '../plugin/include'])
47 core_inc = include_directories(['include', '../plugin/include'])
47
48
48 sciqlop_core_lib = library('sciqlopcore',
49 sciqlop_core_lib = library('sciqlopcore',
49 core_sources,
50 core_sources,
50 core_moc_files,
51 core_moc_files,
51 cpp_args : '-DCORE_LIB',
52 cpp_args : '-DCORE_LIB',
52 include_directories : core_inc,
53 include_directories : core_inc,
53 dependencies : [qt5core, qt5network],
54 dependencies : [qt5core, qt5network],
54 install : true
55 install : true
55 )
56 )
56
57
57
58
58 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
59 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
59 include_directories : core_inc,
60 include_directories : core_inc,
60 dependencies : [qt5core, qt5network])
61 dependencies : [qt5core, qt5network])
61
62
62
63
63 subdir('tests')
64 subdir('tests')
64
65
@@ -1,83 +1,88
1 #include "Data/DataSeriesIterator.h"
1 #include "Data/DataSeriesIterator.h"
2
2
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
5 : m_Impl{std::move(impl)}
5 : m_Impl{std::move(impl)}
6 {
6 {
7 }
7 }
8
8
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
10 : m_Impl{other.m_Impl->clone()}
10 : m_Impl{other.m_Impl->clone()}
11 {
11 {
12 }
12 }
13
13
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
15 {
15 {
16 m_Impl->swap(*other.m_Impl);
16 m_Impl->swap(*other.m_Impl);
17 return *this;
17 return *this;
18 }
18 }
19
19
20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
21 {
21 {
22 return m_Impl->distance(*other.m_Impl);
22 return m_Impl->distance(*other.m_Impl);
23 }
23 }
24
24
25 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
25 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
26 {
26 {
27 return m_Impl->equals(*other.m_Impl);
27 return m_Impl->equals(*other.m_Impl);
28 }
28 }
29
29
30 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
30 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
31 {
31 {
32 return m_Impl->lowerThan(*other.m_Impl);
32 return m_Impl->lowerThan(*other.m_Impl);
33 }
33 }
34
34
35 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
35 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
36 {
36 {
37 return DataSeriesIteratorValue{m_Impl->advance(offset)};
37 return DataSeriesIteratorValue{m_Impl->advance(offset)};
38 }
38 }
39
39
40 void DataSeriesIteratorValue::next(int offset)
40 void DataSeriesIteratorValue::next(int offset)
41 {
41 {
42 m_Impl->next(offset);
42 m_Impl->next(offset);
43 }
43 }
44
44
45 void DataSeriesIteratorValue::prev()
45 void DataSeriesIteratorValue::prev()
46 {
46 {
47 m_Impl->prev();
47 m_Impl->prev();
48 }
48 }
49
49
50 double DataSeriesIteratorValue::x() const
50 double DataSeriesIteratorValue::x() const
51 {
51 {
52 return m_Impl->x();
52 return m_Impl->x();
53 }
53 }
54
54
55 std::vector<double> DataSeriesIteratorValue::y() const
56 {
57 return m_Impl->y();
58 }
59
55 double DataSeriesIteratorValue::value() const
60 double DataSeriesIteratorValue::value() const
56 {
61 {
57 return m_Impl->value();
62 return m_Impl->value();
58 }
63 }
59
64
60 double DataSeriesIteratorValue::value(int componentIndex) const
65 double DataSeriesIteratorValue::value(int componentIndex) const
61 {
66 {
62 return m_Impl->value(componentIndex);
67 return m_Impl->value(componentIndex);
63 }
68 }
64
69
65 double DataSeriesIteratorValue::minValue() const
70 double DataSeriesIteratorValue::minValue() const
66 {
71 {
67 return m_Impl->minValue();
72 return m_Impl->minValue();
68 }
73 }
69
74
70 double DataSeriesIteratorValue::maxValue() const
75 double DataSeriesIteratorValue::maxValue() const
71 {
76 {
72 return m_Impl->maxValue();
77 return m_Impl->maxValue();
73 }
78 }
74
79
75 QVector<double> DataSeriesIteratorValue::values() const
80 QVector<double> DataSeriesIteratorValue::values() const
76 {
81 {
77 return m_Impl->values();
82 return m_Impl->values();
78 }
83 }
79
84
80 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
85 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
81 {
86 {
82 return m_Impl.get();
87 return m_Impl.get();
83 }
88 }
@@ -1,99 +1,107
1 #include <Data/OptionalAxis.h>
1 #include <Data/OptionalAxis.h>
2
2
3 #include "Data/ArrayData.h"
3 #include "Data/ArrayData.h"
4
4
5 OptionalAxis::OptionalAxis() : m_Defined{false}, m_Data{nullptr}, m_Unit{}
5 OptionalAxis::OptionalAxis()
6 : m_Defined{false}, m_Data{std::make_shared<ArrayData<1> >(std::vector<double>{})}, m_Unit{}
6 {
7 {
7 }
8 }
8
9
9 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
10 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
10 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
11 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
11 {
12 {
12 if (m_Data == nullptr) {
13 if (m_Data == nullptr) {
13 throw std::invalid_argument{"Data can't be null for a defined axis"};
14 throw std::invalid_argument{"Data can't be null for a defined axis"};
14 }
15 }
15 }
16 }
16
17
17 OptionalAxis::OptionalAxis(const OptionalAxis &other)
18 OptionalAxis::OptionalAxis(const OptionalAxis &other)
18 : m_Defined{other.m_Defined},
19 : m_Defined{other.m_Defined}, m_Data{other.m_Data}, m_Unit{other.m_Unit}
19 m_Data{other.m_Data ? std::make_shared<ArrayData<1> >(*other.m_Data) : nullptr},
20 m_Unit{other.m_Unit}
21 {
20 {
22 }
21 }
23
22
24 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
23 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
25 {
24 {
26 std::swap(m_Defined, other.m_Defined);
25 std::swap(m_Defined, other.m_Defined);
27 std::swap(m_Data, other.m_Data);
26 std::swap(m_Data, other.m_Data);
28 std::swap(m_Unit, other.m_Unit);
27 std::swap(m_Unit, other.m_Unit);
29
28
30 return *this;
29 return *this;
31 }
30 }
32
31
33 bool OptionalAxis::isDefined() const
32 bool OptionalAxis::isDefined() const
34 {
33 {
35 return m_Defined;
34 return m_Defined;
36 }
35 }
37
36
38 double OptionalAxis::at(int index) const
39 {
40 if (m_Defined) {
41 return (index >= 0 && index < m_Data->size()) ? m_Data->at(index)
42 : std::numeric_limits<double>::quiet_NaN();
43 }
44 else {
45 return std::numeric_limits<double>::quiet_NaN();
46 }
47 }
48
49 std::pair<double, double> OptionalAxis::bounds() const
37 std::pair<double, double> OptionalAxis::bounds() const
50 {
38 {
51 if (!m_Defined || m_Data->size() == 0) {
39 if (!m_Defined || m_Data->size() == 0) {
52 return std::make_pair(std::numeric_limits<double>::quiet_NaN(),
40 return std::make_pair(std::numeric_limits<double>::quiet_NaN(),
53 std::numeric_limits<double>::quiet_NaN());
41 std::numeric_limits<double>::quiet_NaN());
54 }
42 }
55 else {
43 else {
56
44
57 auto minIt = std::min_element(
45 auto minIt = std::min_element(
58 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
46 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
59 return SortUtils::minCompareWithNaN(it1.first(), it2.first());
47 return SortUtils::minCompareWithNaN(it1.first(), it2.first());
60 });
48 });
61
49
62 // Gets the iterator on the max of all values data
50 // Gets the iterator on the max of all values data
63 auto maxIt = std::max_element(
51 auto maxIt = std::max_element(
64 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
52 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
65 return SortUtils::maxCompareWithNaN(it1.first(), it2.first());
53 return SortUtils::maxCompareWithNaN(it1.first(), it2.first());
66 });
54 });
67
55
68 return std::make_pair(minIt->first(), maxIt->first());
56 return std::make_pair(minIt->first(), maxIt->first());
69 }
57 }
70 }
58 }
71
59
72 int OptionalAxis::size() const
60 int OptionalAxis::size() const
73 {
61 {
74 return m_Defined ? m_Data->size() : 0;
62 return m_Defined ? m_Data->size() : 0;
75 }
63 }
76
64
77 Unit OptionalAxis::unit() const
65 Unit OptionalAxis::unit() const
78 {
66 {
79 return m_Defined ? m_Unit : Unit{};
67 return m_Defined ? m_Unit : Unit{};
80 }
68 }
81
69
82 bool OptionalAxis::operator==(const OptionalAxis &other)
70 bool OptionalAxis::operator==(const OptionalAxis &other)
83 {
71 {
84 // Axis not defined
72 // Axis not defined
85 if (!m_Defined) {
73 if (!m_Defined) {
86 return !other.m_Defined;
74 return !other.m_Defined;
87 }
75 }
88
76
89 // Axis defined
77 // Axis defined
90 return m_Unit == other.m_Unit
78 return m_Unit == other.m_Unit
91 && std::equal(
79 && std::equal(
92 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
80 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
93 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
81 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
94 }
82 }
95
83
96 bool OptionalAxis::operator!=(const OptionalAxis &other)
84 bool OptionalAxis::operator!=(const OptionalAxis &other)
97 {
85 {
98 return !(*this == other);
86 return !(*this == other);
99 }
87 }
88
89 ArrayDataIterator OptionalAxis::begin()
90 {
91 return m_Data->begin();
92 }
93
94 ArrayDataIterator OptionalAxis::end()
95 {
96 return m_Data->end();
97 }
98
99 ArrayDataIterator OptionalAxis::cbegin() const
100 {
101 return m_Data->cbegin();
102 }
103
104 ArrayDataIterator OptionalAxis::cend() const
105 {
106 return m_Data->cend();
107 }
@@ -1,45 +1,50
1 #include <Data/SpectrogramSeries.h>
1 #include <Data/SpectrogramSeries.h>
2
2
3 SpectrogramSeries::SpectrogramSeries(std::vector<double> xAxisData, std::vector<double> yAxisData,
3 SpectrogramSeries::SpectrogramSeries(std::vector<double> xAxisData, std::vector<double> yAxisData,
4 std::vector<double> valuesData, const Unit &xAxisUnit,
4 std::vector<double> valuesData, const Unit &xAxisUnit,
5 const Unit &yAxisUnit, const Unit &valuesUnit)
5 const Unit &yAxisUnit, const Unit &valuesUnit,
6 double resolution)
6 : SpectrogramSeries{
7 : SpectrogramSeries{
7 std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
8 std::make_shared<ArrayData<1> >(std::move(xAxisData)),
8 std::make_shared<ArrayData<2> >(std::move(valuesData), yAxisData.size()), valuesUnit,
9 xAxisUnit,
9 OptionalAxis{std::make_shared<ArrayData<1> >(std::move(yAxisData)), yAxisUnit}}
10 std::make_shared<ArrayData<2> >(std::move(valuesData), yAxisData.size()),
11 valuesUnit,
12 OptionalAxis{std::make_shared<ArrayData<1> >(std::move(yAxisData)), yAxisUnit},
13 resolution}
10 {
14 {
11 }
15 }
12
16
13 SpectrogramSeries::SpectrogramSeries(std::shared_ptr<ArrayData<1> > xAxisData,
17 SpectrogramSeries::SpectrogramSeries(std::shared_ptr<ArrayData<1> > xAxisData,
14 const Unit &xAxisUnit,
18 const Unit &xAxisUnit,
15 std::shared_ptr<ArrayData<2> > valuesData,
19 std::shared_ptr<ArrayData<2> > valuesData,
16 const Unit &valuesUnit, OptionalAxis yAxis)
20 const Unit &valuesUnit, OptionalAxis yAxis, double resolution)
17 : DataSeries{std::move(xAxisData), xAxisUnit, std::move(valuesData), valuesUnit,
21 : DataSeries{std::move(xAxisData), xAxisUnit, std::move(valuesData), valuesUnit,
18 std::move(yAxis)}
22 std::move(yAxis)},
23 m_XResolution{resolution}
19 {
24 {
20 }
25 }
21
26
22 std::unique_ptr<IDataSeries> SpectrogramSeries::clone() const
27 std::unique_ptr<IDataSeries> SpectrogramSeries::clone() const
23 {
28 {
24 return std::make_unique<SpectrogramSeries>(*this);
29 return std::make_unique<SpectrogramSeries>(*this);
25 }
30 }
26
31
27 std::shared_ptr<IDataSeries> SpectrogramSeries::subDataSeries(const SqpRange &range)
32 std::shared_ptr<IDataSeries> SpectrogramSeries::subDataSeries(const SqpRange &range)
28 {
33 {
29 auto subXAxisData = std::vector<double>();
34 auto subXAxisData = std::vector<double>();
30 auto subValuesData = QVector<double>(); // Uses QVector to append easily values to it
35 auto subValuesData = QVector<double>(); // Uses QVector to append easily values to it
31 this->lockRead();
36 this->lockRead();
32 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
37 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
33 for (auto it = bounds.first; it != bounds.second; ++it) {
38 for (auto it = bounds.first; it != bounds.second; ++it) {
34 subXAxisData.push_back(it->x());
39 subXAxisData.push_back(it->x());
35 subValuesData.append(it->values());
40 subValuesData.append(it->values());
36 }
41 }
37
42
38 auto yAxis = this->yAxis();
43 auto yAxis = this->yAxis();
39 this->unlock();
44 this->unlock();
40
45
41 return std::make_shared<SpectrogramSeries>(
46 return std::make_shared<SpectrogramSeries>(
42 std::make_shared<ArrayData<1> >(std::move(subXAxisData)), this->xAxisUnit(),
47 std::make_shared<ArrayData<1> >(std::move(subXAxisData)), this->xAxisUnit(),
43 std::make_shared<ArrayData<2> >(subValuesData.toStdVector(), yAxis.size()),
48 std::make_shared<ArrayData<2> >(subValuesData.toStdVector(), yAxis.size()),
44 this->valuesUnit(), std::move(yAxis));
49 this->valuesUnit(), std::move(yAxis));
45 }
50 }
@@ -1,24 +1,24
1 #include "DataSeriesUtils.h"
1 #include "DataSeriesTestsUtils.h"
2
2
3 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
3 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
4 const DataContainer &valuesData)
4 const DataContainer &valuesData)
5 {
5 {
6 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
6 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
7 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
7 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
8 QVERIFY(std::equal(
8 QVERIFY(std::equal(
9 first, last, valuesData.cbegin(), valuesData.cend(),
9 first, last, valuesData.cbegin(), valuesData.cend(),
10 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
10 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
11 }
11 }
12
12
13 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData, const std::vector<DataContainer> &valuesData)
13 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData, const std::vector<DataContainer> &valuesData)
14 {
14 {
15 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
15 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
16 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
16 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
17 for (auto i = 0u; i < valuesData.size(); ++i) {
17 for (auto i = 0u; i < valuesData.size(); ++i) {
18 auto componentData = valuesData.at(i);
18 auto componentData = valuesData.at(i);
19
19
20 QVERIFY(std::equal(
20 QVERIFY(std::equal(
21 first, last, componentData.cbegin(), componentData.cend(),
21 first, last, componentData.cbegin(), componentData.cend(),
22 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
22 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
23 }
23 }
24 }
24 }
@@ -1,373 +1,373
1 /**
1 /**
2 * The DataSeriesUtils file contains a set of utility methods that can be used to test the operations on a DataSeries.
2 * The DataSeriesTestsUtils file contains a set of utility methods that can be used to test the operations on a DataSeries.
3 *
3 *
4 * Most of these methods are template methods to adapt to any series (scalars, vectors, spectrograms...)
4 * Most of these methods are template methods to adapt to any series (scalars, vectors, spectrograms...)
5 *
5 *
6 * @sa DataSeries
6 * @sa DataSeries
7 */
7 */
8 #ifndef SCIQLOP_DATASERIESUTILS_H
8 #ifndef SCIQLOP_DATASERIESTESTSUTILS_H
9 #define SCIQLOP_DATASERIESUTILS_H
9 #define SCIQLOP_DATASERIESTESTSUTILS_H
10
10
11 #include <Data/DataSeriesIterator.h>
11 #include <Data/DataSeriesIterator.h>
12 #include <Data/ScalarSeries.h>
12 #include <Data/ScalarSeries.h>
13 #include <Data/SpectrogramSeries.h>
13 #include <Data/SpectrogramSeries.h>
14 #include <Data/VectorSeries.h>
14 #include <Data/VectorSeries.h>
15
15
16 #include <memory>
16 #include <memory>
17 #include <QtTest>
17 #include <QtTest>
18
18
19 /// Underlying data in ArrayData
19 /// Underlying data in ArrayData
20 using DataContainer = std::vector<double>;
20 using DataContainer = std::vector<double>;
21
21
22 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
22 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
23 Q_DECLARE_METATYPE(std::shared_ptr<SpectrogramSeries>)
23 Q_DECLARE_METATYPE(std::shared_ptr<SpectrogramSeries>)
24 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
24 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
25
25
26 /**
26 /**
27 * Checks that the range of a 1-dim data series contains the expected x-axis data and values data
27 * Checks that the range of a 1-dim data series contains the expected x-axis data and values data
28 * @param first the iterator on the beginning of the range to check
28 * @param first the iterator on the beginning of the range to check
29 * @param last the iterator on the end of the range to check
29 * @param last the iterator on the end of the range to check
30 * @param xData expected x-axis data for the range
30 * @param xData expected x-axis data for the range
31 * @param valuesData expected values data for the range
31 * @param valuesData expected values data for the range
32 */
32 */
33 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
33 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
34 const DataContainer &valuesData);
34 const DataContainer &valuesData);
35
35
36 /**
36 /**
37 * Checks that the range of a 2-dim data series contains the expected x-axis data and values data
37 * Checks that the range of a 2-dim data series contains the expected x-axis data and values data
38 * @param first the iterator on the beginning of the range to check
38 * @param first the iterator on the beginning of the range to check
39 * @param last the iterator on the end of the range to check
39 * @param last the iterator on the end of the range to check
40 * @param xData expected x-axis data for the range
40 * @param xData expected x-axis data for the range
41 * @param valuesData expected values data for the range
41 * @param valuesData expected values data for the range
42 */
42 */
43 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
43 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
44 const std::vector<DataContainer> &valuesData);
44 const std::vector<DataContainer> &valuesData);
45
45
46 /**
46 /**
47 * Sets the structure of unit tests concerning merge of two data series
47 * Sets the structure of unit tests concerning merge of two data series
48 * @tparam DataSeriesType the type of data series to merge
48 * @tparam DataSeriesType the type of data series to merge
49 * @tparam ExpectedValuesType the type of values expected after merge
49 * @tparam ExpectedValuesType the type of values expected after merge
50 * @sa testMerge_t()
50 * @sa testMerge_t()
51 */
51 */
52 template <typename DataSeriesType, typename ExpectedValuesType>
52 template <typename DataSeriesType, typename ExpectedValuesType>
53 void testMerge_struct() {
53 void testMerge_struct() {
54 // Data series to merge
54 // Data series to merge
55 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries");
55 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries");
56 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries2");
56 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries2");
57
57
58 // Expected values in the first data series after merge
58 // Expected values in the first data series after merge
59 QTest::addColumn<DataContainer>("expectedXAxisData");
59 QTest::addColumn<DataContainer>("expectedXAxisData");
60 QTest::addColumn<ExpectedValuesType>("expectedValuesData");
60 QTest::addColumn<ExpectedValuesType>("expectedValuesData");
61 }
61 }
62
62
63 /**
63 /**
64 * Unit test concerning merge of two data series
64 * Unit test concerning merge of two data series
65 * @sa testMerge_struct()
65 * @sa testMerge_struct()
66 */
66 */
67 template <typename DataSeriesType, typename ExpectedValuesType>
67 template <typename DataSeriesType, typename ExpectedValuesType>
68 void testMerge_t(){
68 void testMerge_t(){
69 // Merges series
69 // Merges series
70 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries);
70 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries);
71 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries2);
71 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries2);
72
72
73 dataSeries->merge(dataSeries2.get());
73 dataSeries->merge(dataSeries2.get());
74
74
75 // Validates results : we check that the merge is valid
75 // Validates results : we check that the merge is valid
76 QFETCH(DataContainer, expectedXAxisData);
76 QFETCH(DataContainer, expectedXAxisData);
77 QFETCH(ExpectedValuesType, expectedValuesData);
77 QFETCH(ExpectedValuesType, expectedValuesData);
78
78
79 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
79 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
80 }
80 }
81
81
82 /**
82 /**
83 * Sets the structure of unit tests concerning merge of two data series that are of a different type
83 * Sets the structure of unit tests concerning merge of two data series that are of a different type
84 * @tparam SourceType the type of data series with which to make the merge
84 * @tparam SourceType the type of data series with which to make the merge
85 * @tparam DestType the type of data series in which to make the merge
85 * @tparam DestType the type of data series in which to make the merge
86 * @sa testMergeDifferentTypes_t()
86 * @sa testMergeDifferentTypes_t()
87 */
87 */
88 template <typename SourceType, typename DestType>
88 template <typename SourceType, typename DestType>
89 void testMergeDifferentTypes_struct()
89 void testMergeDifferentTypes_struct()
90 {
90 {
91 // Data series to merge
91 // Data series to merge
92 QTest::addColumn<std::shared_ptr<DestType> >("dest");
92 QTest::addColumn<std::shared_ptr<DestType> >("dest");
93 QTest::addColumn<std::shared_ptr<SourceType> >("source");
93 QTest::addColumn<std::shared_ptr<SourceType> >("source");
94
94
95 // Expected values in the dest data series after merge
95 // Expected values in the dest data series after merge
96 QTest::addColumn<DataContainer>("expectedXAxisData");
96 QTest::addColumn<DataContainer>("expectedXAxisData");
97 QTest::addColumn<DataContainer>("expectedValuesData");
97 QTest::addColumn<DataContainer>("expectedValuesData");
98 }
98 }
99
99
100 /**
100 /**
101 * Unit test concerning merge of two data series that are of a different type
101 * Unit test concerning merge of two data series that are of a different type
102 * @sa testMergeDifferentTypes_struct()
102 * @sa testMergeDifferentTypes_struct()
103 */
103 */
104 template <typename SourceType, typename DestType>
104 template <typename SourceType, typename DestType>
105 void testMergeDifferentTypes_t()
105 void testMergeDifferentTypes_t()
106 {
106 {
107 // Merges series
107 // Merges series
108 QFETCH(std::shared_ptr<SourceType>, source);
108 QFETCH(std::shared_ptr<SourceType>, source);
109 QFETCH(std::shared_ptr<DestType>, dest);
109 QFETCH(std::shared_ptr<DestType>, dest);
110
110
111 dest->merge(source.get());
111 dest->merge(source.get());
112
112
113 // Validates results : we check that the merge is valid and the data series is sorted on its
113 // Validates results : we check that the merge is valid and the data series is sorted on its
114 // x-axis data
114 // x-axis data
115 QFETCH(DataContainer, expectedXAxisData);
115 QFETCH(DataContainer, expectedXAxisData);
116 QFETCH(DataContainer, expectedValuesData);
116 QFETCH(DataContainer, expectedValuesData);
117
117
118 validateRange(dest->cbegin(), dest->cend(), expectedXAxisData, expectedValuesData);
118 validateRange(dest->cbegin(), dest->cend(), expectedXAxisData, expectedValuesData);
119 }
119 }
120
120
121 /**
121 /**
122 * Sets the structure of unit tests concerning getting the min x-axis data of a data series
122 * Sets the structure of unit tests concerning getting the min x-axis data of a data series
123 * @tparam T the type of data series on which to make the operation
123 * @tparam T the type of data series on which to make the operation
124 * @sa testMinXAxisData_t()
124 * @sa testMinXAxisData_t()
125 */
125 */
126 template <typename T>
126 template <typename T>
127 void testMinXAxisData_struct(){
127 void testMinXAxisData_struct(){
128 // Data series to get min data
128 // Data series to get min data
129 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
129 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
130
130
131 // Min data
131 // Min data
132 QTest::addColumn<double>("min");
132 QTest::addColumn<double>("min");
133
133
134 // Expected results
134 // Expected results
135 QTest::addColumn<bool>(
135 QTest::addColumn<bool>(
136 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
136 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
137 QTest::addColumn<double>(
137 QTest::addColumn<double>(
138 "expectedMin"); // Expected value when method doesn't return end iterator
138 "expectedMin"); // Expected value when method doesn't return end iterator
139 }
139 }
140
140
141 /**
141 /**
142 * Unit test concerning getting the min x-axis data of a data series
142 * Unit test concerning getting the min x-axis data of a data series
143 * @sa testMinXAxisData_struct()
143 * @sa testMinXAxisData_struct()
144 */
144 */
145 template <typename T>
145 template <typename T>
146 void testMinXAxisData_t()
146 void testMinXAxisData_t()
147 {
147 {
148 QFETCH(std::shared_ptr<T>, dataSeries);
148 QFETCH(std::shared_ptr<T>, dataSeries);
149 QFETCH(double, min);
149 QFETCH(double, min);
150
150
151 QFETCH(bool, expectedOK);
151 QFETCH(bool, expectedOK);
152 QFETCH(double, expectedMin);
152 QFETCH(double, expectedMin);
153
153
154 auto it = dataSeries->minXAxisData(min);
154 auto it = dataSeries->minXAxisData(min);
155
155
156 QCOMPARE(expectedOK, it != dataSeries->cend());
156 QCOMPARE(expectedOK, it != dataSeries->cend());
157
157
158 // If the method doesn't return a end iterator, checks with expected value
158 // If the method doesn't return a end iterator, checks with expected value
159 if (expectedOK) {
159 if (expectedOK) {
160 QCOMPARE(expectedMin, it->x());
160 QCOMPARE(expectedMin, it->x());
161 }
161 }
162 }
162 }
163
163
164 /**
164 /**
165 * Sets the structure of unit tests concerning getting the max x-axis data of a data series
165 * Sets the structure of unit tests concerning getting the max x-axis data of a data series
166 * @tparam T the type of data series on which to make the operation
166 * @tparam T the type of data series on which to make the operation
167 * @sa testMaxXAxisData_t()
167 * @sa testMaxXAxisData_t()
168 */
168 */
169 template <typename T>
169 template <typename T>
170 void testMaxXAxisData_struct(){
170 void testMaxXAxisData_struct(){
171 // Data series to get max data
171 // Data series to get max data
172 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
172 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
173
173
174 // Max data
174 // Max data
175 QTest::addColumn<double>("max");
175 QTest::addColumn<double>("max");
176
176
177 // Expected results
177 // Expected results
178 QTest::addColumn<bool>(
178 QTest::addColumn<bool>(
179 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
179 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
180 QTest::addColumn<double>(
180 QTest::addColumn<double>(
181 "expectedMax"); // Expected value when method doesn't return end iterator
181 "expectedMax"); // Expected value when method doesn't return end iterator
182
182
183 }
183 }
184
184
185 /**
185 /**
186 * Unit test concerning getting the max x-axis data of a data series
186 * Unit test concerning getting the max x-axis data of a data series
187 * @sa testMaxXAxisData_struct()
187 * @sa testMaxXAxisData_struct()
188 */
188 */
189 template <typename T>
189 template <typename T>
190 void testMaxXAxisData_t()
190 void testMaxXAxisData_t()
191 {
191 {
192 QFETCH(std::shared_ptr<T>, dataSeries);
192 QFETCH(std::shared_ptr<T>, dataSeries);
193 QFETCH(double, max);
193 QFETCH(double, max);
194
194
195 QFETCH(bool, expectedOK);
195 QFETCH(bool, expectedOK);
196 QFETCH(double, expectedMax);
196 QFETCH(double, expectedMax);
197
197
198 auto it = dataSeries->maxXAxisData(max);
198 auto it = dataSeries->maxXAxisData(max);
199
199
200 QCOMPARE(expectedOK, it != dataSeries->cend());
200 QCOMPARE(expectedOK, it != dataSeries->cend());
201
201
202 // If the method doesn't return a end iterator, checks with expected value
202 // If the method doesn't return a end iterator, checks with expected value
203 if (expectedOK) {
203 if (expectedOK) {
204 QCOMPARE(expectedMax, it->x());
204 QCOMPARE(expectedMax, it->x());
205 }
205 }
206 }
206 }
207
207
208 /**
208 /**
209 * Sets the structure of unit tests concerning getting the purge of a data series
209 * Sets the structure of unit tests concerning getting the purge of a data series
210 * @tparam T the type of data series on which to make the operation
210 * @tparam T the type of data series on which to make the operation
211 * @sa testMinXAxisData_t()
211 * @sa testMinXAxisData_t()
212 */
212 */
213 template <typename T>
213 template <typename T>
214 void testPurge_struct()
214 void testPurge_struct()
215 {
215 {
216 // Data series to purge
216 // Data series to purge
217 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
217 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
218 QTest::addColumn<double>("min");
218 QTest::addColumn<double>("min");
219 QTest::addColumn<double>("max");
219 QTest::addColumn<double>("max");
220
220
221 // Expected values after purge
221 // Expected values after purge
222 QTest::addColumn<DataContainer>("expectedXAxisData");
222 QTest::addColumn<DataContainer>("expectedXAxisData");
223 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
223 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
224 }
224 }
225
225
226 /**
226 /**
227 * Unit test concerning getting the purge of a data series
227 * Unit test concerning getting the purge of a data series
228 * @sa testPurge_struct()
228 * @sa testPurge_struct()
229 */
229 */
230 template <typename T>
230 template <typename T>
231 void testPurge_t(){
231 void testPurge_t(){
232 QFETCH(std::shared_ptr<T>, dataSeries);
232 QFETCH(std::shared_ptr<T>, dataSeries);
233 QFETCH(double, min);
233 QFETCH(double, min);
234 QFETCH(double, max);
234 QFETCH(double, max);
235
235
236 dataSeries->purge(min, max);
236 dataSeries->purge(min, max);
237
237
238 // Validates results
238 // Validates results
239 QFETCH(DataContainer, expectedXAxisData);
239 QFETCH(DataContainer, expectedXAxisData);
240 QFETCH(std::vector<DataContainer>, expectedValuesData);
240 QFETCH(std::vector<DataContainer>, expectedValuesData);
241
241
242 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
242 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
243 expectedValuesData);
243 expectedValuesData);
244 }
244 }
245
245
246 /**
246 /**
247 * Sets the structure of unit tests concerning getting subdata of a data series
247 * Sets the structure of unit tests concerning getting subdata of a data series
248 * @tparam DataSeriesType the type of data series on which to make the operation
248 * @tparam DataSeriesType the type of data series on which to make the operation
249 * @tparam ExpectedValuesType the type of values expected after the operation
249 * @tparam ExpectedValuesType the type of values expected after the operation
250 * @sa testSubDataSeries_t()
250 * @sa testSubDataSeries_t()
251 */
251 */
252 template <typename DataSeriesType, typename ExpectedValuesType>
252 template <typename DataSeriesType, typename ExpectedValuesType>
253 void testSubDataSeries_struct() {
253 void testSubDataSeries_struct() {
254 // Data series from which extract the subdata series
254 // Data series from which extract the subdata series
255 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries");
255 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries");
256 // Range to extract
256 // Range to extract
257 QTest::addColumn<SqpRange>("range");
257 QTest::addColumn<SqpRange>("range");
258
258
259 // Expected values for the subdata series
259 // Expected values for the subdata series
260 QTest::addColumn<DataContainer>("expectedXAxisData");
260 QTest::addColumn<DataContainer>("expectedXAxisData");
261 QTest::addColumn<ExpectedValuesType>("expectedValuesData");
261 QTest::addColumn<ExpectedValuesType>("expectedValuesData");
262 }
262 }
263
263
264 /**
264 /**
265 * Unit test concerning getting subdata of a data series
265 * Unit test concerning getting subdata of a data series
266 * @sa testSubDataSeries_struct()
266 * @sa testSubDataSeries_struct()
267 */
267 */
268 template <typename DataSeriesType, typename ExpectedValuesType>
268 template <typename DataSeriesType, typename ExpectedValuesType>
269 void testSubDataSeries_t(){
269 void testSubDataSeries_t(){
270 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries);
270 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries);
271 QFETCH(SqpRange, range);
271 QFETCH(SqpRange, range);
272
272
273 // Makes the operation
273 // Makes the operation
274 auto subDataSeries = std::dynamic_pointer_cast<DataSeriesType>(dataSeries->subDataSeries(range));
274 auto subDataSeries = std::dynamic_pointer_cast<DataSeriesType>(dataSeries->subDataSeries(range));
275 QVERIFY(subDataSeries != nullptr);
275 QVERIFY(subDataSeries != nullptr);
276
276
277 // Validates results
277 // Validates results
278 QFETCH(DataContainer, expectedXAxisData);
278 QFETCH(DataContainer, expectedXAxisData);
279 QFETCH(ExpectedValuesType, expectedValuesData);
279 QFETCH(ExpectedValuesType, expectedValuesData);
280
280
281 validateRange(subDataSeries->cbegin(), subDataSeries->cend(), expectedXAxisData, expectedValuesData);
281 validateRange(subDataSeries->cbegin(), subDataSeries->cend(), expectedXAxisData, expectedValuesData);
282 }
282 }
283
283
284 /**
284 /**
285 * Sets the structure of unit tests concerning getting the range of a data series
285 * Sets the structure of unit tests concerning getting the range of a data series
286 * @tparam T the type of data series on which to make the operation
286 * @tparam T the type of data series on which to make the operation
287 * @sa testXAxisRange_t()
287 * @sa testXAxisRange_t()
288 */
288 */
289 template <typename T>
289 template <typename T>
290 void testXAxisRange_struct(){
290 void testXAxisRange_struct(){
291 // Data series to get x-axis range
291 // Data series to get x-axis range
292 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
292 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
293
293
294 // Min/max values
294 // Min/max values
295 QTest::addColumn<double>("min");
295 QTest::addColumn<double>("min");
296 QTest::addColumn<double>("max");
296 QTest::addColumn<double>("max");
297
297
298 // Expected values
298 // Expected values
299 QTest::addColumn<DataContainer>("expectedXAxisData");
299 QTest::addColumn<DataContainer>("expectedXAxisData");
300 QTest::addColumn<DataContainer>("expectedValuesData");
300 QTest::addColumn<DataContainer>("expectedValuesData");
301 }
301 }
302
302
303 /**
303 /**
304 * Unit test concerning getting the range of a data series
304 * Unit test concerning getting the range of a data series
305 * @sa testXAxisRange_struct()
305 * @sa testXAxisRange_struct()
306 */
306 */
307 template <typename T>
307 template <typename T>
308 void testXAxisRange_t(){
308 void testXAxisRange_t(){
309 QFETCH(std::shared_ptr<T>, dataSeries);
309 QFETCH(std::shared_ptr<T>, dataSeries);
310 QFETCH(double, min);
310 QFETCH(double, min);
311 QFETCH(double, max);
311 QFETCH(double, max);
312
312
313 QFETCH(DataContainer, expectedXAxisData);
313 QFETCH(DataContainer, expectedXAxisData);
314 QFETCH(DataContainer, expectedValuesData);
314 QFETCH(DataContainer, expectedValuesData);
315
315
316 auto bounds = dataSeries->xAxisRange(min, max);
316 auto bounds = dataSeries->xAxisRange(min, max);
317 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
317 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
318 }
318 }
319
319
320 /**
320 /**
321 * Sets the structure of unit tests concerning getting values bounds of a data series
321 * Sets the structure of unit tests concerning getting values bounds of a data series
322 * @tparam T the type of data series on which to make the operation
322 * @tparam T the type of data series on which to make the operation
323 * @sa testValuesBounds_t()
323 * @sa testValuesBounds_t()
324 */
324 */
325 template <typename T>
325 template <typename T>
326 void testValuesBounds_struct()
326 void testValuesBounds_struct()
327 {
327 {
328 // Data series to get values bounds
328 // Data series to get values bounds
329 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
329 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
330
330
331 // x-axis range
331 // x-axis range
332 QTest::addColumn<double>("minXAxis");
332 QTest::addColumn<double>("minXAxis");
333 QTest::addColumn<double>("maxXAxis");
333 QTest::addColumn<double>("maxXAxis");
334
334
335 // Expected results
335 // Expected results
336 QTest::addColumn<bool>(
336 QTest::addColumn<bool>(
337 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
337 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
338 QTest::addColumn<double>("expectedMinValue");
338 QTest::addColumn<double>("expectedMinValue");
339 QTest::addColumn<double>("expectedMaxValue");
339 QTest::addColumn<double>("expectedMaxValue");
340 }
340 }
341
341
342 /**
342 /**
343 * Unit test concerning getting values bounds of a data series
343 * Unit test concerning getting values bounds of a data series
344 * @sa testValuesBounds_struct()
344 * @sa testValuesBounds_struct()
345 */
345 */
346 template <typename T>
346 template <typename T>
347 void testValuesBounds_t()
347 void testValuesBounds_t()
348 {
348 {
349 QFETCH(std::shared_ptr<T>, dataSeries);
349 QFETCH(std::shared_ptr<T>, dataSeries);
350 QFETCH(double, minXAxis);
350 QFETCH(double, minXAxis);
351 QFETCH(double, maxXAxis);
351 QFETCH(double, maxXAxis);
352
352
353 QFETCH(bool, expectedOK);
353 QFETCH(bool, expectedOK);
354 QFETCH(double, expectedMinValue);
354 QFETCH(double, expectedMinValue);
355 QFETCH(double, expectedMaxValue);
355 QFETCH(double, expectedMaxValue);
356
356
357 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
357 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
358 auto end = dataSeries->cend();
358 auto end = dataSeries->cend();
359
359
360 // Checks iterators with expected result
360 // Checks iterators with expected result
361 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
361 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
362
362
363 if (expectedOK) {
363 if (expectedOK) {
364 auto compare = [](const auto &v1, const auto &v2) {
364 auto compare = [](const auto &v1, const auto &v2) {
365 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
365 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
366 };
366 };
367
367
368 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
368 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
369 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
369 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
370 }
370 }
371 }
371 }
372
372
373 #endif // SCIQLOP_DATASERIESUTILS_H
373 #endif // SCIQLOP_DATASERIESTESTSUTILS_H
@@ -1,150 +1,113
1 #include <Data/ArrayData.h>
1 #include <Data/ArrayData.h>
2 #include <Data/OptionalAxis.h>
2 #include <Data/OptionalAxis.h>
3
3
4 #include <QObject>
4 #include <QObject>
5 #include <QtTest>
5 #include <QtTest>
6
6
7 Q_DECLARE_METATYPE(OptionalAxis)
7 Q_DECLARE_METATYPE(OptionalAxis)
8
8
9 class TestOptionalAxis : public QObject {
9 class TestOptionalAxis : public QObject {
10 Q_OBJECT
10 Q_OBJECT
11
11
12 private slots:
12 private slots:
13 /// Tests the creation of a undefined axis
13 /// Tests the creation of a undefined axis
14 void testNotDefinedAxisCtor();
14 void testNotDefinedAxisCtor();
15
15
16 /// Tests the creation of a undefined axis
16 /// Tests the creation of a undefined axis
17 void testDefinedAxisCtor_data();
17 void testDefinedAxisCtor_data();
18 void testDefinedAxisCtor();
18 void testDefinedAxisCtor();
19
19
20 /// Tests @sa OptionalAxis::at() method
21 void testAt_data();
22 void testAt();
23
24 /// Tests @sa OptionalAxis::size() method
20 /// Tests @sa OptionalAxis::size() method
25 void testSize_data();
21 void testSize_data();
26 void testSize();
22 void testSize();
27
23
28 /// Tests @sa OptionalAxis::unit() method
24 /// Tests @sa OptionalAxis::unit() method
29 void testUnit_data();
25 void testUnit_data();
30 void testUnit();
26 void testUnit();
31 };
27 };
32
28
33 void TestOptionalAxis::testNotDefinedAxisCtor()
29 void TestOptionalAxis::testNotDefinedAxisCtor()
34 {
30 {
35 OptionalAxis notDefinedAxis{};
31 OptionalAxis notDefinedAxis{};
36 QVERIFY(!notDefinedAxis.isDefined());
32 QVERIFY(!notDefinedAxis.isDefined());
37 }
33 }
38
34
39 void TestOptionalAxis::testDefinedAxisCtor_data()
35 void TestOptionalAxis::testDefinedAxisCtor_data()
40 {
36 {
41 QTest::addColumn<bool>("noData"); // If set to true, nullptr is passed as data of the axis
37 QTest::addColumn<bool>("noData"); // If set to true, nullptr is passed as data of the axis
42 QTest::addColumn<std::vector<double> >(
38 QTest::addColumn<std::vector<double> >(
43 "data"); // Values assigned to the axis when 'noData' flag is set to false
39 "data"); // Values assigned to the axis when 'noData' flag is set to false
44 QTest::addColumn<Unit>("unit"); // Unit assigned to the axis
40 QTest::addColumn<Unit>("unit"); // Unit assigned to the axis
45
41
46 QTest::newRow("validData") << false << std::vector<double>{1, 2, 3} << Unit{"Hz"};
42 QTest::newRow("validData") << false << std::vector<double>{1, 2, 3} << Unit{"Hz"};
47 QTest::newRow("invalidData") << true << std::vector<double>{} << Unit{"Hz"};
43 QTest::newRow("invalidData") << true << std::vector<double>{} << Unit{"Hz"};
48 }
44 }
49
45
50 void TestOptionalAxis::testDefinedAxisCtor()
46 void TestOptionalAxis::testDefinedAxisCtor()
51 {
47 {
52 QFETCH(bool, noData);
48 QFETCH(bool, noData);
53 QFETCH(Unit, unit);
49 QFETCH(Unit, unit);
54
50
55 // When there is no data, we expect that the constructor returns exception
51 // When there is no data, we expect that the constructor returns exception
56 if (noData) {
52 if (noData) {
57 QVERIFY_EXCEPTION_THROWN(OptionalAxis(nullptr, unit), std::invalid_argument);
53 QVERIFY_EXCEPTION_THROWN(OptionalAxis(nullptr, unit), std::invalid_argument);
58 }
54 }
59 else {
55 else {
60 QFETCH(std::vector<double>, data);
56 QFETCH(std::vector<double>, data);
61
57
62 OptionalAxis axis{std::make_shared<ArrayData<1> >(data), unit};
58 OptionalAxis axis{std::make_shared<ArrayData<1> >(data), unit};
63 QVERIFY(axis.isDefined());
59 QVERIFY(axis.isDefined());
64 }
60 }
65 }
61 }
66
62
67 void TestOptionalAxis::testAt_data()
68 {
69 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
70 QTest::addColumn<int>("index"); // Index to test in the axis
71 QTest::addColumn<double>("expectedValue"); // Expected axis value for the index
72
73 OptionalAxis definedAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}),
74 Unit{"Hz"}};
75
76 QTest::newRow("data1") << definedAxis << 0 << 1.;
77 QTest::newRow("data2") << definedAxis << 1 << 2.;
78 QTest::newRow("data3") << definedAxis << 2 << 3.;
79 QTest::newRow("data4 (index out of bounds)")
80 << definedAxis << 3
81 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
82 QTest::newRow("data5 (index out of bounds)")
83 << definedAxis << -1
84 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
85 QTest::newRow("data6 (axis not defined)")
86 << OptionalAxis{} << 0
87 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for undefined axis
88 }
89
90 void TestOptionalAxis::testAt()
91 {
92 QFETCH(OptionalAxis, axis);
93 QFETCH(int, index);
94 QFETCH(double, expectedValue);
95
96 auto value = axis.at(index);
97 QVERIFY((std::isnan(value) && std::isnan(expectedValue)) || value == expectedValue);
98 }
99
100 void TestOptionalAxis::testSize_data()
63 void TestOptionalAxis::testSize_data()
101 {
64 {
102 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
65 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
103 QTest::addColumn<int>("expectedSize"); // Expected number of data in the axis
66 QTest::addColumn<int>("expectedSize"); // Expected number of data in the axis
104
67
105 // Lambda that creates default defined axis (with the values passed in parameter)
68 // Lambda that creates default defined axis (with the values passed in parameter)
106 auto axis = [](std::vector<double> values) {
69 auto axis = [](std::vector<double> values) {
107 return OptionalAxis{std::make_shared<ArrayData<1> >(std::move(values)), Unit{"Hz"}};
70 return OptionalAxis{std::make_shared<ArrayData<1> >(std::move(values)), Unit{"Hz"}};
108 };
71 };
109
72
110 QTest::newRow("data1") << axis({}) << 0;
73 QTest::newRow("data1") << axis({}) << 0;
111 QTest::newRow("data2") << axis({1, 2, 3}) << 3;
74 QTest::newRow("data2") << axis({1, 2, 3}) << 3;
112 QTest::newRow("data3") << axis({1, 2, 3, 4}) << 4;
75 QTest::newRow("data3") << axis({1, 2, 3, 4}) << 4;
113 QTest::newRow("data4 (axis not defined)") << OptionalAxis{}
76 QTest::newRow("data4 (axis not defined)") << OptionalAxis{}
114 << 0; // Expects 0 for undefined axis
77 << 0; // Expects 0 for undefined axis
115 }
78 }
116
79
117 void TestOptionalAxis::testSize()
80 void TestOptionalAxis::testSize()
118 {
81 {
119 QFETCH(OptionalAxis, axis);
82 QFETCH(OptionalAxis, axis);
120 QFETCH(int, expectedSize);
83 QFETCH(int, expectedSize);
121
84
122 QCOMPARE(axis.size(), expectedSize);
85 QCOMPARE(axis.size(), expectedSize);
123 }
86 }
124
87
125 void TestOptionalAxis::testUnit_data()
88 void TestOptionalAxis::testUnit_data()
126 {
89 {
127 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
90 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
128 QTest::addColumn<Unit>("expectedUnit"); // Expected unit for the axis
91 QTest::addColumn<Unit>("expectedUnit"); // Expected unit for the axis
129
92
130 // Lambda that creates default defined axis (with the unit passed in parameter)
93 // Lambda that creates default defined axis (with the unit passed in parameter)
131 auto axis = [](Unit unit) {
94 auto axis = [](Unit unit) {
132 return OptionalAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}), unit};
95 return OptionalAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}), unit};
133 };
96 };
134
97
135 QTest::newRow("data1") << axis(Unit{"Hz"}) << Unit{"Hz"};
98 QTest::newRow("data1") << axis(Unit{"Hz"}) << Unit{"Hz"};
136 QTest::newRow("data2") << axis(Unit{"t", true}) << Unit{"t", true};
99 QTest::newRow("data2") << axis(Unit{"t", true}) << Unit{"t", true};
137 QTest::newRow("data3 (axis not defined)") << OptionalAxis{}
100 QTest::newRow("data3 (axis not defined)") << OptionalAxis{}
138 << Unit{}; // Expects default unit for undefined axis
101 << Unit{}; // Expects default unit for undefined axis
139 }
102 }
140
103
141 void TestOptionalAxis::testUnit()
104 void TestOptionalAxis::testUnit()
142 {
105 {
143 QFETCH(OptionalAxis, axis);
106 QFETCH(OptionalAxis, axis);
144 QFETCH(Unit, expectedUnit);
107 QFETCH(Unit, expectedUnit);
145
108
146 QCOMPARE(axis.unit(), expectedUnit);
109 QCOMPARE(axis.unit(), expectedUnit);
147 }
110 }
148
111
149 QTEST_MAIN(TestOptionalAxis)
112 QTEST_MAIN(TestOptionalAxis)
150 #include "TestOptionalAxis.moc"
113 #include "TestOptionalAxis.moc"
@@ -1,426 +1,426
1 #include "Data/ScalarSeries.h"
1 #include "Data/ScalarSeries.h"
2
2
3 #include "DataSeriesBuilders.h"
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
4 #include "DataSeriesTestsUtils.h"
5
5
6 #include <QObject>
6 #include <QObject>
7 #include <QtTest>
7 #include <QtTest>
8
8
9 /**
9 /**
10 * @brief The TestScalarSeries class defines unit tests on scalar series.
10 * @brief The TestScalarSeries class defines unit tests on scalar series.
11 *
11 *
12 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
12 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesTestsUtils)
13 */
13 */
14 class TestScalarSeries : public QObject {
14 class TestScalarSeries : public QObject {
15 Q_OBJECT
15 Q_OBJECT
16 private slots:
16 private slots:
17 /// Tests construction of a scalar series
17 /// Tests construction of a scalar series
18 void testCtor_data();
18 void testCtor_data();
19 void testCtor();
19 void testCtor();
20
20
21 /// Tests merge of two scalar series
21 /// Tests merge of two scalar series
22 void testMerge_data();
22 void testMerge_data();
23 void testMerge();
23 void testMerge();
24
24
25 /// Tests merge of a vector series in a scalar series
25 /// Tests merge of a vector series in a scalar series
26 void testMergeWithVector_data();
26 void testMergeWithVector_data();
27 void testMergeWithVector();
27 void testMergeWithVector();
28
28
29 /// Tests get min x-axis data of a scalar series
29 /// Tests get min x-axis data of a scalar series
30 void testMinXAxisData_data();
30 void testMinXAxisData_data();
31 void testMinXAxisData();
31 void testMinXAxisData();
32
32
33 /// Tests get max x-axis data of a scalar series
33 /// Tests get max x-axis data of a scalar series
34 void testMaxXAxisData_data();
34 void testMaxXAxisData_data();
35 void testMaxXAxisData();
35 void testMaxXAxisData();
36
36
37 /// Tests purge of a scalar series
37 /// Tests purge of a scalar series
38 void testPurge_data();
38 void testPurge_data();
39 void testPurge();
39 void testPurge();
40
40
41 /// Tests get x-axis range of a scalar series
41 /// Tests get x-axis range of a scalar series
42 void testXAxisRange_data();
42 void testXAxisRange_data();
43 void testXAxisRange();
43 void testXAxisRange();
44
44
45 /// Tests get values bounds of a scalar series
45 /// Tests get values bounds of a scalar series
46 void testValuesBounds_data();
46 void testValuesBounds_data();
47 void testValuesBounds();
47 void testValuesBounds();
48 };
48 };
49
49
50 void TestScalarSeries::testCtor_data()
50 void TestScalarSeries::testCtor_data()
51 {
51 {
52 // x-axis data
52 // x-axis data
53 QTest::addColumn<DataContainer>("xAxisData");
53 QTest::addColumn<DataContainer>("xAxisData");
54 // values data
54 // values data
55 QTest::addColumn<DataContainer>("valuesData");
55 QTest::addColumn<DataContainer>("valuesData");
56
56
57 // construction expected to be valid
57 // construction expected to be valid
58 QTest::addColumn<bool>("expectOK");
58 QTest::addColumn<bool>("expectOK");
59 // expected x-axis data (when construction is valid)
59 // expected x-axis data (when construction is valid)
60 QTest::addColumn<DataContainer>("expectedXAxisData");
60 QTest::addColumn<DataContainer>("expectedXAxisData");
61 // expected values data (when construction is valid)
61 // expected values data (when construction is valid)
62 QTest::addColumn<DataContainer>("expectedValuesData");
62 QTest::addColumn<DataContainer>("expectedValuesData");
63
63
64 QTest::newRow("invalidData (different sizes of vectors)")
64 QTest::newRow("invalidData (different sizes of vectors)")
65 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << false
65 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << false
66 << DataContainer{} << DataContainer{};
66 << DataContainer{} << DataContainer{};
67
67
68 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
68 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
69 << DataContainer{100., 200., 300., 400., 500.} << true
69 << DataContainer{100., 200., 300., 400., 500.} << true
70 << DataContainer{1., 2., 3., 4., 5.}
70 << DataContainer{1., 2., 3., 4., 5.}
71 << DataContainer{100., 200., 300., 400., 500.};
71 << DataContainer{100., 200., 300., 400., 500.};
72
72
73 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
73 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
74 << DataContainer{100., 200., 300., 400., 500.} << true
74 << DataContainer{100., 200., 300., 400., 500.} << true
75 << DataContainer{1., 2., 3., 4., 5.}
75 << DataContainer{1., 2., 3., 4., 5.}
76 << DataContainer{500., 400., 300., 200., 100.};
76 << DataContainer{500., 400., 300., 200., 100.};
77
77
78 QTest::newRow("unsortedData2")
78 QTest::newRow("unsortedData2")
79 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.} << true
79 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.} << true
80 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
80 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
81 }
81 }
82
82
83 void TestScalarSeries::testCtor()
83 void TestScalarSeries::testCtor()
84 {
84 {
85 // Creates series
85 // Creates series
86 QFETCH(DataContainer, xAxisData);
86 QFETCH(DataContainer, xAxisData);
87 QFETCH(DataContainer, valuesData);
87 QFETCH(DataContainer, valuesData);
88 QFETCH(bool, expectOK);
88 QFETCH(bool, expectOK);
89
89
90 if (expectOK) {
90 if (expectOK) {
91 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
91 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
92 Unit{}, Unit{});
92 Unit{}, Unit{});
93
93
94 // Validates results : we check that the data series is sorted on its x-axis data
94 // Validates results : we check that the data series is sorted on its x-axis data
95 QFETCH(DataContainer, expectedXAxisData);
95 QFETCH(DataContainer, expectedXAxisData);
96 QFETCH(DataContainer, expectedValuesData);
96 QFETCH(DataContainer, expectedValuesData);
97
97
98 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
98 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
99 }
99 }
100 else {
100 else {
101 QVERIFY_EXCEPTION_THROWN(std::make_shared<ScalarSeries>(
101 QVERIFY_EXCEPTION_THROWN(std::make_shared<ScalarSeries>(
102 std::move(xAxisData), std::move(valuesData), Unit{}, Unit{}),
102 std::move(xAxisData), std::move(valuesData), Unit{}, Unit{}),
103 std::invalid_argument);
103 std::invalid_argument);
104 }
104 }
105 }
105 }
106
106
107 void TestScalarSeries::testMerge_data()
107 void TestScalarSeries::testMerge_data()
108 {
108 {
109 testMerge_struct<ScalarSeries, DataContainer>();
109 testMerge_struct<ScalarSeries, DataContainer>();
110
110
111 QTest::newRow("sortedMerge") << ScalarBuilder{}
111 QTest::newRow("sortedMerge") << ScalarBuilder{}
112 .setX({1., 2., 3., 4., 5.})
112 .setX({1., 2., 3., 4., 5.})
113 .setValues({100., 200., 300., 400., 500.})
113 .setValues({100., 200., 300., 400., 500.})
114 .build()
114 .build()
115 << ScalarBuilder{}
115 << ScalarBuilder{}
116 .setX({6., 7., 8., 9., 10.})
116 .setX({6., 7., 8., 9., 10.})
117 .setValues({600., 700., 800., 900., 1000.})
117 .setValues({600., 700., 800., 900., 1000.})
118 .build()
118 .build()
119 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
119 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
120 << DataContainer{100., 200., 300., 400., 500.,
120 << DataContainer{100., 200., 300., 400., 500.,
121 600., 700., 800., 900., 1000.};
121 600., 700., 800., 900., 1000.};
122
122
123 QTest::newRow("unsortedMerge")
123 QTest::newRow("unsortedMerge")
124 << ScalarBuilder{}
124 << ScalarBuilder{}
125 .setX({6., 7., 8., 9., 10.})
125 .setX({6., 7., 8., 9., 10.})
126 .setValues({600., 700., 800., 900., 1000.})
126 .setValues({600., 700., 800., 900., 1000.})
127 .build()
127 .build()
128 << ScalarBuilder{}
128 << ScalarBuilder{}
129 .setX({1., 2., 3., 4., 5.})
129 .setX({1., 2., 3., 4., 5.})
130 .setValues({100., 200., 300., 400., 500.})
130 .setValues({100., 200., 300., 400., 500.})
131 .build()
131 .build()
132 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
132 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
133 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
133 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
134
134
135 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
135 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
136 << ScalarBuilder{}
136 << ScalarBuilder{}
137 .setX({1., 2., 8., 9., 10})
137 .setX({1., 2., 8., 9., 10})
138 .setValues({100., 200., 800., 900., 1000.})
138 .setValues({100., 200., 800., 900., 1000.})
139 .build()
139 .build()
140 << ScalarBuilder{}
140 << ScalarBuilder{}
141 .setX({3., 4., 5., 6., 7.})
141 .setX({3., 4., 5., 6., 7.})
142 .setValues({300., 400., 500., 600., 700.})
142 .setValues({300., 400., 500., 600., 700.})
143 .build()
143 .build()
144 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
144 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
145
145
146 QTest::newRow("unsortedMerge3")
146 QTest::newRow("unsortedMerge3")
147 << ScalarBuilder{}
147 << ScalarBuilder{}
148 .setX({3., 4., 5., 7., 8})
148 .setX({3., 4., 5., 7., 8})
149 .setValues({300., 400., 500., 700., 800.})
149 .setValues({300., 400., 500., 700., 800.})
150 .build()
150 .build()
151 << ScalarBuilder{}
151 << ScalarBuilder{}
152 .setX({1., 2., 3., 7., 10.})
152 .setX({1., 2., 3., 7., 10.})
153 .setValues({100., 200., 333., 777., 1000.})
153 .setValues({100., 200., 333., 777., 1000.})
154 .build()
154 .build()
155 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
155 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
156 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
156 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
157
157
158 QTest::newRow("emptySource") << ScalarBuilder{}
158 QTest::newRow("emptySource") << ScalarBuilder{}
159 .setX({3., 4., 5., 7., 8})
159 .setX({3., 4., 5., 7., 8})
160 .setValues({300., 400., 500., 700., 800.})
160 .setValues({300., 400., 500., 700., 800.})
161 .build()
161 .build()
162 << ScalarBuilder{}.setX({}).setValues({}).build()
162 << ScalarBuilder{}.setX({}).setValues({}).build()
163 << DataContainer{3., 4., 5., 7., 8.}
163 << DataContainer{3., 4., 5., 7., 8.}
164 << DataContainer{300., 400., 500., 700., 800.};
164 << DataContainer{300., 400., 500., 700., 800.};
165 }
165 }
166
166
167 void TestScalarSeries::testMerge()
167 void TestScalarSeries::testMerge()
168 {
168 {
169 testMerge_t<ScalarSeries, DataContainer>();
169 testMerge_t<ScalarSeries, DataContainer>();
170 }
170 }
171
171
172 void TestScalarSeries::testMergeWithVector_data()
172 void TestScalarSeries::testMergeWithVector_data()
173 {
173 {
174 testMergeDifferentTypes_struct<VectorSeries, ScalarSeries>();
174 testMergeDifferentTypes_struct<VectorSeries, ScalarSeries>();
175
175
176 QTest::newRow("mergeVectorInScalar") << ScalarBuilder{}
176 QTest::newRow("mergeVectorInScalar") << ScalarBuilder{}
177 .setX({1., 2., 3., 4., 5.})
177 .setX({1., 2., 3., 4., 5.})
178 .setValues({100., 200., 300., 400., 500.})
178 .setValues({100., 200., 300., 400., 500.})
179 .build()
179 .build()
180 << VectorBuilder{}
180 << VectorBuilder{}
181 .setX({6., 7., 8., 9., 10.})
181 .setX({6., 7., 8., 9., 10.})
182 .setXValues({600., 700., 800., 900., 1000.})
182 .setXValues({600., 700., 800., 900., 1000.})
183 .setYValues({610., 710., 810., 910., 1010.})
183 .setYValues({610., 710., 810., 910., 1010.})
184 .setZValues({620., 720., 820., 920., 1020.})
184 .setZValues({620., 720., 820., 920., 1020.})
185 .build()
185 .build()
186 << DataContainer{1., 2., 3., 4., 5.}
186 << DataContainer{1., 2., 3., 4., 5.}
187 << DataContainer{100., 200., 300., 400., 500.};
187 << DataContainer{100., 200., 300., 400., 500.};
188 }
188 }
189
189
190 void TestScalarSeries::testMergeWithVector()
190 void TestScalarSeries::testMergeWithVector()
191 {
191 {
192 testMergeDifferentTypes_t<VectorSeries, ScalarSeries>();
192 testMergeDifferentTypes_t<VectorSeries, ScalarSeries>();
193 }
193 }
194
194
195 void TestScalarSeries::testMinXAxisData_data()
195 void TestScalarSeries::testMinXAxisData_data()
196 {
196 {
197 testMinXAxisData_struct<ScalarSeries>();
197 testMinXAxisData_struct<ScalarSeries>();
198
198
199 QTest::newRow("minData1") << ScalarBuilder{}
199 QTest::newRow("minData1") << ScalarBuilder{}
200 .setX({1., 2., 3., 4., 5.})
200 .setX({1., 2., 3., 4., 5.})
201 .setValues({100., 200., 300., 400., 500.})
201 .setValues({100., 200., 300., 400., 500.})
202 .build()
202 .build()
203 << 0. << true << 1.;
203 << 0. << true << 1.;
204 QTest::newRow("minData2") << ScalarBuilder{}
204 QTest::newRow("minData2") << ScalarBuilder{}
205 .setX({1., 2., 3., 4., 5.})
205 .setX({1., 2., 3., 4., 5.})
206 .setValues({100., 200., 300., 400., 500.})
206 .setValues({100., 200., 300., 400., 500.})
207 .build()
207 .build()
208 << 1. << true << 1.;
208 << 1. << true << 1.;
209 QTest::newRow("minData3") << ScalarBuilder{}
209 QTest::newRow("minData3") << ScalarBuilder{}
210 .setX({1., 2., 3., 4., 5.})
210 .setX({1., 2., 3., 4., 5.})
211 .setValues({100., 200., 300., 400., 500.})
211 .setValues({100., 200., 300., 400., 500.})
212 .build()
212 .build()
213 << 1.1 << true << 2.;
213 << 1.1 << true << 2.;
214 QTest::newRow("minData4") << ScalarBuilder{}
214 QTest::newRow("minData4") << ScalarBuilder{}
215 .setX({1., 2., 3., 4., 5.})
215 .setX({1., 2., 3., 4., 5.})
216 .setValues({100., 200., 300., 400., 500.})
216 .setValues({100., 200., 300., 400., 500.})
217 .build()
217 .build()
218 << 5. << true << 5.;
218 << 5. << true << 5.;
219 QTest::newRow("minData5") << ScalarBuilder{}
219 QTest::newRow("minData5") << ScalarBuilder{}
220 .setX({1., 2., 3., 4., 5.})
220 .setX({1., 2., 3., 4., 5.})
221 .setValues({100., 200., 300., 400., 500.})
221 .setValues({100., 200., 300., 400., 500.})
222 .build()
222 .build()
223 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
223 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
224 QTest::newRow("minData6") << ScalarBuilder{}.setX({}).setValues({}).build() << 1.1 << false
224 QTest::newRow("minData6") << ScalarBuilder{}.setX({}).setValues({}).build() << 1.1 << false
225 << std::numeric_limits<double>::quiet_NaN();
225 << std::numeric_limits<double>::quiet_NaN();
226 }
226 }
227
227
228 void TestScalarSeries::testMinXAxisData()
228 void TestScalarSeries::testMinXAxisData()
229 {
229 {
230 testMinXAxisData_t<ScalarSeries>();
230 testMinXAxisData_t<ScalarSeries>();
231 }
231 }
232
232
233 void TestScalarSeries::testMaxXAxisData_data()
233 void TestScalarSeries::testMaxXAxisData_data()
234 {
234 {
235 testMaxXAxisData_struct<ScalarSeries>();
235 testMaxXAxisData_struct<ScalarSeries>();
236
236
237 QTest::newRow("maxData1") << ScalarBuilder{}
237 QTest::newRow("maxData1") << ScalarBuilder{}
238 .setX({1., 2., 3., 4., 5.})
238 .setX({1., 2., 3., 4., 5.})
239 .setValues({100., 200., 300., 400., 500.})
239 .setValues({100., 200., 300., 400., 500.})
240 .build()
240 .build()
241 << 6. << true << 5.;
241 << 6. << true << 5.;
242 QTest::newRow("maxData2") << ScalarBuilder{}
242 QTest::newRow("maxData2") << ScalarBuilder{}
243 .setX({1., 2., 3., 4., 5.})
243 .setX({1., 2., 3., 4., 5.})
244 .setValues({100., 200., 300., 400., 500.})
244 .setValues({100., 200., 300., 400., 500.})
245 .build()
245 .build()
246 << 5. << true << 5.;
246 << 5. << true << 5.;
247 QTest::newRow("maxData3") << ScalarBuilder{}
247 QTest::newRow("maxData3") << ScalarBuilder{}
248 .setX({1., 2., 3., 4., 5.})
248 .setX({1., 2., 3., 4., 5.})
249 .setValues({100., 200., 300., 400., 500.})
249 .setValues({100., 200., 300., 400., 500.})
250 .build()
250 .build()
251 << 4.9 << true << 4.;
251 << 4.9 << true << 4.;
252 QTest::newRow("maxData4") << ScalarBuilder{}
252 QTest::newRow("maxData4") << ScalarBuilder{}
253 .setX({1., 2., 3., 4., 5.})
253 .setX({1., 2., 3., 4., 5.})
254 .setValues({100., 200., 300., 400., 500.})
254 .setValues({100., 200., 300., 400., 500.})
255 .build()
255 .build()
256 << 1.1 << true << 1.;
256 << 1.1 << true << 1.;
257 QTest::newRow("maxData5") << ScalarBuilder{}
257 QTest::newRow("maxData5") << ScalarBuilder{}
258 .setX({1., 2., 3., 4., 5.})
258 .setX({1., 2., 3., 4., 5.})
259 .setValues({100., 200., 300., 400., 500.})
259 .setValues({100., 200., 300., 400., 500.})
260 .build()
260 .build()
261 << 1. << true << 1.;
261 << 1. << true << 1.;
262 QTest::newRow("maxData6") << ScalarBuilder{}.setX({}).setValues({}).build() << 1.1 << false
262 QTest::newRow("maxData6") << ScalarBuilder{}.setX({}).setValues({}).build() << 1.1 << false
263 << std::numeric_limits<double>::quiet_NaN();
263 << std::numeric_limits<double>::quiet_NaN();
264 }
264 }
265
265
266 void TestScalarSeries::testMaxXAxisData()
266 void TestScalarSeries::testMaxXAxisData()
267 {
267 {
268 testMaxXAxisData_t<ScalarSeries>();
268 testMaxXAxisData_t<ScalarSeries>();
269 }
269 }
270
270
271 void TestScalarSeries::testPurge_data()
271 void TestScalarSeries::testPurge_data()
272 {
272 {
273 testPurge_struct<ScalarSeries>();
273 testPurge_struct<ScalarSeries>();
274
274
275 QTest::newRow("purgeScalar") << ScalarBuilder{}
275 QTest::newRow("purgeScalar") << ScalarBuilder{}
276 .setX({1., 2., 3., 4., 5.})
276 .setX({1., 2., 3., 4., 5.})
277 .setValues({100., 200., 300., 400., 500.})
277 .setValues({100., 200., 300., 400., 500.})
278 .build()
278 .build()
279 << 2. << 4. << DataContainer{2., 3., 4.}
279 << 2. << 4. << DataContainer{2., 3., 4.}
280 << std::vector<DataContainer>{{200., 300., 400.}};
280 << std::vector<DataContainer>{{200., 300., 400.}};
281 QTest::newRow("purgeScalar1 (min/max swap)") << ScalarBuilder{}
281 QTest::newRow("purgeScalar1 (min/max swap)") << ScalarBuilder{}
282 .setX({1., 2., 3., 4., 5.})
282 .setX({1., 2., 3., 4., 5.})
283 .setValues({100., 200., 300., 400., 500.})
283 .setValues({100., 200., 300., 400., 500.})
284 .build()
284 .build()
285 << 4. << 2. << DataContainer{2., 3., 4.}
285 << 4. << 2. << DataContainer{2., 3., 4.}
286 << std::vector<DataContainer>{{200., 300., 400.}};
286 << std::vector<DataContainer>{{200., 300., 400.}};
287 QTest::newRow("purgeScalar2") << ScalarBuilder{}
287 QTest::newRow("purgeScalar2") << ScalarBuilder{}
288 .setX({1., 2., 3., 4., 5.})
288 .setX({1., 2., 3., 4., 5.})
289 .setValues({100., 200., 300., 400., 500.})
289 .setValues({100., 200., 300., 400., 500.})
290 .build()
290 .build()
291 << 0. << 2.5 << DataContainer{1., 2.}
291 << 0. << 2.5 << DataContainer{1., 2.}
292 << std::vector<DataContainer>{{100., 200.}};
292 << std::vector<DataContainer>{{100., 200.}};
293 QTest::newRow("purgeScalar3") << ScalarBuilder{}
293 QTest::newRow("purgeScalar3") << ScalarBuilder{}
294 .setX({1., 2., 3., 4., 5.})
294 .setX({1., 2., 3., 4., 5.})
295 .setValues({100., 200., 300., 400., 500.})
295 .setValues({100., 200., 300., 400., 500.})
296 .build()
296 .build()
297 << 3.5 << 7. << DataContainer{4., 5.}
297 << 3.5 << 7. << DataContainer{4., 5.}
298 << std::vector<DataContainer>{{400., 500.}};
298 << std::vector<DataContainer>{{400., 500.}};
299 QTest::newRow("purgeScalar4") << ScalarBuilder{}
299 QTest::newRow("purgeScalar4") << ScalarBuilder{}
300 .setX({1., 2., 3., 4., 5.})
300 .setX({1., 2., 3., 4., 5.})
301 .setValues({100., 200., 300., 400., 500.})
301 .setValues({100., 200., 300., 400., 500.})
302 .build()
302 .build()
303 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
303 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
304 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
304 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
305 QTest::newRow("purgeScalar5") << ScalarBuilder{}
305 QTest::newRow("purgeScalar5") << ScalarBuilder{}
306 .setX({1., 2., 3., 4., 5.})
306 .setX({1., 2., 3., 4., 5.})
307 .setValues({100., 200., 300., 400., 500.})
307 .setValues({100., 200., 300., 400., 500.})
308 .build()
308 .build()
309 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
309 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
310 }
310 }
311
311
312 void TestScalarSeries::testPurge()
312 void TestScalarSeries::testPurge()
313 {
313 {
314 testPurge_t<ScalarSeries>();
314 testPurge_t<ScalarSeries>();
315 }
315 }
316
316
317 void TestScalarSeries::testXAxisRange_data()
317 void TestScalarSeries::testXAxisRange_data()
318 {
318 {
319 testXAxisRange_struct<ScalarSeries>();
319 testXAxisRange_struct<ScalarSeries>();
320
320
321 QTest::newRow("xAxisRange") << ScalarBuilder{}
321 QTest::newRow("xAxisRange") << ScalarBuilder{}
322 .setX({1., 2., 3., 4., 5.})
322 .setX({1., 2., 3., 4., 5.})
323 .setValues({100., 200., 300., 400., 500.})
323 .setValues({100., 200., 300., 400., 500.})
324 .build()
324 .build()
325 << -1. << 3.2 << DataContainer{1., 2., 3.}
325 << -1. << 3.2 << DataContainer{1., 2., 3.}
326 << DataContainer{100., 200., 300.};
326 << DataContainer{100., 200., 300.};
327 QTest::newRow("xAxisRange1 (min/max swap)") << ScalarBuilder{}
327 QTest::newRow("xAxisRange1 (min/max swap)") << ScalarBuilder{}
328 .setX({1., 2., 3., 4., 5.})
328 .setX({1., 2., 3., 4., 5.})
329 .setValues({100., 200., 300., 400., 500.})
329 .setValues({100., 200., 300., 400., 500.})
330 .build()
330 .build()
331 << 3.2 << -1. << DataContainer{1., 2., 3.}
331 << 3.2 << -1. << DataContainer{1., 2., 3.}
332 << DataContainer{100., 200., 300.};
332 << DataContainer{100., 200., 300.};
333 QTest::newRow("xAxisRange2") << ScalarBuilder{}
333 QTest::newRow("xAxisRange2") << ScalarBuilder{}
334 .setX({1., 2., 3., 4., 5.})
334 .setX({1., 2., 3., 4., 5.})
335 .setValues({100., 200., 300., 400., 500.})
335 .setValues({100., 200., 300., 400., 500.})
336 .build()
336 .build()
337 << 1. << 4. << DataContainer{1., 2., 3., 4.}
337 << 1. << 4. << DataContainer{1., 2., 3., 4.}
338 << DataContainer{100., 200., 300., 400.};
338 << DataContainer{100., 200., 300., 400.};
339 QTest::newRow("xAxisRange3") << ScalarBuilder{}
339 QTest::newRow("xAxisRange3") << ScalarBuilder{}
340 .setX({1., 2., 3., 4., 5.})
340 .setX({1., 2., 3., 4., 5.})
341 .setValues({100., 200., 300., 400., 500.})
341 .setValues({100., 200., 300., 400., 500.})
342 .build()
342 .build()
343 << 1. << 3.9 << DataContainer{1., 2., 3.}
343 << 1. << 3.9 << DataContainer{1., 2., 3.}
344 << DataContainer{100., 200., 300.};
344 << DataContainer{100., 200., 300.};
345 QTest::newRow("xAxisRange4") << ScalarBuilder{}
345 QTest::newRow("xAxisRange4") << ScalarBuilder{}
346 .setX({1., 2., 3., 4., 5.})
346 .setX({1., 2., 3., 4., 5.})
347 .setValues({100., 200., 300., 400., 500.})
347 .setValues({100., 200., 300., 400., 500.})
348 .build()
348 .build()
349 << 0. << 0.9 << DataContainer{} << DataContainer{};
349 << 0. << 0.9 << DataContainer{} << DataContainer{};
350 QTest::newRow("xAxisRange5") << ScalarBuilder{}
350 QTest::newRow("xAxisRange5") << ScalarBuilder{}
351 .setX({1., 2., 3., 4., 5.})
351 .setX({1., 2., 3., 4., 5.})
352 .setValues({100., 200., 300., 400., 500.})
352 .setValues({100., 200., 300., 400., 500.})
353 .build()
353 .build()
354 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
354 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
355 QTest::newRow("xAxisRange6") << ScalarBuilder{}
355 QTest::newRow("xAxisRange6") << ScalarBuilder{}
356 .setX({1., 2., 3., 4., 5.})
356 .setX({1., 2., 3., 4., 5.})
357 .setValues({100., 200., 300., 400., 500.})
357 .setValues({100., 200., 300., 400., 500.})
358 .build()
358 .build()
359 << 2.1 << 6. << DataContainer{3., 4., 5.}
359 << 2.1 << 6. << DataContainer{3., 4., 5.}
360 << DataContainer{300., 400., 500.};
360 << DataContainer{300., 400., 500.};
361 QTest::newRow("xAxisRange7") << ScalarBuilder{}
361 QTest::newRow("xAxisRange7") << ScalarBuilder{}
362 .setX({1., 2., 3., 4., 5.})
362 .setX({1., 2., 3., 4., 5.})
363 .setValues({100., 200., 300., 400., 500.})
363 .setValues({100., 200., 300., 400., 500.})
364 .build()
364 .build()
365 << 6. << 9. << DataContainer{} << DataContainer{};
365 << 6. << 9. << DataContainer{} << DataContainer{};
366 QTest::newRow("xAxisRange8") << ScalarBuilder{}
366 QTest::newRow("xAxisRange8") << ScalarBuilder{}
367 .setX({1., 2., 3., 4., 5.})
367 .setX({1., 2., 3., 4., 5.})
368 .setValues({100., 200., 300., 400., 500.})
368 .setValues({100., 200., 300., 400., 500.})
369 .build()
369 .build()
370 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
370 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
371 }
371 }
372
372
373 void TestScalarSeries::testXAxisRange()
373 void TestScalarSeries::testXAxisRange()
374 {
374 {
375 testXAxisRange_t<ScalarSeries>();
375 testXAxisRange_t<ScalarSeries>();
376 }
376 }
377
377
378 void TestScalarSeries::testValuesBounds_data()
378 void TestScalarSeries::testValuesBounds_data()
379 {
379 {
380 testValuesBounds_struct<ScalarSeries>();
380 testValuesBounds_struct<ScalarSeries>();
381
381
382 auto nan = std::numeric_limits<double>::quiet_NaN();
382 auto nan = std::numeric_limits<double>::quiet_NaN();
383
383
384 QTest::newRow("scalarBounds1") << ScalarBuilder{}
384 QTest::newRow("scalarBounds1") << ScalarBuilder{}
385 .setX({1., 2., 3., 4., 5.})
385 .setX({1., 2., 3., 4., 5.})
386 .setValues({100., 200., 300., 400., 500.})
386 .setValues({100., 200., 300., 400., 500.})
387 .build()
387 .build()
388 << 0. << 6. << true << 100. << 500.;
388 << 0. << 6. << true << 100. << 500.;
389 QTest::newRow("scalarBounds2") << ScalarBuilder{}
389 QTest::newRow("scalarBounds2") << ScalarBuilder{}
390 .setX({1., 2., 3., 4., 5.})
390 .setX({1., 2., 3., 4., 5.})
391 .setValues({100., 200., 300., 400., 500.})
391 .setValues({100., 200., 300., 400., 500.})
392 .build()
392 .build()
393 << 2. << 4. << true << 200. << 400.;
393 << 2. << 4. << true << 200. << 400.;
394 QTest::newRow("scalarBounds3") << ScalarBuilder{}
394 QTest::newRow("scalarBounds3") << ScalarBuilder{}
395 .setX({1., 2., 3., 4., 5.})
395 .setX({1., 2., 3., 4., 5.})
396 .setValues({100., 200., 300., 400., 500.})
396 .setValues({100., 200., 300., 400., 500.})
397 .build()
397 .build()
398 << 0. << 0.5 << false << nan << nan;
398 << 0. << 0.5 << false << nan << nan;
399 QTest::newRow("scalarBounds4") << ScalarBuilder{}
399 QTest::newRow("scalarBounds4") << ScalarBuilder{}
400 .setX({1., 2., 3., 4., 5.})
400 .setX({1., 2., 3., 4., 5.})
401 .setValues({100., 200., 300., 400., 500.})
401 .setValues({100., 200., 300., 400., 500.})
402 .build()
402 .build()
403 << 5.1 << 6. << false << nan << nan;
403 << 5.1 << 6. << false << nan << nan;
404 QTest::newRow("scalarBounds5") << ScalarBuilder{}.setX({1.}).setValues({100.}).build() << 0.
404 QTest::newRow("scalarBounds5") << ScalarBuilder{}.setX({1.}).setValues({100.}).build() << 0.
405 << 2. << true << 100. << 100.;
405 << 2. << true << 100. << 100.;
406 QTest::newRow("scalarBounds6") << ScalarBuilder{}.setX({}).setValues({}).build() << 0. << 2.
406 QTest::newRow("scalarBounds6") << ScalarBuilder{}.setX({}).setValues({}).build() << 0. << 2.
407 << false << nan << nan;
407 << false << nan << nan;
408
408
409 // Tests with NaN values: NaN values are not included in min/max search
409 // Tests with NaN values: NaN values are not included in min/max search
410 QTest::newRow("scalarBounds7") << ScalarBuilder{}
410 QTest::newRow("scalarBounds7") << ScalarBuilder{}
411 .setX({1., 2., 3., 4., 5.})
411 .setX({1., 2., 3., 4., 5.})
412 .setValues({nan, 200., 300., 400., nan})
412 .setValues({nan, 200., 300., 400., nan})
413 .build()
413 .build()
414 << 0. << 6. << true << 200. << 400.;
414 << 0. << 6. << true << 200. << 400.;
415 QTest::newRow("scalarBounds8")
415 QTest::newRow("scalarBounds8")
416 << ScalarBuilder{}.setX({1., 2., 3., 4., 5.}).setValues({nan, nan, nan, nan, nan}).build()
416 << ScalarBuilder{}.setX({1., 2., 3., 4., 5.}).setValues({nan, nan, nan, nan, nan}).build()
417 << 0. << 6. << true << nan << nan;
417 << 0. << 6. << true << nan << nan;
418 }
418 }
419
419
420 void TestScalarSeries::testValuesBounds()
420 void TestScalarSeries::testValuesBounds()
421 {
421 {
422 testValuesBounds_t<ScalarSeries>();
422 testValuesBounds_t<ScalarSeries>();
423 }
423 }
424
424
425 QTEST_MAIN(TestScalarSeries)
425 QTEST_MAIN(TestScalarSeries)
426 #include "TestScalarSeries.moc"
426 #include "TestScalarSeries.moc"
@@ -1,198 +1,198
1 #include "Data/SpectrogramSeries.h"
1 #include "Data/SpectrogramSeries.h"
2
2
3 #include "DataSeriesBuilders.h"
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
4 #include "DataSeriesTestsUtils.h"
5
5
6 #include <QObject>
6 #include <QObject>
7 #include <QtTest>
7 #include <QtTest>
8
8
9 namespace {
9 namespace {
10
10
11 // Aliases used to facilitate reading of test inputs
11 // Aliases used to facilitate reading of test inputs
12 using X = DataContainer;
12 using X = DataContainer;
13 using Y = DataContainer;
13 using Y = DataContainer;
14 using Values = DataContainer;
14 using Values = DataContainer;
15 using Components = std::vector<DataContainer>;
15 using Components = std::vector<DataContainer>;
16
16
17 } // namespace
17 } // namespace
18
18
19 /**
19 /**
20 * @brief The TestSpectrogramSeries class defines unit tests on spectrogram series.
20 * @brief The TestSpectrogramSeries class defines unit tests on spectrogram series.
21 *
21 *
22 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
22 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesTestsUtils)
23 */
23 */
24 class TestSpectrogramSeries : public QObject {
24 class TestSpectrogramSeries : public QObject {
25 Q_OBJECT
25 Q_OBJECT
26 private slots:
26 private slots:
27
27
28 /// Tests construction of a spectrogram series
28 /// Tests construction of a spectrogram series
29 void testCtor_data();
29 void testCtor_data();
30 void testCtor();
30 void testCtor();
31
31
32 /// Tests merge of two spectrogram series
32 /// Tests merge of two spectrogram series
33 void testMerge_data();
33 void testMerge_data();
34 void testMerge();
34 void testMerge();
35
35
36 /// Tests get subdata of a spectrogram series
36 /// Tests get subdata of a spectrogram series
37 void testSubDataSeries_data();
37 void testSubDataSeries_data();
38 void testSubDataSeries();
38 void testSubDataSeries();
39 };
39 };
40
40
41 void TestSpectrogramSeries::testCtor_data()
41 void TestSpectrogramSeries::testCtor_data()
42 {
42 {
43 // x-axis data
43 // x-axis data
44 QTest::addColumn<X>("xAxisData");
44 QTest::addColumn<X>("xAxisData");
45 // y-axis data
45 // y-axis data
46 QTest::addColumn<Y>("yAxisData");
46 QTest::addColumn<Y>("yAxisData");
47 // values data
47 // values data
48 QTest::addColumn<Values>("valuesData");
48 QTest::addColumn<Values>("valuesData");
49
49
50 // construction expected to be valid
50 // construction expected to be valid
51 QTest::addColumn<bool>("expectOK");
51 QTest::addColumn<bool>("expectOK");
52 // expected x-axis data (when construction is valid)
52 // expected x-axis data (when construction is valid)
53 QTest::addColumn<X>("expectedXAxisData");
53 QTest::addColumn<X>("expectedXAxisData");
54 // expected components data (when construction is valid)
54 // expected components data (when construction is valid)
55 QTest::addColumn<Components>("expectedComponentsData");
55 QTest::addColumn<Components>("expectedComponentsData");
56
56
57 QTest::newRow(
57 QTest::newRow(
58 "invalidData (number of values by component aren't equal to the number of x-axis data)")
58 "invalidData (number of values by component aren't equal to the number of x-axis data)")
59 << X{1., 2., 3., 4., 5.} << Y{1., 2., 3.} << Values{1., 2., 3.} << false << X{}
59 << X{1., 2., 3., 4., 5.} << Y{1., 2., 3.} << Values{1., 2., 3.} << false << X{}
60 << Components{};
60 << Components{};
61
61
62 QTest::newRow("invalidData (number of components aren't equal to the number of y-axis data)")
62 QTest::newRow("invalidData (number of components aren't equal to the number of y-axis data)")
63 << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
63 << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
64 << Values{1., 2., 3., 4., 5.} // 1 component
64 << Values{1., 2., 3., 4., 5.} // 1 component
65 << false << X{} << Components{};
65 << false << X{} << Components{};
66
66
67 QTest::newRow("sortedData") << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
67 QTest::newRow("sortedData") << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
68 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} // 2 components
68 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} // 2 components
69 << true << X{1., 2., 3., 4., 5.}
69 << true << X{1., 2., 3., 4., 5.}
70 << Components{{1., 3., 5., 7., 9.}, {2., 4., 6., 8., 10.}};
70 << Components{{1., 3., 5., 7., 9.}, {2., 4., 6., 8., 10.}};
71
71
72 QTest::newRow("unsortedData") << X{5., 4., 3., 2., 1.} << Y{1., 2.}
72 QTest::newRow("unsortedData") << X{5., 4., 3., 2., 1.} << Y{1., 2.}
73 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << true
73 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << true
74 << X{1., 2., 3., 4., 5.}
74 << X{1., 2., 3., 4., 5.}
75 << Components{{9., 7., 5., 3., 1.}, {10., 8., 6., 4., 2.}};
75 << Components{{9., 7., 5., 3., 1.}, {10., 8., 6., 4., 2.}};
76 }
76 }
77
77
78 void TestSpectrogramSeries::testCtor()
78 void TestSpectrogramSeries::testCtor()
79 {
79 {
80 // Creates series
80 // Creates series
81 QFETCH(X, xAxisData);
81 QFETCH(X, xAxisData);
82 QFETCH(Y, yAxisData);
82 QFETCH(Y, yAxisData);
83 QFETCH(Values, valuesData);
83 QFETCH(Values, valuesData);
84 QFETCH(bool, expectOK);
84 QFETCH(bool, expectOK);
85
85
86 if (expectOK) {
86 if (expectOK) {
87 auto series = SpectrogramBuilder{}
87 auto series = SpectrogramBuilder{}
88 .setX(std::move(xAxisData))
88 .setX(std::move(xAxisData))
89 .setY(std::move(yAxisData))
89 .setY(std::move(yAxisData))
90 .setValues(std::move(valuesData))
90 .setValues(std::move(valuesData))
91 .build();
91 .build();
92
92
93 // Validates results
93 // Validates results
94 QFETCH(X, expectedXAxisData);
94 QFETCH(X, expectedXAxisData);
95 QFETCH(Components, expectedComponentsData);
95 QFETCH(Components, expectedComponentsData);
96 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedComponentsData);
96 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedComponentsData);
97 }
97 }
98 else {
98 else {
99 QVERIFY_EXCEPTION_THROWN(SpectrogramBuilder{}
99 QVERIFY_EXCEPTION_THROWN(SpectrogramBuilder{}
100 .setX(std::move(xAxisData))
100 .setX(std::move(xAxisData))
101 .setY(std::move(yAxisData))
101 .setY(std::move(yAxisData))
102 .setValues(std::move(valuesData))
102 .setValues(std::move(valuesData))
103 .build(),
103 .build(),
104 std::invalid_argument);
104 std::invalid_argument);
105 }
105 }
106 }
106 }
107
107
108 void TestSpectrogramSeries::testMerge_data()
108 void TestSpectrogramSeries::testMerge_data()
109 {
109 {
110 testMerge_struct<SpectrogramSeries, Components>();
110 testMerge_struct<SpectrogramSeries, Components>();
111
111
112 QTest::newRow("sortedMerge") << SpectrogramBuilder{}
112 QTest::newRow("sortedMerge") << SpectrogramBuilder{}
113 .setX({1., 2., 3.})
113 .setX({1., 2., 3.})
114 .setY({1., 2.})
114 .setY({1., 2.})
115 .setValues({10., 11., 20., 21., 30., 31})
115 .setValues({10., 11., 20., 21., 30., 31})
116 .build()
116 .build()
117 << SpectrogramBuilder{}
117 << SpectrogramBuilder{}
118 .setX({4., 5., 6.})
118 .setX({4., 5., 6.})
119 .setY({1., 2.})
119 .setY({1., 2.})
120 .setValues({40., 41., 50., 51., 60., 61})
120 .setValues({40., 41., 50., 51., 60., 61})
121 .build()
121 .build()
122 << DataContainer{1., 2., 3., 4., 5., 6.}
122 << DataContainer{1., 2., 3., 4., 5., 6.}
123 << Components{{10., 20., 30., 40., 50., 60.},
123 << Components{{10., 20., 30., 40., 50., 60.},
124 {11., 21., 31., 41., 51., 61}};
124 {11., 21., 31., 41., 51., 61}};
125
125
126 QTest::newRow(
126 QTest::newRow(
127 "unsortedMerge (merge not made because the two data series have different y-axes)")
127 "unsortedMerge (merge not made because the two data series have different y-axes)")
128 << SpectrogramBuilder{}
128 << SpectrogramBuilder{}
129 .setX({4., 5., 6.})
129 .setX({4., 5., 6.})
130 .setY({1., 2.})
130 .setY({1., 2.})
131 .setValues({40., 41., 50., 51., 60., 61})
131 .setValues({40., 41., 50., 51., 60., 61})
132 .build()
132 .build()
133 << SpectrogramBuilder{}
133 << SpectrogramBuilder{}
134 .setX({1., 2., 3.})
134 .setX({1., 2., 3.})
135 .setY({3., 4.})
135 .setY({3., 4.})
136 .setValues({10., 11., 20., 21., 30., 31})
136 .setValues({10., 11., 20., 21., 30., 31})
137 .build()
137 .build()
138 << DataContainer{4., 5., 6.} << Components{{40., 50., 60.}, {41., 51., 61}};
138 << DataContainer{4., 5., 6.} << Components{{40., 50., 60.}, {41., 51., 61}};
139
139
140 QTest::newRow(
140 QTest::newRow(
141 "unsortedMerge (unsortedMerge (merge is made because the two data series have the same "
141 "unsortedMerge (unsortedMerge (merge is made because the two data series have the same "
142 "y-axis)")
142 "y-axis)")
143 << SpectrogramBuilder{}
143 << SpectrogramBuilder{}
144 .setX({4., 5., 6.})
144 .setX({4., 5., 6.})
145 .setY({1., 2.})
145 .setY({1., 2.})
146 .setValues({40., 41., 50., 51., 60., 61})
146 .setValues({40., 41., 50., 51., 60., 61})
147 .build()
147 .build()
148 << SpectrogramBuilder{}
148 << SpectrogramBuilder{}
149 .setX({1., 2., 3.})
149 .setX({1., 2., 3.})
150 .setY({1., 2.})
150 .setY({1., 2.})
151 .setValues({10., 11., 20., 21., 30., 31})
151 .setValues({10., 11., 20., 21., 30., 31})
152 .build()
152 .build()
153 << DataContainer{1., 2., 3., 4., 5., 6.}
153 << DataContainer{1., 2., 3., 4., 5., 6.}
154 << Components{{10., 20., 30., 40., 50., 60.}, {11., 21., 31., 41., 51., 61}};
154 << Components{{10., 20., 30., 40., 50., 60.}, {11., 21., 31., 41., 51., 61}};
155 }
155 }
156
156
157 void TestSpectrogramSeries::testMerge()
157 void TestSpectrogramSeries::testMerge()
158 {
158 {
159 testMerge_t<SpectrogramSeries, Components>();
159 testMerge_t<SpectrogramSeries, Components>();
160 }
160 }
161
161
162 void TestSpectrogramSeries::testSubDataSeries_data()
162 void TestSpectrogramSeries::testSubDataSeries_data()
163 {
163 {
164 testSubDataSeries_struct<SpectrogramSeries, Components>();
164 testSubDataSeries_struct<SpectrogramSeries, Components>();
165
165
166 QTest::newRow("subDataSeries (the range includes all data)")
166 QTest::newRow("subDataSeries (the range includes all data)")
167 << SpectrogramBuilder{}
167 << SpectrogramBuilder{}
168 .setX({1., 2., 3.})
168 .setX({1., 2., 3.})
169 .setY({1., 2.})
169 .setY({1., 2.})
170 .setValues({10., 11., 20., 21., 30., 31})
170 .setValues({10., 11., 20., 21., 30., 31})
171 .build()
171 .build()
172 << SqpRange{0., 5.} << DataContainer{1., 2., 3.}
172 << SqpRange{0., 5.} << DataContainer{1., 2., 3.}
173 << Components{{10., 20., 30.}, {11., 21., 31.}};
173 << Components{{10., 20., 30.}, {11., 21., 31.}};
174
174
175 QTest::newRow("subDataSeries (the range includes no data)")
175 QTest::newRow("subDataSeries (the range includes no data)")
176 << SpectrogramBuilder{}
176 << SpectrogramBuilder{}
177 .setX({1., 2., 3.})
177 .setX({1., 2., 3.})
178 .setY({1., 2.})
178 .setY({1., 2.})
179 .setValues({10., 11., 20., 21., 30., 31})
179 .setValues({10., 11., 20., 21., 30., 31})
180 .build()
180 .build()
181 << SqpRange{4., 5.} << DataContainer{} << Components{{}, {}};
181 << SqpRange{4., 5.} << DataContainer{} << Components{{}, {}};
182
182
183 QTest::newRow("subDataSeries (the range includes some data)")
183 QTest::newRow("subDataSeries (the range includes some data)")
184 << SpectrogramBuilder{}
184 << SpectrogramBuilder{}
185 .setX({1., 2., 3.})
185 .setX({1., 2., 3.})
186 .setY({1., 2.})
186 .setY({1., 2.})
187 .setValues({10., 11., 20., 21., 30., 31})
187 .setValues({10., 11., 20., 21., 30., 31})
188 .build()
188 .build()
189 << SqpRange{1.1, 3} << DataContainer{2., 3.} << Components{{20., 30.}, {21., 31.}};
189 << SqpRange{1.1, 3} << DataContainer{2., 3.} << Components{{20., 30.}, {21., 31.}};
190 }
190 }
191
191
192 void TestSpectrogramSeries::testSubDataSeries()
192 void TestSpectrogramSeries::testSubDataSeries()
193 {
193 {
194 testSubDataSeries_t<SpectrogramSeries, Components>();
194 testSubDataSeries_t<SpectrogramSeries, Components>();
195 }
195 }
196
196
197 QTEST_MAIN(TestSpectrogramSeries)
197 QTEST_MAIN(TestSpectrogramSeries)
198 #include "TestSpectrogramSeries.moc"
198 #include "TestSpectrogramSeries.moc"
@@ -1,90 +1,90
1 #include "Data/VectorSeries.h"
1 #include "Data/VectorSeries.h"
2
2
3 #include "DataSeriesBuilders.h"
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
4 #include "DataSeriesTestsUtils.h"
5
5
6 #include <QObject>
6 #include <QObject>
7 #include <QtTest>
7 #include <QtTest>
8
8
9 /**
9 /**
10 * @brief The TestVectorSeries class defines unit tests on vector series.
10 * @brief The TestVectorSeries class defines unit tests on vector series.
11 *
11 *
12 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
12 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesTestsUtils)
13 */
13 */
14 class TestVectorSeries : public QObject {
14 class TestVectorSeries : public QObject {
15 Q_OBJECT
15 Q_OBJECT
16 private slots:
16 private slots:
17 /// Tests purge of a vector series
17 /// Tests purge of a vector series
18 void testPurge_data();
18 void testPurge_data();
19 void testPurge();
19 void testPurge();
20
20
21 /// Tests get values bounds of a vector series
21 /// Tests get values bounds of a vector series
22 void testValuesBounds_data();
22 void testValuesBounds_data();
23 void testValuesBounds();
23 void testValuesBounds();
24 };
24 };
25
25
26 void TestVectorSeries::testPurge_data()
26 void TestVectorSeries::testPurge_data()
27 {
27 {
28 testPurge_struct<VectorSeries>();
28 testPurge_struct<VectorSeries>();
29
29
30 QTest::newRow("purgeVector") << VectorBuilder{}
30 QTest::newRow("purgeVector") << VectorBuilder{}
31 .setX({1., 2., 3., 4., 5.})
31 .setX({1., 2., 3., 4., 5.})
32 .setXValues({6., 7., 8., 9., 10.})
32 .setXValues({6., 7., 8., 9., 10.})
33 .setYValues({11., 12., 13., 14., 15.})
33 .setYValues({11., 12., 13., 14., 15.})
34 .setZValues({16., 17., 18., 19., 20.})
34 .setZValues({16., 17., 18., 19., 20.})
35 .build()
35 .build()
36 << 2. << 4. << DataContainer{2., 3., 4.}
36 << 2. << 4. << DataContainer{2., 3., 4.}
37 << std::vector<DataContainer>{
37 << std::vector<DataContainer>{
38 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
38 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
39 }
39 }
40
40
41 void TestVectorSeries::testPurge()
41 void TestVectorSeries::testPurge()
42 {
42 {
43 testPurge_t<VectorSeries>();
43 testPurge_t<VectorSeries>();
44 }
44 }
45
45
46 void TestVectorSeries::testValuesBounds_data()
46 void TestVectorSeries::testValuesBounds_data()
47 {
47 {
48 testValuesBounds_struct<VectorSeries>();
48 testValuesBounds_struct<VectorSeries>();
49
49
50 auto nan = std::numeric_limits<double>::quiet_NaN();
50 auto nan = std::numeric_limits<double>::quiet_NaN();
51
51
52 QTest::newRow("vectorBounds1") << VectorBuilder{}
52 QTest::newRow("vectorBounds1") << VectorBuilder{}
53 .setX({1., 2., 3., 4., 5.})
53 .setX({1., 2., 3., 4., 5.})
54 .setXValues({10., 15., 20., 13., 12.})
54 .setXValues({10., 15., 20., 13., 12.})
55 .setYValues({35., 24., 10., 9., 0.3})
55 .setYValues({35., 24., 10., 9., 0.3})
56 .setZValues({13., 14., 12., 9., 24.})
56 .setZValues({13., 14., 12., 9., 24.})
57 .build()
57 .build()
58 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
58 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
59 QTest::newRow("vectorBounds2") << VectorBuilder{}
59 QTest::newRow("vectorBounds2") << VectorBuilder{}
60 .setX({1., 2., 3., 4., 5.})
60 .setX({1., 2., 3., 4., 5.})
61 .setXValues({2.3, 15., 20., 13., 12.})
61 .setXValues({2.3, 15., 20., 13., 12.})
62 .setYValues({35., 24., 10., 9., 4.})
62 .setYValues({35., 24., 10., 9., 4.})
63 .setZValues({13., 14., 12., 9., 24.})
63 .setZValues({13., 14., 12., 9., 24.})
64 .build()
64 .build()
65 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
65 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
66 QTest::newRow("vectorBounds3") << VectorBuilder{}
66 QTest::newRow("vectorBounds3") << VectorBuilder{}
67 .setX({1., 2., 3., 4., 5.})
67 .setX({1., 2., 3., 4., 5.})
68 .setXValues({2.3, 15., 20., 13., 12.})
68 .setXValues({2.3, 15., 20., 13., 12.})
69 .setYValues({35., 24., 10., 9., 4.})
69 .setYValues({35., 24., 10., 9., 4.})
70 .setZValues({13., 14., 12., 9., 24.})
70 .setZValues({13., 14., 12., 9., 24.})
71 .build()
71 .build()
72 << 2. << 3. << true << 10. << 24.;
72 << 2. << 3. << true << 10. << 24.;
73
73
74 // Tests with NaN values: NaN values are not included in min/max search
74 // Tests with NaN values: NaN values are not included in min/max search
75 QTest::newRow("vectorBounds4") << VectorBuilder{}
75 QTest::newRow("vectorBounds4") << VectorBuilder{}
76 .setX({1., 2.})
76 .setX({1., 2.})
77 .setXValues({nan, nan})
77 .setXValues({nan, nan})
78 .setYValues({nan, nan})
78 .setYValues({nan, nan})
79 .setZValues({nan, nan})
79 .setZValues({nan, nan})
80 .build()
80 .build()
81 << 0. << 6. << true << nan << nan;
81 << 0. << 6. << true << nan << nan;
82 }
82 }
83
83
84 void TestVectorSeries::testValuesBounds()
84 void TestVectorSeries::testValuesBounds()
85 {
85 {
86 testValuesBounds_t<VectorSeries>();
86 testValuesBounds_t<VectorSeries>();
87 }
87 }
88
88
89 QTEST_MAIN(TestVectorSeries)
89 QTEST_MAIN(TestVectorSeries)
90 #include "TestVectorSeries.moc"
90 #include "TestVectorSeries.moc"
@@ -1,31 +1,32
1
1
2
2
3 tests = [
3 tests = [
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
5 [['Data/TestScalarSeries.cpp'],'test_scalar','ScalarSeries test'],
5 [['Data/TestScalarSeries.cpp'],'test_scalar','ScalarSeries test'],
6 [['Data/TestSpectrogramSeries.cpp'],'test_spectrogram','SpectrogramSeries test'],
6 [['Data/TestSpectrogramSeries.cpp'],'test_spectrogram','SpectrogramSeries test'],
7 [['Data/TestVectorSeries.cpp'],'test_vector','VectorSeries test'],
7 [['Data/TestVectorSeries.cpp'],'test_vector','VectorSeries test'],
8 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
8 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
9 [['Data/TestOptionalAxis.cpp'],'test_optional_axis','OptionalAxis test'],
9 [['Data/TestOptionalAxis.cpp'],'test_optional_axis','OptionalAxis test'],
10 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
10 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
11 [['Data/TestDataSeriesUtils.cpp'],'test_dataseries_util','Data series utils test'],
11 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
12 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
12 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
13 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
13 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
14 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
14 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
15 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
15 ]
16 ]
16
17
17 amdatest_sources = [
18 amdatest_sources = [
18 'Data/DataSeriesBuilders.h',
19 'Data/DataSeriesBuilders.h',
19 'Data/DataSeriesBuilders.cpp',
20 'Data/DataSeriesBuilders.cpp',
20 'Data/DataSeriesUtils.h',
21 'Data/DataSeriesTestsUtils.h',
21 'Data/DataSeriesUtils.cpp'
22 'Data/DataSeriesTestsUtils.cpp'
22 ]
23 ]
23
24
24 foreach unit_test : tests
25 foreach unit_test : tests
25 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
26 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
26 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
27 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
27 dependencies : [sciqlop_core, qt5test],
28 dependencies : [sciqlop_core, qt5test],
28 sources : [amdatest_sources])
29 sources : [amdatest_sources])
29 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
30 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
30 endforeach
31 endforeach
31
32
@@ -1,173 +1,174
1 #include "Visualization/AxisRenderingUtils.h"
1 #include "Visualization/AxisRenderingUtils.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/SpectrogramSeries.h>
4 #include <Data/SpectrogramSeries.h>
5 #include <Data/VectorSeries.h>
5 #include <Data/VectorSeries.h>
6
6
7 #include <Visualization/qcustomplot.h>
7 #include <Visualization/qcustomplot.h>
8
8
9 Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils")
9 Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils")
10
10
11 namespace {
11 namespace {
12
12
13 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
13 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
14
14
15 /// Format for datetimes on a axis
15 /// Format for datetimes on a axis
16 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
16 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
17
17
18 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
18 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
19 /// non-time data
19 /// non-time data
20 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType)
20 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType)
21 {
21 {
22 if (isTimeAxis) {
22 if (isTimeAxis) {
23 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
23 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
24 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
24 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
25 dateTicker->setDateTimeSpec(Qt::UTC);
25 dateTicker->setDateTimeSpec(Qt::UTC);
26
26
27 return dateTicker;
27 return dateTicker;
28 }
28 }
29 else if (scaleType == QCPAxis::stLogarithmic) {
29 else if (scaleType == QCPAxis::stLogarithmic) {
30 return QSharedPointer<QCPAxisTickerLog>::create();
30 return QSharedPointer<QCPAxisTickerLog>::create();
31 }
31 }
32 else {
32 else {
33 // default ticker
33 // default ticker
34 return QSharedPointer<QCPAxisTicker>::create();
34 return QSharedPointer<QCPAxisTicker>::create();
35 }
35 }
36 }
36 }
37
37
38 /**
38 /**
39 * Sets properties of the axis passed as parameter
39 * Sets properties of the axis passed as parameter
40 * @param axis the axis to set
40 * @param axis the axis to set
41 * @param unit the unit to set for the axis
41 * @param unit the unit to set for the axis
42 * @param scaleType the scale type to set for the axis
42 * @param scaleType the scale type to set for the axis
43 */
43 */
44 void setAxisProperties(QCPAxis &axis, const Unit &unit,
44 void setAxisProperties(QCPAxis &axis, const Unit &unit,
45 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
45 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
46 {
46 {
47 // label (unit name)
47 // label (unit name)
48 axis.setLabel(unit.m_Name);
48 axis.setLabel(unit.m_Name);
49
49
50 // scale type
50 // scale type
51 axis.setScaleType(scaleType);
51 axis.setScaleType(scaleType);
52 if (scaleType == QCPAxis::stLogarithmic) {
52 if (scaleType == QCPAxis::stLogarithmic) {
53 // Scientific notation
53 // Scientific notation
54 axis.setNumberPrecision(0);
54 axis.setNumberPrecision(0);
55 axis.setNumberFormat("eb");
55 axis.setNumberFormat("eb");
56 }
56 }
57
57
58 // ticker (depending on the type of unit)
58 // ticker (depending on the type of unit)
59 axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType));
59 axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType));
60 }
60 }
61
61
62 /**
62 /**
63 * Delegate used to set axes properties
63 * Delegate used to set axes properties
64 */
64 */
65 template <typename T, typename Enabled = void>
65 template <typename T, typename Enabled = void>
66 struct AxisSetter {
66 struct AxisSetter {
67 static void setProperties(T &, QCustomPlot &, QCPColorScale &)
67 static void setProperties(T &, QCustomPlot &, QCPColorScale &)
68 {
68 {
69 // Default implementation does nothing
69 // Default implementation does nothing
70 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data";
70 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data";
71 }
71 }
72 };
72 };
73
73
74 /**
74 /**
75 * Specialization of AxisSetter for scalars and vectors
75 * Specialization of AxisSetter for scalars and vectors
76 * @sa ScalarSeries
76 * @sa ScalarSeries
77 * @sa VectorSeries
77 * @sa VectorSeries
78 */
78 */
79 template <typename T>
79 template <typename T>
80 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
80 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
81 or std::is_base_of<VectorSeries, T>::value> > {
81 or std::is_base_of<VectorSeries, T>::value> > {
82 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &)
82 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &)
83 {
83 {
84 dataSeries.lockRead();
84 dataSeries.lockRead();
85 auto xAxisUnit = dataSeries.xAxisUnit();
85 auto xAxisUnit = dataSeries.xAxisUnit();
86 auto valuesUnit = dataSeries.valuesUnit();
86 auto valuesUnit = dataSeries.valuesUnit();
87 dataSeries.unlock();
87 dataSeries.unlock();
88
88
89 setAxisProperties(*plot.xAxis, xAxisUnit);
89 setAxisProperties(*plot.xAxis, xAxisUnit);
90 setAxisProperties(*plot.yAxis, valuesUnit);
90 setAxisProperties(*plot.yAxis, valuesUnit);
91 }
91 }
92 };
92 };
93
93
94 /**
94 /**
95 * Specialization of AxisSetter for spectrograms
95 * Specialization of AxisSetter for spectrograms
96 * @sa SpectrogramSeries
96 * @sa SpectrogramSeries
97 */
97 */
98 template <typename T>
98 template <typename T>
99 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
99 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
100 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &colorScale)
100 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &colorScale)
101 {
101 {
102 dataSeries.lockRead();
102 dataSeries.lockRead();
103 auto xAxisUnit = dataSeries.xAxisUnit();
103 auto xAxisUnit = dataSeries.xAxisUnit();
104 /// @todo ALX: use iterators here
104 auto yAxisUnit = dataSeries.yAxisUnit();
105 auto yAxisUnit = dataSeries.yAxis().unit();
106 auto valuesUnit = dataSeries.valuesUnit();
105 auto valuesUnit = dataSeries.valuesUnit();
107 dataSeries.unlock();
106 dataSeries.unlock();
108
107
109 setAxisProperties(*plot.xAxis, xAxisUnit);
108 setAxisProperties(*plot.xAxis, xAxisUnit);
110 setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic);
109 setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic);
111
110
112 // Displays color scale in plot
111 // Displays color scale in plot
113 plot.plotLayout()->insertRow(0);
112 plot.plotLayout()->insertRow(0);
114 plot.plotLayout()->addElement(0, 0, &colorScale);
113 plot.plotLayout()->addElement(0, 0, &colorScale);
115 colorScale.setType(QCPAxis::atTop);
114 colorScale.setType(QCPAxis::atTop);
116 colorScale.setMinimumMargins(QMargins{0, 0, 0, 0});
115 colorScale.setMinimumMargins(QMargins{0, 0, 0, 0});
117
116
118 // Aligns color scale with axes
117 // Aligns color scale with axes
119 auto marginGroups = plot.axisRect()->marginGroups();
118 auto marginGroups = plot.axisRect()->marginGroups();
120 for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) {
119 for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) {
121 colorScale.setMarginGroup(it.key(), it.value());
120 colorScale.setMarginGroup(it.key(), it.value());
122 }
121 }
123
122
124 // Set color scale properties
123 // Set color scale properties
125 setAxisProperties(*colorScale.axis(), valuesUnit, QCPAxis::stLogarithmic);
124 setAxisProperties(*colorScale.axis(), valuesUnit, QCPAxis::stLogarithmic);
125 /// @todo ALX: temp data range, remove it when widget to set data range is implemented
126 colorScale.setDataRange(QCPRange{8.32e2, 1.77e7});
126 }
127 }
127 };
128 };
128
129
129 /**
130 /**
130 * Default implementation of IAxisHelper, which takes data series to set axes properties
131 * Default implementation of IAxisHelper, which takes data series to set axes properties
131 * @tparam T the data series' type
132 * @tparam T the data series' type
132 */
133 */
133 template <typename T>
134 template <typename T>
134 struct AxisHelper : public IAxisHelper {
135 struct AxisHelper : public IAxisHelper {
135 explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
136 explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
136
137
137 void setProperties(QCustomPlot &plot, QCPColorScale &colorScale) override
138 void setProperties(QCustomPlot &plot, QCPColorScale &colorScale) override
138 {
139 {
139 AxisSetter<T>::setProperties(m_DataSeries, plot, colorScale);
140 AxisSetter<T>::setProperties(m_DataSeries, plot, colorScale);
140 }
141 }
141
142
142 T &m_DataSeries;
143 T &m_DataSeries;
143 };
144 };
144
145
145 } // namespace
146 } // namespace
146
147
147 QString formatValue(double value, const QCPAxis &axis)
148 QString formatValue(double value, const QCPAxis &axis)
148 {
149 {
149 // If the axis is a time axis, formats the value as a date
150 // If the axis is a time axis, formats the value as a date
150 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
151 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
151 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
152 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
152 }
153 }
153 else {
154 else {
154 return QString::number(value);
155 return QString::number(value);
155 }
156 }
156 }
157 }
157
158
158 std::unique_ptr<IAxisHelper>
159 std::unique_ptr<IAxisHelper>
159 IAxisHelperFactory::create(std::shared_ptr<IDataSeries> dataSeries) noexcept
160 IAxisHelperFactory::create(std::shared_ptr<IDataSeries> dataSeries) noexcept
160 {
161 {
161 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
162 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
162 return std::make_unique<AxisHelper<ScalarSeries> >(*scalarSeries);
163 return std::make_unique<AxisHelper<ScalarSeries> >(*scalarSeries);
163 }
164 }
164 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
165 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
165 return std::make_unique<AxisHelper<SpectrogramSeries> >(*spectrogramSeries);
166 return std::make_unique<AxisHelper<SpectrogramSeries> >(*spectrogramSeries);
166 }
167 }
167 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
168 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
168 return std::make_unique<AxisHelper<VectorSeries> >(*vectorSeries);
169 return std::make_unique<AxisHelper<VectorSeries> >(*vectorSeries);
169 }
170 }
170 else {
171 else {
171 return std::make_unique<AxisHelper<IDataSeries> >(*dataSeries);
172 return std::make_unique<AxisHelper<IDataSeries> >(*dataSeries);
172 }
173 }
173 }
174 }
@@ -1,345 +1,344
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Data/DataSeriesUtils.h>
4 #include <Data/ScalarSeries.h>
5 #include <Data/ScalarSeries.h>
5 #include <Data/SpectrogramSeries.h>
6 #include <Data/SpectrogramSeries.h>
6 #include <Data/VectorSeries.h>
7 #include <Data/VectorSeries.h>
7
8
8 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
9
10
10 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11
12
12 namespace {
13 namespace {
13
14
14 class SqpDataContainer : public QCPGraphDataContainer {
15 class SqpDataContainer : public QCPGraphDataContainer {
15 public:
16 public:
16 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
17 };
18 };
18
19
19 /**
20 /**
20 * Struct used to create plottables, depending on the type of the data series from which to create
21 * Struct used to create plottables, depending on the type of the data series from which to create
21 * them
22 * them
22 * @tparam T the data series' type
23 * @tparam T the data series' type
23 * @remarks Default implementation can't create plottables
24 * @remarks Default implementation can't create plottables
24 */
25 */
25 template <typename T, typename Enabled = void>
26 template <typename T, typename Enabled = void>
26 struct PlottablesCreator {
27 struct PlottablesCreator {
27 static PlottablesMap createPlottables(T &, QCustomPlot &)
28 static PlottablesMap createPlottables(T &, QCustomPlot &)
28 {
29 {
29 qCCritical(LOG_DataSeries())
30 qCCritical(LOG_DataSeries())
30 << QObject::tr("Can't create plottables: unmanaged data series type");
31 << QObject::tr("Can't create plottables: unmanaged data series type");
31 return {};
32 return {};
32 }
33 }
33 };
34 };
34
35
35 /**
36 /**
36 * Specialization of PlottablesCreator for scalars and vectors
37 * Specialization of PlottablesCreator for scalars and vectors
37 * @sa ScalarSeries
38 * @sa ScalarSeries
38 * @sa VectorSeries
39 * @sa VectorSeries
39 */
40 */
40 template <typename T>
41 template <typename T>
41 struct PlottablesCreator<T,
42 struct PlottablesCreator<T,
42 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
43 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
43 or std::is_base_of<VectorSeries, T>::value> > {
44 or std::is_base_of<VectorSeries, T>::value> > {
44 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
45 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
45 {
46 {
46 PlottablesMap result{};
47 PlottablesMap result{};
47
48
48 // Gets the number of components of the data series
49 // Gets the number of components of the data series
49 dataSeries.lockRead();
50 dataSeries.lockRead();
50 auto componentCount = dataSeries.valuesData()->componentCount();
51 auto componentCount = dataSeries.valuesData()->componentCount();
51 dataSeries.unlock();
52 dataSeries.unlock();
52
53
53 // For each component of the data series, creates a QCPGraph to add to the plot
54 // For each component of the data series, creates a QCPGraph to add to the plot
54 for (auto i = 0; i < componentCount; ++i) {
55 for (auto i = 0; i < componentCount; ++i) {
55 auto graph = plot.addGraph();
56 auto graph = plot.addGraph();
56 result.insert({i, graph});
57 result.insert({i, graph});
57 }
58 }
58
59
59 plot.replot();
60 plot.replot();
60
61
61 return result;
62 return result;
62 }
63 }
63 };
64 };
64
65
65 /**
66 /**
66 * Specialization of PlottablesCreator for spectrograms
67 * Specialization of PlottablesCreator for spectrograms
67 * @sa SpectrogramSeries
68 * @sa SpectrogramSeries
68 */
69 */
69 template <typename T>
70 template <typename T>
70 struct PlottablesCreator<T,
71 struct PlottablesCreator<T,
71 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
72 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
72 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
73 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
73 {
74 {
74 PlottablesMap result{};
75 PlottablesMap result{};
75 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
76 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
76
77
77 plot.replot();
78 plot.replot();
78
79
79 return result;
80 return result;
80 }
81 }
81 };
82 };
82
83
83 /**
84 /**
84 * Struct used to update plottables, depending on the type of the data series from which to update
85 * Struct used to update plottables, depending on the type of the data series from which to update
85 * them
86 * them
86 * @tparam T the data series' type
87 * @tparam T the data series' type
87 * @remarks Default implementation can't update plottables
88 * @remarks Default implementation can't update plottables
88 */
89 */
89 template <typename T, typename Enabled = void>
90 template <typename T, typename Enabled = void>
90 struct PlottablesUpdater {
91 struct PlottablesUpdater {
91 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
92 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
92 {
93 {
93 qCCritical(LOG_VisualizationGraphHelper())
94 qCCritical(LOG_VisualizationGraphHelper())
94 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
95 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
95 }
96 }
96
97
97 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
98 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
98 {
99 {
99 qCCritical(LOG_VisualizationGraphHelper())
100 qCCritical(LOG_VisualizationGraphHelper())
100 << QObject::tr("Can't update plottables: unmanaged data series type");
101 << QObject::tr("Can't update plottables: unmanaged data series type");
101 }
102 }
102 };
103 };
103
104
104 /**
105 /**
105 * Specialization of PlottablesUpdater for scalars and vectors
106 * Specialization of PlottablesUpdater for scalars and vectors
106 * @sa ScalarSeries
107 * @sa ScalarSeries
107 * @sa VectorSeries
108 * @sa VectorSeries
108 */
109 */
109 template <typename T>
110 template <typename T>
110 struct PlottablesUpdater<T,
111 struct PlottablesUpdater<T,
111 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
112 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
112 or std::is_base_of<VectorSeries, T>::value> > {
113 or std::is_base_of<VectorSeries, T>::value> > {
113 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
114 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
114 {
115 {
115 auto minValue = 0., maxValue = 0.;
116 auto minValue = 0., maxValue = 0.;
116
117
117 dataSeries.lockRead();
118 dataSeries.lockRead();
118 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
119 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
119 auto end = dataSeries.cend();
120 auto end = dataSeries.cend();
120 if (valuesBounds.first != end && valuesBounds.second != end) {
121 if (valuesBounds.first != end && valuesBounds.second != end) {
121 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
122 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
122
123
123 minValue = rangeValue(valuesBounds.first->minValue());
124 minValue = rangeValue(valuesBounds.first->minValue());
124 maxValue = rangeValue(valuesBounds.second->maxValue());
125 maxValue = rangeValue(valuesBounds.second->maxValue());
125 }
126 }
126 dataSeries.unlock();
127 dataSeries.unlock();
127
128
128 plot.yAxis->setRange(QCPRange{minValue, maxValue});
129 plot.yAxis->setRange(QCPRange{minValue, maxValue});
129 }
130 }
130
131
131 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
132 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
132 bool rescaleAxes)
133 bool rescaleAxes)
133 {
134 {
134
135
135 // For each plottable to update, resets its data
136 // For each plottable to update, resets its data
136 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
137 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
137 for (const auto &plottable : plottables) {
138 for (const auto &plottable : plottables) {
138 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
139 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
139 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
140 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
140 graph->setData(dataContainer);
141 graph->setData(dataContainer);
141
142
142 dataContainers.insert({plottable.first, dataContainer});
143 dataContainers.insert({plottable.first, dataContainer});
143 }
144 }
144 }
145 }
145 dataSeries.lockRead();
146 dataSeries.lockRead();
146
147
147 // - Gets the data of the series included in the current range
148 // - Gets the data of the series included in the current range
148 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
149 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
149 // and value data. The correct value is retrieved according to the index of the component
150 // and value data. The correct value is retrieved according to the index of the component
150 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
151 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
151 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
152 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
152 for (const auto &dataContainer : dataContainers) {
153 for (const auto &dataContainer : dataContainers) {
153 auto componentIndex = dataContainer.first;
154 auto componentIndex = dataContainer.first;
154 dataContainer.second->appendGraphData(
155 dataContainer.second->appendGraphData(
155 QCPGraphData(it->x(), it->value(componentIndex)));
156 QCPGraphData(it->x(), it->value(componentIndex)));
156 }
157 }
157 }
158 }
158
159
159 dataSeries.unlock();
160 dataSeries.unlock();
160
161
161 if (!plottables.empty()) {
162 if (!plottables.empty()) {
162 auto plot = plottables.begin()->second->parentPlot();
163 auto plot = plottables.begin()->second->parentPlot();
163
164
164 if (rescaleAxes) {
165 if (rescaleAxes) {
165 plot->rescaleAxes();
166 plot->rescaleAxes();
166 }
167 }
167
168
168 plot->replot();
169 plot->replot();
169 }
170 }
170 }
171 }
171 };
172 };
172
173
173 /**
174 /**
174 * Specialization of PlottablesUpdater for spectrograms
175 * Specialization of PlottablesUpdater for spectrograms
175 * @sa SpectrogramSeries
176 * @sa SpectrogramSeries
176 */
177 */
177 template <typename T>
178 template <typename T>
178 struct PlottablesUpdater<T,
179 struct PlottablesUpdater<T,
179 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
180 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
180 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
181 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
181 {
182 {
182 double min, max;
183 double min, max;
183 /// @todo ALX: use iterators here
184 std::tie(min, max) = dataSeries.yBounds();
184 std::tie(min, max) = dataSeries.yAxis().bounds();
185
185
186 if (!std::isnan(min) && !std::isnan(max)) {
186 if (!std::isnan(min) && !std::isnan(max)) {
187 plot.yAxis->setRange(QCPRange{min, max});
187 plot.yAxis->setRange(QCPRange{min, max});
188 }
188 }
189 }
189 }
190
190
191 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
191 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
192 bool rescaleAxes)
192 bool rescaleAxes)
193 {
193 {
194 if (plottables.empty()) {
194 if (plottables.empty()) {
195 qCDebug(LOG_VisualizationGraphHelper())
195 qCDebug(LOG_VisualizationGraphHelper())
196 << QObject::tr("Can't update spectrogram: no colormap has been associated");
196 << QObject::tr("Can't update spectrogram: no colormap has been associated");
197 return;
197 return;
198 }
198 }
199
199
200 // Gets the colormap to update (normally there is only one colormap)
200 // Gets the colormap to update (normally there is only one colormap)
201 Q_ASSERT(plottables.size() == 1);
201 Q_ASSERT(plottables.size() == 1);
202 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
202 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
203 Q_ASSERT(colormap != nullptr);
203 Q_ASSERT(colormap != nullptr);
204
204
205 dataSeries.lockRead();
205 dataSeries.lockRead();
206
206
207 // Processing spectrogram data for display in QCustomPlot
207 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
208 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
208 /// @todo ALX: use iterators here
209 auto yAxis = dataSeries.yAxis();
210
211 // Gets properties of x-axis and y-axis to set size and range of the colormap
212 auto nbX = std::distance(its.first, its.second);
213 auto xMin = nbX != 0 ? its.first->x() : 0.;
214 auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.;
215
216 auto nbY = yAxis.size();
217 auto yMin = 0., yMax = 0.;
218 if (nbY != 0) {
219 std::tie(yMin, yMax) = yAxis.bounds();
220 }
221
209
222 colormap->data()->setSize(nbX, nbY);
210 // Computes logarithmic y-axis resolution for the spectrogram
223 colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax});
211 auto yData = its.first->y();
212 auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
213
214 // Generates mesh for colormap
215 auto mesh = DataSeriesUtils::regularMesh(
216 its.first, its.second, DataSeriesUtils::Resolution{dataSeries.xResolution()},
217 yResolution);
218
219 dataSeries.unlock();
224
220
225 // Sets values
221 colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
226 auto xIndex = 0;
222 if (!mesh.isEmpty()) {
227 for (auto it = its.first; it != its.second; ++it, ++xIndex) {
223 colormap->data()->setRange(
228 for (auto yIndex = 0; yIndex < nbY; ++yIndex) {
224 QCPRange{mesh.m_XMin, mesh.xMax()},
229 auto value = it->value(yIndex);
225 // y-axis range is converted to linear values
226 QCPRange{std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax())});
230
227
231 colormap->data()->setCell(xIndex, yIndex, value);
228 // Sets values
229 auto index = 0;
230 for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it, ++index) {
231 auto xIndex = index % mesh.m_NbX;
232 auto yIndex = index / mesh.m_NbX;
232
233
233 // Processing spectrogram data for display in QCustomPlot
234 colormap->data()->setCell(xIndex, yIndex, *it);
234 /// For the moment, we just make the NaN values to be transparent in the colormap
235
235 /// @todo ALX: complete treatments (mesh generation, etc.)
236 // Makes the NaN values to be transparent in the colormap
236 if (std::isnan(value)) {
237 if (std::isnan(*it)) {
237 colormap->data()->setAlpha(xIndex, yIndex, 0);
238 colormap->data()->setAlpha(xIndex, yIndex, 0);
238 }
239 }
239 }
240 }
240 }
241 }
241
242
242 dataSeries.unlock();
243
244 // Rescales axes
243 // Rescales axes
245 auto plot = colormap->parentPlot();
244 auto plot = colormap->parentPlot();
246
245
247 if (rescaleAxes) {
246 if (rescaleAxes) {
248 plot->rescaleAxes();
247 plot->rescaleAxes();
249 }
248 }
250
249
251 plot->replot();
250 plot->replot();
252 }
251 }
253 };
252 };
254
253
255 /**
254 /**
256 * Helper used to create/update plottables
255 * Helper used to create/update plottables
257 */
256 */
258 struct IPlottablesHelper {
257 struct IPlottablesHelper {
259 virtual ~IPlottablesHelper() noexcept = default;
258 virtual ~IPlottablesHelper() noexcept = default;
260 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
259 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
261 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
260 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
262 virtual void update(PlottablesMap &plottables, const SqpRange &range,
261 virtual void update(PlottablesMap &plottables, const SqpRange &range,
263 bool rescaleAxes = false) const = 0;
262 bool rescaleAxes = false) const = 0;
264 };
263 };
265
264
266 /**
265 /**
267 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
266 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
268 * @tparam T the data series' type
267 * @tparam T the data series' type
269 */
268 */
270 template <typename T>
269 template <typename T>
271 struct PlottablesHelper : public IPlottablesHelper {
270 struct PlottablesHelper : public IPlottablesHelper {
272 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
271 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
273
272
274 PlottablesMap create(QCustomPlot &plot) const override
273 PlottablesMap create(QCustomPlot &plot) const override
275 {
274 {
276 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
275 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
277 }
276 }
278
277
279 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
278 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
280 {
279 {
281 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
280 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
282 }
281 }
283
282
284 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
283 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
285 {
284 {
286 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
285 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
287 }
286 }
288
287
289 T &m_DataSeries;
288 T &m_DataSeries;
290 };
289 };
291
290
292 /// Creates IPlottablesHelper according to a data series
291 /// Creates IPlottablesHelper according to a data series
293 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
292 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
294 {
293 {
295 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
294 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
296 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
295 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
297 }
296 }
298 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
297 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
299 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
298 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
300 }
299 }
301 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
300 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
302 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
301 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
303 }
302 }
304 else {
303 else {
305 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
304 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
306 }
305 }
307 }
306 }
308
307
309 } // namespace
308 } // namespace
310
309
311 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
310 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
312 QCustomPlot &plot) noexcept
311 QCustomPlot &plot) noexcept
313 {
312 {
314 if (variable) {
313 if (variable) {
315 auto helper = createHelper(variable->dataSeries());
314 auto helper = createHelper(variable->dataSeries());
316 auto plottables = helper->create(plot);
315 auto plottables = helper->create(plot);
317 return plottables;
316 return plottables;
318 }
317 }
319 else {
318 else {
320 qCDebug(LOG_VisualizationGraphHelper())
319 qCDebug(LOG_VisualizationGraphHelper())
321 << QObject::tr("Can't create graph plottables : the variable is null");
320 << QObject::tr("Can't create graph plottables : the variable is null");
322 return PlottablesMap{};
321 return PlottablesMap{};
323 }
322 }
324 }
323 }
325
324
326 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
325 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
327 QCustomPlot &plot) noexcept
326 QCustomPlot &plot) noexcept
328 {
327 {
329 if (variable) {
328 if (variable) {
330 auto helper = createHelper(variable->dataSeries());
329 auto helper = createHelper(variable->dataSeries());
331 helper->setYAxisRange(variable->range(), plot);
330 helper->setYAxisRange(variable->range(), plot);
332 }
331 }
333 else {
332 else {
334 qCDebug(LOG_VisualizationGraphHelper())
333 qCDebug(LOG_VisualizationGraphHelper())
335 << QObject::tr("Can't set y-axis range of plot: the variable is null");
334 << QObject::tr("Can't set y-axis range of plot: the variable is null");
336 }
335 }
337 }
336 }
338
337
339 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
338 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
340 std::shared_ptr<IDataSeries> dataSeries,
339 std::shared_ptr<IDataSeries> dataSeries,
341 const SqpRange &dateTime)
340 const SqpRange &dateTime)
342 {
341 {
343 auto helper = createHelper(dataSeries);
342 auto helper = createHelper(dataSeries);
344 helper->update(plottables, dateTime);
343 helper->update(plottables, dateTime);
345 }
344 }
@@ -1,68 +1,76
1 #ifndef SCIQLOP_AMDARESULTPARSERDEFS_H
1 #ifndef SCIQLOP_AMDARESULTPARSERDEFS_H
2 #define SCIQLOP_AMDARESULTPARSERDEFS_H
2 #define SCIQLOP_AMDARESULTPARSERDEFS_H
3
3
4 #include <QtCore/QRegularExpression>
4 #include <QtCore/QRegularExpression>
5 #include <QtCore/QString>
5 #include <QtCore/QString>
6 #include <QtCore/QVariantHash>
6 #include <QtCore/QVariantHash>
7
7
8 // ////////// //
8 // ////////// //
9 // Properties //
9 // Properties //
10 // ////////// //
10 // ////////// //
11
11
12 /// Alias to represent properties read in the header of AMDA file
12 /// Alias to represent properties read in the header of AMDA file
13 using Properties = QVariantHash;
13 using Properties = QVariantHash;
14
14
15 extern const QString END_TIME_PROPERTY;
15 extern const QString FILL_VALUE_PROPERTY;
16 extern const QString FILL_VALUE_PROPERTY;
16 extern const QString MAX_BANDS_PROPERTY;
17 extern const QString MAX_BANDS_PROPERTY;
17 extern const QString MIN_BANDS_PROPERTY;
18 extern const QString MIN_BANDS_PROPERTY;
18 extern const QString MAX_SAMPLING_PROPERTY;
19 extern const QString MAX_SAMPLING_PROPERTY;
19 extern const QString MIN_SAMPLING_PROPERTY;
20 extern const QString MIN_SAMPLING_PROPERTY;
21 extern const QString START_TIME_PROPERTY;
20 extern const QString X_AXIS_UNIT_PROPERTY;
22 extern const QString X_AXIS_UNIT_PROPERTY;
21 extern const QString Y_AXIS_UNIT_PROPERTY;
23 extern const QString Y_AXIS_UNIT_PROPERTY;
22 extern const QString VALUES_UNIT_PROPERTY;
24 extern const QString VALUES_UNIT_PROPERTY;
23
25
24 // /////////////////// //
26 // /////////////////// //
25 // Regular expressions //
27 // Regular expressions //
26 // /////////////////// //
28 // /////////////////// //
27
29
28 // AMDA V2
30 // AMDA V2
29 // /// Regex to find the header of the data in the file. This header indicates the end of comments
31 // /// Regex to find the header of the data in the file. This header indicates the end of comments
30 // in the file
32 // in the file
31 // const auto DATA_HEADER_REGEX = QRegularExpression{QStringLiteral("#\\s*DATA\\s*:")};
33 // const auto DATA_HEADER_REGEX = QRegularExpression{QStringLiteral("#\\s*DATA\\s*:")};
32
34
33 // AMDA V2
35 // AMDA V2
34 // /// ... PARAMETER_UNITS : nT ...
36 // /// ... PARAMETER_UNITS : nT ...
35 // /// ... PARAMETER_UNITS:nT ...
37 // /// ... PARAMETER_UNITS:nT ...
36 // /// ... PARAMETER_UNITS: mΒ² ...
38 // /// ... PARAMETER_UNITS: mΒ² ...
37 // /// ... PARAMETER_UNITS : m/s ...
39 // /// ... PARAMETER_UNITS : m/s ...
38 // const auto UNIT_REGEX = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.+)")};
40 // const auto UNIT_REGEX = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.+)")};
39
41
40 /// Regex to find x-axis unit in a line. Examples of valid lines:
42 /// Regex to find x-axis unit in a line. Examples of valid lines:
41 /// ... - Units : nT - ...
43 /// ... - Units : nT - ...
42 /// ... -Units:nT- ...
44 /// ... -Units:nT- ...
43 /// ... -Units: mΒ²- ...
45 /// ... -Units: mΒ²- ...
44 /// ... - Units : m/s - ...
46 /// ... - Units : m/s - ...
45 extern const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX;
47 extern const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX;
46
48
49 /// Regex to find end time of data in a line for a spectrogram
50 extern const QRegularExpression SPECTROGRAM_END_TIME_REGEX;
51
47 /// Regex to find fill value used in a line for a spectrogram
52 /// Regex to find fill value used in a line for a spectrogram
48 extern const QRegularExpression SPECTROGRAM_FILL_VALUE_REGEX;
53 extern const QRegularExpression SPECTROGRAM_FILL_VALUE_REGEX;
49
54
50 /// Regex to find max bands in a line for a spectrogram
55 /// Regex to find max bands in a line for a spectrogram
51 extern const QRegularExpression SPECTROGRAM_MAX_BANDS_REGEX;
56 extern const QRegularExpression SPECTROGRAM_MAX_BANDS_REGEX;
52
57
53 /// Regex to find min bands in a line for a spectrogram
58 /// Regex to find min bands in a line for a spectrogram
54 extern const QRegularExpression SPECTROGRAM_MIN_BANDS_REGEX;
59 extern const QRegularExpression SPECTROGRAM_MIN_BANDS_REGEX;
55
60
56 /// Regex to find max x-axis sampling in a line for a spectrogram
61 /// Regex to find max x-axis sampling in a line for a spectrogram
57 extern const QRegularExpression SPECTROGRAM_MAX_SAMPLING_REGEX;
62 extern const QRegularExpression SPECTROGRAM_MAX_SAMPLING_REGEX;
58
63
59 /// Regex to find min x-axis sampling in a line for a spectrogram
64 /// Regex to find min x-axis sampling in a line for a spectrogram
60 extern const QRegularExpression SPECTROGRAM_MIN_SAMPLING_REGEX;
65 extern const QRegularExpression SPECTROGRAM_MIN_SAMPLING_REGEX;
61
66
67 /// Regex to find start time of data in a line for a spectrogram
68 extern const QRegularExpression SPECTROGRAM_START_TIME_REGEX;
69
62 /// Regex to find y-axis unit in a line for a spectrogram
70 /// Regex to find y-axis unit in a line for a spectrogram
63 extern const QRegularExpression SPECTROGRAM_Y_AXIS_UNIT_REGEX;
71 extern const QRegularExpression SPECTROGRAM_Y_AXIS_UNIT_REGEX;
64
72
65 /// Regex to find values unit in a line for a spectrogram
73 /// Regex to find values unit in a line for a spectrogram
66 extern const QRegularExpression SPECTROGRAM_VALUES_UNIT_REGEX;
74 extern const QRegularExpression SPECTROGRAM_VALUES_UNIT_REGEX;
67
75
68 #endif // SCIQLOP_AMDARESULTPARSERDEFS_H
76 #endif // SCIQLOP_AMDARESULTPARSERDEFS_H
@@ -1,105 +1,107
1 #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H
1 #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H
2 #define SCIQLOP_AMDARESULTPARSERHELPER_H
2 #define SCIQLOP_AMDARESULTPARSERHELPER_H
3
3
4 #include "AmdaResultParserDefs.h"
4 #include "AmdaResultParserDefs.h"
5
5
6 #include <QtCore/QLoggingCategory>
6 #include <QtCore/QLoggingCategory>
7 #include <QtCore/QString>
7 #include <QtCore/QString>
8
8
9 #include <memory>
9 #include <memory>
10
10
11 class IDataSeries;
11 class IDataSeries;
12
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper)
13 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper)
14
14
15 /**
15 /**
16 * Helper used to interpret the data of an AMDA result file and generate the corresponding data
16 * Helper used to interpret the data of an AMDA result file and generate the corresponding data
17 * series.
17 * series.
18 *
18 *
19 * It proposes methods allowing to read line by line an AMDA file and to extract the properties
19 * It proposes methods allowing to read line by line an AMDA file and to extract the properties
20 * (from the header) and the values corresponding to the data series
20 * (from the header) and the values corresponding to the data series
21 *
21 *
22 * @sa DataSeries
22 * @sa DataSeries
23 */
23 */
24 struct IAmdaResultParserHelper {
24 struct IAmdaResultParserHelper {
25 virtual ~IAmdaResultParserHelper() noexcept = default;
25 virtual ~IAmdaResultParserHelper() noexcept = default;
26
26
27 /// Verifies that the extracted properties are well formed and possibly applies other treatments
27 /// Verifies that the extracted properties are well formed and possibly applies other treatments
28 /// on them
28 /// on them
29 /// @return true if the properties are well formed, false otherwise
29 /// @return true if the properties are well formed, false otherwise
30 virtual bool checkProperties() = 0;
30 virtual bool checkProperties() = 0;
31
31
32 /// Creates the data series from the properties and values extracted from the AMDA file.
32 /// Creates the data series from the properties and values extracted from the AMDA file.
33 /// @warning as the data are moved in the data series, the helper shouldn't be used after
33 /// @warning as the data are moved in the data series, the helper shouldn't be used after
34 /// calling this method
34 /// calling this method
35 /// @return the data series created
35 /// @return the data series created
36 virtual std::shared_ptr<IDataSeries> createSeries() = 0;
36 virtual std::shared_ptr<IDataSeries> createSeries() = 0;
37
37
38 /// Reads a line from the AMDA file to extract a property that will be used to generate the data
38 /// Reads a line from the AMDA file to extract a property that will be used to generate the data
39 /// series
39 /// series
40 /// @param line tahe line to interpret
40 /// @param line tahe line to interpret
41 virtual void readPropertyLine(const QString &line) = 0;
41 virtual void readPropertyLine(const QString &line) = 0;
42
42
43 /// Reads a line from the AMDA file to extract a value that will be set in the data series
43 /// Reads a line from the AMDA file to extract a value that will be set in the data series
44 /// @param line the line to interpret
44 /// @param line the line to interpret
45 virtual void readResultLine(const QString &line) = 0;
45 virtual void readResultLine(const QString &line) = 0;
46 };
46 };
47
47
48 /**
48 /**
49 * Implementation of @sa IAmdaResultParserHelper for scalars
49 * Implementation of @sa IAmdaResultParserHelper for scalars
50 */
50 */
51 class ScalarParserHelper : public IAmdaResultParserHelper {
51 class ScalarParserHelper : public IAmdaResultParserHelper {
52 public:
52 public:
53 bool checkProperties() override;
53 bool checkProperties() override;
54 std::shared_ptr<IDataSeries> createSeries() override;
54 std::shared_ptr<IDataSeries> createSeries() override;
55 void readPropertyLine(const QString &line) override;
55 void readPropertyLine(const QString &line) override;
56 void readResultLine(const QString &line) override;
56 void readResultLine(const QString &line) override;
57
57
58 private:
58 private:
59 /// @return the reading order of the "value" columns for a result line of the AMDA file
59 /// @return the reading order of the "value" columns for a result line of the AMDA file
60 std::vector<int> valuesIndexes() const;
60 std::vector<int> valuesIndexes() const;
61
61
62 Properties m_Properties{};
62 Properties m_Properties{};
63 std::vector<double> m_XAxisData{};
63 std::vector<double> m_XAxisData{};
64 std::vector<double> m_ValuesData{};
64 std::vector<double> m_ValuesData{};
65 };
65 };
66
66
67 /**
67 /**
68 * Implementation of @sa IAmdaResultParserHelper for spectrograms
68 * Implementation of @sa IAmdaResultParserHelper for spectrograms
69 */
69 */
70 class SpectrogramParserHelper : public IAmdaResultParserHelper {
70 class SpectrogramParserHelper : public IAmdaResultParserHelper {
71 public:
71 public:
72 bool checkProperties() override;
72 bool checkProperties() override;
73 std::shared_ptr<IDataSeries> createSeries() override;
73 std::shared_ptr<IDataSeries> createSeries() override;
74 void readPropertyLine(const QString &line) override;
74 void readPropertyLine(const QString &line) override;
75 void readResultLine(const QString &line) override;
75 void readResultLine(const QString &line) override;
76
76
77 private:
77 private:
78 void handleDataHoles();
79
78 Properties m_Properties{};
80 Properties m_Properties{};
79 std::vector<double> m_XAxisData{};
81 std::vector<double> m_XAxisData{};
80 std::vector<double> m_YAxisData{};
82 std::vector<double> m_YAxisData{};
81 std::vector<double> m_ValuesData{};
83 std::vector<double> m_ValuesData{};
82 std::vector<int> m_ValuesIndexes{};
84 std::vector<int> m_ValuesIndexes{};
83 double m_FillValue{std::numeric_limits<double>::quiet_NaN()};
85 double m_FillValue{std::numeric_limits<double>::quiet_NaN()};
84 };
86 };
85
87
86 /**
88 /**
87 * Implementation of @sa IAmdaResultParserHelper for vectors
89 * Implementation of @sa IAmdaResultParserHelper for vectors
88 */
90 */
89 class VectorParserHelper : public IAmdaResultParserHelper {
91 class VectorParserHelper : public IAmdaResultParserHelper {
90 public:
92 public:
91 bool checkProperties() override;
93 bool checkProperties() override;
92 std::shared_ptr<IDataSeries> createSeries() override;
94 std::shared_ptr<IDataSeries> createSeries() override;
93 void readPropertyLine(const QString &line) override;
95 void readPropertyLine(const QString &line) override;
94 void readResultLine(const QString &line) override;
96 void readResultLine(const QString &line) override;
95
97
96 private:
98 private:
97 /// @return the reading order of the "value" columns for a result line of the AMDA file
99 /// @return the reading order of the "value" columns for a result line of the AMDA file
98 std::vector<int> valuesIndexes() const;
100 std::vector<int> valuesIndexes() const;
99
101
100 Properties m_Properties{};
102 Properties m_Properties{};
101 std::vector<double> m_XAxisData{};
103 std::vector<double> m_XAxisData{};
102 std::vector<double> m_ValuesData{};
104 std::vector<double> m_ValuesData{};
103 };
105 };
104
106
105 #endif // SCIQLOP_AMDARESULTPARSERHELPER_H
107 #endif // SCIQLOP_AMDARESULTPARSERHELPER_H
@@ -1,34 +1,42
1 #include "AmdaResultParserDefs.h"
1 #include "AmdaResultParserDefs.h"
2
2
3 const QString END_TIME_PROPERTY = QStringLiteral("endTime");
3 const QString FILL_VALUE_PROPERTY = QStringLiteral("fillValue");
4 const QString FILL_VALUE_PROPERTY = QStringLiteral("fillValue");
4 const QString MAX_BANDS_PROPERTY = QStringLiteral("maxBands");
5 const QString MAX_BANDS_PROPERTY = QStringLiteral("maxBands");
5 const QString MIN_BANDS_PROPERTY = QStringLiteral("minBands");
6 const QString MIN_BANDS_PROPERTY = QStringLiteral("minBands");
6 const QString MAX_SAMPLING_PROPERTY = QStringLiteral("maxSampling");
7 const QString MAX_SAMPLING_PROPERTY = QStringLiteral("maxSampling");
7 const QString MIN_SAMPLING_PROPERTY = QStringLiteral("minSampling");
8 const QString MIN_SAMPLING_PROPERTY = QStringLiteral("minSampling");
9 const QString START_TIME_PROPERTY = QStringLiteral("startTime");
8 const QString X_AXIS_UNIT_PROPERTY = QStringLiteral("xAxisUnit");
10 const QString X_AXIS_UNIT_PROPERTY = QStringLiteral("xAxisUnit");
9 const QString Y_AXIS_UNIT_PROPERTY = QStringLiteral("yAxisUnit");
11 const QString Y_AXIS_UNIT_PROPERTY = QStringLiteral("yAxisUnit");
10 const QString VALUES_UNIT_PROPERTY = QStringLiteral("valuesUnit");
12 const QString VALUES_UNIT_PROPERTY = QStringLiteral("valuesUnit");
11
13
12 const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX
14 const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX
13 = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
15 = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
14
16
17 const QRegularExpression SPECTROGRAM_END_TIME_REGEX
18 = QRegularExpression{QStringLiteral("\\s*INTERVAL_STOP\\s*:\\s*(.*)")};
19
15 const QRegularExpression SPECTROGRAM_FILL_VALUE_REGEX
20 const QRegularExpression SPECTROGRAM_FILL_VALUE_REGEX
16 = QRegularExpression{QStringLiteral("\\s*PARAMETER_FILL_VALUE\\s*:\\s*(.*)")};
21 = QRegularExpression{QStringLiteral("\\s*PARAMETER_FILL_VALUE\\s*:\\s*(.*)")};
17
22
18 const QRegularExpression SPECTROGRAM_MAX_BANDS_REGEX
23 const QRegularExpression SPECTROGRAM_MAX_BANDS_REGEX
19 = QRegularExpression{QStringLiteral("\\s*PARAMETER_TABLE_MAX_VALUES\\[0\\]\\s*:\\s*(.*)")};
24 = QRegularExpression{QStringLiteral("\\s*PARAMETER_TABLE_MAX_VALUES\\[0\\]\\s*:\\s*(.*)")};
20
25
21 const QRegularExpression SPECTROGRAM_MIN_BANDS_REGEX
26 const QRegularExpression SPECTROGRAM_MIN_BANDS_REGEX
22 = QRegularExpression{QStringLiteral("\\s*PARAMETER_TABLE_MIN_VALUES\\[0\\]\\s*:\\s*(.*)")};
27 = QRegularExpression{QStringLiteral("\\s*PARAMETER_TABLE_MIN_VALUES\\[0\\]\\s*:\\s*(.*)")};
23
28
24 const QRegularExpression SPECTROGRAM_MAX_SAMPLING_REGEX
29 const QRegularExpression SPECTROGRAM_MAX_SAMPLING_REGEX
25 = QRegularExpression{QStringLiteral("\\s*DATASET_MAX_SAMPLING\\s*:\\s*(.*)")};
30 = QRegularExpression{QStringLiteral("\\s*DATASET_MAX_SAMPLING\\s*:\\s*(.*)")};
26
31
27 const QRegularExpression SPECTROGRAM_MIN_SAMPLING_REGEX
32 const QRegularExpression SPECTROGRAM_MIN_SAMPLING_REGEX
28 = QRegularExpression{QStringLiteral("\\s*DATASET_MIN_SAMPLING\\s*:\\s*(.*)")};
33 = QRegularExpression{QStringLiteral("\\s*DATASET_MIN_SAMPLING\\s*:\\s*(.*)")};
29
34
35 const QRegularExpression SPECTROGRAM_START_TIME_REGEX
36 = QRegularExpression{QStringLiteral("\\s*INTERVAL_START\\s*:\\s*(.*)")};
37
30 const QRegularExpression SPECTROGRAM_Y_AXIS_UNIT_REGEX
38 const QRegularExpression SPECTROGRAM_Y_AXIS_UNIT_REGEX
31 = QRegularExpression{QStringLiteral("\\s*PARAMETER_TABLE_UNITS\\[0\\]\\s*:\\s*(.*)")};
39 = QRegularExpression{QStringLiteral("\\s*PARAMETER_TABLE_UNITS\\[0\\]\\s*:\\s*(.*)")};
32
40
33 const QRegularExpression SPECTROGRAM_VALUES_UNIT_REGEX
41 const QRegularExpression SPECTROGRAM_VALUES_UNIT_REGEX
34 = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.*)")};
42 = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.*)")};
@@ -1,387 +1,423
1 #include "AmdaResultParserHelper.h"
1 #include "AmdaResultParserHelper.h"
2
2
3 #include <Common/DateUtils.h>
3 #include <Common/DateUtils.h>
4 #include <Common/SortUtils.h>
4 #include <Common/SortUtils.h>
5
5
6 #include <Data/DataSeriesUtils.h>
6 #include <Data/ScalarSeries.h>
7 #include <Data/ScalarSeries.h>
7 #include <Data/SpectrogramSeries.h>
8 #include <Data/SpectrogramSeries.h>
8 #include <Data/Unit.h>
9 #include <Data/Unit.h>
9 #include <Data/VectorSeries.h>
10 #include <Data/VectorSeries.h>
10
11
11 #include <QtCore/QDateTime>
12 #include <QtCore/QDateTime>
12 #include <QtCore/QRegularExpression>
13 #include <QtCore/QRegularExpression>
13
14
14 #include <functional>
15 #include <functional>
15
16
16 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
17 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
17
18
18 namespace {
19 namespace {
19
20
20 // ///////// //
21 // ///////// //
21 // Constants //
22 // Constants //
22 // ///////// //
23 // ///////// //
23
24
24 /// Separator between values in a result line
25 /// Separator between values in a result line
25 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
26 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
26
27
27 /// Format for dates in result files
28 /// Format for dates in result files
28 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
29 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
29
30
30 // /////// //
31 // /////// //
31 // Methods //
32 // Methods //
32 // /////// //
33 // /////// //
33
34
34 /**
35 /**
35 * Checks that the properties contain a specific unit and that this unit is valid
36 * Checks that the properties contain a specific unit and that this unit is valid
36 * @param properties the properties map in which to search unit
37 * @param properties the properties map in which to search unit
37 * @param key the key to search for the unit in the properties
38 * @param key the key to search for the unit in the properties
38 * @param errorMessage the error message to log in case the unit is invalid
39 * @param errorMessage the error message to log in case the unit is invalid
39 * @return true if the unit is valid, false it it's invalid or was not found in the properties
40 * @return true if the unit is valid, false it it's invalid or was not found in the properties
40 */
41 */
41 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
42 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
42 {
43 {
43 auto unit = properties.value(key).value<Unit>();
44 auto unit = properties.value(key).value<Unit>();
44 if (unit.m_Name.isEmpty()) {
45 if (unit.m_Name.isEmpty()) {
45 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
46 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
46 return false;
47 return false;
47 }
48 }
48
49
49 return true;
50 return true;
50 }
51 }
51
52
52 QDateTime dateTimeFromString(const QString &stringDate) noexcept
53 QDateTime dateTimeFromString(const QString &stringDate) noexcept
53 {
54 {
54 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
55 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
55 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
56 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
56 #else
57 #else
57 return QDateTime::fromString(stringDate, DATE_FORMAT);
58 return QDateTime::fromString(stringDate, DATE_FORMAT);
58 #endif
59 #endif
59 }
60 }
60
61
61 /// Converts a string date to a double date
62 /// Converts a string date to a double date
62 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
63 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
63 double doubleDate(const QString &stringDate) noexcept
64 double doubleDate(const QString &stringDate) noexcept
64 {
65 {
65 // Format: yyyy-MM-ddThh:mm:ss.zzz
66 // Format: yyyy-MM-ddThh:mm:ss.zzz
66 auto dateTime = dateTimeFromString(stringDate);
67 auto dateTime = dateTimeFromString(stringDate);
67 dateTime.setTimeSpec(Qt::UTC);
68 dateTime.setTimeSpec(Qt::UTC);
68 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
69 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
69 : std::numeric_limits<double>::quiet_NaN();
70 : std::numeric_limits<double>::quiet_NaN();
70 }
71 }
71
72
72 /**
73 /**
73 * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
74 * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
74 * @param xAxisData the vector in which to store the x-axis data extracted
75 * @param xAxisData the vector in which to store the x-axis data extracted
75 * @param valuesData the vector in which to store the value extracted
76 * @param valuesData the vector in which to store the value extracted
76 * @param line the line to read to extract the property
77 * @param line the line to read to extract the property
77 * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
78 * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
78 * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
79 * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
79 * and inserted first, then the value of the first column, and finally the value of the second
80 * and inserted first, then the value of the first column, and finally the value of the second
80 * column.
81 * column.
81 * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
82 * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
82 * value is -1, then this value is considered as invalid and converted to NaN
83 * value is -1, then this value is considered as invalid and converted to NaN
83 */
84 */
84 void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
85 void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
85 const QString &line, const std::vector<int> &valuesIndexes,
86 const QString &line, const std::vector<int> &valuesIndexes,
86 double fillValue = std::numeric_limits<double>::quiet_NaN())
87 double fillValue = std::numeric_limits<double>::quiet_NaN())
87 {
88 {
88 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
89 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
89
90
90 // Checks that the line contains expected number of values + x-axis value
91 // Checks that the line contains expected number of values + x-axis value
91 if (static_cast<size_t>(lineData.size()) == valuesIndexes.size() + 1) {
92 if (static_cast<size_t>(lineData.size()) == valuesIndexes.size() + 1) {
92 // X : the data is converted from date to double (in secs)
93 // X : the data is converted from date to double (in secs)
93 auto x = doubleDate(lineData.at(0));
94 auto x = doubleDate(lineData.at(0));
94
95
95 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
96 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
96 if (!std::isnan(x)) {
97 if (!std::isnan(x)) {
97 xAxisData.push_back(x);
98 xAxisData.push_back(x);
98
99
99 // Values
100 // Values
100 for (auto valueIndex : valuesIndexes) {
101 for (auto valueIndex : valuesIndexes) {
101 bool valueOk;
102 bool valueOk;
102 // we use valueIndex + 1 to skip column 0 (x-axis value)
103 // we use valueIndex + 1 to skip column 0 (x-axis value)
103 auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
104 auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
104
105
105 if (!valueOk) {
106 if (!valueOk) {
106 qCWarning(LOG_AmdaResultParserHelper())
107 qCWarning(LOG_AmdaResultParserHelper())
107 << QObject::tr(
108 << QObject::tr(
108 "Value from (line %1, column %2) is invalid and will be "
109 "Value from (line %1, column %2) is invalid and will be "
109 "converted to NaN")
110 "converted to NaN")
110 .arg(line, valueIndex);
111 .arg(line, valueIndex);
111 value = std::numeric_limits<double>::quiet_NaN();
112 value = std::numeric_limits<double>::quiet_NaN();
112 }
113 }
113
114
114 // Handles fill value
115 // Handles fill value
115 if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
116 if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
116 value = std::numeric_limits<double>::quiet_NaN();
117 value = std::numeric_limits<double>::quiet_NaN();
117 }
118 }
118
119
119 valuesData.push_back(value);
120 valuesData.push_back(value);
120 }
121 }
121 }
122 }
122 else {
123 else {
123 qCWarning(LOG_AmdaResultParserHelper())
124 qCWarning(LOG_AmdaResultParserHelper())
124 << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
125 << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
125 }
126 }
126 }
127 }
127 else {
128 else {
128 qCWarning(LOG_AmdaResultParserHelper())
129 qCWarning(LOG_AmdaResultParserHelper())
129 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
130 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
130 }
131 }
131 }
132 }
132
133
133 /**
134 /**
134 * Reads a line from the AMDA file and tries to extract a property from it
135 * Reads a line from the AMDA file and tries to extract a property from it
135 * @param properties the properties map in which to put the property extracted from the line
136 * @param properties the properties map in which to put the property extracted from the line
136 * @param key the key to which the property is added in the properties map
137 * @param key the key to which the property is added in the properties map
137 * @param line the line to read to extract the property
138 * @param line the line to read to extract the property
138 * @param regex the expected regex to extract the property. If the line matches this regex, the
139 * @param regex the expected regex to extract the property. If the line matches this regex, the
139 * property is generated
140 * property is generated
140 * @param fun the function used to generate the property
141 * @param fun the function used to generate the property
141 * @return true if the property could be generated, false if the line does not match the regex, or
142 * @return true if the property could be generated, false if the line does not match the regex, or
142 * if a property has already been generated for the key
143 * if a property has already been generated for the key
143 */
144 */
144 template <typename GeneratePropertyFun>
145 template <typename GeneratePropertyFun>
145 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
146 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
146 const QRegularExpression &regex, GeneratePropertyFun fun)
147 const QRegularExpression &regex, GeneratePropertyFun fun)
147 {
148 {
148 if (properties.contains(key)) {
149 if (properties.contains(key)) {
149 return false;
150 return false;
150 }
151 }
151
152
152 auto match = regex.match(line);
153 auto match = regex.match(line);
153 if (match.hasMatch()) {
154 if (match.hasMatch()) {
154 properties.insert(key, fun(match));
155 properties.insert(key, fun(match));
155 }
156 }
156
157
157 return match.hasMatch();
158 return match.hasMatch();
158 }
159 }
159
160
160 /**
161 /**
162 * Reads a line from the AMDA file and tries to extract a data from it. Date is converted to double
163 * @sa tryReadProperty()
164 */
165 bool tryReadDate(Properties &properties, const QString &key, const QString &line,
166 const QRegularExpression &regex, bool timeUnit = false)
167 {
168 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
169 return QVariant::fromValue(doubleDate(match.captured(1)));
170 });
171 }
172
173 /**
161 * Reads a line from the AMDA file and tries to extract a double from it
174 * Reads a line from the AMDA file and tries to extract a double from it
162 * @sa tryReadProperty()
175 * @sa tryReadProperty()
163 */
176 */
164 bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
177 bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
165 const QRegularExpression &regex)
178 const QRegularExpression &regex)
166 {
179 {
167 return tryReadProperty(properties, key, line, regex, [](const auto &match) {
180 return tryReadProperty(properties, key, line, regex, [](const auto &match) {
168 bool ok;
181 bool ok;
169
182
170 // If the value can't be converted to double, it is set to NaN
183 // If the value can't be converted to double, it is set to NaN
171 auto doubleValue = match.captured(1).toDouble(&ok);
184 auto doubleValue = match.captured(1).toDouble(&ok);
172 if (!ok) {
185 if (!ok) {
173 doubleValue = std::numeric_limits<double>::quiet_NaN();
186 doubleValue = std::numeric_limits<double>::quiet_NaN();
174 }
187 }
175
188
176 return QVariant::fromValue(doubleValue);
189 return QVariant::fromValue(doubleValue);
177 });
190 });
178 }
191 }
179
192
180 /**
193 /**
181 * Reads a line from the AMDA file and tries to extract a vector of doubles from it
194 * Reads a line from the AMDA file and tries to extract a vector of doubles from it
182 * @param sep the separator of double values in the line
195 * @param sep the separator of double values in the line
183 * @sa tryReadProperty()
196 * @sa tryReadProperty()
184 */
197 */
185 bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
198 bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
186 const QRegularExpression &regex, const QString &sep = QStringLiteral(","))
199 const QRegularExpression &regex, const QString &sep = QStringLiteral(","))
187 {
200 {
188 return tryReadProperty(properties, key, line, regex, [sep](const auto &match) {
201 return tryReadProperty(properties, key, line, regex, [sep](const auto &match) {
189 std::vector<double> doubleValues{};
202 std::vector<double> doubleValues{};
190
203
191 // If the value can't be converted to double, it is set to NaN
204 // If the value can't be converted to double, it is set to NaN
192 auto values = match.captured(1).split(sep);
205 auto values = match.captured(1).split(sep);
193 for (auto value : values) {
206 for (auto value : values) {
194 bool ok;
207 bool ok;
195
208
196 auto doubleValue = value.toDouble(&ok);
209 auto doubleValue = value.toDouble(&ok);
197 if (!ok) {
210 if (!ok) {
198 doubleValue = std::numeric_limits<double>::quiet_NaN();
211 doubleValue = std::numeric_limits<double>::quiet_NaN();
199 }
212 }
200
213
201 doubleValues.push_back(doubleValue);
214 doubleValues.push_back(doubleValue);
202 }
215 }
203
216
204 return QVariant::fromValue(doubleValues);
217 return QVariant::fromValue(doubleValues);
205 });
218 });
206 }
219 }
207
220
208 /**
221 /**
209 * Reads a line from the AMDA file and tries to extract a unit from it
222 * Reads a line from the AMDA file and tries to extract a unit from it
210 * @sa tryReadProperty()
223 * @sa tryReadProperty()
211 */
224 */
212 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
225 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
213 const QRegularExpression &regex, bool timeUnit = false)
226 const QRegularExpression &regex, bool timeUnit = false)
214 {
227 {
215 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
228 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
216 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
229 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
217 });
230 });
218 }
231 }
219
232
220 } // namespace
233 } // namespace
221
234
222 // ////////////////// //
235 // ////////////////// //
223 // ScalarParserHelper //
236 // ScalarParserHelper //
224 // ////////////////// //
237 // ////////////////// //
225
238
226 bool ScalarParserHelper::checkProperties()
239 bool ScalarParserHelper::checkProperties()
227 {
240 {
228 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
241 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
229 QObject::tr("The x-axis unit could not be found in the file"));
242 QObject::tr("The x-axis unit could not be found in the file"));
230 }
243 }
231
244
232 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
245 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
233 {
246 {
234 return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
247 return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
235 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
248 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
236 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
249 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
237 }
250 }
238
251
239 void ScalarParserHelper::readPropertyLine(const QString &line)
252 void ScalarParserHelper::readPropertyLine(const QString &line)
240 {
253 {
241 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
254 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
242 }
255 }
243
256
244 void ScalarParserHelper::readResultLine(const QString &line)
257 void ScalarParserHelper::readResultLine(const QString &line)
245 {
258 {
246 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
259 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
247 }
260 }
248
261
249 std::vector<int> ScalarParserHelper::valuesIndexes() const
262 std::vector<int> ScalarParserHelper::valuesIndexes() const
250 {
263 {
251 // Only one value to read
264 // Only one value to read
252 static auto result = std::vector<int>{0};
265 static auto result = std::vector<int>{0};
253 return result;
266 return result;
254 }
267 }
255
268
256 // /////////////////////// //
269 // /////////////////////// //
257 // SpectrogramParserHelper //
270 // SpectrogramParserHelper //
258 // /////////////////////// //
271 // /////////////////////// //
259
272
260 bool SpectrogramParserHelper::checkProperties()
273 bool SpectrogramParserHelper::checkProperties()
261 {
274 {
262 // Generates y-axis data from bands extracted (take the middle of the intervals)
275 // Generates y-axis data from bands extracted (take the middle of the intervals)
263 auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
276 auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
264 auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
277 auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
265
278
266 if (minBands.size() != maxBands.size()) {
279 if (minBands.size() < 2 || minBands.size() != maxBands.size()) {
267 qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
280 qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
268 "Can't generate y-axis data from bands extracted: bands intervals are invalid");
281 "Can't generate y-axis data from bands extracted: bands intervals are invalid");
269 return false;
282 return false;
270 }
283 }
271
284
272 std::transform(
285 std::transform(
273 minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
286 minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
274 [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
287 [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
275
288
276 // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
289 // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
277 // order of the associated bands)
290 // order of the associated bands)
278 m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
291 m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
279
292
280 // Sorts y-axis data accoding to the ascending order
293 // Sorts y-axis data accoding to the ascending order
281 m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
294 m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
282
295
283 // Sets fill value
296 // Sets fill value
284 m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
297 m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
285
298
286 /// @todo: handle min/max samplings?
287
288 return true;
299 return true;
289 }
300 }
290
301
291 std::shared_ptr<IDataSeries> SpectrogramParserHelper::createSeries()
302 std::shared_ptr<IDataSeries> SpectrogramParserHelper::createSeries()
292 {
303 {
304 // Before creating the series, we handle its data holes
305 handleDataHoles();
306
293 return std::make_shared<SpectrogramSeries>(
307 return std::make_shared<SpectrogramSeries>(
294 std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
308 std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
295 Unit{"t", true}, // x-axis unit is always a time unit
309 Unit{"t", true}, // x-axis unit is always a time unit
296 m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
310 m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
297 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
311 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>(),
312 m_Properties.value(MIN_SAMPLING_PROPERTY).value<double>());
298 }
313 }
299
314
300 void SpectrogramParserHelper::readPropertyLine(const QString &line)
315 void SpectrogramParserHelper::readPropertyLine(const QString &line)
301 {
316 {
302 // Set of functions to test on the line to generate a property. If a function is valid (i.e. a
317 // Set of functions to test on the line to generate a property. If a function is valid (i.e. a
303 // property has been generated for the line), the line is treated as processed and the other
318 // property has been generated for the line), the line is treated as processed and the other
304 // functions are not called
319 // functions are not called
305 std::vector<std::function<bool()> > functions{
320 std::vector<std::function<bool()> > functions{
306 // values unit
321 // values unit
307 [&] {
322 [&] {
308 return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
323 return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
309 SPECTROGRAM_VALUES_UNIT_REGEX);
324 SPECTROGRAM_VALUES_UNIT_REGEX);
310 },
325 },
311 // y-axis unit
326 // y-axis unit
312 [&] {
327 [&] {
313 return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
328 return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
314 SPECTROGRAM_Y_AXIS_UNIT_REGEX);
329 SPECTROGRAM_Y_AXIS_UNIT_REGEX);
315 },
330 },
316 // min sampling
331 // min sampling
317 [&] {
332 [&] {
318 return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
333 return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
319 SPECTROGRAM_MIN_SAMPLING_REGEX);
334 SPECTROGRAM_MIN_SAMPLING_REGEX);
320 },
335 },
321 // max sampling
336 // max sampling
322 [&] {
337 [&] {
323 return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
338 return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
324 SPECTROGRAM_MAX_SAMPLING_REGEX);
339 SPECTROGRAM_MAX_SAMPLING_REGEX);
325 },
340 },
326 // fill value
341 // fill value
327 [&] {
342 [&] {
328 return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
343 return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
329 SPECTROGRAM_FILL_VALUE_REGEX);
344 SPECTROGRAM_FILL_VALUE_REGEX);
330 },
345 },
331 // min bounds of each band
346 // min bounds of each band
332 [&] {
347 [&] {
333 return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
348 return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
334 SPECTROGRAM_MIN_BANDS_REGEX);
349 SPECTROGRAM_MIN_BANDS_REGEX);
335 },
350 },
336 // max bounds of each band
351 // max bounds of each band
337 [&] {
352 [&] {
338 return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
353 return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
339 SPECTROGRAM_MAX_BANDS_REGEX);
354 SPECTROGRAM_MAX_BANDS_REGEX);
355 },
356 // start time of data
357 [&] {
358 return tryReadDate(m_Properties, START_TIME_PROPERTY, line,
359 SPECTROGRAM_START_TIME_REGEX);
360 },
361 // end time of data
362 [&] {
363 return tryReadDate(m_Properties, END_TIME_PROPERTY, line, SPECTROGRAM_END_TIME_REGEX);
340 }};
364 }};
341
365
342 for (auto function : functions) {
366 for (auto function : functions) {
343 // Stops at the first function that is valid
367 // Stops at the first function that is valid
344 if (function()) {
368 if (function()) {
345 return;
369 return;
346 }
370 }
347 }
371 }
348 }
372 }
349
373
350 void SpectrogramParserHelper::readResultLine(const QString &line)
374 void SpectrogramParserHelper::readResultLine(const QString &line)
351 {
375 {
352 tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
376 tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
353 }
377 }
354
378
379 void SpectrogramParserHelper::handleDataHoles()
380 {
381 // Fills data holes according to the max resolution found in the AMDA file
382 auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>();
383 auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
384 auto minBound = m_Properties.value(START_TIME_PROPERTY).value<double>();
385 auto maxBound = m_Properties.value(END_TIME_PROPERTY).value<double>();
386
387 DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue, minBound,
388 maxBound);
389 }
390
355 // ////////////////// //
391 // ////////////////// //
356 // VectorParserHelper //
392 // VectorParserHelper //
357 // ////////////////// //
393 // ////////////////// //
358
394
359 bool VectorParserHelper::checkProperties()
395 bool VectorParserHelper::checkProperties()
360 {
396 {
361 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
397 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
362 QObject::tr("The x-axis unit could not be found in the file"));
398 QObject::tr("The x-axis unit could not be found in the file"));
363 }
399 }
364
400
365 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
401 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
366 {
402 {
367 return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
403 return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
368 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
404 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
369 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
405 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
370 }
406 }
371
407
372 void VectorParserHelper::readPropertyLine(const QString &line)
408 void VectorParserHelper::readPropertyLine(const QString &line)
373 {
409 {
374 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
410 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
375 }
411 }
376
412
377 void VectorParserHelper::readResultLine(const QString &line)
413 void VectorParserHelper::readResultLine(const QString &line)
378 {
414 {
379 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
415 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
380 }
416 }
381
417
382 std::vector<int> VectorParserHelper::valuesIndexes() const
418 std::vector<int> VectorParserHelper::valuesIndexes() const
383 {
419 {
384 // 3 values to read, in order in the file (x, y, z)
420 // 3 values to read, in order in the file (x, y, z)
385 static auto result = std::vector<int>{0, 1, 2};
421 static auto result = std::vector<int>{0, 1, 2};
386 return result;
422 return result;
387 }
423 }
@@ -1,451 +1,579
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/SpectrogramSeries.h>
4 #include <Data/SpectrogramSeries.h>
5 #include <Data/VectorSeries.h>
5 #include <Data/VectorSeries.h>
6
6
7 #include <QObject>
7 #include <QObject>
8 #include <QtTest>
8 #include <QtTest>
9
9
10 namespace {
10 namespace {
11
11
12 /// Path for the tests
12 /// Path for the tests
13 const auto TESTS_RESOURCES_PATH
13 const auto TESTS_RESOURCES_PATH
14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
15
15
16 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
17 {
17 {
18 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
19 }
19 }
20
20
21 QString inputFilePath(const QString &inputFileName)
21 QString inputFilePath(const QString &inputFileName)
22 {
22 {
23 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
23 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
24 }
24 }
25
25
26 template <typename T>
26 template <typename T>
27 struct ExpectedResults {
27 struct ExpectedResults {
28
28
29 ExpectedResults &setParsingOK(bool parsingOK)
29 ExpectedResults &setParsingOK(bool parsingOK)
30 {
30 {
31 m_ParsingOK = parsingOK;
31 m_ParsingOK = parsingOK;
32 return *this;
32 return *this;
33 }
33 }
34
34
35 ExpectedResults &setXAxisUnit(Unit xAxisUnit)
35 ExpectedResults &setXAxisUnit(Unit xAxisUnit)
36 {
36 {
37 m_XAxisUnit = std::move(xAxisUnit);
37 m_XAxisUnit = std::move(xAxisUnit);
38 return *this;
38 return *this;
39 }
39 }
40
40
41 ExpectedResults &setXAxisData(const QVector<QDateTime> &xAxisData)
41 ExpectedResults &setXAxisData(const QVector<QDateTime> &xAxisData)
42 {
42 {
43 m_XAxisData.clear();
43 m_XAxisData.clear();
44
44
45 // Converts QVector<QDateTime> to QVector<double>
45 // Converts QVector<QDateTime> to QVector<double>
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
48
48
49 return *this;
49 return *this;
50 }
50 }
51
51
52 ExpectedResults &setValuesUnit(Unit valuesUnit)
52 ExpectedResults &setValuesUnit(Unit valuesUnit)
53 {
53 {
54 m_ValuesUnit = std::move(valuesUnit);
54 m_ValuesUnit = std::move(valuesUnit);
55 return *this;
55 return *this;
56 }
56 }
57
57
58 ExpectedResults &setValuesData(QVector<double> valuesData)
58 ExpectedResults &setValuesData(QVector<double> valuesData)
59 {
59 {
60 m_ValuesData.clear();
60 m_ValuesData.clear();
61 m_ValuesData.push_back(std::move(valuesData));
61 m_ValuesData.push_back(std::move(valuesData));
62 return *this;
62 return *this;
63 }
63 }
64
64
65 ExpectedResults &setValuesData(QVector<QVector<double> > valuesData)
65 ExpectedResults &setValuesData(QVector<QVector<double> > valuesData)
66 {
66 {
67 m_ValuesData = std::move(valuesData);
67 m_ValuesData = std::move(valuesData);
68 return *this;
68 return *this;
69 }
69 }
70
70
71 ExpectedResults &setYAxisEnabled(bool yAxisEnabled)
71 ExpectedResults &setYAxisEnabled(bool yAxisEnabled)
72 {
72 {
73 m_YAxisEnabled = yAxisEnabled;
73 m_YAxisEnabled = yAxisEnabled;
74 return *this;
74 return *this;
75 }
75 }
76
76
77 ExpectedResults &setYAxisUnit(Unit yAxisUnit)
77 ExpectedResults &setYAxisUnit(Unit yAxisUnit)
78 {
78 {
79 m_YAxisUnit = std::move(yAxisUnit);
79 m_YAxisUnit = std::move(yAxisUnit);
80 return *this;
80 return *this;
81 }
81 }
82
82
83 ExpectedResults &setYAxisData(QVector<double> yAxisData)
83 ExpectedResults &setYAxisData(QVector<double> yAxisData)
84 {
84 {
85 m_YAxisData = std::move(yAxisData);
85 m_YAxisData = std::move(yAxisData);
86 return *this;
86 return *this;
87 }
87 }
88
88
89 /**
89 /**
90 * Validates a DataSeries compared to the expected results
90 * Validates a DataSeries compared to the expected results
91 * @param results the DataSeries to validate
91 * @param results the DataSeries to validate
92 */
92 */
93 void validate(std::shared_ptr<IDataSeries> results)
93 void validate(std::shared_ptr<IDataSeries> results)
94 {
94 {
95 if (m_ParsingOK) {
95 if (m_ParsingOK) {
96 auto dataSeries = dynamic_cast<T *>(results.get());
96 auto dataSeries = dynamic_cast<T *>(results.get());
97 if (dataSeries == nullptr) {
97 if (dataSeries == nullptr) {
98
98
99 // No unit detected, parsink ok but data is nullptr
99 // No unit detected, parsink ok but data is nullptr
100 // TODO, improve the test to verify that the data is null
100 // TODO, improve the test to verify that the data is null
101 return;
101 return;
102 }
102 }
103
103
104 // Checks units
104 // Checks units
105 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
105 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
106 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
106 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
107
107
108 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
108 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
109 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
109 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
110 expectedData.cend(),
110 expectedData.cend(),
111 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
111 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
112 return equalFun(dataSeriesIt, expectedX);
112 return equalFun(dataSeriesIt, expectedX);
113 }));
113 }));
114 };
114 };
115
115
116 // Checks x-axis data
116 // Checks x-axis data
117 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
117 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
118 return seriesIt.x() == value;
118 return seriesIt.x() == value;
119 });
119 });
120
120
121 // Checks values data of each component
121 // Checks values data of each component
122 for (auto i = 0; i < m_ValuesData.size(); ++i) {
122 for (auto i = 0; i < m_ValuesData.size(); ++i) {
123 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
123 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
124 auto itValue = seriesIt.value(i);
124 auto itValue = seriesIt.value(i);
125 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
125 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
126 });
126 });
127 }
127 }
128
128
129 // Checks y-axis (if defined)
129 // Checks y-axis (if defined)
130 auto yAxis = dataSeries->yAxis();
130 auto yAxis = dataSeries->yAxis();
131 QCOMPARE(yAxis.isDefined(), m_YAxisEnabled);
131 QCOMPARE(yAxis.isDefined(), m_YAxisEnabled);
132
132
133 if (m_YAxisEnabled) {
133 if (m_YAxisEnabled) {
134 // Unit
134 // Unit
135 QCOMPARE(yAxis.unit(), m_YAxisUnit);
135 QCOMPARE(yAxis.unit(), m_YAxisUnit);
136
136
137 // Data
137 // Data
138 auto yAxisSize = yAxis.size();
138 QVERIFY(std::equal(yAxis.cbegin(), yAxis.cend(), m_YAxisData.cbegin(),
139 QCOMPARE(yAxisSize, m_YAxisData.size());
139 m_YAxisData.cend(), [](const auto &it, const auto &expectedVal) {
140 for (auto i = 0; i < yAxisSize; ++i) {
140 return it.first() == expectedVal;
141 QCOMPARE(yAxis.at(i), m_YAxisData.at(i));
141 }));
142 }
143 }
142 }
144 }
143 }
145 else {
144 else {
146 QVERIFY(results == nullptr);
145 QVERIFY(results == nullptr);
147 }
146 }
148 }
147 }
149
148
150 // Parsing was successfully completed
149 // Parsing was successfully completed
151 bool m_ParsingOK{false};
150 bool m_ParsingOK{false};
152 // Expected x-axis unit
151 // Expected x-axis unit
153 Unit m_XAxisUnit{};
152 Unit m_XAxisUnit{};
154 // Expected x-axis data
153 // Expected x-axis data
155 QVector<double> m_XAxisData{};
154 QVector<double> m_XAxisData{};
156 // Expected values unit
155 // Expected values unit
157 Unit m_ValuesUnit{};
156 Unit m_ValuesUnit{};
158 // Expected values data
157 // Expected values data
159 QVector<QVector<double> > m_ValuesData{};
158 QVector<QVector<double> > m_ValuesData{};
160 // Expected data series has y-axis
159 // Expected data series has y-axis
161 bool m_YAxisEnabled{false};
160 bool m_YAxisEnabled{false};
162 // Expected y-axis unit (if axis defined)
161 // Expected y-axis unit (if axis defined)
163 Unit m_YAxisUnit{};
162 Unit m_YAxisUnit{};
164 // Expected y-axis data (if axis defined)
163 // Expected y-axis data (if axis defined)
165 QVector<double> m_YAxisData{};
164 QVector<double> m_YAxisData{};
166 };
165 };
167
166
168 } // namespace
167 } // namespace
169
168
170 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
169 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
171 Q_DECLARE_METATYPE(ExpectedResults<SpectrogramSeries>)
170 Q_DECLARE_METATYPE(ExpectedResults<SpectrogramSeries>)
172 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
171 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
173
172
174 class TestAmdaResultParser : public QObject {
173 class TestAmdaResultParser : public QObject {
175 Q_OBJECT
174 Q_OBJECT
176 private:
175 private:
177 template <typename T>
176 template <typename T>
178 void testReadDataStructure()
177 void testReadDataStructure()
179 {
178 {
180 // ////////////// //
179 // ////////////// //
181 // Test structure //
180 // Test structure //
182 // ////////////// //
181 // ////////////// //
183
182
184 // Name of TXT file to read
183 // Name of TXT file to read
185 QTest::addColumn<QString>("inputFileName");
184 QTest::addColumn<QString>("inputFileName");
186 // Expected results
185 // Expected results
187 QTest::addColumn<ExpectedResults<T> >("expectedResults");
186 QTest::addColumn<ExpectedResults<T> >("expectedResults");
188 }
187 }
189
188
190 template <typename T>
189 template <typename T>
191 void testRead(AmdaResultParser::ValueType valueType)
190 void testRead(AmdaResultParser::ValueType valueType)
192 {
191 {
193 QFETCH(QString, inputFileName);
192 QFETCH(QString, inputFileName);
194 QFETCH(ExpectedResults<T>, expectedResults);
193 QFETCH(ExpectedResults<T>, expectedResults);
195
194
196 // Parses file
195 // Parses file
197 auto filePath = inputFilePath(inputFileName);
196 auto filePath = inputFilePath(inputFileName);
198 auto results = AmdaResultParser::readTxt(filePath, valueType);
197 auto results = AmdaResultParser::readTxt(filePath, valueType);
199
198
200 // ///////////////// //
199 // ///////////////// //
201 // Validates results //
200 // Validates results //
202 // ///////////////// //
201 // ///////////////// //
203 expectedResults.validate(results);
202 expectedResults.validate(results);
204 }
203 }
205
204
206 private slots:
205 private slots:
207 /// Input test data
206 /// Input test data
208 /// @sa testReadScalarTxt()
207 /// @sa testReadScalarTxt()
209 void testReadScalarTxt_data();
208 void testReadScalarTxt_data();
210
209
211 /// Tests parsing scalar series of a TXT file
210 /// Tests parsing scalar series of a TXT file
212 void testReadScalarTxt();
211 void testReadScalarTxt();
213
212
214 /// Input test data
213 /// Input test data
215 /// @sa testReadSpectrogramTxt()
214 /// @sa testReadSpectrogramTxt()
216 void testReadSpectrogramTxt_data();
215 void testReadSpectrogramTxt_data();
217
216
218 /// Tests parsing spectrogram series of a TXT file
217 /// Tests parsing spectrogram series of a TXT file
219 void testReadSpectrogramTxt();
218 void testReadSpectrogramTxt();
220
219
221 /// Input test data
220 /// Input test data
222 /// @sa testReadVectorTxt()
221 /// @sa testReadVectorTxt()
223 void testReadVectorTxt_data();
222 void testReadVectorTxt_data();
224
223
225 /// Tests parsing vector series of a TXT file
224 /// Tests parsing vector series of a TXT file
226 void testReadVectorTxt();
225 void testReadVectorTxt();
227 };
226 };
228
227
229 void TestAmdaResultParser::testReadScalarTxt_data()
228 void TestAmdaResultParser::testReadScalarTxt_data()
230 {
229 {
231 testReadDataStructure<ScalarSeries>();
230 testReadDataStructure<ScalarSeries>();
232
231
233 // ////////// //
232 // ////////// //
234 // Test cases //
233 // Test cases //
235 // ////////// //
234 // ////////// //
236
235
237 // Valid files
236 // Valid files
238 QTest::newRow("Valid file")
237 QTest::newRow("Valid file")
239 << QStringLiteral("ValidScalar1.txt")
238 << QStringLiteral("ValidScalar1.txt")
240 << ExpectedResults<ScalarSeries>{}
239 << ExpectedResults<ScalarSeries>{}
241 .setParsingOK(true)
240 .setParsingOK(true)
242 .setXAxisUnit(Unit{"nT", true})
241 .setXAxisUnit(Unit{"nT", true})
243 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
242 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
244 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
243 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
245 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
244 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
246 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
245 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
247 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)})
246 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)})
248 .setValuesData({-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
247 .setValuesData({-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
249 -2.55800, -2.43250, -2.42200});
248 -2.55800, -2.43250, -2.42200});
250
249
251 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
250 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
252 << QStringLiteral("WrongValue.txt")
251 << QStringLiteral("WrongValue.txt")
253 << ExpectedResults<ScalarSeries>{}
252 << ExpectedResults<ScalarSeries>{}
254 .setParsingOK(true)
253 .setParsingOK(true)
255 .setXAxisUnit(Unit{"nT", true})
254 .setXAxisUnit(Unit{"nT", true})
256 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
255 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
257 dateTime(2013, 9, 23, 9, 2, 30)})
256 dateTime(2013, 9, 23, 9, 2, 30)})
258 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
257 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
259
258
260 QTest::newRow("Valid file that contains NaN values")
259 QTest::newRow("Valid file that contains NaN values")
261 << QStringLiteral("NaNValue.txt")
260 << QStringLiteral("NaNValue.txt")
262 << ExpectedResults<ScalarSeries>{}
261 << ExpectedResults<ScalarSeries>{}
263 .setParsingOK(true)
262 .setParsingOK(true)
264 .setXAxisUnit(Unit{("nT"), true})
263 .setXAxisUnit(Unit{("nT"), true})
265 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
264 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
266 dateTime(2013, 9, 23, 9, 2, 30)})
265 dateTime(2013, 9, 23, 9, 2, 30)})
267 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
266 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
268
267
269 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
268 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
270 QTest::newRow("No unit file")
269 QTest::newRow("No unit file")
271 << QStringLiteral("NoUnit.txt")
270 << QStringLiteral("NoUnit.txt")
272 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"", true});
271 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"", true});
273
272
274 QTest::newRow("Wrong unit file")
273 QTest::newRow("Wrong unit file")
275 << QStringLiteral("WrongUnit.txt")
274 << QStringLiteral("WrongUnit.txt")
276 << ExpectedResults<ScalarSeries>{}
275 << ExpectedResults<ScalarSeries>{}
277 .setParsingOK(true)
276 .setParsingOK(true)
278 .setXAxisUnit(Unit{"", true})
277 .setXAxisUnit(Unit{"", true})
279 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
278 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
280 dateTime(2013, 9, 23, 9, 2, 30)})
279 dateTime(2013, 9, 23, 9, 2, 30)})
281 .setValuesData({-2.83950, -2.71850, -2.52150});
280 .setValuesData({-2.83950, -2.71850, -2.52150});
282
281
283 QTest::newRow("Wrong results file (date of first line is invalid")
282 QTest::newRow("Wrong results file (date of first line is invalid")
284 << QStringLiteral("WrongDate.txt")
283 << QStringLiteral("WrongDate.txt")
285 << ExpectedResults<ScalarSeries>{}
284 << ExpectedResults<ScalarSeries>{}
286 .setParsingOK(true)
285 .setParsingOK(true)
287 .setXAxisUnit(Unit{"nT", true})
286 .setXAxisUnit(Unit{"nT", true})
288 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
287 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
289 .setValuesData({-2.71850, -2.52150});
288 .setValuesData({-2.71850, -2.52150});
290
289
291 QTest::newRow("Wrong results file (too many values for first line")
290 QTest::newRow("Wrong results file (too many values for first line")
292 << QStringLiteral("TooManyValues.txt")
291 << QStringLiteral("TooManyValues.txt")
293 << ExpectedResults<ScalarSeries>{}
292 << ExpectedResults<ScalarSeries>{}
294 .setParsingOK(true)
293 .setParsingOK(true)
295 .setXAxisUnit(Unit{"nT", true})
294 .setXAxisUnit(Unit{"nT", true})
296 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
295 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
297 .setValuesData({-2.71850, -2.52150});
296 .setValuesData({-2.71850, -2.52150});
298
297
299 QTest::newRow("Wrong results file (x of first line is NaN")
298 QTest::newRow("Wrong results file (x of first line is NaN")
300 << QStringLiteral("NaNX.txt")
299 << QStringLiteral("NaNX.txt")
301 << ExpectedResults<ScalarSeries>{}
300 << ExpectedResults<ScalarSeries>{}
302 .setParsingOK(true)
301 .setParsingOK(true)
303 .setXAxisUnit(Unit{"nT", true})
302 .setXAxisUnit(Unit{"nT", true})
304 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
303 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
305 .setValuesData({-2.71850, -2.52150});
304 .setValuesData({-2.71850, -2.52150});
306
305
307 QTest::newRow("Invalid file type (vector)")
306 QTest::newRow("Invalid file type (vector)")
308 << QStringLiteral("ValidVector1.txt")
307 << QStringLiteral("ValidVector1.txt")
309 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"nT", true});
308 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"nT", true});
310
309
311 // Invalid files
310 // Invalid files
312 QTest::newRow("Invalid file (unexisting file)")
311 QTest::newRow("Invalid file (unexisting file)")
313 << QStringLiteral("UnexistingFile.txt")
312 << QStringLiteral("UnexistingFile.txt")
314 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
313 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
315
314
316 QTest::newRow("Invalid file (file not found on server)")
315 QTest::newRow("Invalid file (file not found on server)")
317 << QStringLiteral("FileNotFound.txt")
316 << QStringLiteral("FileNotFound.txt")
318 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
317 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
319 }
318 }
320
319
321 void TestAmdaResultParser::testReadScalarTxt()
320 void TestAmdaResultParser::testReadScalarTxt()
322 {
321 {
323 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
322 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
324 }
323 }
325
324
326 void TestAmdaResultParser::testReadSpectrogramTxt_data()
325 void TestAmdaResultParser::testReadSpectrogramTxt_data()
327 {
326 {
328 testReadDataStructure<SpectrogramSeries>();
327 testReadDataStructure<SpectrogramSeries>();
329
328
330 // ////////// //
329 // ////////// //
331 // Test cases //
330 // Test cases //
332 // ////////// //
331 // ////////// //
333
332
334 // Valid files
333 // Valid files
335 QTest::newRow("Valid file (three bands)")
334 QTest::newRow("Valid file (three bands)")
336 << QStringLiteral("spectro/ValidSpectrogram1.txt")
335 << QStringLiteral("spectro/ValidSpectrogram1.txt")
337 << ExpectedResults<SpectrogramSeries>{}
336 << ExpectedResults<SpectrogramSeries>{}
338 .setParsingOK(true)
337 .setParsingOK(true)
339 .setXAxisUnit(Unit{"t", true})
338 .setXAxisUnit(Unit{"t", true})
340 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
339 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
341 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
340 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
342 dateTime(2012, 11, 6, 9, 20, 55)})
341 dateTime(2012, 11, 6, 9, 20, 55)})
343 .setYAxisEnabled(true)
342 .setYAxisEnabled(true)
344 .setYAxisUnit(Unit{"eV"})
343 .setYAxisUnit(Unit{"eV"})
345 .setYAxisData({5.75, 7.6, 10.05}) // middle of the intervals of each band
344 .setYAxisData({5.75, 7.6, 10.05}) // middle of the intervals of each band
346 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
345 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
347 .setValuesData(QVector<QVector<double> >{
346 .setValuesData(QVector<QVector<double> >{
348 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
347 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
349 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
348 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
350 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221}});
349 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221}});
351
350
352 auto fourBandsResult
351 auto fourBandsResult
353 = ExpectedResults<SpectrogramSeries>{}
352 = ExpectedResults<SpectrogramSeries>{}
354 .setParsingOK(true)
353 .setParsingOK(true)
355 .setXAxisUnit(Unit{"t", true})
354 .setXAxisUnit(Unit{"t", true})
356 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
355 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
357 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
356 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
358 dateTime(2012, 11, 6, 9, 20, 55)})
357 dateTime(2012, 11, 6, 9, 20, 55)})
359 .setYAxisEnabled(true)
358 .setYAxisEnabled(true)
360 .setYAxisUnit(Unit{"eV"})
359 .setYAxisUnit(Unit{"eV"})
361 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
360 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
362 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
361 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
363 .setValuesData(QVector<QVector<double> >{
362 .setValuesData(QVector<QVector<double> >{
364 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
363 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
365 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
364 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
366 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
365 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
367 {20907.664, 32076.725, 13008.381, 13142.759, 23226.998}});
366 {20907.664, 32076.725, 13008.381, 13142.759, 23226.998}});
368
367
369 QTest::newRow("Valid file (four bands)")
368 QTest::newRow("Valid file (four bands)")
370 << QStringLiteral("spectro/ValidSpectrogram2.txt") << fourBandsResult;
369 << QStringLiteral("spectro/ValidSpectrogram2.txt") << fourBandsResult;
371 QTest::newRow("Valid file (four unsorted bands)")
370 QTest::newRow("Valid file (four unsorted bands)")
372 << QStringLiteral("spectro/ValidSpectrogram3.txt")
371 << QStringLiteral("spectro/ValidSpectrogram3.txt")
373 << fourBandsResult; // Bands and values are sorted
372 << fourBandsResult; // Bands and values are sorted
374
373
375 auto nan = std::numeric_limits<double>::quiet_NaN();
374 auto nan = std::numeric_limits<double>::quiet_NaN();
376
375
377 auto nanValuesResult
376 auto nanValuesResult
378 = ExpectedResults<SpectrogramSeries>{}
377 = ExpectedResults<SpectrogramSeries>{}
379 .setParsingOK(true)
378 .setParsingOK(true)
380 .setXAxisUnit(Unit{"t", true})
379 .setXAxisUnit(Unit{"t", true})
381 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
380 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
382 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
381 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
383 dateTime(2012, 11, 6, 9, 20, 55)})
382 dateTime(2012, 11, 6, 9, 20, 55)})
384 .setYAxisEnabled(true)
383 .setYAxisEnabled(true)
385 .setYAxisUnit(Unit{"eV"})
384 .setYAxisUnit(Unit{"eV"})
386 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
385 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
387 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
386 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
388 .setValuesData(
387 .setValuesData(
389 QVector<QVector<double> >{{nan, 12631.465, 8223.368, 27595.301, 12820.613},
388 QVector<QVector<double> >{{nan, 12631.465, 8223.368, 27595.301, 12820.613},
390 {15405.838, nan, nan, 25617.533, 11179.109},
389 {15405.838, nan, nan, 25617.533, 11179.109},
391 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
390 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
392 {nan, nan, nan, nan, nan}});
391 {nan, nan, nan, nan, nan}});
393
392
394 QTest::newRow("Valid file (containing NaN values)")
393 QTest::newRow("Valid file (containing NaN values)")
395 << QStringLiteral("spectro/ValidSpectrogramNaNValues.txt") << nanValuesResult;
394 << QStringLiteral("spectro/ValidSpectrogramNaNValues.txt") << nanValuesResult;
396 QTest::newRow("Valid file (containing fill values)")
395 QTest::newRow("Valid file (containing fill values)")
397 << QStringLiteral("spectro/ValidSpectrogramFillValues.txt")
396 << QStringLiteral("spectro/ValidSpectrogramFillValues.txt")
398 << nanValuesResult; // Fill values are replaced by NaN values in the data series
397 << nanValuesResult; // Fill values are replaced by NaN values in the data series
399
398
399 QTest::newRow("Valid file (containing data holes, resolution = 3 minutes)")
400 << QStringLiteral("spectro/ValidSpectrogramDataHoles.txt")
401 << ExpectedResults<SpectrogramSeries>{}
402 .setParsingOK(true)
403 .setXAxisUnit(Unit{"t", true})
404 .setXAxisData({dateTime(2011, 12, 10, 12, 10, 54), //
405 dateTime(2011, 12, 10, 12, 13, 54), // Data hole
406 dateTime(2011, 12, 10, 12, 16, 54), // Data hole
407 dateTime(2011, 12, 10, 12, 17, 23), //
408 dateTime(2011, 12, 10, 12, 20, 23), // Data hole
409 dateTime(2011, 12, 10, 12, 23, 23), // Data hole
410 dateTime(2011, 12, 10, 12, 23, 51), //
411 dateTime(2011, 12, 10, 12, 26, 51), // Data hole
412 dateTime(2011, 12, 10, 12, 29, 51), // Data hole
413 dateTime(2011, 12, 10, 12, 30, 19), //
414 dateTime(2011, 12, 10, 12, 33, 19), // Data hole
415 dateTime(2011, 12, 10, 12, 35, 04), //
416 dateTime(2011, 12, 10, 12, 36, 41), //
417 dateTime(2011, 12, 10, 12, 38, 18), //
418 dateTime(2011, 12, 10, 12, 39, 55)})
419 .setYAxisEnabled(true)
420 .setYAxisUnit(Unit{"eV"})
421 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
422 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
423 .setValuesData(QVector<QVector<double> >{{2577578.000, //
424 nan, // Data hole
425 nan, // Data hole
426 2314121.500, //
427 nan, // Data hole
428 nan, // Data hole
429 2063608.750, //
430 nan, // Data hole
431 nan, // Data hole
432 2234525.500, //
433 nan, // Data hole
434 1670215.250, //
435 1689243.250, //
436 1654617.125, //
437 1504983.750},
438 {2336016.000, //
439 nan, // Data hole
440 nan, // Data hole
441 1712093.125, //
442 nan, // Data hole
443 nan, // Data hole
444 1614491.625, //
445 nan, // Data hole
446 nan, // Data hole
447 1764516.500, //
448 nan, // Data hole
449 1688078.500, //
450 1743183.500, //
451 1733603.250, //
452 1708356.500}});
453
454 QTest::newRow(
455 "Valid file (containing data holes at the beginning and the end, resolution = 4 minutes)")
456 << QStringLiteral("spectro/ValidSpectrogramDataHoles2.txt")
457 << ExpectedResults<SpectrogramSeries>{}
458 .setParsingOK(true)
459 .setXAxisUnit(Unit{"t", true})
460 .setXAxisData({
461 dateTime(2011, 12, 10, 12, 2, 54), // Data hole
462 dateTime(2011, 12, 10, 12, 6, 54), // Data hole
463 dateTime(2011, 12, 10, 12, 10, 54), //
464 dateTime(2011, 12, 10, 12, 14, 54), // Data hole
465 dateTime(2011, 12, 10, 12, 17, 23), //
466 dateTime(2011, 12, 10, 12, 21, 23), // Data hole
467 dateTime(2011, 12, 10, 12, 23, 51), //
468 dateTime(2011, 12, 10, 12, 27, 51), // Data hole
469 dateTime(2011, 12, 10, 12, 30, 19), //
470 dateTime(2011, 12, 10, 12, 34, 19), // Data hole
471 dateTime(2011, 12, 10, 12, 35, 04), //
472 dateTime(2011, 12, 10, 12, 36, 41), //
473 dateTime(2011, 12, 10, 12, 38, 18), //
474 dateTime(2011, 12, 10, 12, 39, 55),
475 dateTime(2011, 12, 10, 12, 43, 55), // Data hole
476 dateTime(2011, 12, 10, 12, 47, 55), // Data hole
477 dateTime(2011, 12, 10, 12, 51, 55), // Data hole
478 dateTime(2011, 12, 10, 12, 55, 55), // Data hole
479 dateTime(2011, 12, 10, 12, 59, 55) // Data hole
480 })
481 .setYAxisEnabled(true)
482 .setYAxisUnit(Unit{"eV"})
483 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
484 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
485 .setValuesData(QVector<QVector<double> >{{
486 nan, // Data hole
487 nan, // Data hole
488 2577578.000, //
489 nan, // Data hole
490 2314121.500, //
491 nan, // Data hole
492 2063608.750, //
493 nan, // Data hole
494 2234525.500, //
495 nan, // Data hole
496 1670215.250, //
497 1689243.250, //
498 1654617.125, //
499 1504983.750, //
500 nan, // Data hole
501 nan, // Data hole
502 nan, // Data hole
503 nan, // Data hole
504 nan // Data hole
505 },
506 {
507 nan, // Data hole
508 nan, // Data hole
509 2336016.000, //
510 nan, // Data hole
511 1712093.125, //
512 nan, // Data hole
513 1614491.625, //
514 nan, // Data hole
515 1764516.500, //
516 nan, // Data hole
517 1688078.500, //
518 1743183.500, //
519 1733603.250, //
520 1708356.500, //
521 nan, // Data hole
522 nan, // Data hole
523 nan, // Data hole
524 nan, // Data hole
525 nan // Data hole
526 }});
527
400 // Invalid files
528 // Invalid files
401 QTest::newRow("Invalid file (inconsistent bands)")
529 QTest::newRow("Invalid file (inconsistent bands)")
402 << QStringLiteral("spectro/InvalidSpectrogramWrongBands.txt")
530 << QStringLiteral("spectro/InvalidSpectrogramWrongBands.txt")
403 << ExpectedResults<SpectrogramSeries>{}.setParsingOK(false);
531 << ExpectedResults<SpectrogramSeries>{}.setParsingOK(false);
404 }
532 }
405
533
406 void TestAmdaResultParser::testReadSpectrogramTxt()
534 void TestAmdaResultParser::testReadSpectrogramTxt()
407 {
535 {
408 testRead<SpectrogramSeries>(AmdaResultParser::ValueType::SPECTROGRAM);
536 testRead<SpectrogramSeries>(AmdaResultParser::ValueType::SPECTROGRAM);
409 }
537 }
410
538
411 void TestAmdaResultParser::testReadVectorTxt_data()
539 void TestAmdaResultParser::testReadVectorTxt_data()
412 {
540 {
413 testReadDataStructure<VectorSeries>();
541 testReadDataStructure<VectorSeries>();
414
542
415 // ////////// //
543 // ////////// //
416 // Test cases //
544 // Test cases //
417 // ////////// //
545 // ////////// //
418
546
419 // Valid files
547 // Valid files
420 QTest::newRow("Valid file")
548 QTest::newRow("Valid file")
421 << QStringLiteral("ValidVector1.txt")
549 << QStringLiteral("ValidVector1.txt")
422 << ExpectedResults<VectorSeries>{}
550 << ExpectedResults<VectorSeries>{}
423 .setParsingOK(true)
551 .setParsingOK(true)
424 .setXAxisUnit(Unit{"nT", true})
552 .setXAxisUnit(Unit{"nT", true})
425 .setXAxisData({dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
553 .setXAxisData({dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
426 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
554 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
427 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
555 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
428 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
556 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
429 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)})
557 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)})
430 .setValuesData(
558 .setValuesData(
431 {{-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
559 {{-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
432 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
560 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
433 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}});
561 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}});
434
562
435 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
563 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
436 QTest::newRow("Invalid file type (scalar)")
564 QTest::newRow("Invalid file type (scalar)")
437 << QStringLiteral("ValidScalar1.txt")
565 << QStringLiteral("ValidScalar1.txt")
438 << ExpectedResults<VectorSeries>{}
566 << ExpectedResults<VectorSeries>{}
439 .setParsingOK(true)
567 .setParsingOK(true)
440 .setXAxisUnit(Unit{"nT", true})
568 .setXAxisUnit(Unit{"nT", true})
441 .setXAxisData({})
569 .setXAxisData({})
442 .setValuesData(QVector<QVector<double> >{{}, {}, {}});
570 .setValuesData(QVector<QVector<double> >{{}, {}, {}});
443 }
571 }
444
572
445 void TestAmdaResultParser::testReadVectorTxt()
573 void TestAmdaResultParser::testReadVectorTxt()
446 {
574 {
447 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
575 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
448 }
576 }
449
577
450 QTEST_MAIN(TestAmdaResultParser)
578 QTEST_MAIN(TestAmdaResultParser)
451 #include "TestAmdaResultParser.moc"
579 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now