##// END OF EJS Templates
Implements purge() method for DataSeries
Alexandre Leroux -
r629:34ec67ab5cd8
parent child
Show More
@@ -1,359 +1,374
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/DataSeriesMergeHelper.h>
10 10 #include <Data/IDataSeries.h>
11 11
12 12 #include <QLoggingCategory>
13 13 #include <QReadLocker>
14 14 #include <QReadWriteLock>
15 15 #include <memory>
16 16
17 17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 18 // definitions with inheritance. Inline method is used instead
19 19 inline const QLoggingCategory &LOG_DataSeries()
20 20 {
21 21 static const QLoggingCategory category{"DataSeries"};
22 22 return category;
23 23 }
24 24
25 25 template <int Dim>
26 26 class DataSeries;
27 27
28 28 namespace dataseries_detail {
29 29
30 30 template <int Dim, bool IsConst>
31 31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 32 public:
33 33 friend class DataSeries<Dim>;
34 34
35 35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 39 {
40 40 }
41 41
42 42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 46 : dataSeries.valuesData()->cend())
47 47 {
48 48 }
49 49
50 50 IteratorValue(const IteratorValue &other) = default;
51 51
52 52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 53 {
54 54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 55 }
56 56
57 57 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
58 58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 59 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
60 60 }
61 61 catch (const std::bad_cast &) {
62 62 return false;
63 63 }
64 64
65 65 void next() override
66 66 {
67 67 ++m_XIt;
68 68 ++m_ValuesIt;
69 69 }
70 70
71 71 void prev() override
72 72 {
73 73 --m_XIt;
74 74 --m_ValuesIt;
75 75 }
76 76
77 77 double x() const override { return m_XIt->at(0); }
78 78 double value() const override { return m_ValuesIt->at(0); }
79 79 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
80 80 double minValue() const override { return m_ValuesIt->min(); }
81 81 double maxValue() const override { return m_ValuesIt->max(); }
82 82 QVector<double> values() const override { return m_ValuesIt->values(); }
83 83
84 84 void swap(DataSeriesIteratorValue::Impl &other) override
85 85 {
86 86 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
87 87 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
88 88 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
89 89 }
90 90
91 91 private:
92 92 ArrayDataIterator m_XIt;
93 93 ArrayDataIterator m_ValuesIt;
94 94 };
95 95 } // namespace dataseries_detail
96 96
97 97 /**
98 98 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
99 99 *
100 100 * It proposes to set a dimension for the values ​​data.
101 101 *
102 102 * A DataSeries is always sorted on its x-axis data.
103 103 *
104 104 * @tparam Dim The dimension of the values data
105 105 *
106 106 */
107 107 template <int Dim>
108 108 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
109 109 friend class DataSeriesMergeHelper;
110 110
111 111 public:
112 112 /// Tag needed to define the push_back() method
113 113 /// @sa push_back()
114 114 using value_type = DataSeriesIteratorValue;
115 115
116 116 /// @sa IDataSeries::xAxisData()
117 117 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
118 118 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
119 119
120 120 /// @sa IDataSeries::xAxisUnit()
121 121 Unit xAxisUnit() const override { return m_XAxisUnit; }
122 122
123 123 /// @return the values dataset
124 124 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
125 125 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
126 126
127 127 /// @sa IDataSeries::valuesUnit()
128 128 Unit valuesUnit() const override { return m_ValuesUnit; }
129 129
130 130
131 131 SqpRange range() const override
132 132 {
133 133 if (!m_XAxisData->cdata().isEmpty()) {
134 134 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
135 135 }
136 136
137 137 return SqpRange{};
138 138 }
139 139
140 140 void clear()
141 141 {
142 142 m_XAxisData->clear();
143 143 m_ValuesData->clear();
144 144 }
145 145
146 146 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
147 147
148 148 /// Merges into the data series an other data series
149 149 /// @remarks the data series to merge with is cleared after the operation
150 150 void merge(IDataSeries *dataSeries) override
151 151 {
152 152 dataSeries->lockWrite();
153 153 lockWrite();
154 154
155 155 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
156 156 DataSeriesMergeHelper::merge(*other, *this);
157 157 }
158 158 else {
159 159 qCWarning(LOG_DataSeries())
160 160 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
161 161 }
162 162 unlock();
163 163 dataSeries->unlock();
164 164 }
165 165
166 void purge(double min, double max) override
167 {
168 if (min > max) {
169 std::swap(min, max);
170 }
171
172 lockWrite();
173
174 auto it = std::remove_if(
175 begin(), end(), [min, max](const auto &it) { return it.x() < min || it.x() > max; });
176 erase(it, end());
177
178 unlock();
179 }
180
166 181 // ///////// //
167 182 // Iterators //
168 183 // ///////// //
169 184
170 185 DataSeriesIterator begin() override
171 186 {
172 187 return DataSeriesIterator{DataSeriesIteratorValue{
173 188 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
174 189 }
175 190
176 191 DataSeriesIterator end() override
177 192 {
178 193 return DataSeriesIterator{DataSeriesIteratorValue{
179 194 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
180 195 }
181 196
182 197 DataSeriesIterator cbegin() const override
183 198 {
184 199 return DataSeriesIterator{DataSeriesIteratorValue{
185 200 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
186 201 }
187 202
188 203 DataSeriesIterator cend() const override
189 204 {
190 205 return DataSeriesIterator{DataSeriesIteratorValue{
191 206 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
192 207 }
193 208
194 209 void erase(DataSeriesIterator first, DataSeriesIterator last)
195 210 {
196 211 auto firstImpl
197 212 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
198 213 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
199 214
200 215 if (firstImpl && lastImpl) {
201 216 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
202 217 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
203 218 }
204 219 }
205 220
206 221 /// @sa IDataSeries::minXAxisData()
207 222 DataSeriesIterator minXAxisData(double minXAxisData) const override
208 223 {
209 224 return std::lower_bound(
210 225 cbegin(), cend(), minXAxisData,
211 226 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
212 227 }
213 228
214 229 /// @sa IDataSeries::maxXAxisData()
215 230 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
216 231 {
217 232 // Gets the first element that greater than max value
218 233 auto it = std::upper_bound(
219 234 cbegin(), cend(), maxXAxisData,
220 235 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
221 236
222 237 return it == cbegin() ? cend() : --it;
223 238 }
224 239
225 240 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
226 241 double maxXAxisData) const override
227 242 {
228 243 if (minXAxisData > maxXAxisData) {
229 244 std::swap(minXAxisData, maxXAxisData);
230 245 }
231 246
232 247 auto begin = cbegin();
233 248 auto end = cend();
234 249
235 250 auto lowerIt = std::lower_bound(
236 251 begin, end, minXAxisData,
237 252 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
238 253 auto upperIt = std::upper_bound(
239 254 begin, end, maxXAxisData,
240 255 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
241 256
242 257 return std::make_pair(lowerIt, upperIt);
243 258 }
244 259
245 260 std::pair<DataSeriesIterator, DataSeriesIterator>
246 261 valuesBounds(double minXAxisData, double maxXAxisData) const override
247 262 {
248 263 // Places iterators to the correct x-axis range
249 264 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
250 265
251 266 // Returns end iterators if the range is empty
252 267 if (xAxisRangeIts.first == xAxisRangeIts.second) {
253 268 return std::make_pair(cend(), cend());
254 269 }
255 270
256 271 // Gets the iterator on the min of all values data
257 272 auto minIt = std::min_element(
258 273 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
259 274 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
260 275 });
261 276
262 277 // Gets the iterator on the max of all values data
263 278 auto maxIt = std::max_element(
264 279 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
265 280 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
266 281 });
267 282
268 283 return std::make_pair(minIt, maxIt);
269 284 }
270 285
271 286 // /////// //
272 287 // Mutexes //
273 288 // /////// //
274 289
275 290 virtual void lockRead() { m_Lock.lockForRead(); }
276 291 virtual void lockWrite() { m_Lock.lockForWrite(); }
277 292 virtual void unlock() { m_Lock.unlock(); }
278 293
279 294 // ///// //
280 295 // Other //
281 296 // ///// //
282 297
283 298 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
284 299 /// method is intended to be used in the context of generating a back insert iterator
285 300 /// @param iteratorValue the iterator value containing the values to insert
286 301 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
287 302 /// @sa merge()
288 303 /// @sa value_type
289 304 void push_back(const value_type &iteratorValue)
290 305 {
291 306 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
292 307 m_ValuesData->push_back(iteratorValue.values());
293 308 }
294 309
295 310 protected:
296 311 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
297 312 /// DataSeries with no values will be created.
298 313 /// @remarks data series is automatically sorted on its x-axis data
299 314 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
300 315 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
301 316 : m_XAxisData{xAxisData},
302 317 m_XAxisUnit{xAxisUnit},
303 318 m_ValuesData{valuesData},
304 319 m_ValuesUnit{valuesUnit}
305 320 {
306 321 if (m_XAxisData->size() != m_ValuesData->size()) {
307 322 clear();
308 323 }
309 324
310 325 // Sorts data if it's not the case
311 326 const auto &xAxisCData = m_XAxisData->cdata();
312 327 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
313 328 sort();
314 329 }
315 330 }
316 331
317 332 /// Copy ctor
318 333 explicit DataSeries(const DataSeries<Dim> &other)
319 334 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
320 335 m_XAxisUnit{other.m_XAxisUnit},
321 336 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
322 337 m_ValuesUnit{other.m_ValuesUnit}
323 338 {
324 339 // Since a series is ordered from its construction and is always ordered, it is not
325 340 // necessary to call the sort method here ('other' is sorted)
326 341 }
327 342
328 343 /// Assignment operator
329 344 template <int D>
330 345 DataSeries &operator=(DataSeries<D> other)
331 346 {
332 347 std::swap(m_XAxisData, other.m_XAxisData);
333 348 std::swap(m_XAxisUnit, other.m_XAxisUnit);
334 349 std::swap(m_ValuesData, other.m_ValuesData);
335 350 std::swap(m_ValuesUnit, other.m_ValuesUnit);
336 351
337 352 return *this;
338 353 }
339 354
340 355 private:
341 356 /**
342 357 * Sorts data series on its x-axis data
343 358 */
344 359 void sort() noexcept
345 360 {
346 361 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
347 362 m_XAxisData = m_XAxisData->sort(permutation);
348 363 m_ValuesData = m_ValuesData->sort(permutation);
349 364 }
350 365
351 366 std::shared_ptr<ArrayData<1> > m_XAxisData;
352 367 Unit m_XAxisUnit;
353 368 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
354 369 Unit m_ValuesUnit;
355 370
356 371 QReadWriteLock m_Lock;
357 372 };
358 373
359 374 #endif // SCIQLOP_DATASERIES_H
@@ -1,106 +1,109
1 1 #ifndef SCIQLOP_IDATASERIES_H
2 2 #define SCIQLOP_IDATASERIES_H
3 3
4 4 #include <Common/MetaTypes.h>
5 5 #include <Data/DataSeriesIterator.h>
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <memory>
9 9
10 10 #include <QString>
11 11
12 12 template <int Dim>
13 13 class ArrayData;
14 14
15 15 struct Unit {
16 16 explicit Unit(const QString &name = {}, bool timeUnit = false)
17 17 : m_Name{name}, m_TimeUnit{timeUnit}
18 18 {
19 19 }
20 20
21 21 inline bool operator==(const Unit &other) const
22 22 {
23 23 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
24 24 }
25 25 inline bool operator!=(const Unit &other) const { return !(*this == other); }
26 26
27 27 QString m_Name; ///< Unit name
28 28 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
29 29 };
30 30
31 31 /**
32 32 * @brief The IDataSeries aims to declare a data series.
33 33 *
34 34 * A data series is an entity that contains at least :
35 35 * - one dataset representing the x-axis
36 36 * - one dataset representing the values
37 37 *
38 38 * Each dataset is represented by an ArrayData, and is associated with a unit.
39 39 *
40 40 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
41 41 * IDataSeries. The x-axis dataset is always unidimensional.
42 42 *
43 43 * @sa ArrayData
44 44 */
45 45 class IDataSeries {
46 46 public:
47 47 virtual ~IDataSeries() noexcept = default;
48 48
49 49 /// Returns the x-axis dataset
50 50 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
51 51
52 52 /// Returns the x-axis dataset (as const)
53 53 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
54 54
55 55 virtual Unit xAxisUnit() const = 0;
56 56
57 57 virtual Unit valuesUnit() const = 0;
58 58
59 59 virtual void merge(IDataSeries *dataSeries) = 0;
60 /// Removes from data series all entries whose value on the x-axis is not between min and max
61 virtual void purge(double min, double max) = 0;
62
60 63 /// @todo Review the name and signature of this method
61 64 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
62 65
63 66 virtual std::unique_ptr<IDataSeries> clone() const = 0;
64 67 virtual SqpRange range() const = 0;
65 68
66 69 // ///////// //
67 70 // Iterators //
68 71 // ///////// //
69 72
70 73 virtual DataSeriesIterator cbegin() const = 0;
71 74 virtual DataSeriesIterator cend() const = 0;
72 75 virtual DataSeriesIterator begin() = 0;
73 76 virtual DataSeriesIterator end() = 0;
74 77
75 78 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
76 79 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
77 80 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
78 81
79 82 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
80 83 /// equal to the value passed in parameter, or the end iterator if there is no matching value
81 84 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
82 85
83 86 /// @return the iterators pointing to the range of data whose x-axis values are between min and
84 87 /// max passed in parameters
85 88 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
86 89 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
87 90
88 91 /// @return two iterators pointing to the data that have respectively the min and the max value
89 92 /// data of a data series' range. The search is performed for a given x-axis range.
90 93 /// @sa xAxisRange()
91 94 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
92 95 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
93 96
94 97 // /////// //
95 98 // Mutexes //
96 99 // /////// //
97 100
98 101 virtual void lockRead() = 0;
99 102 virtual void lockWrite() = 0;
100 103 virtual void unlock() = 0;
101 104 };
102 105
103 106 // Required for using shared_ptr in signals/slots
104 107 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
105 108
106 109 #endif // SCIQLOP_IDATASERIES_H
General Comments 0
You need to be logged in to leave comments. Login now