##// 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
@@ -36,7 +36,9 public:
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
@@ -44,7 +46,9 public:
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
@@ -65,7 +69,9 public:
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;
@@ -99,6 +105,15 public:
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(); }
@@ -110,11 +125,15 public:
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
@@ -193,6 +212,9 public:
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; }
@@ -202,6 +224,8 public:
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();
@@ -384,8 +408,8 public:
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 //
@@ -27,6 +27,7 public:
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;
@@ -51,6 +52,8 public:
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
@@ -39,6 +39,9 public:
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;
@@ -53,6 +56,9 public:
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 // ///////// //
@@ -1,6 +1,8
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
@@ -38,10 +40,6 public:
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
@@ -53,6 +51,12 public:
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
@@ -16,18 +16,25 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
@@ -27,6 +27,7 core_sources = [
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',
@@ -52,6 +52,11 double DataSeriesIteratorValue::x() const
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();
@@ -2,7 +2,8
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
@@ -15,9 +16,7 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
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
@@ -35,17 +34,6 bool OptionalAxis::isDefined() const
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) {
@@ -97,3 +85,23 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 }
@@ -2,20 +2,25
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
@@ -1,4 +1,4
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)
@@ -1,12 +1,12
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>
@@ -370,4 +370,4 void testValuesBounds_t()
370 }
370 }
371 }
371 }
372
372
373 #endif // SCIQLOP_DATASERIESUTILS_H
373 #endif // SCIQLOP_DATASERIESTESTSUTILS_H
@@ -17,10 +17,6 private slots:
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();
@@ -64,39 +60,6 void TestOptionalAxis::testDefinedAxisCtor()
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)
@@ -1,7 +1,7
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>
@@ -9,7 +9,7
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
@@ -1,7 +1,7
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>
@@ -19,7 +19,7 using Components = std::vector<DataContainer>;
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
@@ -1,7 +1,7
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>
@@ -9,7 +9,7
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
@@ -8,6 +8,7 tests = [
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'],
@@ -17,8 +18,8 tests = [
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
@@ -101,8 +101,7 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries
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
@@ -123,6 +122,8 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries
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
@@ -1,6 +1,7
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>
@@ -180,8 +181,7 struct PlottablesUpdater<T,
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});
@@ -204,43 +204,42 struct PlottablesUpdater<T,
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
@@ -12,11 +12,13
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;
@@ -44,6 +46,9 extern const QString VALUES_UNIT_PROPERTY;
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
@@ -59,6 +64,9 extern const QRegularExpression SPECTROGRAM_MAX_SAMPLING_REGEX;
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
@@ -75,6 +75,8 public:
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{};
@@ -1,10 +1,12
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");
@@ -12,6 +14,9 const QString VALUES_UNIT_PROPERTY = QStringLiteral("valuesUnit");
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
@@ -27,6 +32,9 const QRegularExpression SPECTROGRAM_MAX_SAMPLING_REGEX
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
@@ -3,6 +3,7
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>
@@ -158,6 +159,18 bool tryReadProperty(Properties &properties, const QString &key, const QString &
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 */
@@ -263,7 +276,7 bool SpectrogramParserHelper::checkProperties()
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;
@@ -283,18 +296,20 bool SpectrogramParserHelper::checkProperties()
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)
@@ -337,6 +352,15 void SpectrogramParserHelper::readPropertyLine(const QString &line)
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) {
@@ -352,6 +376,18 void SpectrogramParserHelper::readResultLine(const QString &line)
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 // ////////////////// //
@@ -135,11 +135,10 struct ExpectedResults {
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 {
@@ -397,6 +396,135 void TestAmdaResultParser::testReadSpectrogramTxt_data()
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")
General Comments 0
You need to be logged in to leave comments. Login now