##// END OF EJS Templates
More fixes on spectrograms...
jeandet -
r1471:d48510a1cc4a
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit 1e3f92d40b0ec24443e067aaa9e7bca385ec27b2
1 Subproject commit 70d6748a06620f865b683c86c89cc27e1cb0d4b9
@@ -1,603 +1,591
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 340 inline std::vector<std::pair<int, int>> build_access_pattern(const std::vector<double>& axis,
341 341 const TimeSeriesUtils::axis_properties& axisProperties,
342 342 const ColomapProperties& colormap_properties)
343 343 {
344 344 std::vector<std::pair<int, int>> access_pattern;
345 for (int index = 0, cel_index = 0; index < colormap_properties.v_size_px; index++)
345 for (int index = 0, cel_index = axis.size() - 1; index < colormap_properties.v_size_px; index++)
346 346 {
347 347 double current_y = pow(10., (axisProperties.max_resolution * index) + axisProperties.min);
348 348 if (current_y > axis[cel_index])
349 cel_index++;
350 access_pattern.push_back({ index, axis.size() - 1 - cel_index });
349 cel_index--;
350 access_pattern.push_back({ index, cel_index });
351 351 }
352 352 return access_pattern;
353 353 }
354 354
355 355 /*=============================================================*/
356 356
357 357 /**
358 358 * Specialization of PlottablesUpdater for spectrograms
359 359 * @sa SpectrogramSeries
360 360 */
361 361 template <typename T>
362 362 struct PlottablesUpdater<T,
363 363 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
364 364 {
365 365 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
366 366 {
367 367 auto [minValue, maxValue] = dataSeries.axis_range(1);
368 368 std::cout << "min=" << minValue << " max=" << maxValue << std::endl;
369 369 plot.yAxis->setRange(QCPRange { minValue, maxValue });
370 370 }
371 371
372 372 static void updatePlottables(
373 373 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
374 374 {
375 375 if (plottables.empty())
376 376 {
377 377 qCDebug(LOG_VisualizationGraphHelper())
378 378 << QObject::tr("Can't update spectrogram: no colormap has been associated");
379 379 return;
380 380 }
381 381
382 382 // Gets the colormap to update (normally there is only one colormap)
383 383 Q_ASSERT(plottables.size() == 1);
384 384 auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
385 385 Q_ASSERT(colormap != nullptr);
386 386 auto plot = colormap->parentPlot();
387 387 auto [minValue, maxValue] = dataSeries.axis_range(1);
388 388 plot->yAxis->setRange(QCPRange { minValue, maxValue });
389 389 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
390 390 {
391 391 if (serie->size(0) > 2)
392 392 {
393 393 const auto& xAxis = serie->axis(0);
394 394 auto yAxis = serie->axis(1); // copy for in place reverse order
395 395 std::reverse(std::begin(yAxis), std::end(yAxis));
396 396 auto xAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLinear,
397 397 TimeSeriesUtils::CheckMedian>(xAxis, serie->min_sampling);
398 398 auto yAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLog,
399 399 TimeSeriesUtils::DontCheckMedian>(yAxis);
400 400 auto colormap_properties = CMAxisAnalysis(xAxisProperties, yAxisProperties);
401 401
402 402 colormap->data()->setSize(
403 403 colormap_properties.h_size_px, colormap_properties.v_size_px);
404 404 colormap->data()->setRange(
405 405 QCPRange { xAxisProperties.min, xAxisProperties.max }, { minValue, maxValue });
406 406
407 407 auto y_access_pattern
408 = build_access_pattern(yAxis, yAxisProperties, colormap_properties);
408 = build_access_pattern(serie->axis(1), yAxisProperties, colormap_properties);
409 409
410 410 auto line = serie->begin();
411 411 auto next_line = line + 1;
412 412 double current_time = xAxisProperties.min;
413 413 int x_index = 0;
414 414 auto x_min_resolution
415 415 = std::fmin(2. * serie->max_sampling, xAxisProperties.max_resolution * 100.);
416 416 std::vector<double> line_values(serie->size(1));
417 417 double avg_coef = 0.;
418 bool has_nan = false;
419 418 while (x_index < colormap_properties.h_size_px)
420 419 {
421 420 if (next_line != std::end(*serie) and current_time >= next_line->t())
422 421 {
423 422 line = next_line;
424 423 next_line++;
425 424 }
426 425 if ((current_time - xAxisProperties.min)
427 426 > (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon))
428 427 {
429 428 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
430 429 [&colormap, &line_values, x_index, avg_coef](const auto& acc) {
431 430 colormap->data()->setCell(
432 431 x_index, acc.first, line_values[acc.second] / avg_coef);
433 432 });
434 433 std::fill(std::begin(line_values), std::end(line_values), 0.);
435 434 x_index++;
436 435 avg_coef = 0.;
437 has_nan = false;
438 436 }
439 437 if (line->t() + x_min_resolution > current_time)
440 438 {
441 if (has_nan)
442 {
443 std::transform(std::begin(*line), std::end(*line),
444 std::begin(line_values),
445 [](const auto& input) { return input.v(); });
446 has_nan = false;
447 }
448 else
449 439 {
450 440 std::transform(std::begin(*line), std::end(*line),
451 441 std::cbegin(line_values), std::begin(line_values),
452 442 [](const auto& input, auto output) { return input.v() + output; });
453 443 }
454 444 avg_coef += 1.;
455 445 }
456 446 else
457 447 {
458 448 for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++)
459 449 {
460 450 if (avg_coef > 0.)
461 451 {
462 has_nan = true;
463 std::fill(
464 std::begin(line_values), std::end(line_values), std::nan(""));
452 std::fill(std::begin(line_values), std::end(line_values), 0);
465 453 }
466 454 }
467 455 }
468 current_time += xAxisProperties.max_resolution;
456 current_time += xAxisProperties.max_resolution * 0.9;
469 457 }
470 458 }
471 459 colormap->rescaleDataRange(true);
472 460 if (rescaleAxes)
473 461 {
474 462 plot->rescaleAxes();
475 463 }
476 464 }
477 465 }
478 466 };
479 467
480 468 /**
481 469 * Helper used to create/update plottables
482 470 */
483 471 struct IPlottablesHelper
484 472 {
485 473 virtual ~IPlottablesHelper() noexcept = default;
486 474 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
487 475 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
488 476 virtual void update(
489 477 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
490 478 };
491 479
492 480 /**
493 481 * Default implementation of IPlottablesHelper, which takes data series to create/update
494 482 * plottables
495 483 * @tparam T the data series' type
496 484 */
497 485 template <typename T>
498 486 struct PlottablesHelper : public IPlottablesHelper
499 487 {
500 488 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
501 489
502 490 PlottablesMap create(QCustomPlot& plot) const override
503 491 {
504 492 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
505 493 }
506 494
507 495 void update(
508 496 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
509 497 {
510 498 if (m_DataSeries)
511 499 {
512 500 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
513 501 }
514 502 else
515 503 {
516 504 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
517 505 "between the type of data series and the "
518 506 "type supposed";
519 507 }
520 508 }
521 509
522 510 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
523 511 {
524 512 if (m_DataSeries)
525 513 {
526 514 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
527 515 }
528 516 else
529 517 {
530 518 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
531 519 "between the type of data series and the "
532 520 "type supposed";
533 521 }
534 522 }
535 523
536 524 std::shared_ptr<T> m_DataSeries;
537 525 };
538 526
539 527 /// Creates IPlottablesHelper according to the type of data series a variable holds
540 528 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
541 529 {
542 530 switch (variable->type())
543 531 {
544 532 case DataSeriesType::SCALAR:
545 533 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
546 534 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
547 535 case DataSeriesType::SPECTROGRAM:
548 536 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
549 537 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
550 538 case DataSeriesType::VECTOR:
551 539 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
552 540 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
553 541 case DataSeriesType::MULTICOMPONENT:
554 542 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
555 543 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
556 544 default:
557 545 // Creates default helper
558 546 break;
559 547 }
560 548
561 549 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
562 550 }
563 551
564 552 } // namespace
565 553
566 554 PlottablesMap VisualizationGraphHelper::create(
567 555 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
568 556 {
569 557 if (variable)
570 558 {
571 559 auto helper = createHelper(variable);
572 560 auto plottables = helper->create(plot);
573 561 return plottables;
574 562 }
575 563 else
576 564 {
577 565 qCDebug(LOG_VisualizationGraphHelper())
578 566 << QObject::tr("Can't create graph plottables : the variable is null");
579 567 return PlottablesMap {};
580 568 }
581 569 }
582 570
583 571 void VisualizationGraphHelper::setYAxisRange(
584 572 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
585 573 {
586 574 if (variable)
587 575 {
588 576 auto helper = createHelper(variable);
589 577 helper->setYAxisRange(variable->range(), plot);
590 578 }
591 579 else
592 580 {
593 581 qCDebug(LOG_VisualizationGraphHelper())
594 582 << QObject::tr("Can't set y-axis range of plot: the variable is null");
595 583 }
596 584 }
597 585
598 586 void VisualizationGraphHelper::updateData(
599 587 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
600 588 {
601 589 auto helper = createHelper(variable);
602 590 helper->update(plottables, dateTime);
603 591 }
General Comments 0
You need to be logged in to leave comments. Login now