@@ -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 |
|
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() |
|
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)), |
|
8 | std::make_shared<ArrayData<1> >(std::move(xAxisData)), | |
8 | std::make_shared<ArrayData<2> >(std::move(valuesData), yAxisData.size()), valuesUnit, |
|
9 | xAxisUnit, | |
9 |
|
|
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( |
|
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 ®ex, 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