##// END OF EJS Templates
Updates access to y-axis properties of the data series (2)...
Alexandre Leroux -
r989:958f5923ee65
parent child
Show More
@@ -1,488 +1,507
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11 #include <Data/OptionalAxis.h>
12
12
13 #include <QLoggingCategory>
13 #include <QLoggingCategory>
14 #include <QReadLocker>
14 #include <QReadLocker>
15 #include <QReadWriteLock>
15 #include <QReadWriteLock>
16 #include <memory>
16 #include <memory>
17
17
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
19 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
20 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
21 {
21 {
22 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
23 return category;
23 return category;
24 }
24 }
25
25
26 template <int Dim>
26 template <int Dim>
27 class DataSeries;
27 class DataSeries;
28
28
29 namespace dataseries_detail {
29 namespace dataseries_detail {
30
30
31 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
33 public:
33 public:
34 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
35
35
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end()),
40 m_YItBegin{dataSeries.yAxis().begin()},
41 m_YItEnd{dataSeries.yAxis().end()}
40 {
42 {
41 }
43 }
42
44
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
45 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
46 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
47 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
48 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
47 : dataSeries.valuesData()->cend())
49 : dataSeries.valuesData()->cend()),
50 m_YItBegin{dataSeries.yAxis().cbegin()},
51 m_YItEnd{dataSeries.yAxis().cend()}
48 {
52 {
49 }
53 }
50
54
51 IteratorValue(const IteratorValue &other) = default;
55 IteratorValue(const IteratorValue &other) = default;
52
56
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
57 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
54 {
58 {
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
59 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
56 }
60 }
57
61
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
62 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
63 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
60 return m_XIt->distance(*otherImpl.m_XIt);
64 return m_XIt->distance(*otherImpl.m_XIt);
61 }
65 }
62 catch (const std::bad_cast &) {
66 catch (const std::bad_cast &) {
63 return 0;
67 return 0;
64 }
68 }
65
69
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
70 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
71 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
72 return std::tie(m_XIt, m_ValuesIt, m_YItBegin, m_YItEnd)
73 == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt, otherImpl.m_YItBegin,
74 otherImpl.m_YItEnd);
69 }
75 }
70 catch (const std::bad_cast &) {
76 catch (const std::bad_cast &) {
71 return false;
77 return false;
72 }
78 }
73
79
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
80 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
81 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
82 return m_XIt->lowerThan(*otherImpl.m_XIt);
77 }
83 }
78 catch (const std::bad_cast &) {
84 catch (const std::bad_cast &) {
79 return false;
85 return false;
80 }
86 }
81
87
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
88 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
83 {
89 {
84 auto result = clone();
90 auto result = clone();
85 result->next(offset);
91 result->next(offset);
86 return result;
92 return result;
87 }
93 }
88
94
89 void next(int offset) override
95 void next(int offset) override
90 {
96 {
91 m_XIt->next(offset);
97 m_XIt->next(offset);
92 m_ValuesIt->next(offset);
98 m_ValuesIt->next(offset);
93 }
99 }
94
100
95 void prev() override
101 void prev() override
96 {
102 {
97 --m_XIt;
103 --m_XIt;
98 --m_ValuesIt;
104 --m_ValuesIt;
99 }
105 }
100
106
101 double x() const override { return m_XIt->at(0); }
107 double x() const override { return m_XIt->at(0); }
108 std::vector<double> y() const override
109 {
110 std::vector<double> result{};
111 std::transform(m_YItBegin, m_YItEnd, std::back_inserter(result),
112 [](const auto &it) { return it.first(); });
113
114 return result;
115 }
116
102 double value() const override { return m_ValuesIt->at(0); }
117 double value() const override { return m_ValuesIt->at(0); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
118 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
104 double minValue() const override { return m_ValuesIt->min(); }
119 double minValue() const override { return m_ValuesIt->min(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
120 double maxValue() const override { return m_ValuesIt->max(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
121 QVector<double> values() const override { return m_ValuesIt->values(); }
107
122
108 void swap(DataSeriesIteratorValue::Impl &other) override
123 void swap(DataSeriesIteratorValue::Impl &other) override
109 {
124 {
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
125 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
126 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
127 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
128 m_YItBegin->impl()->swap(*otherImpl.m_YItBegin->impl());
129 m_YItEnd->impl()->swap(*otherImpl.m_YItEnd->impl());
113 }
130 }
114
131
115 private:
132 private:
116 ArrayDataIterator m_XIt;
133 ArrayDataIterator m_XIt;
117 ArrayDataIterator m_ValuesIt;
134 ArrayDataIterator m_ValuesIt;
135 ArrayDataIterator m_YItBegin;
136 ArrayDataIterator m_YItEnd;
118 };
137 };
119 } // namespace dataseries_detail
138 } // namespace dataseries_detail
120
139
121 /**
140 /**
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
141 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
123 *
142 *
124 * The DataSeries represents values on one or two axes, according to these rules:
143 * The DataSeries represents values on one or two axes, according to these rules:
125 * - the x-axis is always defined
144 * - the x-axis is always defined
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
145 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
127 * below)
146 * below)
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
147 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
129 * distributed into components (for example, a vector defines three components)
148 * distributed into components (for example, a vector defines three components)
130 * - New values can be added to the series, on the x-axis.
149 * - New values can be added to the series, on the x-axis.
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
150 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
132 * - Data representing values and axes are associated with a unit
151 * - Data representing values and axes are associated with a unit
133 * - The data series is always sorted in ascending order on the x-axis.
152 * - The data series is always sorted in ascending order on the x-axis.
134 *
153 *
135 * Consistency checks are carried out between the axes and the values. These controls are provided
154 * Consistency checks are carried out between the axes and the values. These controls are provided
136 * throughout the DataSeries lifecycle:
155 * throughout the DataSeries lifecycle:
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
156 * - the number of data on the x-axis must be equal to the number of values (in the case of
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
157 * 2-dim ArrayData for values, the test is performed on the number of values per component)
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
158 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
140 * number of data on the y-axis.
159 * number of data on the y-axis.
141 *
160 *
142 * Examples:
161 * Examples:
143 * 1)
162 * 1)
144 * - x-axis: [1 ; 2 ; 3]
163 * - x-axis: [1 ; 2 ; 3]
145 * - y-axis: not defined
164 * - y-axis: not defined
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
165 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
147 * => the DataSeries is valid, as x-axis and values have the same number of data
166 * => the DataSeries is valid, as x-axis and values have the same number of data
148 *
167 *
149 * 2)
168 * 2)
150 * - x-axis: [1 ; 2 ; 3]
169 * - x-axis: [1 ; 2 ; 3]
151 * - y-axis: not defined
170 * - y-axis: not defined
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
171 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
172 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
154 *
173 *
155 * 3)
174 * 3)
156 * - x-axis: [1 ; 2 ; 3]
175 * - x-axis: [1 ; 2 ; 3]
157 * - y-axis: not defined
176 * - y-axis: not defined
158 * - values: [10 ; 20 ; 30
177 * - values: [10 ; 20 ; 30
159 * 40 ; 50 ; 60] (2-dim ArrayData)
178 * 40 ; 50 ; 60] (2-dim ArrayData)
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
179 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
161 * data each
180 * data each
162 *
181 *
163 * 4)
182 * 4)
164 * - x-axis: [1 ; 2 ; 3]
183 * - x-axis: [1 ; 2 ; 3]
165 * - y-axis: [1 ; 2]
184 * - y-axis: [1 ; 2]
166 * - values: [10 ; 20 ; 30
185 * - values: [10 ; 20 ; 30
167 * 40 ; 50 ; 60] (2-dim ArrayData)
186 * 40 ; 50 ; 60] (2-dim ArrayData)
168 * => the DataSeries is valid, as:
187 * => the DataSeries is valid, as:
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
188 * - x-axis has 3 data and values contains 2 components with 3 data each AND
170 * - y-axis has 2 data and values contains 2 components
189 * - y-axis has 2 data and values contains 2 components
171 *
190 *
172 * 5)
191 * 5)
173 * - x-axis: [1 ; 2 ; 3]
192 * - x-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
193 * - y-axis: [1 ; 2 ; 3]
175 * - values: [10 ; 20 ; 30
194 * - values: [10 ; 20 ; 30
176 * 40 ; 50 ; 60] (2-dim ArrayData)
195 * 40 ; 50 ; 60] (2-dim ArrayData)
177 * => the DataSeries is invalid, as:
196 * => the DataSeries is invalid, as:
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
197 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
179 * - y-axis has 3 data and values contains only 2 components
198 * - y-axis has 3 data and values contains only 2 components
180 *
199 *
181 * @tparam Dim The dimension of the values data
200 * @tparam Dim The dimension of the values data
182 *
201 *
183 */
202 */
184 template <int Dim>
203 template <int Dim>
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
204 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
186 friend class DataSeriesMergeHelper;
205 friend class DataSeriesMergeHelper;
187
206
188 public:
207 public:
189 /// @sa IDataSeries::xAxisData()
208 /// @sa IDataSeries::xAxisData()
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
209 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
210 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
192
211
193 /// @sa IDataSeries::xAxisUnit()
212 /// @sa IDataSeries::xAxisUnit()
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
213 Unit xAxisUnit() const override { return m_XAxisUnit; }
195
214
196 /// @sa IDataSeries::yAxisUnit()
215 /// @sa IDataSeries::yAxisUnit()
197 Unit yAxisUnit() const override { return m_YAxis.unit(); }
216 Unit yAxisUnit() const override { return m_YAxis.unit(); }
198
217
199 /// @return the values dataset
218 /// @return the values dataset
200 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
219 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
201 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
220 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
202
221
203 /// @sa IDataSeries::valuesUnit()
222 /// @sa IDataSeries::valuesUnit()
204 Unit valuesUnit() const override { return m_ValuesUnit; }
223 Unit valuesUnit() const override { return m_ValuesUnit; }
205
224
206 int nbPoints() const override { return m_ValuesData->totalSize(); }
225 int nbPoints() const override { return m_ValuesData->totalSize(); }
207
226
208 std::pair<double, double> yBounds() const override { return m_YAxis.bounds(); }
227 std::pair<double, double> yBounds() const override { return m_YAxis.bounds(); }
209
228
210 void clear()
229 void clear()
211 {
230 {
212 m_XAxisData->clear();
231 m_XAxisData->clear();
213 m_ValuesData->clear();
232 m_ValuesData->clear();
214 }
233 }
215
234
216 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
235 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
217
236
218 /// Merges into the data series an other data series.
237 /// Merges into the data series an other data series.
219 ///
238 ///
220 /// The two dataseries:
239 /// The two dataseries:
221 /// - must be of the same dimension
240 /// - must be of the same dimension
222 /// - must have the same y-axis (if defined)
241 /// - must have the same y-axis (if defined)
223 ///
242 ///
224 /// If the prerequisites are not valid, the method does nothing
243 /// If the prerequisites are not valid, the method does nothing
225 ///
244 ///
226 /// @remarks the data series to merge with is cleared after the operation
245 /// @remarks the data series to merge with is cleared after the operation
227 void merge(IDataSeries *dataSeries) override
246 void merge(IDataSeries *dataSeries) override
228 {
247 {
229 dataSeries->lockWrite();
248 dataSeries->lockWrite();
230 lockWrite();
249 lockWrite();
231
250
232 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
251 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
233 if (m_YAxis == other->m_YAxis) {
252 if (m_YAxis == other->m_YAxis) {
234 DataSeriesMergeHelper::merge(*other, *this);
253 DataSeriesMergeHelper::merge(*other, *this);
235 }
254 }
236 else {
255 else {
237 qCWarning(LOG_DataSeries())
256 qCWarning(LOG_DataSeries())
238 << QObject::tr("Can't merge data series that have not the same y-axis");
257 << QObject::tr("Can't merge data series that have not the same y-axis");
239 }
258 }
240 }
259 }
241 else {
260 else {
242 qCWarning(LOG_DataSeries())
261 qCWarning(LOG_DataSeries())
243 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
262 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
244 }
263 }
245 unlock();
264 unlock();
246 dataSeries->unlock();
265 dataSeries->unlock();
247 }
266 }
248
267
249 void purge(double min, double max) override
268 void purge(double min, double max) override
250 {
269 {
251 // Nothing to purge if series is empty
270 // Nothing to purge if series is empty
252 if (isEmpty()) {
271 if (isEmpty()) {
253 return;
272 return;
254 }
273 }
255
274
256 if (min > max) {
275 if (min > max) {
257 std::swap(min, max);
276 std::swap(min, max);
258 }
277 }
259
278
260 // Nothing to purge if series min/max are inside purge range
279 // Nothing to purge if series min/max are inside purge range
261 auto xMin = cbegin()->x();
280 auto xMin = cbegin()->x();
262 auto xMax = (--cend())->x();
281 auto xMax = (--cend())->x();
263 if (xMin >= min && xMax <= max) {
282 if (xMin >= min && xMax <= max) {
264 return;
283 return;
265 }
284 }
266
285
267 auto lowerIt = std::lower_bound(
286 auto lowerIt = std::lower_bound(
268 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
287 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
269 erase(begin(), lowerIt);
288 erase(begin(), lowerIt);
270 auto upperIt = std::upper_bound(
289 auto upperIt = std::upper_bound(
271 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
290 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
272 erase(upperIt, end());
291 erase(upperIt, end());
273 }
292 }
274
293
275 // ///////// //
294 // ///////// //
276 // Iterators //
295 // Iterators //
277 // ///////// //
296 // ///////// //
278
297
279 DataSeriesIterator begin() override
298 DataSeriesIterator begin() override
280 {
299 {
281 return DataSeriesIterator{DataSeriesIteratorValue{
300 return DataSeriesIterator{DataSeriesIteratorValue{
282 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
301 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
283 }
302 }
284
303
285 DataSeriesIterator end() override
304 DataSeriesIterator end() override
286 {
305 {
287 return DataSeriesIterator{DataSeriesIteratorValue{
306 return DataSeriesIterator{DataSeriesIteratorValue{
288 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
307 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
289 }
308 }
290
309
291 DataSeriesIterator cbegin() const override
310 DataSeriesIterator cbegin() const override
292 {
311 {
293 return DataSeriesIterator{DataSeriesIteratorValue{
312 return DataSeriesIterator{DataSeriesIteratorValue{
294 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
313 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
295 }
314 }
296
315
297 DataSeriesIterator cend() const override
316 DataSeriesIterator cend() const override
298 {
317 {
299 return DataSeriesIterator{DataSeriesIteratorValue{
318 return DataSeriesIterator{DataSeriesIteratorValue{
300 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
319 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
301 }
320 }
302
321
303 void erase(DataSeriesIterator first, DataSeriesIterator last)
322 void erase(DataSeriesIterator first, DataSeriesIterator last)
304 {
323 {
305 auto firstImpl
324 auto firstImpl
306 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
325 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
307 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
326 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
308
327
309 if (firstImpl && lastImpl) {
328 if (firstImpl && lastImpl) {
310 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
329 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
311 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
330 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
312 }
331 }
313 }
332 }
314
333
315 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
334 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
316 {
335 {
317 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
336 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
318 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
337 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
319
338
320 if (firstImpl && lastImpl) {
339 if (firstImpl && lastImpl) {
321 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
340 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
322 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
341 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
323 }
342 }
324 }
343 }
325
344
326 /// @sa IDataSeries::minXAxisData()
345 /// @sa IDataSeries::minXAxisData()
327 DataSeriesIterator minXAxisData(double minXAxisData) const override
346 DataSeriesIterator minXAxisData(double minXAxisData) const override
328 {
347 {
329 return std::lower_bound(
348 return std::lower_bound(
330 cbegin(), cend(), minXAxisData,
349 cbegin(), cend(), minXAxisData,
331 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
350 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
332 }
351 }
333
352
334 /// @sa IDataSeries::maxXAxisData()
353 /// @sa IDataSeries::maxXAxisData()
335 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
354 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
336 {
355 {
337 // Gets the first element that greater than max value
356 // Gets the first element that greater than max value
338 auto it = std::upper_bound(
357 auto it = std::upper_bound(
339 cbegin(), cend(), maxXAxisData,
358 cbegin(), cend(), maxXAxisData,
340 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
359 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
341
360
342 return it == cbegin() ? cend() : --it;
361 return it == cbegin() ? cend() : --it;
343 }
362 }
344
363
345 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
364 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
346 double maxXAxisData) const override
365 double maxXAxisData) const override
347 {
366 {
348 if (minXAxisData > maxXAxisData) {
367 if (minXAxisData > maxXAxisData) {
349 std::swap(minXAxisData, maxXAxisData);
368 std::swap(minXAxisData, maxXAxisData);
350 }
369 }
351
370
352 auto begin = cbegin();
371 auto begin = cbegin();
353 auto end = cend();
372 auto end = cend();
354
373
355 auto lowerIt = std::lower_bound(
374 auto lowerIt = std::lower_bound(
356 begin, end, minXAxisData,
375 begin, end, minXAxisData,
357 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
376 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
358 auto upperIt = std::upper_bound(
377 auto upperIt = std::upper_bound(
359 lowerIt, end, maxXAxisData,
378 lowerIt, end, maxXAxisData,
360 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
379 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
361
380
362 return std::make_pair(lowerIt, upperIt);
381 return std::make_pair(lowerIt, upperIt);
363 }
382 }
364
383
365 std::pair<DataSeriesIterator, DataSeriesIterator>
384 std::pair<DataSeriesIterator, DataSeriesIterator>
366 valuesBounds(double minXAxisData, double maxXAxisData) const override
385 valuesBounds(double minXAxisData, double maxXAxisData) const override
367 {
386 {
368 // Places iterators to the correct x-axis range
387 // Places iterators to the correct x-axis range
369 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
388 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
370
389
371 // Returns end iterators if the range is empty
390 // Returns end iterators if the range is empty
372 if (xAxisRangeIts.first == xAxisRangeIts.second) {
391 if (xAxisRangeIts.first == xAxisRangeIts.second) {
373 return std::make_pair(cend(), cend());
392 return std::make_pair(cend(), cend());
374 }
393 }
375
394
376 // Gets the iterator on the min of all values data
395 // Gets the iterator on the min of all values data
377 auto minIt = std::min_element(
396 auto minIt = std::min_element(
378 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
397 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
379 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
398 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
380 });
399 });
381
400
382 // Gets the iterator on the max of all values data
401 // Gets the iterator on the max of all values data
383 auto maxIt = std::max_element(
402 auto maxIt = std::max_element(
384 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
403 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
385 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
404 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
386 });
405 });
387
406
388 return std::make_pair(minIt, maxIt);
407 return std::make_pair(minIt, maxIt);
389 }
408 }
390
409
391 /// @return the y-axis associated to the data series
410 /// @return the y-axis associated to the data series
392 const OptionalAxis &yAxis() const { return m_YAxis; }
411 const OptionalAxis &yAxis() const { return m_YAxis; }
393 OptionalAxis &yAxis() { return m_YAxis; }
412 OptionalAxis &yAxis() { return m_YAxis; }
394
413
395 // /////// //
414 // /////// //
396 // Mutexes //
415 // Mutexes //
397 // /////// //
416 // /////// //
398
417
399 virtual void lockRead() { m_Lock.lockForRead(); }
418 virtual void lockRead() { m_Lock.lockForRead(); }
400 virtual void lockWrite() { m_Lock.lockForWrite(); }
419 virtual void lockWrite() { m_Lock.lockForWrite(); }
401 virtual void unlock() { m_Lock.unlock(); }
420 virtual void unlock() { m_Lock.unlock(); }
402
421
403 protected:
422 protected:
404 /// Protected ctor (DataSeries is abstract).
423 /// Protected ctor (DataSeries is abstract).
405 ///
424 ///
406 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
425 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
407 /// class description for consistent rules)
426 /// class description for consistent rules)
408 /// @remarks data series is automatically sorted on its x-axis data
427 /// @remarks data series is automatically sorted on its x-axis data
409 /// @throws std::invalid_argument if the data are inconsistent with each other
428 /// @throws std::invalid_argument if the data are inconsistent with each other
410 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
429 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
411 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
430 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
412 OptionalAxis yAxis = OptionalAxis{})
431 OptionalAxis yAxis = OptionalAxis{})
413 : m_XAxisData{xAxisData},
432 : m_XAxisData{xAxisData},
414 m_XAxisUnit{xAxisUnit},
433 m_XAxisUnit{xAxisUnit},
415 m_ValuesData{valuesData},
434 m_ValuesData{valuesData},
416 m_ValuesUnit{valuesUnit},
435 m_ValuesUnit{valuesUnit},
417 m_YAxis{std::move(yAxis)}
436 m_YAxis{std::move(yAxis)}
418 {
437 {
419 if (m_XAxisData->size() != m_ValuesData->size()) {
438 if (m_XAxisData->size() != m_ValuesData->size()) {
420 throw std::invalid_argument{
439 throw std::invalid_argument{
421 "The number of values by component must be equal to the number of x-axis data"};
440 "The number of values by component must be equal to the number of x-axis data"};
422 }
441 }
423
442
424 // Validates y-axis (if defined)
443 // Validates y-axis (if defined)
425 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
444 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
426 throw std::invalid_argument{
445 throw std::invalid_argument{
427 "As the y-axis is defined, the number of value components must be equal to the "
446 "As the y-axis is defined, the number of value components must be equal to the "
428 "number of y-axis data"};
447 "number of y-axis data"};
429 }
448 }
430
449
431 // Sorts data if it's not the case
450 // Sorts data if it's not the case
432 const auto &xAxisCData = m_XAxisData->cdata();
451 const auto &xAxisCData = m_XAxisData->cdata();
433 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
452 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
434 sort();
453 sort();
435 }
454 }
436 }
455 }
437
456
438 /// Copy ctor
457 /// Copy ctor
439 explicit DataSeries(const DataSeries<Dim> &other)
458 explicit DataSeries(const DataSeries<Dim> &other)
440 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
459 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
441 m_XAxisUnit{other.m_XAxisUnit},
460 m_XAxisUnit{other.m_XAxisUnit},
442 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
461 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
443 m_ValuesUnit{other.m_ValuesUnit},
462 m_ValuesUnit{other.m_ValuesUnit},
444 m_YAxis{other.m_YAxis}
463 m_YAxis{other.m_YAxis}
445 {
464 {
446 // Since a series is ordered from its construction and is always ordered, it is not
465 // Since a series is ordered from its construction and is always ordered, it is not
447 // necessary to call the sort method here ('other' is sorted)
466 // necessary to call the sort method here ('other' is sorted)
448 }
467 }
449
468
450 /// Assignment operator
469 /// Assignment operator
451 template <int D>
470 template <int D>
452 DataSeries &operator=(DataSeries<D> other)
471 DataSeries &operator=(DataSeries<D> other)
453 {
472 {
454 std::swap(m_XAxisData, other.m_XAxisData);
473 std::swap(m_XAxisData, other.m_XAxisData);
455 std::swap(m_XAxisUnit, other.m_XAxisUnit);
474 std::swap(m_XAxisUnit, other.m_XAxisUnit);
456 std::swap(m_ValuesData, other.m_ValuesData);
475 std::swap(m_ValuesData, other.m_ValuesData);
457 std::swap(m_ValuesUnit, other.m_ValuesUnit);
476 std::swap(m_ValuesUnit, other.m_ValuesUnit);
458 std::swap(m_YAxis, other.m_YAxis);
477 std::swap(m_YAxis, other.m_YAxis);
459
478
460 return *this;
479 return *this;
461 }
480 }
462
481
463 private:
482 private:
464 /**
483 /**
465 * Sorts data series on its x-axis data
484 * Sorts data series on its x-axis data
466 */
485 */
467 void sort() noexcept
486 void sort() noexcept
468 {
487 {
469 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
488 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
470 m_XAxisData = m_XAxisData->sort(permutation);
489 m_XAxisData = m_XAxisData->sort(permutation);
471 m_ValuesData = m_ValuesData->sort(permutation);
490 m_ValuesData = m_ValuesData->sort(permutation);
472 }
491 }
473
492
474 // x-axis
493 // x-axis
475 std::shared_ptr<ArrayData<1> > m_XAxisData;
494 std::shared_ptr<ArrayData<1> > m_XAxisData;
476 Unit m_XAxisUnit;
495 Unit m_XAxisUnit;
477
496
478 // values
497 // values
479 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
498 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
480 Unit m_ValuesUnit;
499 Unit m_ValuesUnit;
481
500
482 // y-axis (optional)
501 // y-axis (optional)
483 OptionalAxis m_YAxis;
502 OptionalAxis m_YAxis;
484
503
485 QReadWriteLock m_Lock;
504 QReadWriteLock m_Lock;
486 };
505 };
487
506
488 #endif // SCIQLOP_DATASERIES_H
507 #endif // SCIQLOP_DATASERIES_H
@@ -1,78 +1,81
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
5 #include "Data/SqpIterator.h"
6
6
7 #include <QVector>
7 #include <QVector>
8 #include <memory>
8 #include <memory>
9
9
10 /**
10 /**
11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
13 * each series to define its own implementation of how to retrieve this data, by implementing the
13 * each series to define its own implementation of how to retrieve this data, by implementing the
14 * DataSeriesIteratorValue::Impl interface
14 * DataSeriesIteratorValue::Impl interface
15 *
15 *
16 * @sa DataSeriesIterator
16 * @sa DataSeriesIterator
17 */
17 */
18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
19 public:
19 public:
20 struct Impl {
20 struct Impl {
21 virtual ~Impl() noexcept = default;
21 virtual ~Impl() noexcept = default;
22 virtual std::unique_ptr<Impl> clone() const = 0;
22 virtual std::unique_ptr<Impl> clone() const = 0;
23 virtual int distance(const Impl &other) const = 0;
23 virtual int distance(const Impl &other) const = 0;
24 virtual bool equals(const Impl &other) const = 0;
24 virtual bool equals(const Impl &other) const = 0;
25 virtual bool lowerThan(const Impl &other) const = 0;
25 virtual bool lowerThan(const Impl &other) const = 0;
26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
27 virtual void next(int offset) = 0;
27 virtual void next(int offset) = 0;
28 virtual void prev() = 0;
28 virtual void prev() = 0;
29 virtual double x() const = 0;
29 virtual double x() const = 0;
30 virtual std::vector<double> y() const = 0;
30 virtual double value() const = 0;
31 virtual double value() const = 0;
31 virtual double value(int componentIndex) const = 0;
32 virtual double value(int componentIndex) const = 0;
32 virtual double minValue() const = 0;
33 virtual double minValue() const = 0;
33 virtual double maxValue() const = 0;
34 virtual double maxValue() const = 0;
34 virtual QVector<double> values() const = 0;
35 virtual QVector<double> values() const = 0;
35
36
36 virtual void swap(Impl &other) = 0;
37 virtual void swap(Impl &other) = 0;
37 };
38 };
38
39
39 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
40 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
40 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
41 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
41 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
42 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
42
43
43 int distance(const DataSeriesIteratorValue &other) const;
44 int distance(const DataSeriesIteratorValue &other) const;
44 bool equals(const DataSeriesIteratorValue &other) const;
45 bool equals(const DataSeriesIteratorValue &other) const;
45 bool lowerThan(const DataSeriesIteratorValue &other) const;
46 bool lowerThan(const DataSeriesIteratorValue &other) const;
46
47
47 DataSeriesIteratorValue advance(int offset) const;
48 DataSeriesIteratorValue advance(int offset) const;
48 /// Advances to the next value
49 /// Advances to the next value
49 void next(int offset = 1);
50 void next(int offset = 1);
50 /// Moves back to the previous value
51 /// Moves back to the previous value
51 void prev();
52 void prev();
52 /// Gets x-axis data
53 /// Gets x-axis data
53 double x() const;
54 double x() const;
55 /// Gets y-axis data
56 std::vector<double> y() const;
54 /// Gets value data
57 /// Gets value data
55 double value() const;
58 double value() const;
56 /// Gets value data depending on an index
59 /// Gets value data depending on an index
57 double value(int componentIndex) const;
60 double value(int componentIndex) const;
58 /// Gets min of all values data
61 /// Gets min of all values data
59 double minValue() const;
62 double minValue() const;
60 /// Gets max of all values data
63 /// Gets max of all values data
61 double maxValue() const;
64 double maxValue() const;
62 /// Gets all values data
65 /// Gets all values data
63 QVector<double> values() const;
66 QVector<double> values() const;
64
67
65 Impl *impl();
68 Impl *impl();
66
69
67 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
70 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
68 {
71 {
69 std::swap(lhs.m_Impl, rhs.m_Impl);
72 std::swap(lhs.m_Impl, rhs.m_Impl);
70 }
73 }
71
74
72 private:
75 private:
73 std::unique_ptr<Impl> m_Impl;
76 std::unique_ptr<Impl> m_Impl;
74 };
77 };
75
78
76 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
79 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
77
80
78 #endif // SCIQLOP_DATASERIESITERATOR_H
81 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -1,62 +1,66
1 #ifndef SCIQLOP_OPTIONALAXIS_H
1 #ifndef SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
3
3
4 #include <Data/ArrayDataIterator.h>
5
4 #include "CoreGlobal.h"
6 #include "CoreGlobal.h"
5 #include "Unit.h"
7 #include "Unit.h"
6
8
7 #include <memory>
9 #include <memory>
8
10
9 template <int Dim>
11 template <int Dim>
10 class ArrayData;
12 class ArrayData;
11
13
12 /**
14 /**
13 * @brief The OptionalAxis class defines an optional data axis for a series of data.
15 * @brief The OptionalAxis class defines an optional data axis for a series of data.
14 *
16 *
15 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
17 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
16 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
18 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
17 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
19 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
18 * data or the unit are always callable but will return undefined values.
20 * data or the unit are always callable but will return undefined values.
19 *
21 *
20 * @sa DataSeries
22 * @sa DataSeries
21 * @sa ArrayData
23 * @sa ArrayData
22 */
24 */
23 class SCIQLOP_CORE_EXPORT OptionalAxis {
25 class SCIQLOP_CORE_EXPORT OptionalAxis {
24 public:
26 public:
25 /// Ctor for an undefined axis
27 /// Ctor for an undefined axis
26 explicit OptionalAxis();
28 explicit OptionalAxis();
27 /// Ctor for a defined axis
29 /// Ctor for a defined axis
28 /// @param data the axis' data
30 /// @param data the axis' data
29 /// @param unit the axis' unit
31 /// @param unit the axis' unit
30 /// @throws std::invalid_argument if no data is associated to the axis
32 /// @throws std::invalid_argument if no data is associated to the axis
31 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
33 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
32
34
33 /// Copy ctor
35 /// Copy ctor
34 OptionalAxis(const OptionalAxis &other);
36 OptionalAxis(const OptionalAxis &other);
35 /// Assignment operator
37 /// Assignment operator
36 OptionalAxis &operator=(OptionalAxis other);
38 OptionalAxis &operator=(OptionalAxis other);
37
39
38 /// @return the flag that indicates if the axis is defined or not
40 /// @return the flag that indicates if the axis is defined or not
39 bool isDefined() const;
41 bool isDefined() const;
40
42
41 /// @return gets the data at the index passed in parameter, NaN if the index is outside the
42 /// bounds of the axis, or if the axis is undefined
43 double at(int index) const;
44
45 ///@return the min and max values of the data on the axis, NaN values if there is no data
43 ///@return the min and max values of the data on the axis, NaN values if there is no data
46 std::pair<double, double> bounds() const;
44 std::pair<double, double> bounds() const;
47
45
48 /// @return the number of data on the axis, 0 if the axis is not defined
46 /// @return the number of data on the axis, 0 if the axis is not defined
49 int size() const;
47 int size() const;
50 /// @return the unit of the axis, an empty unit if the axis is not defined
48 /// @return the unit of the axis, an empty unit if the axis is not defined
51 Unit unit() const;
49 Unit unit() const;
52
50
53 bool operator==(const OptionalAxis &other);
51 bool operator==(const OptionalAxis &other);
54 bool operator!=(const OptionalAxis &other);
52 bool operator!=(const OptionalAxis &other);
55
53
54 // Iterators on data
55 ArrayDataIterator begin();
56 ArrayDataIterator end();
57 ArrayDataIterator cbegin() const;
58 ArrayDataIterator cend() const;
59
56 private:
60 private:
57 bool m_Defined; ///< Axis is defined or not
61 bool m_Defined; ///< Axis is defined or not
58 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
62 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
59 Unit m_Unit; ///< Axis' unit
63 Unit m_Unit; ///< Axis' unit
60 };
64 };
61
65
62 #endif // SCIQLOP_OPTIONALAXIS_H
66 #endif // SCIQLOP_OPTIONALAXIS_H
@@ -1,83 +1,88
1 #include "Data/DataSeriesIterator.h"
1 #include "Data/DataSeriesIterator.h"
2
2
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
5 : m_Impl{std::move(impl)}
5 : m_Impl{std::move(impl)}
6 {
6 {
7 }
7 }
8
8
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
10 : m_Impl{other.m_Impl->clone()}
10 : m_Impl{other.m_Impl->clone()}
11 {
11 {
12 }
12 }
13
13
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
15 {
15 {
16 m_Impl->swap(*other.m_Impl);
16 m_Impl->swap(*other.m_Impl);
17 return *this;
17 return *this;
18 }
18 }
19
19
20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
21 {
21 {
22 return m_Impl->distance(*other.m_Impl);
22 return m_Impl->distance(*other.m_Impl);
23 }
23 }
24
24
25 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
25 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
26 {
26 {
27 return m_Impl->equals(*other.m_Impl);
27 return m_Impl->equals(*other.m_Impl);
28 }
28 }
29
29
30 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
30 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
31 {
31 {
32 return m_Impl->lowerThan(*other.m_Impl);
32 return m_Impl->lowerThan(*other.m_Impl);
33 }
33 }
34
34
35 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
35 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
36 {
36 {
37 return DataSeriesIteratorValue{m_Impl->advance(offset)};
37 return DataSeriesIteratorValue{m_Impl->advance(offset)};
38 }
38 }
39
39
40 void DataSeriesIteratorValue::next(int offset)
40 void DataSeriesIteratorValue::next(int offset)
41 {
41 {
42 m_Impl->next(offset);
42 m_Impl->next(offset);
43 }
43 }
44
44
45 void DataSeriesIteratorValue::prev()
45 void DataSeriesIteratorValue::prev()
46 {
46 {
47 m_Impl->prev();
47 m_Impl->prev();
48 }
48 }
49
49
50 double DataSeriesIteratorValue::x() const
50 double DataSeriesIteratorValue::x() const
51 {
51 {
52 return m_Impl->x();
52 return m_Impl->x();
53 }
53 }
54
54
55 std::vector<double> DataSeriesIteratorValue::y() const
56 {
57 return m_Impl->y();
58 }
59
55 double DataSeriesIteratorValue::value() const
60 double DataSeriesIteratorValue::value() const
56 {
61 {
57 return m_Impl->value();
62 return m_Impl->value();
58 }
63 }
59
64
60 double DataSeriesIteratorValue::value(int componentIndex) const
65 double DataSeriesIteratorValue::value(int componentIndex) const
61 {
66 {
62 return m_Impl->value(componentIndex);
67 return m_Impl->value(componentIndex);
63 }
68 }
64
69
65 double DataSeriesIteratorValue::minValue() const
70 double DataSeriesIteratorValue::minValue() const
66 {
71 {
67 return m_Impl->minValue();
72 return m_Impl->minValue();
68 }
73 }
69
74
70 double DataSeriesIteratorValue::maxValue() const
75 double DataSeriesIteratorValue::maxValue() const
71 {
76 {
72 return m_Impl->maxValue();
77 return m_Impl->maxValue();
73 }
78 }
74
79
75 QVector<double> DataSeriesIteratorValue::values() const
80 QVector<double> DataSeriesIteratorValue::values() const
76 {
81 {
77 return m_Impl->values();
82 return m_Impl->values();
78 }
83 }
79
84
80 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
85 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
81 {
86 {
82 return m_Impl.get();
87 return m_Impl.get();
83 }
88 }
@@ -1,99 +1,107
1 #include <Data/OptionalAxis.h>
1 #include <Data/OptionalAxis.h>
2
2
3 #include "Data/ArrayData.h"
3 #include "Data/ArrayData.h"
4
4
5 OptionalAxis::OptionalAxis() : m_Defined{false}, m_Data{nullptr}, m_Unit{}
5 OptionalAxis::OptionalAxis()
6 : m_Defined{false}, m_Data{std::make_shared<ArrayData<1> >(std::vector<double>{})}, m_Unit{}
6 {
7 {
7 }
8 }
8
9
9 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
10 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
10 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
11 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
11 {
12 {
12 if (m_Data == nullptr) {
13 if (m_Data == nullptr) {
13 throw std::invalid_argument{"Data can't be null for a defined axis"};
14 throw std::invalid_argument{"Data can't be null for a defined axis"};
14 }
15 }
15 }
16 }
16
17
17 OptionalAxis::OptionalAxis(const OptionalAxis &other)
18 OptionalAxis::OptionalAxis(const OptionalAxis &other)
18 : m_Defined{other.m_Defined},
19 : m_Defined{other.m_Defined}, m_Data{other.m_Data}, m_Unit{other.m_Unit}
19 m_Data{other.m_Data ? std::make_shared<ArrayData<1> >(*other.m_Data) : nullptr},
20 m_Unit{other.m_Unit}
21 {
20 {
22 }
21 }
23
22
24 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
23 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
25 {
24 {
26 std::swap(m_Defined, other.m_Defined);
25 std::swap(m_Defined, other.m_Defined);
27 std::swap(m_Data, other.m_Data);
26 std::swap(m_Data, other.m_Data);
28 std::swap(m_Unit, other.m_Unit);
27 std::swap(m_Unit, other.m_Unit);
29
28
30 return *this;
29 return *this;
31 }
30 }
32
31
33 bool OptionalAxis::isDefined() const
32 bool OptionalAxis::isDefined() const
34 {
33 {
35 return m_Defined;
34 return m_Defined;
36 }
35 }
37
36
38 double OptionalAxis::at(int index) const
39 {
40 if (m_Defined) {
41 return (index >= 0 && index < m_Data->size()) ? m_Data->at(index)
42 : std::numeric_limits<double>::quiet_NaN();
43 }
44 else {
45 return std::numeric_limits<double>::quiet_NaN();
46 }
47 }
48
49 std::pair<double, double> OptionalAxis::bounds() const
37 std::pair<double, double> OptionalAxis::bounds() const
50 {
38 {
51 if (!m_Defined || m_Data->size() == 0) {
39 if (!m_Defined || m_Data->size() == 0) {
52 return std::make_pair(std::numeric_limits<double>::quiet_NaN(),
40 return std::make_pair(std::numeric_limits<double>::quiet_NaN(),
53 std::numeric_limits<double>::quiet_NaN());
41 std::numeric_limits<double>::quiet_NaN());
54 }
42 }
55 else {
43 else {
56
44
57 auto minIt = std::min_element(
45 auto minIt = std::min_element(
58 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
46 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
59 return SortUtils::minCompareWithNaN(it1.first(), it2.first());
47 return SortUtils::minCompareWithNaN(it1.first(), it2.first());
60 });
48 });
61
49
62 // Gets the iterator on the max of all values data
50 // Gets the iterator on the max of all values data
63 auto maxIt = std::max_element(
51 auto maxIt = std::max_element(
64 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
52 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
65 return SortUtils::maxCompareWithNaN(it1.first(), it2.first());
53 return SortUtils::maxCompareWithNaN(it1.first(), it2.first());
66 });
54 });
67
55
68 return std::make_pair(minIt->first(), maxIt->first());
56 return std::make_pair(minIt->first(), maxIt->first());
69 }
57 }
70 }
58 }
71
59
72 int OptionalAxis::size() const
60 int OptionalAxis::size() const
73 {
61 {
74 return m_Defined ? m_Data->size() : 0;
62 return m_Defined ? m_Data->size() : 0;
75 }
63 }
76
64
77 Unit OptionalAxis::unit() const
65 Unit OptionalAxis::unit() const
78 {
66 {
79 return m_Defined ? m_Unit : Unit{};
67 return m_Defined ? m_Unit : Unit{};
80 }
68 }
81
69
82 bool OptionalAxis::operator==(const OptionalAxis &other)
70 bool OptionalAxis::operator==(const OptionalAxis &other)
83 {
71 {
84 // Axis not defined
72 // Axis not defined
85 if (!m_Defined) {
73 if (!m_Defined) {
86 return !other.m_Defined;
74 return !other.m_Defined;
87 }
75 }
88
76
89 // Axis defined
77 // Axis defined
90 return m_Unit == other.m_Unit
78 return m_Unit == other.m_Unit
91 && std::equal(
79 && std::equal(
92 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
80 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
93 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
81 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
94 }
82 }
95
83
96 bool OptionalAxis::operator!=(const OptionalAxis &other)
84 bool OptionalAxis::operator!=(const OptionalAxis &other)
97 {
85 {
98 return !(*this == other);
86 return !(*this == other);
99 }
87 }
88
89 ArrayDataIterator OptionalAxis::begin()
90 {
91 return m_Data->begin();
92 }
93
94 ArrayDataIterator OptionalAxis::end()
95 {
96 return m_Data->end();
97 }
98
99 ArrayDataIterator OptionalAxis::cbegin() const
100 {
101 return m_Data->cbegin();
102 }
103
104 ArrayDataIterator OptionalAxis::cend() const
105 {
106 return m_Data->cend();
107 }
@@ -1,150 +1,113
1 #include <Data/ArrayData.h>
1 #include <Data/ArrayData.h>
2 #include <Data/OptionalAxis.h>
2 #include <Data/OptionalAxis.h>
3
3
4 #include <QObject>
4 #include <QObject>
5 #include <QtTest>
5 #include <QtTest>
6
6
7 Q_DECLARE_METATYPE(OptionalAxis)
7 Q_DECLARE_METATYPE(OptionalAxis)
8
8
9 class TestOptionalAxis : public QObject {
9 class TestOptionalAxis : public QObject {
10 Q_OBJECT
10 Q_OBJECT
11
11
12 private slots:
12 private slots:
13 /// Tests the creation of a undefined axis
13 /// Tests the creation of a undefined axis
14 void testNotDefinedAxisCtor();
14 void testNotDefinedAxisCtor();
15
15
16 /// Tests the creation of a undefined axis
16 /// Tests the creation of a undefined axis
17 void testDefinedAxisCtor_data();
17 void testDefinedAxisCtor_data();
18 void testDefinedAxisCtor();
18 void testDefinedAxisCtor();
19
19
20 /// Tests @sa OptionalAxis::at() method
21 void testAt_data();
22 void testAt();
23
24 /// Tests @sa OptionalAxis::size() method
20 /// Tests @sa OptionalAxis::size() method
25 void testSize_data();
21 void testSize_data();
26 void testSize();
22 void testSize();
27
23
28 /// Tests @sa OptionalAxis::unit() method
24 /// Tests @sa OptionalAxis::unit() method
29 void testUnit_data();
25 void testUnit_data();
30 void testUnit();
26 void testUnit();
31 };
27 };
32
28
33 void TestOptionalAxis::testNotDefinedAxisCtor()
29 void TestOptionalAxis::testNotDefinedAxisCtor()
34 {
30 {
35 OptionalAxis notDefinedAxis{};
31 OptionalAxis notDefinedAxis{};
36 QVERIFY(!notDefinedAxis.isDefined());
32 QVERIFY(!notDefinedAxis.isDefined());
37 }
33 }
38
34
39 void TestOptionalAxis::testDefinedAxisCtor_data()
35 void TestOptionalAxis::testDefinedAxisCtor_data()
40 {
36 {
41 QTest::addColumn<bool>("noData"); // If set to true, nullptr is passed as data of the axis
37 QTest::addColumn<bool>("noData"); // If set to true, nullptr is passed as data of the axis
42 QTest::addColumn<std::vector<double> >(
38 QTest::addColumn<std::vector<double> >(
43 "data"); // Values assigned to the axis when 'noData' flag is set to false
39 "data"); // Values assigned to the axis when 'noData' flag is set to false
44 QTest::addColumn<Unit>("unit"); // Unit assigned to the axis
40 QTest::addColumn<Unit>("unit"); // Unit assigned to the axis
45
41
46 QTest::newRow("validData") << false << std::vector<double>{1, 2, 3} << Unit{"Hz"};
42 QTest::newRow("validData") << false << std::vector<double>{1, 2, 3} << Unit{"Hz"};
47 QTest::newRow("invalidData") << true << std::vector<double>{} << Unit{"Hz"};
43 QTest::newRow("invalidData") << true << std::vector<double>{} << Unit{"Hz"};
48 }
44 }
49
45
50 void TestOptionalAxis::testDefinedAxisCtor()
46 void TestOptionalAxis::testDefinedAxisCtor()
51 {
47 {
52 QFETCH(bool, noData);
48 QFETCH(bool, noData);
53 QFETCH(Unit, unit);
49 QFETCH(Unit, unit);
54
50
55 // When there is no data, we expect that the constructor returns exception
51 // When there is no data, we expect that the constructor returns exception
56 if (noData) {
52 if (noData) {
57 QVERIFY_EXCEPTION_THROWN(OptionalAxis(nullptr, unit), std::invalid_argument);
53 QVERIFY_EXCEPTION_THROWN(OptionalAxis(nullptr, unit), std::invalid_argument);
58 }
54 }
59 else {
55 else {
60 QFETCH(std::vector<double>, data);
56 QFETCH(std::vector<double>, data);
61
57
62 OptionalAxis axis{std::make_shared<ArrayData<1> >(data), unit};
58 OptionalAxis axis{std::make_shared<ArrayData<1> >(data), unit};
63 QVERIFY(axis.isDefined());
59 QVERIFY(axis.isDefined());
64 }
60 }
65 }
61 }
66
62
67 void TestOptionalAxis::testAt_data()
68 {
69 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
70 QTest::addColumn<int>("index"); // Index to test in the axis
71 QTest::addColumn<double>("expectedValue"); // Expected axis value for the index
72
73 OptionalAxis definedAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}),
74 Unit{"Hz"}};
75
76 QTest::newRow("data1") << definedAxis << 0 << 1.;
77 QTest::newRow("data2") << definedAxis << 1 << 2.;
78 QTest::newRow("data3") << definedAxis << 2 << 3.;
79 QTest::newRow("data4 (index out of bounds)")
80 << definedAxis << 3
81 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
82 QTest::newRow("data5 (index out of bounds)")
83 << definedAxis << -1
84 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
85 QTest::newRow("data6 (axis not defined)")
86 << OptionalAxis{} << 0
87 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for undefined axis
88 }
89
90 void TestOptionalAxis::testAt()
91 {
92 QFETCH(OptionalAxis, axis);
93 QFETCH(int, index);
94 QFETCH(double, expectedValue);
95
96 auto value = axis.at(index);
97 QVERIFY((std::isnan(value) && std::isnan(expectedValue)) || value == expectedValue);
98 }
99
100 void TestOptionalAxis::testSize_data()
63 void TestOptionalAxis::testSize_data()
101 {
64 {
102 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
65 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
103 QTest::addColumn<int>("expectedSize"); // Expected number of data in the axis
66 QTest::addColumn<int>("expectedSize"); // Expected number of data in the axis
104
67
105 // Lambda that creates default defined axis (with the values passed in parameter)
68 // Lambda that creates default defined axis (with the values passed in parameter)
106 auto axis = [](std::vector<double> values) {
69 auto axis = [](std::vector<double> values) {
107 return OptionalAxis{std::make_shared<ArrayData<1> >(std::move(values)), Unit{"Hz"}};
70 return OptionalAxis{std::make_shared<ArrayData<1> >(std::move(values)), Unit{"Hz"}};
108 };
71 };
109
72
110 QTest::newRow("data1") << axis({}) << 0;
73 QTest::newRow("data1") << axis({}) << 0;
111 QTest::newRow("data2") << axis({1, 2, 3}) << 3;
74 QTest::newRow("data2") << axis({1, 2, 3}) << 3;
112 QTest::newRow("data3") << axis({1, 2, 3, 4}) << 4;
75 QTest::newRow("data3") << axis({1, 2, 3, 4}) << 4;
113 QTest::newRow("data4 (axis not defined)") << OptionalAxis{}
76 QTest::newRow("data4 (axis not defined)") << OptionalAxis{}
114 << 0; // Expects 0 for undefined axis
77 << 0; // Expects 0 for undefined axis
115 }
78 }
116
79
117 void TestOptionalAxis::testSize()
80 void TestOptionalAxis::testSize()
118 {
81 {
119 QFETCH(OptionalAxis, axis);
82 QFETCH(OptionalAxis, axis);
120 QFETCH(int, expectedSize);
83 QFETCH(int, expectedSize);
121
84
122 QCOMPARE(axis.size(), expectedSize);
85 QCOMPARE(axis.size(), expectedSize);
123 }
86 }
124
87
125 void TestOptionalAxis::testUnit_data()
88 void TestOptionalAxis::testUnit_data()
126 {
89 {
127 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
90 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
128 QTest::addColumn<Unit>("expectedUnit"); // Expected unit for the axis
91 QTest::addColumn<Unit>("expectedUnit"); // Expected unit for the axis
129
92
130 // Lambda that creates default defined axis (with the unit passed in parameter)
93 // Lambda that creates default defined axis (with the unit passed in parameter)
131 auto axis = [](Unit unit) {
94 auto axis = [](Unit unit) {
132 return OptionalAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}), unit};
95 return OptionalAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}), unit};
133 };
96 };
134
97
135 QTest::newRow("data1") << axis(Unit{"Hz"}) << Unit{"Hz"};
98 QTest::newRow("data1") << axis(Unit{"Hz"}) << Unit{"Hz"};
136 QTest::newRow("data2") << axis(Unit{"t", true}) << Unit{"t", true};
99 QTest::newRow("data2") << axis(Unit{"t", true}) << Unit{"t", true};
137 QTest::newRow("data3 (axis not defined)") << OptionalAxis{}
100 QTest::newRow("data3 (axis not defined)") << OptionalAxis{}
138 << Unit{}; // Expects default unit for undefined axis
101 << Unit{}; // Expects default unit for undefined axis
139 }
102 }
140
103
141 void TestOptionalAxis::testUnit()
104 void TestOptionalAxis::testUnit()
142 {
105 {
143 QFETCH(OptionalAxis, axis);
106 QFETCH(OptionalAxis, axis);
144 QFETCH(Unit, expectedUnit);
107 QFETCH(Unit, expectedUnit);
145
108
146 QCOMPARE(axis.unit(), expectedUnit);
109 QCOMPARE(axis.unit(), expectedUnit);
147 }
110 }
148
111
149 QTEST_MAIN(TestOptionalAxis)
112 QTEST_MAIN(TestOptionalAxis)
150 #include "TestOptionalAxis.moc"
113 #include "TestOptionalAxis.moc"
@@ -1,580 +1,579
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/SpectrogramSeries.h>
4 #include <Data/SpectrogramSeries.h>
5 #include <Data/VectorSeries.h>
5 #include <Data/VectorSeries.h>
6
6
7 #include <QObject>
7 #include <QObject>
8 #include <QtTest>
8 #include <QtTest>
9
9
10 namespace {
10 namespace {
11
11
12 /// Path for the tests
12 /// Path for the tests
13 const auto TESTS_RESOURCES_PATH
13 const auto TESTS_RESOURCES_PATH
14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
15
15
16 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
17 {
17 {
18 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
19 }
19 }
20
20
21 QString inputFilePath(const QString &inputFileName)
21 QString inputFilePath(const QString &inputFileName)
22 {
22 {
23 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
23 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
24 }
24 }
25
25
26 template <typename T>
26 template <typename T>
27 struct ExpectedResults {
27 struct ExpectedResults {
28
28
29 ExpectedResults &setParsingOK(bool parsingOK)
29 ExpectedResults &setParsingOK(bool parsingOK)
30 {
30 {
31 m_ParsingOK = parsingOK;
31 m_ParsingOK = parsingOK;
32 return *this;
32 return *this;
33 }
33 }
34
34
35 ExpectedResults &setXAxisUnit(Unit xAxisUnit)
35 ExpectedResults &setXAxisUnit(Unit xAxisUnit)
36 {
36 {
37 m_XAxisUnit = std::move(xAxisUnit);
37 m_XAxisUnit = std::move(xAxisUnit);
38 return *this;
38 return *this;
39 }
39 }
40
40
41 ExpectedResults &setXAxisData(const QVector<QDateTime> &xAxisData)
41 ExpectedResults &setXAxisData(const QVector<QDateTime> &xAxisData)
42 {
42 {
43 m_XAxisData.clear();
43 m_XAxisData.clear();
44
44
45 // Converts QVector<QDateTime> to QVector<double>
45 // Converts QVector<QDateTime> to QVector<double>
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
48
48
49 return *this;
49 return *this;
50 }
50 }
51
51
52 ExpectedResults &setValuesUnit(Unit valuesUnit)
52 ExpectedResults &setValuesUnit(Unit valuesUnit)
53 {
53 {
54 m_ValuesUnit = std::move(valuesUnit);
54 m_ValuesUnit = std::move(valuesUnit);
55 return *this;
55 return *this;
56 }
56 }
57
57
58 ExpectedResults &setValuesData(QVector<double> valuesData)
58 ExpectedResults &setValuesData(QVector<double> valuesData)
59 {
59 {
60 m_ValuesData.clear();
60 m_ValuesData.clear();
61 m_ValuesData.push_back(std::move(valuesData));
61 m_ValuesData.push_back(std::move(valuesData));
62 return *this;
62 return *this;
63 }
63 }
64
64
65 ExpectedResults &setValuesData(QVector<QVector<double> > valuesData)
65 ExpectedResults &setValuesData(QVector<QVector<double> > valuesData)
66 {
66 {
67 m_ValuesData = std::move(valuesData);
67 m_ValuesData = std::move(valuesData);
68 return *this;
68 return *this;
69 }
69 }
70
70
71 ExpectedResults &setYAxisEnabled(bool yAxisEnabled)
71 ExpectedResults &setYAxisEnabled(bool yAxisEnabled)
72 {
72 {
73 m_YAxisEnabled = yAxisEnabled;
73 m_YAxisEnabled = yAxisEnabled;
74 return *this;
74 return *this;
75 }
75 }
76
76
77 ExpectedResults &setYAxisUnit(Unit yAxisUnit)
77 ExpectedResults &setYAxisUnit(Unit yAxisUnit)
78 {
78 {
79 m_YAxisUnit = std::move(yAxisUnit);
79 m_YAxisUnit = std::move(yAxisUnit);
80 return *this;
80 return *this;
81 }
81 }
82
82
83 ExpectedResults &setYAxisData(QVector<double> yAxisData)
83 ExpectedResults &setYAxisData(QVector<double> yAxisData)
84 {
84 {
85 m_YAxisData = std::move(yAxisData);
85 m_YAxisData = std::move(yAxisData);
86 return *this;
86 return *this;
87 }
87 }
88
88
89 /**
89 /**
90 * Validates a DataSeries compared to the expected results
90 * Validates a DataSeries compared to the expected results
91 * @param results the DataSeries to validate
91 * @param results the DataSeries to validate
92 */
92 */
93 void validate(std::shared_ptr<IDataSeries> results)
93 void validate(std::shared_ptr<IDataSeries> results)
94 {
94 {
95 if (m_ParsingOK) {
95 if (m_ParsingOK) {
96 auto dataSeries = dynamic_cast<T *>(results.get());
96 auto dataSeries = dynamic_cast<T *>(results.get());
97 if (dataSeries == nullptr) {
97 if (dataSeries == nullptr) {
98
98
99 // No unit detected, parsink ok but data is nullptr
99 // No unit detected, parsink ok but data is nullptr
100 // TODO, improve the test to verify that the data is null
100 // TODO, improve the test to verify that the data is null
101 return;
101 return;
102 }
102 }
103
103
104 // Checks units
104 // Checks units
105 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
105 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
106 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
106 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
107
107
108 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
108 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
109 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
109 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
110 expectedData.cend(),
110 expectedData.cend(),
111 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
111 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
112 return equalFun(dataSeriesIt, expectedX);
112 return equalFun(dataSeriesIt, expectedX);
113 }));
113 }));
114 };
114 };
115
115
116 // Checks x-axis data
116 // Checks x-axis data
117 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
117 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
118 return seriesIt.x() == value;
118 return seriesIt.x() == value;
119 });
119 });
120
120
121 // Checks values data of each component
121 // Checks values data of each component
122 for (auto i = 0; i < m_ValuesData.size(); ++i) {
122 for (auto i = 0; i < m_ValuesData.size(); ++i) {
123 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
123 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
124 auto itValue = seriesIt.value(i);
124 auto itValue = seriesIt.value(i);
125 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
125 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
126 });
126 });
127 }
127 }
128
128
129 // Checks y-axis (if defined)
129 // Checks y-axis (if defined)
130 auto yAxis = dataSeries->yAxis();
130 auto yAxis = dataSeries->yAxis();
131 QCOMPARE(yAxis.isDefined(), m_YAxisEnabled);
131 QCOMPARE(yAxis.isDefined(), m_YAxisEnabled);
132
132
133 if (m_YAxisEnabled) {
133 if (m_YAxisEnabled) {
134 // Unit
134 // Unit
135 QCOMPARE(yAxis.unit(), m_YAxisUnit);
135 QCOMPARE(yAxis.unit(), m_YAxisUnit);
136
136
137 // Data
137 // Data
138 auto yAxisSize = yAxis.size();
138 QVERIFY(std::equal(yAxis.cbegin(), yAxis.cend(), m_YAxisData.cbegin(),
139 QCOMPARE(yAxisSize, m_YAxisData.size());
139 m_YAxisData.cend(), [](const auto &it, const auto &expectedVal) {
140 for (auto i = 0; i < yAxisSize; ++i) {
140 return it.first() == expectedVal;
141 QCOMPARE(yAxis.at(i), m_YAxisData.at(i));
141 }));
142 }
143 }
142 }
144 }
143 }
145 else {
144 else {
146 QVERIFY(results == nullptr);
145 QVERIFY(results == nullptr);
147 }
146 }
148 }
147 }
149
148
150 // Parsing was successfully completed
149 // Parsing was successfully completed
151 bool m_ParsingOK{false};
150 bool m_ParsingOK{false};
152 // Expected x-axis unit
151 // Expected x-axis unit
153 Unit m_XAxisUnit{};
152 Unit m_XAxisUnit{};
154 // Expected x-axis data
153 // Expected x-axis data
155 QVector<double> m_XAxisData{};
154 QVector<double> m_XAxisData{};
156 // Expected values unit
155 // Expected values unit
157 Unit m_ValuesUnit{};
156 Unit m_ValuesUnit{};
158 // Expected values data
157 // Expected values data
159 QVector<QVector<double> > m_ValuesData{};
158 QVector<QVector<double> > m_ValuesData{};
160 // Expected data series has y-axis
159 // Expected data series has y-axis
161 bool m_YAxisEnabled{false};
160 bool m_YAxisEnabled{false};
162 // Expected y-axis unit (if axis defined)
161 // Expected y-axis unit (if axis defined)
163 Unit m_YAxisUnit{};
162 Unit m_YAxisUnit{};
164 // Expected y-axis data (if axis defined)
163 // Expected y-axis data (if axis defined)
165 QVector<double> m_YAxisData{};
164 QVector<double> m_YAxisData{};
166 };
165 };
167
166
168 } // namespace
167 } // namespace
169
168
170 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
169 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
171 Q_DECLARE_METATYPE(ExpectedResults<SpectrogramSeries>)
170 Q_DECLARE_METATYPE(ExpectedResults<SpectrogramSeries>)
172 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
171 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
173
172
174 class TestAmdaResultParser : public QObject {
173 class TestAmdaResultParser : public QObject {
175 Q_OBJECT
174 Q_OBJECT
176 private:
175 private:
177 template <typename T>
176 template <typename T>
178 void testReadDataStructure()
177 void testReadDataStructure()
179 {
178 {
180 // ////////////// //
179 // ////////////// //
181 // Test structure //
180 // Test structure //
182 // ////////////// //
181 // ////////////// //
183
182
184 // Name of TXT file to read
183 // Name of TXT file to read
185 QTest::addColumn<QString>("inputFileName");
184 QTest::addColumn<QString>("inputFileName");
186 // Expected results
185 // Expected results
187 QTest::addColumn<ExpectedResults<T> >("expectedResults");
186 QTest::addColumn<ExpectedResults<T> >("expectedResults");
188 }
187 }
189
188
190 template <typename T>
189 template <typename T>
191 void testRead(AmdaResultParser::ValueType valueType)
190 void testRead(AmdaResultParser::ValueType valueType)
192 {
191 {
193 QFETCH(QString, inputFileName);
192 QFETCH(QString, inputFileName);
194 QFETCH(ExpectedResults<T>, expectedResults);
193 QFETCH(ExpectedResults<T>, expectedResults);
195
194
196 // Parses file
195 // Parses file
197 auto filePath = inputFilePath(inputFileName);
196 auto filePath = inputFilePath(inputFileName);
198 auto results = AmdaResultParser::readTxt(filePath, valueType);
197 auto results = AmdaResultParser::readTxt(filePath, valueType);
199
198
200 // ///////////////// //
199 // ///////////////// //
201 // Validates results //
200 // Validates results //
202 // ///////////////// //
201 // ///////////////// //
203 expectedResults.validate(results);
202 expectedResults.validate(results);
204 }
203 }
205
204
206 private slots:
205 private slots:
207 /// Input test data
206 /// Input test data
208 /// @sa testReadScalarTxt()
207 /// @sa testReadScalarTxt()
209 void testReadScalarTxt_data();
208 void testReadScalarTxt_data();
210
209
211 /// Tests parsing scalar series of a TXT file
210 /// Tests parsing scalar series of a TXT file
212 void testReadScalarTxt();
211 void testReadScalarTxt();
213
212
214 /// Input test data
213 /// Input test data
215 /// @sa testReadSpectrogramTxt()
214 /// @sa testReadSpectrogramTxt()
216 void testReadSpectrogramTxt_data();
215 void testReadSpectrogramTxt_data();
217
216
218 /// Tests parsing spectrogram series of a TXT file
217 /// Tests parsing spectrogram series of a TXT file
219 void testReadSpectrogramTxt();
218 void testReadSpectrogramTxt();
220
219
221 /// Input test data
220 /// Input test data
222 /// @sa testReadVectorTxt()
221 /// @sa testReadVectorTxt()
223 void testReadVectorTxt_data();
222 void testReadVectorTxt_data();
224
223
225 /// Tests parsing vector series of a TXT file
224 /// Tests parsing vector series of a TXT file
226 void testReadVectorTxt();
225 void testReadVectorTxt();
227 };
226 };
228
227
229 void TestAmdaResultParser::testReadScalarTxt_data()
228 void TestAmdaResultParser::testReadScalarTxt_data()
230 {
229 {
231 testReadDataStructure<ScalarSeries>();
230 testReadDataStructure<ScalarSeries>();
232
231
233 // ////////// //
232 // ////////// //
234 // Test cases //
233 // Test cases //
235 // ////////// //
234 // ////////// //
236
235
237 // Valid files
236 // Valid files
238 QTest::newRow("Valid file")
237 QTest::newRow("Valid file")
239 << QStringLiteral("ValidScalar1.txt")
238 << QStringLiteral("ValidScalar1.txt")
240 << ExpectedResults<ScalarSeries>{}
239 << ExpectedResults<ScalarSeries>{}
241 .setParsingOK(true)
240 .setParsingOK(true)
242 .setXAxisUnit(Unit{"nT", true})
241 .setXAxisUnit(Unit{"nT", true})
243 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
242 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
244 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
243 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
245 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
244 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
246 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
245 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
247 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)})
246 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)})
248 .setValuesData({-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
247 .setValuesData({-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
249 -2.55800, -2.43250, -2.42200});
248 -2.55800, -2.43250, -2.42200});
250
249
251 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
250 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
252 << QStringLiteral("WrongValue.txt")
251 << QStringLiteral("WrongValue.txt")
253 << ExpectedResults<ScalarSeries>{}
252 << ExpectedResults<ScalarSeries>{}
254 .setParsingOK(true)
253 .setParsingOK(true)
255 .setXAxisUnit(Unit{"nT", true})
254 .setXAxisUnit(Unit{"nT", true})
256 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
255 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
257 dateTime(2013, 9, 23, 9, 2, 30)})
256 dateTime(2013, 9, 23, 9, 2, 30)})
258 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
257 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
259
258
260 QTest::newRow("Valid file that contains NaN values")
259 QTest::newRow("Valid file that contains NaN values")
261 << QStringLiteral("NaNValue.txt")
260 << QStringLiteral("NaNValue.txt")
262 << ExpectedResults<ScalarSeries>{}
261 << ExpectedResults<ScalarSeries>{}
263 .setParsingOK(true)
262 .setParsingOK(true)
264 .setXAxisUnit(Unit{("nT"), true})
263 .setXAxisUnit(Unit{("nT"), true})
265 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
264 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
266 dateTime(2013, 9, 23, 9, 2, 30)})
265 dateTime(2013, 9, 23, 9, 2, 30)})
267 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
266 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
268
267
269 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
268 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
270 QTest::newRow("No unit file")
269 QTest::newRow("No unit file")
271 << QStringLiteral("NoUnit.txt")
270 << QStringLiteral("NoUnit.txt")
272 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"", true});
271 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"", true});
273
272
274 QTest::newRow("Wrong unit file")
273 QTest::newRow("Wrong unit file")
275 << QStringLiteral("WrongUnit.txt")
274 << QStringLiteral("WrongUnit.txt")
276 << ExpectedResults<ScalarSeries>{}
275 << ExpectedResults<ScalarSeries>{}
277 .setParsingOK(true)
276 .setParsingOK(true)
278 .setXAxisUnit(Unit{"", true})
277 .setXAxisUnit(Unit{"", true})
279 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
278 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
280 dateTime(2013, 9, 23, 9, 2, 30)})
279 dateTime(2013, 9, 23, 9, 2, 30)})
281 .setValuesData({-2.83950, -2.71850, -2.52150});
280 .setValuesData({-2.83950, -2.71850, -2.52150});
282
281
283 QTest::newRow("Wrong results file (date of first line is invalid")
282 QTest::newRow("Wrong results file (date of first line is invalid")
284 << QStringLiteral("WrongDate.txt")
283 << QStringLiteral("WrongDate.txt")
285 << ExpectedResults<ScalarSeries>{}
284 << ExpectedResults<ScalarSeries>{}
286 .setParsingOK(true)
285 .setParsingOK(true)
287 .setXAxisUnit(Unit{"nT", true})
286 .setXAxisUnit(Unit{"nT", true})
288 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
287 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
289 .setValuesData({-2.71850, -2.52150});
288 .setValuesData({-2.71850, -2.52150});
290
289
291 QTest::newRow("Wrong results file (too many values for first line")
290 QTest::newRow("Wrong results file (too many values for first line")
292 << QStringLiteral("TooManyValues.txt")
291 << QStringLiteral("TooManyValues.txt")
293 << ExpectedResults<ScalarSeries>{}
292 << ExpectedResults<ScalarSeries>{}
294 .setParsingOK(true)
293 .setParsingOK(true)
295 .setXAxisUnit(Unit{"nT", true})
294 .setXAxisUnit(Unit{"nT", true})
296 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
295 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
297 .setValuesData({-2.71850, -2.52150});
296 .setValuesData({-2.71850, -2.52150});
298
297
299 QTest::newRow("Wrong results file (x of first line is NaN")
298 QTest::newRow("Wrong results file (x of first line is NaN")
300 << QStringLiteral("NaNX.txt")
299 << QStringLiteral("NaNX.txt")
301 << ExpectedResults<ScalarSeries>{}
300 << ExpectedResults<ScalarSeries>{}
302 .setParsingOK(true)
301 .setParsingOK(true)
303 .setXAxisUnit(Unit{"nT", true})
302 .setXAxisUnit(Unit{"nT", true})
304 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
303 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
305 .setValuesData({-2.71850, -2.52150});
304 .setValuesData({-2.71850, -2.52150});
306
305
307 QTest::newRow("Invalid file type (vector)")
306 QTest::newRow("Invalid file type (vector)")
308 << QStringLiteral("ValidVector1.txt")
307 << QStringLiteral("ValidVector1.txt")
309 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"nT", true});
308 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"nT", true});
310
309
311 // Invalid files
310 // Invalid files
312 QTest::newRow("Invalid file (unexisting file)")
311 QTest::newRow("Invalid file (unexisting file)")
313 << QStringLiteral("UnexistingFile.txt")
312 << QStringLiteral("UnexistingFile.txt")
314 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
313 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
315
314
316 QTest::newRow("Invalid file (file not found on server)")
315 QTest::newRow("Invalid file (file not found on server)")
317 << QStringLiteral("FileNotFound.txt")
316 << QStringLiteral("FileNotFound.txt")
318 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
317 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
319 }
318 }
320
319
321 void TestAmdaResultParser::testReadScalarTxt()
320 void TestAmdaResultParser::testReadScalarTxt()
322 {
321 {
323 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
322 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
324 }
323 }
325
324
326 void TestAmdaResultParser::testReadSpectrogramTxt_data()
325 void TestAmdaResultParser::testReadSpectrogramTxt_data()
327 {
326 {
328 testReadDataStructure<SpectrogramSeries>();
327 testReadDataStructure<SpectrogramSeries>();
329
328
330 // ////////// //
329 // ////////// //
331 // Test cases //
330 // Test cases //
332 // ////////// //
331 // ////////// //
333
332
334 // Valid files
333 // Valid files
335 QTest::newRow("Valid file (three bands)")
334 QTest::newRow("Valid file (three bands)")
336 << QStringLiteral("spectro/ValidSpectrogram1.txt")
335 << QStringLiteral("spectro/ValidSpectrogram1.txt")
337 << ExpectedResults<SpectrogramSeries>{}
336 << ExpectedResults<SpectrogramSeries>{}
338 .setParsingOK(true)
337 .setParsingOK(true)
339 .setXAxisUnit(Unit{"t", true})
338 .setXAxisUnit(Unit{"t", true})
340 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
339 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
341 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
340 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
342 dateTime(2012, 11, 6, 9, 20, 55)})
341 dateTime(2012, 11, 6, 9, 20, 55)})
343 .setYAxisEnabled(true)
342 .setYAxisEnabled(true)
344 .setYAxisUnit(Unit{"eV"})
343 .setYAxisUnit(Unit{"eV"})
345 .setYAxisData({5.75, 7.6, 10.05}) // middle of the intervals of each band
344 .setYAxisData({5.75, 7.6, 10.05}) // middle of the intervals of each band
346 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
345 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
347 .setValuesData(QVector<QVector<double> >{
346 .setValuesData(QVector<QVector<double> >{
348 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
347 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
349 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
348 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
350 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221}});
349 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221}});
351
350
352 auto fourBandsResult
351 auto fourBandsResult
353 = ExpectedResults<SpectrogramSeries>{}
352 = ExpectedResults<SpectrogramSeries>{}
354 .setParsingOK(true)
353 .setParsingOK(true)
355 .setXAxisUnit(Unit{"t", true})
354 .setXAxisUnit(Unit{"t", true})
356 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
355 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
357 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
356 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
358 dateTime(2012, 11, 6, 9, 20, 55)})
357 dateTime(2012, 11, 6, 9, 20, 55)})
359 .setYAxisEnabled(true)
358 .setYAxisEnabled(true)
360 .setYAxisUnit(Unit{"eV"})
359 .setYAxisUnit(Unit{"eV"})
361 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
360 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
362 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
361 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
363 .setValuesData(QVector<QVector<double> >{
362 .setValuesData(QVector<QVector<double> >{
364 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
363 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
365 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
364 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
366 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
365 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
367 {20907.664, 32076.725, 13008.381, 13142.759, 23226.998}});
366 {20907.664, 32076.725, 13008.381, 13142.759, 23226.998}});
368
367
369 QTest::newRow("Valid file (four bands)")
368 QTest::newRow("Valid file (four bands)")
370 << QStringLiteral("spectro/ValidSpectrogram2.txt") << fourBandsResult;
369 << QStringLiteral("spectro/ValidSpectrogram2.txt") << fourBandsResult;
371 QTest::newRow("Valid file (four unsorted bands)")
370 QTest::newRow("Valid file (four unsorted bands)")
372 << QStringLiteral("spectro/ValidSpectrogram3.txt")
371 << QStringLiteral("spectro/ValidSpectrogram3.txt")
373 << fourBandsResult; // Bands and values are sorted
372 << fourBandsResult; // Bands and values are sorted
374
373
375 auto nan = std::numeric_limits<double>::quiet_NaN();
374 auto nan = std::numeric_limits<double>::quiet_NaN();
376
375
377 auto nanValuesResult
376 auto nanValuesResult
378 = ExpectedResults<SpectrogramSeries>{}
377 = ExpectedResults<SpectrogramSeries>{}
379 .setParsingOK(true)
378 .setParsingOK(true)
380 .setXAxisUnit(Unit{"t", true})
379 .setXAxisUnit(Unit{"t", true})
381 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
380 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
382 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
381 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
383 dateTime(2012, 11, 6, 9, 20, 55)})
382 dateTime(2012, 11, 6, 9, 20, 55)})
384 .setYAxisEnabled(true)
383 .setYAxisEnabled(true)
385 .setYAxisUnit(Unit{"eV"})
384 .setYAxisUnit(Unit{"eV"})
386 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
385 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
387 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
386 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
388 .setValuesData(
387 .setValuesData(
389 QVector<QVector<double> >{{nan, 12631.465, 8223.368, 27595.301, 12820.613},
388 QVector<QVector<double> >{{nan, 12631.465, 8223.368, 27595.301, 12820.613},
390 {15405.838, nan, nan, 25617.533, 11179.109},
389 {15405.838, nan, nan, 25617.533, 11179.109},
391 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
390 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
392 {nan, nan, nan, nan, nan}});
391 {nan, nan, nan, nan, nan}});
393
392
394 QTest::newRow("Valid file (containing NaN values)")
393 QTest::newRow("Valid file (containing NaN values)")
395 << QStringLiteral("spectro/ValidSpectrogramNaNValues.txt") << nanValuesResult;
394 << QStringLiteral("spectro/ValidSpectrogramNaNValues.txt") << nanValuesResult;
396 QTest::newRow("Valid file (containing fill values)")
395 QTest::newRow("Valid file (containing fill values)")
397 << QStringLiteral("spectro/ValidSpectrogramFillValues.txt")
396 << QStringLiteral("spectro/ValidSpectrogramFillValues.txt")
398 << nanValuesResult; // Fill values are replaced by NaN values in the data series
397 << nanValuesResult; // Fill values are replaced by NaN values in the data series
399
398
400 QTest::newRow("Valid file (containing data holes, resolution = 3 minutes)")
399 QTest::newRow("Valid file (containing data holes, resolution = 3 minutes)")
401 << QStringLiteral("spectro/ValidSpectrogramDataHoles.txt")
400 << QStringLiteral("spectro/ValidSpectrogramDataHoles.txt")
402 << ExpectedResults<SpectrogramSeries>{}
401 << ExpectedResults<SpectrogramSeries>{}
403 .setParsingOK(true)
402 .setParsingOK(true)
404 .setXAxisUnit(Unit{"t", true})
403 .setXAxisUnit(Unit{"t", true})
405 .setXAxisData({dateTime(2011, 12, 10, 12, 10, 54), //
404 .setXAxisData({dateTime(2011, 12, 10, 12, 10, 54), //
406 dateTime(2011, 12, 10, 12, 13, 54), // Data hole
405 dateTime(2011, 12, 10, 12, 13, 54), // Data hole
407 dateTime(2011, 12, 10, 12, 16, 54), // Data hole
406 dateTime(2011, 12, 10, 12, 16, 54), // Data hole
408 dateTime(2011, 12, 10, 12, 17, 23), //
407 dateTime(2011, 12, 10, 12, 17, 23), //
409 dateTime(2011, 12, 10, 12, 20, 23), // Data hole
408 dateTime(2011, 12, 10, 12, 20, 23), // Data hole
410 dateTime(2011, 12, 10, 12, 23, 23), // Data hole
409 dateTime(2011, 12, 10, 12, 23, 23), // Data hole
411 dateTime(2011, 12, 10, 12, 23, 51), //
410 dateTime(2011, 12, 10, 12, 23, 51), //
412 dateTime(2011, 12, 10, 12, 26, 51), // Data hole
411 dateTime(2011, 12, 10, 12, 26, 51), // Data hole
413 dateTime(2011, 12, 10, 12, 29, 51), // Data hole
412 dateTime(2011, 12, 10, 12, 29, 51), // Data hole
414 dateTime(2011, 12, 10, 12, 30, 19), //
413 dateTime(2011, 12, 10, 12, 30, 19), //
415 dateTime(2011, 12, 10, 12, 33, 19), // Data hole
414 dateTime(2011, 12, 10, 12, 33, 19), // Data hole
416 dateTime(2011, 12, 10, 12, 35, 04), //
415 dateTime(2011, 12, 10, 12, 35, 04), //
417 dateTime(2011, 12, 10, 12, 36, 41), //
416 dateTime(2011, 12, 10, 12, 36, 41), //
418 dateTime(2011, 12, 10, 12, 38, 18), //
417 dateTime(2011, 12, 10, 12, 38, 18), //
419 dateTime(2011, 12, 10, 12, 39, 55)})
418 dateTime(2011, 12, 10, 12, 39, 55)})
420 .setYAxisEnabled(true)
419 .setYAxisEnabled(true)
421 .setYAxisUnit(Unit{"eV"})
420 .setYAxisUnit(Unit{"eV"})
422 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
421 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
423 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
422 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
424 .setValuesData(QVector<QVector<double> >{{2577578.000, //
423 .setValuesData(QVector<QVector<double> >{{2577578.000, //
425 nan, // Data hole
424 nan, // Data hole
426 nan, // Data hole
425 nan, // Data hole
427 2314121.500, //
426 2314121.500, //
428 nan, // Data hole
427 nan, // Data hole
429 nan, // Data hole
428 nan, // Data hole
430 2063608.750, //
429 2063608.750, //
431 nan, // Data hole
430 nan, // Data hole
432 nan, // Data hole
431 nan, // Data hole
433 2234525.500, //
432 2234525.500, //
434 nan, // Data hole
433 nan, // Data hole
435 1670215.250, //
434 1670215.250, //
436 1689243.250, //
435 1689243.250, //
437 1654617.125, //
436 1654617.125, //
438 1504983.750},
437 1504983.750},
439 {2336016.000, //
438 {2336016.000, //
440 nan, // Data hole
439 nan, // Data hole
441 nan, // Data hole
440 nan, // Data hole
442 1712093.125, //
441 1712093.125, //
443 nan, // Data hole
442 nan, // Data hole
444 nan, // Data hole
443 nan, // Data hole
445 1614491.625, //
444 1614491.625, //
446 nan, // Data hole
445 nan, // Data hole
447 nan, // Data hole
446 nan, // Data hole
448 1764516.500, //
447 1764516.500, //
449 nan, // Data hole
448 nan, // Data hole
450 1688078.500, //
449 1688078.500, //
451 1743183.500, //
450 1743183.500, //
452 1733603.250, //
451 1733603.250, //
453 1708356.500}});
452 1708356.500}});
454
453
455 QTest::newRow(
454 QTest::newRow(
456 "Valid file (containing data holes at the beginning and the end, resolution = 4 minutes)")
455 "Valid file (containing data holes at the beginning and the end, resolution = 4 minutes)")
457 << QStringLiteral("spectro/ValidSpectrogramDataHoles2.txt")
456 << QStringLiteral("spectro/ValidSpectrogramDataHoles2.txt")
458 << ExpectedResults<SpectrogramSeries>{}
457 << ExpectedResults<SpectrogramSeries>{}
459 .setParsingOK(true)
458 .setParsingOK(true)
460 .setXAxisUnit(Unit{"t", true})
459 .setXAxisUnit(Unit{"t", true})
461 .setXAxisData({
460 .setXAxisData({
462 dateTime(2011, 12, 10, 12, 2, 54), // Data hole
461 dateTime(2011, 12, 10, 12, 2, 54), // Data hole
463 dateTime(2011, 12, 10, 12, 6, 54), // Data hole
462 dateTime(2011, 12, 10, 12, 6, 54), // Data hole
464 dateTime(2011, 12, 10, 12, 10, 54), //
463 dateTime(2011, 12, 10, 12, 10, 54), //
465 dateTime(2011, 12, 10, 12, 14, 54), // Data hole
464 dateTime(2011, 12, 10, 12, 14, 54), // Data hole
466 dateTime(2011, 12, 10, 12, 17, 23), //
465 dateTime(2011, 12, 10, 12, 17, 23), //
467 dateTime(2011, 12, 10, 12, 21, 23), // Data hole
466 dateTime(2011, 12, 10, 12, 21, 23), // Data hole
468 dateTime(2011, 12, 10, 12, 23, 51), //
467 dateTime(2011, 12, 10, 12, 23, 51), //
469 dateTime(2011, 12, 10, 12, 27, 51), // Data hole
468 dateTime(2011, 12, 10, 12, 27, 51), // Data hole
470 dateTime(2011, 12, 10, 12, 30, 19), //
469 dateTime(2011, 12, 10, 12, 30, 19), //
471 dateTime(2011, 12, 10, 12, 34, 19), // Data hole
470 dateTime(2011, 12, 10, 12, 34, 19), // Data hole
472 dateTime(2011, 12, 10, 12, 35, 04), //
471 dateTime(2011, 12, 10, 12, 35, 04), //
473 dateTime(2011, 12, 10, 12, 36, 41), //
472 dateTime(2011, 12, 10, 12, 36, 41), //
474 dateTime(2011, 12, 10, 12, 38, 18), //
473 dateTime(2011, 12, 10, 12, 38, 18), //
475 dateTime(2011, 12, 10, 12, 39, 55),
474 dateTime(2011, 12, 10, 12, 39, 55),
476 dateTime(2011, 12, 10, 12, 43, 55), // Data hole
475 dateTime(2011, 12, 10, 12, 43, 55), // Data hole
477 dateTime(2011, 12, 10, 12, 47, 55), // Data hole
476 dateTime(2011, 12, 10, 12, 47, 55), // Data hole
478 dateTime(2011, 12, 10, 12, 51, 55), // Data hole
477 dateTime(2011, 12, 10, 12, 51, 55), // Data hole
479 dateTime(2011, 12, 10, 12, 55, 55), // Data hole
478 dateTime(2011, 12, 10, 12, 55, 55), // Data hole
480 dateTime(2011, 12, 10, 12, 59, 55) // Data hole
479 dateTime(2011, 12, 10, 12, 59, 55) // Data hole
481 })
480 })
482 .setYAxisEnabled(true)
481 .setYAxisEnabled(true)
483 .setYAxisUnit(Unit{"eV"})
482 .setYAxisUnit(Unit{"eV"})
484 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
483 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
485 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
484 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
486 .setValuesData(QVector<QVector<double> >{{
485 .setValuesData(QVector<QVector<double> >{{
487 nan, // Data hole
486 nan, // Data hole
488 nan, // Data hole
487 nan, // Data hole
489 2577578.000, //
488 2577578.000, //
490 nan, // Data hole
489 nan, // Data hole
491 2314121.500, //
490 2314121.500, //
492 nan, // Data hole
491 nan, // Data hole
493 2063608.750, //
492 2063608.750, //
494 nan, // Data hole
493 nan, // Data hole
495 2234525.500, //
494 2234525.500, //
496 nan, // Data hole
495 nan, // Data hole
497 1670215.250, //
496 1670215.250, //
498 1689243.250, //
497 1689243.250, //
499 1654617.125, //
498 1654617.125, //
500 1504983.750, //
499 1504983.750, //
501 nan, // Data hole
500 nan, // Data hole
502 nan, // Data hole
501 nan, // Data hole
503 nan, // Data hole
502 nan, // Data hole
504 nan, // Data hole
503 nan, // Data hole
505 nan // Data hole
504 nan // Data hole
506 },
505 },
507 {
506 {
508 nan, // Data hole
507 nan, // Data hole
509 nan, // Data hole
508 nan, // Data hole
510 2336016.000, //
509 2336016.000, //
511 nan, // Data hole
510 nan, // Data hole
512 1712093.125, //
511 1712093.125, //
513 nan, // Data hole
512 nan, // Data hole
514 1614491.625, //
513 1614491.625, //
515 nan, // Data hole
514 nan, // Data hole
516 1764516.500, //
515 1764516.500, //
517 nan, // Data hole
516 nan, // Data hole
518 1688078.500, //
517 1688078.500, //
519 1743183.500, //
518 1743183.500, //
520 1733603.250, //
519 1733603.250, //
521 1708356.500, //
520 1708356.500, //
522 nan, // Data hole
521 nan, // Data hole
523 nan, // Data hole
522 nan, // Data hole
524 nan, // Data hole
523 nan, // Data hole
525 nan, // Data hole
524 nan, // Data hole
526 nan // Data hole
525 nan // Data hole
527 }});
526 }});
528
527
529 // Invalid files
528 // Invalid files
530 QTest::newRow("Invalid file (inconsistent bands)")
529 QTest::newRow("Invalid file (inconsistent bands)")
531 << QStringLiteral("spectro/InvalidSpectrogramWrongBands.txt")
530 << QStringLiteral("spectro/InvalidSpectrogramWrongBands.txt")
532 << ExpectedResults<SpectrogramSeries>{}.setParsingOK(false);
531 << ExpectedResults<SpectrogramSeries>{}.setParsingOK(false);
533 }
532 }
534
533
535 void TestAmdaResultParser::testReadSpectrogramTxt()
534 void TestAmdaResultParser::testReadSpectrogramTxt()
536 {
535 {
537 testRead<SpectrogramSeries>(AmdaResultParser::ValueType::SPECTROGRAM);
536 testRead<SpectrogramSeries>(AmdaResultParser::ValueType::SPECTROGRAM);
538 }
537 }
539
538
540 void TestAmdaResultParser::testReadVectorTxt_data()
539 void TestAmdaResultParser::testReadVectorTxt_data()
541 {
540 {
542 testReadDataStructure<VectorSeries>();
541 testReadDataStructure<VectorSeries>();
543
542
544 // ////////// //
543 // ////////// //
545 // Test cases //
544 // Test cases //
546 // ////////// //
545 // ////////// //
547
546
548 // Valid files
547 // Valid files
549 QTest::newRow("Valid file")
548 QTest::newRow("Valid file")
550 << QStringLiteral("ValidVector1.txt")
549 << QStringLiteral("ValidVector1.txt")
551 << ExpectedResults<VectorSeries>{}
550 << ExpectedResults<VectorSeries>{}
552 .setParsingOK(true)
551 .setParsingOK(true)
553 .setXAxisUnit(Unit{"nT", true})
552 .setXAxisUnit(Unit{"nT", true})
554 .setXAxisData({dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
553 .setXAxisData({dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
555 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
554 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
556 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
555 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
557 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
556 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
558 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)})
557 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)})
559 .setValuesData(
558 .setValuesData(
560 {{-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
559 {{-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
561 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
560 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
562 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}});
561 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}});
563
562
564 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
563 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
565 QTest::newRow("Invalid file type (scalar)")
564 QTest::newRow("Invalid file type (scalar)")
566 << QStringLiteral("ValidScalar1.txt")
565 << QStringLiteral("ValidScalar1.txt")
567 << ExpectedResults<VectorSeries>{}
566 << ExpectedResults<VectorSeries>{}
568 .setParsingOK(true)
567 .setParsingOK(true)
569 .setXAxisUnit(Unit{"nT", true})
568 .setXAxisUnit(Unit{"nT", true})
570 .setXAxisData({})
569 .setXAxisData({})
571 .setValuesData(QVector<QVector<double> >{{}, {}, {}});
570 .setValuesData(QVector<QVector<double> >{{}, {}, {}});
572 }
571 }
573
572
574 void TestAmdaResultParser::testReadVectorTxt()
573 void TestAmdaResultParser::testReadVectorTxt()
575 {
574 {
576 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
575 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
577 }
576 }
578
577
579 QTEST_MAIN(TestAmdaResultParser)
578 QTEST_MAIN(TestAmdaResultParser)
580 #include "TestAmdaResultParser.moc"
579 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now