##// END OF EJS Templates
Merge branch 'feature/MinMaxVariable' into develop
Alexandre Leroux -
r602:91bcf3ba1822 merge
parent child
Show More
@@ -0,0 +1,82
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
3
4 #include "CoreGlobal.h"
5
6 #include <memory>
7
8 /**
9 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
10 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
11 * each series to define its own implementation of how to retrieve this data, by implementing the
12 * DataSeriesIteratorValue::Impl interface
13 *
14 * @sa DataSeriesIterator
15 */
16 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
17 public:
18 struct Impl {
19 virtual ~Impl() noexcept = default;
20 virtual std::unique_ptr<Impl> clone() const = 0;
21 virtual bool equals(const Impl &other) const = 0;
22 virtual void next() = 0;
23 virtual void prev() = 0;
24 virtual double x() const = 0;
25 virtual double value() const = 0;
26 virtual double value(int componentIndex) const = 0;
27 };
28
29 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
30 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
31 DataSeriesIteratorValue(DataSeriesIteratorValue &&other) = default;
32 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
33
34 bool equals(const DataSeriesIteratorValue &other) const;
35
36 /// Advances to the next value
37 void next();
38 /// Moves back to the previous value
39 void prev();
40 /// Gets x-axis data
41 double x() const;
42 /// Gets value data
43 double value() const;
44 /// Gets value data depending on an index
45 double value(int componentIndex) const;
46
47 private:
48 std::unique_ptr<Impl> m_Impl;
49 };
50
51 /**
52 * @brief The DataSeriesIterator class represents an iterator used for data series. It defines all
53 * operators needed for a standard forward iterator
54 * @sa http://www.cplusplus.com/reference/iterator/
55 */
56 class SCIQLOP_CORE_EXPORT DataSeriesIterator {
57 public:
58 using iterator_category = std::forward_iterator_tag;
59 using value_type = const DataSeriesIteratorValue;
60 using difference_type = std::ptrdiff_t;
61 using pointer = value_type *;
62 using reference = value_type &;
63
64 explicit DataSeriesIterator(DataSeriesIteratorValue value);
65 virtual ~DataSeriesIterator() noexcept = default;
66 DataSeriesIterator(const DataSeriesIterator &) = default;
67 DataSeriesIterator(DataSeriesIterator &&) = default;
68 DataSeriesIterator &operator=(const DataSeriesIterator &) = default;
69 DataSeriesIterator &operator=(DataSeriesIterator &&) = default;
70
71 DataSeriesIterator &operator++();
72 DataSeriesIterator &operator--();
73 pointer operator->() const { return &m_CurrentValue; }
74 reference operator*() const { return m_CurrentValue; }
75 bool operator==(const DataSeriesIterator &other) const;
76 bool operator!=(const DataSeriesIterator &other) const;
77
78 private:
79 DataSeriesIteratorValue m_CurrentValue;
80 };
81
82 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -0,0 +1,75
1 #include "Data/DataSeriesIterator.h"
2
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
5 : m_Impl{std::move(impl)}
6 {
7 }
8
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
10 : m_Impl{other.m_Impl->clone()}
11 {
12 }
13
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
15 {
16 std::swap(m_Impl, other.m_Impl);
17 return *this;
18 }
19
20 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
21 {
22 return m_Impl->equals(*other.m_Impl);
23 }
24
25 void DataSeriesIteratorValue::next()
26 {
27 m_Impl->next();
28 }
29
30 void DataSeriesIteratorValue::prev()
31 {
32 m_Impl->prev();
33 }
34
35 double DataSeriesIteratorValue::x() const
36 {
37 return m_Impl->x();
38 }
39
40 double DataSeriesIteratorValue::value() const
41 {
42 return m_Impl->value();
43 }
44
45 double DataSeriesIteratorValue::value(int componentIndex) const
46 {
47 return m_Impl->value(componentIndex);
48 }
49
50 DataSeriesIterator::DataSeriesIterator(DataSeriesIteratorValue value)
51 : m_CurrentValue{std::move(value)}
52 {
53 }
54
55 DataSeriesIterator &DataSeriesIterator::operator++()
56 {
57 m_CurrentValue.next();
58 return *this;
59 }
60
61 DataSeriesIterator &DataSeriesIterator::operator--()
62 {
63 m_CurrentValue.prev();
64 return *this;
65 }
66
67 bool DataSeriesIterator::operator==(const DataSeriesIterator &other) const
68 {
69 return m_CurrentValue.equals(other.m_CurrentValue);
70 }
71
72 bool DataSeriesIterator::operator!=(const DataSeriesIterator &other) const
73 {
74 return !(*this == other);
75 }
@@ -77,6 +77,13 public:
77 77 }
78 78 }
79 79
80 void prev()
81 {
82 for (auto &it : m_Its) {
83 --it;
84 }
85 }
86
80 87 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
81 88
82 89 private:
@@ -105,6 +112,12 public:
105 112 return *this;
106 113 }
107 114
115 Iterator &operator--()
116 {
117 m_CurrentValue.prev();
118 return *this;
119 }
120
108 121 pointer operator->() const { return &m_CurrentValue; }
109 122 reference operator*() const { return m_CurrentValue; }
110 123
@@ -21,6 +21,57 inline const QLoggingCategory &LOG_DataSeries()
21 21 return category;
22 22 }
23 23
24 template <int Dim>
25 class DataSeries;
26
27 namespace dataseries_detail {
28
29 template <int Dim>
30 class IteratorValue : public DataSeriesIteratorValue::Impl {
31 public:
32 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
33 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
34 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
35 : dataSeries.valuesData()->cend())
36 {
37 }
38 IteratorValue(const IteratorValue &other) = default;
39
40 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
41 {
42 return std::make_unique<IteratorValue<Dim> >(*this);
43 }
44
45 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
46 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
47 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
48 }
49 catch (const std::bad_cast &) {
50 return false;
51 }
52
53 void next() override
54 {
55 ++m_XIt;
56 ++m_ValuesIt;
57 }
58
59 void prev() override
60 {
61 --m_XIt;
62 --m_ValuesIt;
63 }
64
65 double x() const override { return m_XIt->at(0); }
66 double value() const override { return m_ValuesIt->at(0); }
67 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
68
69 private:
70 ArrayData<1>::Iterator m_XIt;
71 typename ArrayData<Dim>::Iterator m_ValuesIt;
72 };
73 } // namespace dataseries_detail
74
24 75 /**
25 76 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
26 77 *
@@ -34,71 +85,6 inline const QLoggingCategory &LOG_DataSeries()
34 85 template <int Dim>
35 86 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
36 87 public:
37 class IteratorValue {
38 public:
39 explicit IteratorValue(const DataSeries &dataSeries, bool begin)
40 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
41 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
42 : dataSeries.valuesData()->cend())
43 {
44 }
45
46 double x() const { return m_XIt->at(0); }
47 double value() const { return m_ValuesIt->at(0); }
48 double value(int componentIndex) const { return m_ValuesIt->at(componentIndex); }
49
50 void next()
51 {
52 ++m_XIt;
53 ++m_ValuesIt;
54 }
55
56 bool operator==(const IteratorValue &other) const
57 {
58 return std::tie(m_XIt, m_ValuesIt) == std::tie(other.m_XIt, other.m_ValuesIt);
59 }
60
61 private:
62 ArrayData<1>::Iterator m_XIt;
63 typename ArrayData<Dim>::Iterator m_ValuesIt;
64 };
65
66 class Iterator {
67 public:
68 using iterator_category = std::forward_iterator_tag;
69 using value_type = const IteratorValue;
70 using difference_type = std::ptrdiff_t;
71 using pointer = value_type *;
72 using reference = value_type &;
73
74 Iterator(const DataSeries &dataSeries, bool begin) : m_CurrentValue{dataSeries, begin} {}
75 virtual ~Iterator() noexcept = default;
76 Iterator(const Iterator &) = default;
77 Iterator(Iterator &&) = default;
78 Iterator &operator=(const Iterator &) = default;
79 Iterator &operator=(Iterator &&) = default;
80
81 Iterator &operator++()
82 {
83 m_CurrentValue.next();
84 return *this;
85 }
86
87 pointer operator->() const { return &m_CurrentValue; }
88
89 reference operator*() const { return m_CurrentValue; }
90
91 bool operator==(const Iterator &other) const
92 {
93 return m_CurrentValue == other.m_CurrentValue;
94 }
95
96 bool operator!=(const Iterator &other) const { return !(*this == other); }
97
98 private:
99 IteratorValue m_CurrentValue;
100 };
101
102 88 /// @sa IDataSeries::xAxisData()
103 89 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
104 90 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
@@ -193,11 +179,38 public:
193 179 // Iterators //
194 180 // ///////// //
195 181
196 Iterator cbegin() const { return Iterator{*this, true}; }
182 DataSeriesIterator cbegin() const override
183 {
184 return DataSeriesIterator{DataSeriesIteratorValue{
185 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
186 }
187
188 DataSeriesIterator cend() const override
189 {
190 return DataSeriesIterator{DataSeriesIteratorValue{
191 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
192 }
193
194 /// @sa IDataSeries::minData()
195 DataSeriesIterator minData(double minXAxisData) const override
196 {
197 return std::lower_bound(
198 cbegin(), cend(), minXAxisData,
199 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
200 }
201
202 /// @sa IDataSeries::maxData()
203 DataSeriesIterator maxData(double maxXAxisData) const override
204 {
205 // Gets the first element that greater than max value
206 auto it = std::upper_bound(
207 cbegin(), cend(), maxXAxisData,
208 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
197 209
198 Iterator cend() const { return Iterator{*this, false}; }
210 return it == cbegin() ? cend() : --it;
211 }
199 212
200 std::pair<Iterator, Iterator> subData(double min, double max) const
213 std::pair<DataSeriesIterator, DataSeriesIterator> subData(double min, double max) const override
201 214 {
202 215 if (min > max) {
203 216 std::swap(min, max);
@@ -2,6 +2,7
2 2 #define SCIQLOP_IDATASERIES_H
3 3
4 4 #include <Common/MetaTypes.h>
5 #include <Data/DataSeriesIterator.h>
5 6 #include <Data/SqpRange.h>
6 7
7 8 #include <memory>
@@ -62,6 +63,28 public:
62 63 virtual std::unique_ptr<IDataSeries> clone() const = 0;
63 64 virtual SqpRange range() const = 0;
64 65
66 // ///////// //
67 // Iterators //
68 // ///////// //
69
70 virtual DataSeriesIterator cbegin() const = 0;
71 virtual DataSeriesIterator cend() const = 0;
72
73 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
74 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
75 virtual DataSeriesIterator minData(double minXAxisData) const = 0;
76
77 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
78 /// equal to the value passed in parameter, or the end iterator if there is no matching value
79 virtual DataSeriesIterator maxData(double maxXAxisData) const = 0;
80
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator> subData(double min,
82 double max) const = 0;
83
84 // /////// //
85 // Mutexes //
86 // /////// //
87
65 88 virtual void lockRead() = 0;
66 89 virtual void lockWrite() = 0;
67 90 virtual void unlock() = 0;
@@ -154,18 +154,31 QVariant VariableModel::data(const QModelIndex &index, int role) const
154 154 if (role == Qt::DisplayRole) {
155 155 if (auto variable = impl->m_Variables.at(index.row()).get()) {
156 156 /// Lambda function that builds the variant to return for a time value
157 auto dateTimeVariant = [](double secs) {
158 auto dateTime = DateUtils::dateTime(secs);
159 return dateTime.toString(DATETIME_FORMAT);
157 /// @param getValueFun function used to get for a data series the iterator on the entry
158 /// that contains the time value to display
159 auto dateTimeVariant = [variable](const auto &getValueFun) {
160 if (auto dataSeries = variable->dataSeries()) {
161 auto it = getValueFun(*dataSeries);
162 return (it != dataSeries->cend())
163 ? DateUtils::dateTime(it->x()).toString(DATETIME_FORMAT)
164 : QVariant{};
165 }
166 else {
167 return QVariant{};
168 }
160 169 };
161 170
162 171 switch (index.column()) {
163 172 case NAME_COLUMN:
164 173 return variable->name();
165 174 case TSTART_COLUMN:
166 return dateTimeVariant(variable->range().m_TStart);
175 // Shows the min value of the data series above the range tstart
176 return dateTimeVariant([min = variable->range().m_TStart](
177 const auto &dataSeries) { return dataSeries.minData(min); });
167 178 case TEND_COLUMN:
168 return dateTimeVariant(variable->range().m_TEnd);
179 // Shows the max value of the data series under the range tend
180 return dateTimeVariant([max = variable->range().m_TEnd](
181 const auto &dataSeries) { return dataSeries.maxData(max); });
169 182 case UNIT_COLUMN:
170 183 return variable->metadata().value(QStringLiteral("units"));
171 184 case MISSION_COLUMN:
@@ -24,6 +24,20 private slots:
24 24 void testMerge();
25 25
26 26 /// Input test data
27 /// @sa testMinData()
28 void testMinData_data();
29
30 /// Tests get min data of a data series
31 void testMinData();
32
33 /// Input test data
34 /// @sa testMaxData()
35 void testMaxData_data();
36
37 /// Tests get max data of a data series
38 void testMaxData();
39
40 /// Input test data
27 41 /// @sa testSubdata()
28 42 void testSubdata_data();
29 43
@@ -167,6 +181,114 void TestDataSeries::testMerge()
167 181 seriesValuesData.cbegin()));
168 182 }
169 183
184 void TestDataSeries::testMinData_data()
185 {
186 // ////////////// //
187 // Test structure //
188 // ////////////// //
189
190 // Data series to get min data
191 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
192
193 // Min data
194 QTest::addColumn<double>("min");
195
196 // Expected results
197 QTest::addColumn<bool>(
198 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
199 QTest::addColumn<double>(
200 "expectedMin"); // Expected value when method doesn't return end iterator
201
202 // ////////// //
203 // Test cases //
204 // ////////// //
205
206 QTest::newRow("minData1") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
207 << 0. << true << 1.;
208 QTest::newRow("minData2") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
209 << 1. << true << 1.;
210 QTest::newRow("minData3") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
211 << 1.1 << true << 2.;
212 QTest::newRow("minData4") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
213 << 5. << true << 5.;
214 QTest::newRow("minData5") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
215 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
216 QTest::newRow("minData6") << createSeries({}, {}) << 1.1 << false
217 << std::numeric_limits<double>::quiet_NaN();
218 }
219
220 void TestDataSeries::testMinData()
221 {
222 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
223 QFETCH(double, min);
224
225 QFETCH(bool, expectedOK);
226 QFETCH(double, expectedMin);
227
228 auto it = dataSeries->minData(min);
229
230 QCOMPARE(expectedOK, it != dataSeries->cend());
231
232 // If the method doesn't return a end iterator, checks with expected value
233 if (expectedOK) {
234 QCOMPARE(expectedMin, it->x());
235 }
236 }
237
238 void TestDataSeries::testMaxData_data()
239 {
240 // ////////////// //
241 // Test structure //
242 // ////////////// //
243
244 // Data series to get max data
245 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
246
247 // Max data
248 QTest::addColumn<double>("max");
249
250 // Expected results
251 QTest::addColumn<bool>(
252 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
253 QTest::addColumn<double>(
254 "expectedMax"); // Expected value when method doesn't return end iterator
255
256 // ////////// //
257 // Test cases //
258 // ////////// //
259
260 QTest::newRow("maxData1") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
261 << 6. << true << 5.;
262 QTest::newRow("maxData2") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
263 << 5. << true << 5.;
264 QTest::newRow("maxData3") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
265 << 4.9 << true << 4.;
266 QTest::newRow("maxData4") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
267 << 1.1 << true << 1.;
268 QTest::newRow("maxData5") << createSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
269 << 1. << true << 1.;
270 QTest::newRow("maxData6") << createSeries({}, {}) << 1.1 << false
271 << std::numeric_limits<double>::quiet_NaN();
272 }
273
274 void TestDataSeries::testMaxData()
275 {
276 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
277 QFETCH(double, max);
278
279 QFETCH(bool, expectedOK);
280 QFETCH(double, expectedMax);
281
282 auto it = dataSeries->maxData(max);
283
284 QCOMPARE(expectedOK, it != dataSeries->cend());
285
286 // If the method doesn't return a end iterator, checks with expected value
287 if (expectedOK) {
288 QCOMPARE(expectedMax, it->x());
289 }
290 }
291
170 292 void TestDataSeries::testSubdata_data()
171 293 {
172 294 // ////////////// //
@@ -3,6 +3,8 Common/spimpl\.h:\d+:.*
3 3
4 4 # Ignore false positive relative to two class definitions in a same file
5 5 DataSourceItem\.h:\d+:.*IPSIS_S01.*
6 DataSeries\.h:\d+:.*IPSIS_S01.*
7 DataSeriesIterator\.h:\d+:.*IPSIS_S01.*
6 8
7 9 # Ignore false positive relative to a template class
8 10 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D)
@@ -11,6 +13,7 ArrayData\.h:\d+:.*IPSIS_S06.*found: (D)
11 13 ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim)
12 14 DataSeries\.h:\d+:.*IPSIS_S04_METHOD.*found: LOG_DataSeries
13 15 DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.*
16 DataSeries\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (dataseries_detail)
14 17
15 18 # Ignore false positive relative to iterators
16 19 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (forward_iterator_tag)
@@ -27,16 +30,20 ArrayData\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
27 30 ArrayData\.h:\d+:.*IPSIS_S06.*found: (pointer)
28 31 ArrayData\.h:\d+:.*IPSIS_S06.*found: (reference)
29 32 ArrayData\.h:\d+:.*IPSIS_S06.*found: (value_type)
30 DataSeries\.h:\d+:.*IPSIS_S05.*
31 DataSeries\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
32 DataSeries\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
33 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
34 DataSeries\.h:\d+:.*IPSIS_S06.*found: (IteratorValue)
35 DataSeries\.h:\d+:.*IPSIS_S06.*found: (difference_type)
36 DataSeries\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
37 DataSeries\.h:\d+:.*IPSIS_S06.*found: (pointer)
38 DataSeries\.h:\d+:.*IPSIS_S06.*found: (reference)
39 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
33 DataSeriesIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (forward_iterator_tag)
34 DataSeriesIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (DataSeriesIteratorValue)
35 DataSeriesIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (ptrdiff_t)
36 DataSeriesIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (value_type)
37 DataSeriesIterator\.h:\d+:.*IPSIS_S05.*
38 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
39 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
40 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (value_type)
41 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (DataSeriesIteratorValue)
42 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (difference_type)
43 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
44 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (pointer)
45 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (reference)
46 DataSeriesIterator\.h:\d+:.*IPSIS_S06.*found: (value_type)
40 47
41 48 # Ignore false positive relative to an alias
42 49 DataSourceItemAction\.h:\d+:.*IPSIS_S06.*found: (ExecuteFunction)
General Comments 0
You need to be logged in to leave comments. Login now