##// END OF EJS Templates
Implements method to get min/max values of a dataseries giving a range (2)
Alexandre Leroux -
r570:3d5120e0d250
parent child
Show More
@@ -1,320 +1,333
1 1 #ifndef SCIQLOP_DATASERIES_H
2 2 #define SCIQLOP_DATASERIES_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/SortUtils.h>
7 7
8 8 #include <Data/ArrayData.h>
9 9 #include <Data/IDataSeries.h>
10 10
11 11 #include <QLoggingCategory>
12 12 #include <QReadLocker>
13 13 #include <QReadWriteLock>
14 14 #include <memory>
15 15
16 16 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
17 17 // definitions with inheritance. Inline method is used instead
18 18 inline const QLoggingCategory &LOG_DataSeries()
19 19 {
20 20 static const QLoggingCategory category{"DataSeries"};
21 21 return category;
22 22 }
23 23
24 24 template <int Dim>
25 25 class DataSeries;
26 26
27 27 namespace dataseries_detail {
28 28
29 29 template <int Dim>
30 30 class IteratorValue : public DataSeriesIteratorValue::Impl {
31 31 public:
32 32 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
33 33 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
34 34 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
35 35 : dataSeries.valuesData()->cend())
36 36 {
37 37 }
38 38 IteratorValue(const IteratorValue &other) = default;
39 39
40 40 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
41 41 {
42 42 return std::make_unique<IteratorValue<Dim> >(*this);
43 43 }
44 44
45 45 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
46 46 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
47 47 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
48 48 }
49 49 catch (const std::bad_cast &) {
50 50 return false;
51 51 }
52 52
53 53 void next() override
54 54 {
55 55 ++m_XIt;
56 56 ++m_ValuesIt;
57 57 }
58 58
59 59 void prev() override
60 60 {
61 61 --m_XIt;
62 62 --m_ValuesIt;
63 63 }
64 64
65 65 double x() const override { return m_XIt->at(0); }
66 66 double value() const override { return m_ValuesIt->at(0); }
67 67 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
68 double minValue() const override { return m_ValuesIt->min(); }
69 double maxValue() const override { return m_ValuesIt->max(); }
68 70
69 71 private:
70 72 ArrayData<1>::Iterator m_XIt;
71 73 typename ArrayData<Dim>::Iterator m_ValuesIt;
72 74 };
73 75 } // namespace dataseries_detail
74 76
75 77 /**
76 78 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
77 79 *
78 80 * It proposes to set a dimension for the values ​​data.
79 81 *
80 82 * A DataSeries is always sorted on its x-axis data.
81 83 *
82 84 * @tparam Dim The dimension of the values data
83 85 *
84 86 */
85 87 template <int Dim>
86 88 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
87 89 public:
88 90 /// @sa IDataSeries::xAxisData()
89 91 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
90 92 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
91 93
92 94 /// @sa IDataSeries::xAxisUnit()
93 95 Unit xAxisUnit() const override { return m_XAxisUnit; }
94 96
95 97 /// @return the values dataset
96 98 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
97 99 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
98 100
99 101 /// @sa IDataSeries::valuesUnit()
100 102 Unit valuesUnit() const override { return m_ValuesUnit; }
101 103
102 104
103 105 SqpRange range() const override
104 106 {
105 107 if (!m_XAxisData->cdata().isEmpty()) {
106 108 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
107 109 }
108 110
109 111 return SqpRange{};
110 112 }
111 113
112 114 void clear()
113 115 {
114 116 m_XAxisData->clear();
115 117 m_ValuesData->clear();
116 118 }
117 119
118 120 /// Merges into the data series an other data series
119 121 /// @remarks the data series to merge with is cleared after the operation
120 122 void merge(IDataSeries *dataSeries) override
121 123 {
122 124 dataSeries->lockWrite();
123 125 lockWrite();
124 126
125 127 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
126 128 const auto &otherXAxisData = other->xAxisData()->cdata();
127 129 const auto &xAxisData = m_XAxisData->cdata();
128 130
129 131 // As data series are sorted, we can improve performances of merge, by call the sort
130 132 // method only if the two data series overlap.
131 133 if (!otherXAxisData.empty()) {
132 134 auto firstValue = otherXAxisData.front();
133 135 auto lastValue = otherXAxisData.back();
134 136
135 137 auto xAxisDataBegin = xAxisData.cbegin();
136 138 auto xAxisDataEnd = xAxisData.cend();
137 139
138 140 bool prepend;
139 141 bool sortNeeded;
140 142
141 143 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
142 144 // Other data series if after data series
143 145 prepend = false;
144 146 sortNeeded = false;
145 147 }
146 148 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
147 149 == xAxisDataBegin) {
148 150 // Other data series if before data series
149 151 prepend = true;
150 152 sortNeeded = false;
151 153 }
152 154 else {
153 155 // The two data series overlap
154 156 prepend = false;
155 157 sortNeeded = true;
156 158 }
157 159
158 160 // Makes the merge
159 161 m_XAxisData->add(*other->xAxisData(), prepend);
160 162 m_ValuesData->add(*other->valuesData(), prepend);
161 163
162 164 if (sortNeeded) {
163 165 sort();
164 166 }
165 167 }
166 168
167 169 // Clears the other data series
168 170 other->clear();
169 171 }
170 172 else {
171 173 qCWarning(LOG_DataSeries())
172 174 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
173 175 }
174 176 unlock();
175 177 dataSeries->unlock();
176 178 }
177 179
178 180 // ///////// //
179 181 // Iterators //
180 182 // ///////// //
181 183
182 184 DataSeriesIterator cbegin() const override
183 185 {
184 186 return DataSeriesIterator{DataSeriesIteratorValue{
185 187 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
186 188 }
187 189
188 190 DataSeriesIterator cend() const override
189 191 {
190 192 return DataSeriesIterator{DataSeriesIteratorValue{
191 193 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
192 194 }
193 195
194 196 /// @sa IDataSeries::minXAxisData()
195 197 DataSeriesIterator minXAxisData(double minXAxisData) const override
196 198 {
197 199 return std::lower_bound(
198 200 cbegin(), cend(), minXAxisData,
199 201 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
200 202 }
201 203
202 204 /// @sa IDataSeries::maxXAxisData()
203 205 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
204 206 {
205 207 // Gets the first element that greater than max value
206 208 auto it = std::upper_bound(
207 209 cbegin(), cend(), maxXAxisData,
208 210 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
209 211
210 212 return it == cbegin() ? cend() : --it;
211 213 }
212 214
213 215 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
214 216 double maxXAxisData) const override
215 217 {
216 218 if (minXAxisData > maxXAxisData) {
217 219 std::swap(minXAxisData, maxXAxisData);
218 220 }
219 221
220 222 auto begin = cbegin();
221 223 auto end = cend();
222 224
223 225 auto lowerIt = std::lower_bound(
224 226 begin, end, minXAxisData,
225 227 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
226 228 auto upperIt = std::upper_bound(
227 229 begin, end, maxXAxisData,
228 230 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
229 231
230 232 return std::make_pair(lowerIt, upperIt);
231 233 }
232 234
233 235 std::pair<DataSeriesIterator, DataSeriesIterator>
234 236 valuesBounds(double minXAxisData, double maxXAxisData) const override
235 237 {
236 238 // Places iterators to the correct x-axis range
237 239 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
238 240
239 241 // Returns end iterators if the range is empty
240 242 if (xAxisRangeIts.first == xAxisRangeIts.second) {
241 243 return std::make_pair(cend(), cend());
242 244 }
243 245
244 /// @todo ALX: complete
246 // Gets the iterator on the min of all values data
247 auto minIt = std::min_element(
248 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
249 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
250 });
245 251
252 // Gets the iterator on the max of all values data
253 auto maxIt = std::max_element(
254 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
255 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
256 });
257
258 return std::make_pair(minIt, maxIt);
246 259 }
247 260
248 261 // /////// //
249 262 // Mutexes //
250 263 // /////// //
251 264
252 265 virtual void lockRead() { m_Lock.lockForRead(); }
253 266 virtual void lockWrite() { m_Lock.lockForWrite(); }
254 267 virtual void unlock() { m_Lock.unlock(); }
255 268
256 269 protected:
257 270 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
258 271 /// DataSeries with no values will be created.
259 272 /// @remarks data series is automatically sorted on its x-axis data
260 273 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
261 274 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
262 275 : m_XAxisData{xAxisData},
263 276 m_XAxisUnit{xAxisUnit},
264 277 m_ValuesData{valuesData},
265 278 m_ValuesUnit{valuesUnit}
266 279 {
267 280 if (m_XAxisData->size() != m_ValuesData->size()) {
268 281 clear();
269 282 }
270 283
271 284 // Sorts data if it's not the case
272 285 const auto &xAxisCData = m_XAxisData->cdata();
273 286 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
274 287 sort();
275 288 }
276 289 }
277 290
278 291 /// Copy ctor
279 292 explicit DataSeries(const DataSeries<Dim> &other)
280 293 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
281 294 m_XAxisUnit{other.m_XAxisUnit},
282 295 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
283 296 m_ValuesUnit{other.m_ValuesUnit}
284 297 {
285 298 // Since a series is ordered from its construction and is always ordered, it is not
286 299 // necessary to call the sort method here ('other' is sorted)
287 300 }
288 301
289 302 /// Assignment operator
290 303 template <int D>
291 304 DataSeries &operator=(DataSeries<D> other)
292 305 {
293 306 std::swap(m_XAxisData, other.m_XAxisData);
294 307 std::swap(m_XAxisUnit, other.m_XAxisUnit);
295 308 std::swap(m_ValuesData, other.m_ValuesData);
296 309 std::swap(m_ValuesUnit, other.m_ValuesUnit);
297 310
298 311 return *this;
299 312 }
300 313
301 314 private:
302 315 /**
303 316 * Sorts data series on its x-axis data
304 317 */
305 318 void sort() noexcept
306 319 {
307 320 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
308 321 m_XAxisData = m_XAxisData->sort(permutation);
309 322 m_ValuesData = m_ValuesData->sort(permutation);
310 323 }
311 324
312 325 std::shared_ptr<ArrayData<1> > m_XAxisData;
313 326 Unit m_XAxisUnit;
314 327 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
315 328 Unit m_ValuesUnit;
316 329
317 330 QReadWriteLock m_Lock;
318 331 };
319 332
320 333 #endif // SCIQLOP_DATASERIES_H
@@ -1,82 +1,88
1 1 #ifndef SCIQLOP_DATASERIESITERATOR_H
2 2 #define SCIQLOP_DATASERIESITERATOR_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <memory>
7 7
8 8 /**
9 9 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
10 10 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
11 11 * each series to define its own implementation of how to retrieve this data, by implementing the
12 12 * DataSeriesIteratorValue::Impl interface
13 13 *
14 14 * @sa DataSeriesIterator
15 15 */
16 16 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
17 17 public:
18 18 struct Impl {
19 19 virtual ~Impl() noexcept = default;
20 20 virtual std::unique_ptr<Impl> clone() const = 0;
21 21 virtual bool equals(const Impl &other) const = 0;
22 22 virtual void next() = 0;
23 23 virtual void prev() = 0;
24 24 virtual double x() const = 0;
25 25 virtual double value() const = 0;
26 26 virtual double value(int componentIndex) const = 0;
27 virtual double minValue() const = 0;
28 virtual double maxValue() const = 0;
27 29 };
28 30
29 31 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
30 32 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
31 33 DataSeriesIteratorValue(DataSeriesIteratorValue &&other) = default;
32 34 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
33 35
34 36 bool equals(const DataSeriesIteratorValue &other) const;
35 37
36 38 /// Advances to the next value
37 39 void next();
38 40 /// Moves back to the previous value
39 41 void prev();
40 42 /// Gets x-axis data
41 43 double x() const;
42 44 /// Gets value data
43 45 double value() const;
44 46 /// Gets value data depending on an index
45 47 double value(int componentIndex) const;
48 /// Gets min of all values data
49 double minValue() const;
50 /// Gets max of all values data
51 double maxValue() const;
46 52
47 53 private:
48 54 std::unique_ptr<Impl> m_Impl;
49 55 };
50 56
51 57 /**
52 58 * @brief The DataSeriesIterator class represents an iterator used for data series. It defines all
53 59 * operators needed for a standard forward iterator
54 60 * @sa http://www.cplusplus.com/reference/iterator/
55 61 */
56 62 class SCIQLOP_CORE_EXPORT DataSeriesIterator {
57 63 public:
58 64 using iterator_category = std::forward_iterator_tag;
59 65 using value_type = const DataSeriesIteratorValue;
60 66 using difference_type = std::ptrdiff_t;
61 67 using pointer = value_type *;
62 68 using reference = value_type &;
63 69
64 70 explicit DataSeriesIterator(DataSeriesIteratorValue value);
65 71 virtual ~DataSeriesIterator() noexcept = default;
66 72 DataSeriesIterator(const DataSeriesIterator &) = default;
67 73 DataSeriesIterator(DataSeriesIterator &&) = default;
68 74 DataSeriesIterator &operator=(const DataSeriesIterator &) = default;
69 75 DataSeriesIterator &operator=(DataSeriesIterator &&) = default;
70 76
71 77 DataSeriesIterator &operator++();
72 78 DataSeriesIterator &operator--();
73 79 pointer operator->() const { return &m_CurrentValue; }
74 80 reference operator*() const { return m_CurrentValue; }
75 81 bool operator==(const DataSeriesIterator &other) const;
76 82 bool operator!=(const DataSeriesIterator &other) const;
77 83
78 84 private:
79 85 DataSeriesIteratorValue m_CurrentValue;
80 86 };
81 87
82 88 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -1,75 +1,85
1 1 #include "Data/DataSeriesIterator.h"
2 2
3 3 DataSeriesIteratorValue::DataSeriesIteratorValue(
4 4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
5 5 : m_Impl{std::move(impl)}
6 6 {
7 7 }
8 8
9 9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
10 10 : m_Impl{other.m_Impl->clone()}
11 11 {
12 12 }
13 13
14 14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
15 15 {
16 16 std::swap(m_Impl, other.m_Impl);
17 17 return *this;
18 18 }
19 19
20 20 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
21 21 {
22 22 return m_Impl->equals(*other.m_Impl);
23 23 }
24 24
25 25 void DataSeriesIteratorValue::next()
26 26 {
27 27 m_Impl->next();
28 28 }
29 29
30 30 void DataSeriesIteratorValue::prev()
31 31 {
32 32 m_Impl->prev();
33 33 }
34 34
35 35 double DataSeriesIteratorValue::x() const
36 36 {
37 37 return m_Impl->x();
38 38 }
39 39
40 40 double DataSeriesIteratorValue::value() const
41 41 {
42 42 return m_Impl->value();
43 43 }
44 44
45 45 double DataSeriesIteratorValue::value(int componentIndex) const
46 46 {
47 47 return m_Impl->value(componentIndex);
48 48 }
49 49
50 double DataSeriesIteratorValue::minValue() const
51 {
52 return m_Impl->minValue();
53 }
54
55 double DataSeriesIteratorValue::maxValue() const
56 {
57 return m_Impl->maxValue();
58 }
59
50 60 DataSeriesIterator::DataSeriesIterator(DataSeriesIteratorValue value)
51 61 : m_CurrentValue{std::move(value)}
52 62 {
53 63 }
54 64
55 65 DataSeriesIterator &DataSeriesIterator::operator++()
56 66 {
57 67 m_CurrentValue.next();
58 68 return *this;
59 69 }
60 70
61 71 DataSeriesIterator &DataSeriesIterator::operator--()
62 72 {
63 73 m_CurrentValue.prev();
64 74 return *this;
65 75 }
66 76
67 77 bool DataSeriesIterator::operator==(const DataSeriesIterator &other) const
68 78 {
69 79 return m_CurrentValue.equals(other.m_CurrentValue);
70 80 }
71 81
72 82 bool DataSeriesIterator::operator!=(const DataSeriesIterator &other) const
73 83 {
74 84 return !(*this == other);
75 85 }
General Comments 0
You need to be logged in to leave comments. Login now