##// END OF EJS Templates
Some refac on Spectrograms...
jeandet -
r1472:8b544a6af98d
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit 70d6748a06620f865b683c86c89cc27e1cb0d4b9
1 Subproject commit c6cf2dba079da680390ae2de0522a39964c5e629
@@ -1,591 +1,638
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Data/ScalarTimeSerie.h>
5 5 #include <Data/SpectrogramTimeSerie.h>
6 6 #include <Data/TimeSeriesUtils.h>
7 7 #include <Data/VectorTimeSerie.h>
8 8
9 9 #include <Common/cpp_utils.h>
10 10 #include <Variable/Variable2.h>
11 11 #include <algorithm>
12 12 #include <cmath>
13 13
14 14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
15 15
16 16 namespace
17 17 {
18 18
19 19 class SqpDataContainer : public QCPGraphDataContainer
20 20 {
21 21 public:
22 22 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
23 23 };
24 24
25 25 /**
26 26 * Struct used to create plottables, depending on the type of the data series from which to create
27 27 * them
28 28 * @tparam T the data series' type
29 29 * @remarks Default implementation can't create plottables
30 30 */
31 31 template <typename T, typename Enabled = void>
32 32 struct PlottablesCreator
33 33 {
34 34 static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries)
35 35 {
36 36 return {};
37 37 }
38 38 };
39 39
40 40 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
41 41 {
42 42 PlottablesMap result {};
43 43
44 44 // Creates {nbGraphs} QCPGraph to add to the plot
45 45 for (auto i = 0; i < nbGraphs; ++i)
46 46 {
47 47 auto graph = plot.addGraph();
48 48 result.insert({ i, graph });
49 49 }
50 50
51 51 plot.replot();
52 52
53 53 return result;
54 54 }
55 55
56 56 /**
57 57 * Specialization of PlottablesCreator for scalars
58 58 * @sa ScalarSeries
59 59 */
60 60 template <typename T>
61 61 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
62 62 {
63 63 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
64 64 {
65 65 return createGraphs(plot, 1);
66 66 }
67 67 };
68 68
69 69 /**
70 70 * Specialization of PlottablesCreator for vectors
71 71 * @sa VectorSeries
72 72 */
73 73 template <typename T>
74 74 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
75 75 {
76 76 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
77 77 {
78 78 return createGraphs(plot, 3);
79 79 }
80 80 };
81 81
82 82 /**
83 83 * Specialization of PlottablesCreator for MultiComponentTimeSeries
84 84 * @sa VectorSeries
85 85 */
86 86 template <typename T>
87 87 struct PlottablesCreator<T,
88 88 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
89 89 {
90 90 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
91 91 {
92 92 return createGraphs(plot, dataSeries->size(1));
93 93 }
94 94 };
95 95
96 96 /**
97 97 * Specialization of PlottablesCreator for spectrograms
98 98 * @sa SpectrogramSeries
99 99 */
100 100 template <typename T>
101 101 struct PlottablesCreator<T,
102 102 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
103 103 {
104 104 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
105 105 {
106 106 PlottablesMap result {};
107 107 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
108 108
109 109 plot.replot();
110 110
111 111 return result;
112 112 }
113 113 };
114 114
115 115 /**
116 116 * Struct used to update plottables, depending on the type of the data series from which to update
117 117 * them
118 118 * @tparam T the data series' type
119 119 * @remarks Default implementation can't update plottables
120 120 */
121 121 template <typename T, typename Enabled = void>
122 122 struct PlottablesUpdater
123 123 {
124 124 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
125 125 {
126 126 qCCritical(LOG_VisualizationGraphHelper())
127 127 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
128 128 }
129 129
130 130 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
131 131 {
132 132 qCCritical(LOG_VisualizationGraphHelper())
133 133 << QObject::tr("Can't update plottables: unmanaged data series type");
134 134 }
135 135 };
136 136
137 137 /**
138 138 * Specialization of PlottablesUpdater for scalars and vectors
139 139 * @sa ScalarSeries
140 140 * @sa VectorSeries
141 141 */
142 142 template <typename T>
143 143 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
144 144 {
145 145 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
146 146 {
147 147 auto minValue = 0., maxValue = 0.;
148 148 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
149 149 {
150 150 if (serie->size())
151 151 {
152 152 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
153 153 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
154 154 }
155 155 }
156 156 plot.yAxis->setRange(QCPRange { minValue, maxValue });
157 157 }
158 158
159 159 static void updatePlottables(
160 160 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
161 161 {
162 162
163 163 // For each plottable to update, resets its data
164 164 for (const auto& plottable : plottables)
165 165 {
166 166 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
167 167 {
168 168 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
169 169 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
170 170 {
171 171 std::for_each(
172 172 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
173 173 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
174 174 });
175 175 }
176 176 graph->setData(dataContainer);
177 177 }
178 178 }
179 179
180 180 if (!plottables.empty())
181 181 {
182 182 auto plot = plottables.begin()->second->parentPlot();
183 183
184 184 if (rescaleAxes)
185 185 {
186 186 plot->rescaleAxes();
187 187 }
188 188 }
189 189 }
190 190 };
191 191
192 192
193 193 template <typename T>
194 194 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
195 195 {
196 196 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
197 197 {
198 198 double minValue = 0., maxValue = 0.;
199 199 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
200 200 {
201 201 std::for_each(
202 202 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
203 203 minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z });
204 204 maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z });
205 205 });
206 206 }
207 207
208 208 plot.yAxis->setRange(QCPRange { minValue, maxValue });
209 209 }
210 210
211 211 static void updatePlottables(
212 212 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
213 213 {
214 214
215 215 // For each plottable to update, resets its data
216 216 for (const auto& plottable : plottables)
217 217 {
218 218 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
219 219 {
220 220 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
221 221 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
222 222 {
223 223 switch (plottable.first)
224 224 {
225 225 case 0:
226 226 std::for_each(std::begin(*serie), std::end(*serie),
227 227 [&dataContainer](const auto& value) {
228 228 dataContainer->appendGraphData(
229 229 QCPGraphData(value.t(), value.v().x));
230 230 });
231 231 break;
232 232 case 1:
233 233 std::for_each(std::begin(*serie), std::end(*serie),
234 234 [&dataContainer](const auto& value) {
235 235 dataContainer->appendGraphData(
236 236 QCPGraphData(value.t(), value.v().y));
237 237 });
238 238 break;
239 239 case 2:
240 240 std::for_each(std::begin(*serie), std::end(*serie),
241 241 [&dataContainer](const auto& value) {
242 242 dataContainer->appendGraphData(
243 243 QCPGraphData(value.t(), value.v().z));
244 244 });
245 245 break;
246 246 default:
247 247 break;
248 248 }
249 249 }
250 250 graph->setData(dataContainer);
251 251 }
252 252 }
253 253
254 254 if (!plottables.empty())
255 255 {
256 256 auto plot = plottables.begin()->second->parentPlot();
257 257
258 258 if (rescaleAxes)
259 259 {
260 260 plot->rescaleAxes();
261 261 }
262 262 }
263 263 }
264 264 };
265 265
266 266
267 267 template <typename T>
268 268 struct PlottablesUpdater<T,
269 269 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
270 270 {
271 271 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
272 272 {
273 273 double minValue = 0., maxValue = 0.;
274 274 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
275 275 {
276 276 std::for_each(std::begin(*serie), std::end(*serie), [&minValue, &maxValue](auto& v) {
277 277 minValue = std::min(minValue, std::min_element(v.begin(), v.end())->v());
278 278 maxValue = std::max(maxValue, std::max_element(v.begin(), v.end())->v());
279 279 });
280 280 }
281 281 plot.yAxis->setRange(QCPRange { minValue, maxValue });
282 282 }
283 283
284 284 static void updatePlottables(
285 285 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
286 286 {
287 287 for (const auto& plottable : plottables)
288 288 {
289 289 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
290 290 {
291 291 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
292 292 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
293 293 {
294 294 // TODO
295 295 std::for_each(std::begin(*serie), std::end(*serie),
296 296 [&dataContainer, component = plottable.first](const auto& value) {
297 297 dataContainer->appendGraphData(
298 298 QCPGraphData(value.t(), value[component]));
299 299 });
300 300 }
301 301 graph->setData(dataContainer);
302 302 }
303 303 }
304 304
305 305 if (!plottables.empty())
306 306 {
307 307 auto plot = plottables.begin()->second->parentPlot();
308 308
309 309 if (rescaleAxes)
310 310 {
311 311 plot->rescaleAxes();
312 312 }
313 313 }
314 314 }
315 315 };
316 316
317 317 /*=============================================================*/
318 318 // TODO move this to dedicated srcs
319 319 /*=============================================================*/
320 320 struct ColomapProperties
321 321 {
322 322 int h_size_px;
323 323 int v_size_px;
324 324 double h_resolutuon;
325 325 double v_resolutuon;
326 326 };
327 327
328 328 inline ColomapProperties CMAxisAnalysis(const TimeSeriesUtils::axis_properties& xAxisProperties,
329 329 const TimeSeriesUtils::axis_properties& yAxisProperties)
330 330 {
331 331 int colormap_h_size
332 332 = std::min(32000, static_cast<int>(xAxisProperties.range / xAxisProperties.max_resolution));
333 333 int colormap_v_size = static_cast<int>(yAxisProperties.range / yAxisProperties.max_resolution);
334 334 double colormap_h_resolution = xAxisProperties.range / static_cast<double>(colormap_h_size);
335 335 double colormap_v_resolution = yAxisProperties.range / static_cast<double>(colormap_v_size);
336 336 return ColomapProperties { colormap_h_size, colormap_v_size, colormap_h_resolution,
337 337 colormap_v_resolution };
338 338 }
339 339
340
341 template <bool condition, typename T, typename U>
342 std::enable_if_t<condition, T> constexpr conditional_v(T first, U second)
343 {
344 return first;
345 }
346
347 template <bool condition, typename T, typename U>
348 std::enable_if_t<!condition, U> constexpr conditional_v(T first, U second)
349 {
350 return second;
351 }
352
353 template <bool reversedAxis = true, bool reversedData = true>
340 354 inline std::vector<std::pair<int, int>> build_access_pattern(const std::vector<double>& axis,
341 355 const TimeSeriesUtils::axis_properties& axisProperties,
342 356 const ColomapProperties& colormap_properties)
343 357 {
344 358 std::vector<std::pair<int, int>> access_pattern;
345 for (int index = 0, cel_index = axis.size() - 1; index < colormap_properties.v_size_px; index++)
359 for (int index = 0, axis_index = conditional_v<reversedAxis>(axis.size() - 1, 0),
360 data_index = conditional_v<reversedData>(axis.size() - 1, 0);
361 index < colormap_properties.v_size_px; index++)
346 362 {
347 double current_y = pow(10., (axisProperties.max_resolution * index) + axisProperties.min);
348 if (current_y > axis[cel_index])
349 cel_index--;
350 access_pattern.push_back({ index, cel_index });
363 double current_y = (axisProperties.max_resolution * index) + axisProperties.min;
364 if (current_y > axis[axis_index])
365 {
366 conditional_v<reversedAxis>(
367 [&axis_index]() { axis_index--; }, [&axis_index]() { axis_index++; })();
368 conditional_v<reversedData>(
369 [&data_index]() { data_index--; }, [&data_index]() { data_index++; })();
370 }
371 access_pattern.push_back({ index, data_index });
351 372 }
352 373 return access_pattern;
353 374 }
354 375
376 inline bool is_log(const std::vector<double>& axis)
377 {
378 if (axis.size() > 2)
379 {
380 auto first = axis.front(), midle = axis[axis.size() / 2], last = axis.back();
381 auto error_linear = (midle - (last + first) / 2) / midle;
382 first = log10(first);
383 midle = log10(midle);
384 last = log10(last);
385 auto error_log = (midle - (last + first) / 2) / midle;
386 return error_log < error_linear;
387 }
388 return false;
389 }
390
391 template <typename accessPattern_t, typename colomapT, typename axProp_t, typename cmProp_t>
392 inline void fill_data(SpectrogramTimeSerie* serie, colomapT* colormap,
393 const accessPattern_t& y_access_pattern, const axProp_t& xAxisProperties,
394 const cmProp_t& colormap_properties)
395 {
396 auto line = serie->begin();
397 auto next_line = line + 1;
398 double current_time = xAxisProperties.min;
399 int x_index = 0;
400 auto x_min_resolution
401 = std::fmin(2. * serie->max_sampling, xAxisProperties.max_resolution * 100.);
402 std::vector<double> line_values(serie->size(1));
403 double avg_coef = 0.;
404 while (x_index < colormap_properties.h_size_px)
405 {
406 if (next_line != std::end(*serie) and current_time >= next_line->t())
407 {
408 line = next_line;
409 next_line++;
410 }
411 if ((current_time - xAxisProperties.min)
412 > (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon))
413 {
414 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
415 [&colormap, &line_values, x_index, avg_coef](const auto& acc) {
416 colormap->data()->setCell(
417 x_index, acc.first, line_values[acc.second] / avg_coef);
418 });
419 std::fill(std::begin(line_values), std::end(line_values), 0.);
420 x_index++;
421 avg_coef = 0.;
422 }
423 if (line->t() + x_min_resolution > current_time)
424 {
425 {
426 std::transform(std::begin(*line), std::end(*line), std::cbegin(line_values),
427 std::begin(line_values),
428 [](const auto& input, auto output) { return input.v() + output; });
429 }
430 avg_coef += 1.;
431 }
432 else
433 {
434 for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++)
435 {
436 if (avg_coef > 0.)
437 {
438 std::fill(std::begin(line_values), std::end(line_values), 0);
439 }
440 }
441 }
442 current_time += xAxisProperties.max_resolution * 0.9;
443 }
444 }
445
355 446 /*=============================================================*/
356 447
357 448 /**
358 449 * Specialization of PlottablesUpdater for spectrograms
359 450 * @sa SpectrogramSeries
360 451 */
361 452 template <typename T>
362 453 struct PlottablesUpdater<T,
363 454 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
364 455 {
365 456 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
366 457 {
367 458 auto [minValue, maxValue] = dataSeries.axis_range(1);
368 std::cout << "min=" << minValue << " max=" << maxValue << std::endl;
369 459 plot.yAxis->setRange(QCPRange { minValue, maxValue });
370 460 }
371 461
372 462 static void updatePlottables(
373 463 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
374 464 {
375 465 if (plottables.empty())
376 466 {
377 467 qCDebug(LOG_VisualizationGraphHelper())
378 468 << QObject::tr("Can't update spectrogram: no colormap has been associated");
379 469 return;
380 470 }
381 471
382 472 // Gets the colormap to update (normally there is only one colormap)
383 473 Q_ASSERT(plottables.size() == 1);
384 474 auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
385 475 Q_ASSERT(colormap != nullptr);
386 476 auto plot = colormap->parentPlot();
387 477 auto [minValue, maxValue] = dataSeries.axis_range(1);
388 478 plot->yAxis->setRange(QCPRange { minValue, maxValue });
389 479 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
390 480 {
391 481 if (serie->size(0) > 2)
392 482 {
483 if (serie->y_is_log)
484 colormap->setDataScaleType(QCPAxis::stLogarithmic);
485 else
486 colormap->setDataScaleType(QCPAxis::stLinear);
393 487 const auto& xAxis = serie->axis(0);
394 488 auto yAxis = serie->axis(1); // copy for in place reverse order
489 auto y_is_log = is_log(yAxis);
395 490 std::reverse(std::begin(yAxis), std::end(yAxis));
396 491 auto xAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLinear,
397 492 TimeSeriesUtils::CheckMedian>(xAxis, serie->min_sampling);
398 493 auto yAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLog,
399 494 TimeSeriesUtils::DontCheckMedian>(yAxis);
400 495 auto colormap_properties = CMAxisAnalysis(xAxisProperties, yAxisProperties);
401 496
402 497 colormap->data()->setSize(
403 498 colormap_properties.h_size_px, colormap_properties.v_size_px);
404 499 colormap->data()->setRange(
405 500 QCPRange { xAxisProperties.min, xAxisProperties.max }, { minValue, maxValue });
406 501
407 auto y_access_pattern
408 = build_access_pattern(serie->axis(1), yAxisProperties, colormap_properties);
409
410 auto line = serie->begin();
411 auto next_line = line + 1;
412 double current_time = xAxisProperties.min;
413 int x_index = 0;
414 auto x_min_resolution
415 = std::fmin(2. * serie->max_sampling, xAxisProperties.max_resolution * 100.);
416 std::vector<double> line_values(serie->size(1));
417 double avg_coef = 0.;
418 while (x_index < colormap_properties.h_size_px)
419 {
420 if (next_line != std::end(*serie) and current_time >= next_line->t())
421 {
422 line = next_line;
423 next_line++;
424 }
425 if ((current_time - xAxisProperties.min)
426 > (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon))
427 {
428 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
429 [&colormap, &line_values, x_index, avg_coef](const auto& acc) {
430 colormap->data()->setCell(
431 x_index, acc.first, line_values[acc.second] / avg_coef);
432 });
433 std::fill(std::begin(line_values), std::end(line_values), 0.);
434 x_index++;
435 avg_coef = 0.;
436 }
437 if (line->t() + x_min_resolution > current_time)
438 {
439 {
440 std::transform(std::begin(*line), std::end(*line),
441 std::cbegin(line_values), std::begin(line_values),
442 [](const auto& input, auto output) { return input.v() + output; });
443 }
444 avg_coef += 1.;
445 }
446 else
447 {
448 for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++)
449 {
450 if (avg_coef > 0.)
451 {
452 std::fill(std::begin(line_values), std::end(line_values), 0);
453 }
454 }
455 }
456 current_time += xAxisProperties.max_resolution * 0.9;
457 }
502 auto y_access_pattern = build_access_pattern<false, true>(
503 yAxis, yAxisProperties, colormap_properties);
504 fill_data(serie, colormap, y_access_pattern, xAxisProperties, colormap_properties);
458 505 }
459 506 colormap->rescaleDataRange(true);
460 507 if (rescaleAxes)
461 508 {
462 509 plot->rescaleAxes();
463 510 }
464 511 }
465 512 }
466 513 };
467 514
468 515 /**
469 516 * Helper used to create/update plottables
470 517 */
471 518 struct IPlottablesHelper
472 519 {
473 520 virtual ~IPlottablesHelper() noexcept = default;
474 521 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
475 522 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
476 523 virtual void update(
477 524 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
478 525 };
479 526
480 527 /**
481 528 * Default implementation of IPlottablesHelper, which takes data series to create/update
482 529 * plottables
483 530 * @tparam T the data series' type
484 531 */
485 532 template <typename T>
486 533 struct PlottablesHelper : public IPlottablesHelper
487 534 {
488 535 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
489 536
490 537 PlottablesMap create(QCustomPlot& plot) const override
491 538 {
492 539 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
493 540 }
494 541
495 542 void update(
496 543 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
497 544 {
498 545 if (m_DataSeries)
499 546 {
500 547 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
501 548 }
502 549 else
503 550 {
504 551 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
505 552 "between the type of data series and the "
506 553 "type supposed";
507 554 }
508 555 }
509 556
510 557 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
511 558 {
512 559 if (m_DataSeries)
513 560 {
514 561 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
515 562 }
516 563 else
517 564 {
518 565 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
519 566 "between the type of data series and the "
520 567 "type supposed";
521 568 }
522 569 }
523 570
524 571 std::shared_ptr<T> m_DataSeries;
525 572 };
526 573
527 574 /// Creates IPlottablesHelper according to the type of data series a variable holds
528 575 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
529 576 {
530 577 switch (variable->type())
531 578 {
532 579 case DataSeriesType::SCALAR:
533 580 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
534 581 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
535 582 case DataSeriesType::SPECTROGRAM:
536 583 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
537 584 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
538 585 case DataSeriesType::VECTOR:
539 586 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
540 587 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
541 588 case DataSeriesType::MULTICOMPONENT:
542 589 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
543 590 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
544 591 default:
545 592 // Creates default helper
546 593 break;
547 594 }
548 595
549 596 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
550 597 }
551 598
552 599 } // namespace
553 600
554 601 PlottablesMap VisualizationGraphHelper::create(
555 602 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
556 603 {
557 604 if (variable)
558 605 {
559 606 auto helper = createHelper(variable);
560 607 auto plottables = helper->create(plot);
561 608 return plottables;
562 609 }
563 610 else
564 611 {
565 612 qCDebug(LOG_VisualizationGraphHelper())
566 613 << QObject::tr("Can't create graph plottables : the variable is null");
567 614 return PlottablesMap {};
568 615 }
569 616 }
570 617
571 618 void VisualizationGraphHelper::setYAxisRange(
572 619 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
573 620 {
574 621 if (variable)
575 622 {
576 623 auto helper = createHelper(variable);
577 624 helper->setYAxisRange(variable->range(), plot);
578 625 }
579 626 else
580 627 {
581 628 qCDebug(LOG_VisualizationGraphHelper())
582 629 << QObject::tr("Can't set y-axis range of plot: the variable is null");
583 630 }
584 631 }
585 632
586 633 void VisualizationGraphHelper::updateData(
587 634 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
588 635 {
589 636 auto helper = createHelper(variable);
590 637 helper->update(plottables, dateTime);
591 638 }
@@ -1,100 +1,100
1 1 import traceback
2 2 import os
3 3 from datetime import datetime, timedelta, timezone
4 4 import PythonProviders
5 5 import pysciqlopcore
6 6 import numpy as np
7 7 import requests
8 8 import copy
9 9 from spwc.amda import AMDA
10 10
11 11 amda = AMDA()
12 12
13 13 def amda_make_scalar(var=None):
14 14 if var is None:
15 15 return pysciqlopcore.ScalarTimeSerie(1)
16 16 else:
17 17 return pysciqlopcore.ScalarTimeSerie(var.time,var.data)
18 18
19 19 def amda_make_vector(var=None):
20 20 if var is None:
21 21 return pysciqlopcore.VectorTimeSerie(1)
22 22 else:
23 23 return pysciqlopcore.VectorTimeSerie(var.time,var.data)
24 24
25 25 def amda_make_multi_comp(var=None):
26 26 if var is None:
27 27 return pysciqlopcore.MultiComponentTimeSerie((0,2))
28 28 else:
29 29 return pysciqlopcore.MultiComponentTimeSerie(var.time,var.data)
30 30
31 31 def amda_make_spectro(var=None):
32 32 if var is None:
33 33 return pysciqlopcore.SpectrogramTimeSerie((0,2))
34 34 else:
35 35 min_sampling = float(var.meta.get("DATASET_MIN_SAMPLING","nan"))
36 36 max_sampling = float(var.meta.get("DATASET_MAX_SAMPLING","nan"))
37 37 if "PARAMETER_TABLE_MIN_VALUES[1]" in var.meta:
38 38 min_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MIN_VALUES[1]"].split(',') ])
39 39 max_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MAX_VALUES[1]"].split(',') ])
40 40 y = (max_v + min_v)/2.
41 41 elif "PARAMETER_TABLE_MIN_VALUES[0]" in var.meta:
42 42 min_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MIN_VALUES[0]"].split(',') ])
43 43 max_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MAX_VALUES[0]"].split(',') ])
44 44 y = (max_v + min_v)/2.
45 45 else:
46 46 y = np.logspace(1,3,var.data.shape[1])[::-1]
47 return pysciqlopcore.SpectrogramTimeSerie(var.time,y,var.data,min_sampling,max_sampling)
47 return pysciqlopcore.SpectrogramTimeSerie(var.time,y,var.data,min_sampling,max_sampling,True)
48 48
49 49 def amda_get_sample(metadata,start,stop):
50 50 ts_type = amda_make_scalar
51 51 try:
52 52 param_id = None
53 53 for key,value in metadata:
54 54 if key == 'xml:id':
55 55 param_id = value
56 56 elif key == 'type':
57 57 if value == 'vector':
58 58 ts_type = amda_make_vector
59 59 elif value == 'multicomponent':
60 60 ts_type = amda_make_multi_comp
61 61 elif value == 'spectrogram':
62 62 ts_type = amda_make_spectro
63 63 tstart=datetime.fromtimestamp(start, tz=timezone.utc)
64 64 tend=datetime.fromtimestamp(stop, tz=timezone.utc)
65 65 var = amda.get_parameter(start_time=tstart, stop_time=tend, parameter_id=param_id, method="REST")
66 66 return ts_type(var)
67 67 except Exception as e:
68 68 print(traceback.format_exc())
69 69 print("Error in amda.py ",str(e))
70 70 return ts_type()
71 71
72 72
73 73 if len(amda.component) is 0:
74 74 amda.update_inventory()
75 75 parameters = copy.deepcopy(amda.parameter)
76 76 for name,component in amda.component.items():
77 77 if 'components' in parameters[component['parameter']]:
78 78 parameters[component['parameter']]['components'].append(component)
79 79 else:
80 80 parameters[component['parameter']]['components']=[component]
81 81
82 82 products = []
83 83 for key,parameter in parameters.items():
84 84 path = f"/AMDA/{parameter['mission']}/{parameter.get('observatory','')}/{parameter['instrument']}/{parameter['dataset']}/{parameter['name']}"
85 85 components = [component['name'] for component in parameter.get('components',[])]
86 86 metadata = [ (key,item) for key,item in parameter.items() if key is not 'components' ]
87 87 n_components = parameter.get('size',0)
88 88 if n_components == '3':
89 89 metadata.append(("type","vector"))
90 90 elif parameter.get('display_type','')=="spectrogram":
91 91 metadata.append(("type","spectrogram"))
92 92 elif n_components !=0:
93 93 metadata.append(("type","multicomponent"))
94 94 else:
95 95 metadata.append(("type","scalar"))
96 96 products.append( (path, components, metadata))
97 97
98 98 PythonProviders.register_product(products, amda_get_sample)
99 99
100 100
@@ -1,96 +1,96
1 1 import traceback
2 2 import pandas as pds
3 3 import PythonProviders
4 4 import pysciqlopcore
5 5 import numpy as np
6 6 import math
7 7 from spwc.cache import _cache
8 8 from spwc.common.datetime_range import DateTimeRange
9 9 from functools import partial
10 10 from datetime import datetime, timedelta, timezone
11 11 from spwc.common.variable import SpwcVariable
12 12
13 13 def make_scalar(x):
14 14 y = np.cos(x/10.)
15 15 return SpwcVariable(time=x, data=y)
16 16
17 17 def make_vector(x):
18 18 v=np.ones((len(x),3))
19 19 for i in range(3):
20 20 v.transpose()[:][i] = np.cos(x/10. + float(i)) + (100. * np.cos(x/10000. + float(i)))
21 21 return SpwcVariable(time=x, data=v)
22 22
23 23
24 24 def make_multicomponent(x):
25 25 v=np.ones((len(x),4))
26 26 for i in range(4):
27 27 v.transpose()[:][i] = float(i+1) * np.cos(x/10. + float(i))
28 28 return SpwcVariable(time=x, data=v)
29 29
30 30 def make_spectrogram(x):
31 31 v=np.ones((len(x),32))
32 32 for i in range(32):
33 33 v.transpose()[:][i] = 100.*(2.+ float(i+1) * np.cos(x/1024. + float(i)))
34 34 return SpwcVariable(time=x, data=v)
35 35
36 36
37 37 def _get_data(p_type, start, stop):
38 38 if type(start) is datetime:
39 39 start = start.timestamp()
40 40 stop = stop.timestamp()
41 41 x = np.arange(math.ceil(start), math.floor(stop))
42 42 if p_type == 'scalar':
43 43 return make_scalar(x)
44 44 if p_type == 'vector':
45 45 return make_vector(x)
46 46 if p_type == 'multicomponent':
47 47 return make_multicomponent(x)
48 48 if p_type == 'spectrogram':
49 49 return make_spectrogram(np.arange(math.ceil(start), math.floor(stop),15.))
50 50 return None
51 51
52 52 def get_data(metadata,start,stop):
53 53 ts_type = pysciqlopcore.ScalarTimeSerie
54 54 default_ctor_args = 1
55 55 use_cache = False
56 56 p_type = 'scalar'
57 57 try:
58 58 for key,value in metadata:
59 59 if key == 'type':
60 60 p_type = value
61 61 if value == 'vector':
62 62 ts_type = pysciqlopcore.VectorTimeSerie
63 63 elif value == 'multicomponent':
64 64 ts_type = pysciqlopcore.MultiComponentTimeSerie
65 65 default_ctor_args = (0,2)
66 66 elif value == 'spectrogram':
67 ts_type = lambda t,values: pysciqlopcore.SpectrogramTimeSerie(t,np.logspace(1,3,32)[::-1],values,np.nan,np.nan)
67 ts_type = lambda t,values: pysciqlopcore.SpectrogramTimeSerie(t,np.logspace(1,3,32)[::-1],values,np.nan,np.nan,True)
68 68 default_ctor_args = (0,2)
69 69 if key == 'cache' and value == 'true':
70 70 use_cache = True
71 71 if use_cache:
72 72 cache_product = f"tests/{p_type}"
73 73 var = _cache.get_data(cache_product, DateTimeRange(datetime.fromtimestamp(start, tz=timezone.utc), datetime.fromtimestamp(stop, tz=timezone.utc)),
74 74 partial(_get_data, p_type),
75 75 fragment_hours=24)
76 76 else:
77 77 print("No Cache")
78 78 var = _get_data(p_type, start, stop)
79 79 return ts_type(var.time,var.data)
80 80 except Exception as e:
81 81 print(traceback.format_exc())
82 82 print("Error in test.py ",str(e))
83 83 return ts_type(default_ctor_args)
84 84
85 85 products = [
86 86 ("/tests/without_cache/scalar",[],[("type","scalar")]),
87 87 ("/tests/without_cache/vector",[],[("type","vector")]),
88 88 ("/tests/without_cache/multicomponent",[],[("type","multicomponent"),('size','4')]),
89 89 ("/tests/without_cache/spectrogram",[],[("type","spectrogram"),('size','32')]),
90 90 ("/tests/with_cache/scalar",[],[("type","scalar"), ("cache","true")]),
91 91 ("/tests/with_cache/vector",[],[("type","vector"), ("cache","true")]),
92 92 ("/tests/with_cache/multicomponent",[],[("type","multicomponent"),('size','4'), ("cache","true")])
93 93 ]
94 94
95 95
96 96 PythonProviders.register_product(products ,get_data)
General Comments 0
You need to be logged in to leave comments. Login now