##// END OF EJS Templates
Spectrogram segfault should be fixed now, added ability to provide Spectrogram min sampling time to avoid computation when possible...
jeandet -
r1467:da44adcd99e4
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit 483146a07a5ffeec8f0a2d61459e94d95e851572
1 Subproject commit 1e3f92d40b0ec24443e067aaa9e7bca385ec27b2
@@ -1,158 +1,158
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6
7 7 #include <DragAndDrop/DragDropGuiController.h>
8 8 #include <SqpApplication.h>
9 9 #include <Time/TimeController.h>
10 10
11 11 #include <QDrag>
12 12 #include <QDragEnterEvent>
13 13 #include <QDropEvent>
14 14 #include <QMimeData>
15 15 #include <QStyle>
16 16
17 17
18 18 struct TimeWidget::TimeWidgetPrivate
19 19 {
20 20
21 21 explicit TimeWidgetPrivate() {}
22 22
23 23 QPoint m_DragStartPosition;
24 24 };
25 25
26 26 TimeWidget::TimeWidget(QWidget* parent)
27 27 : QWidget { parent }
28 28 , ui { new Ui::TimeWidget }
29 29 , impl { spimpl::make_unique_impl<TimeWidgetPrivate>() }
30 30 {
31 31 ui->setupUi(this);
32 32
33 33 // Connection
34 34 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
35 35 &TimeWidget::onTimeUpdateRequested);
36 36
37 37 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
38 38 &TimeWidget::onTimeUpdateRequested);
39 39
40 40 // Initialisation
41 41 auto endDateTime = QDateTime::currentDateTimeUtc();
42 42 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
43 43
44 44 ui->startDateTimeEdit->setDateTime(startDateTime);
45 45 ui->endDateTimeEdit->setDateTime(endDateTime);
46 46
47 47 auto dateTime = DateTimeRange { DateUtils::secondsSinceEpoch(startDateTime),
48 48 DateUtils::secondsSinceEpoch(endDateTime) };
49 49
50 50 sqpApp->timeController().setDateTimeRange(dateTime);
51 51 }
52 52
53 53
54 54 TimeWidget::~TimeWidget()
55 55 {
56 56 delete ui;
57 57 }
58 58
59 59 void TimeWidget::setTimeRange(DateTimeRange time)
60 60 {
61 61 auto startDateTime = DateUtils::dateTime(time.m_TStart);
62 62 auto endDateTime = DateUtils::dateTime(time.m_TEnd);
63 63
64 64 ui->startDateTimeEdit->setDateTime(startDateTime);
65 65 ui->endDateTimeEdit->setDateTime(endDateTime);
66 66 }
67 67
68 68 DateTimeRange TimeWidget::timeRange() const
69 69 {
70 70 return DateTimeRange { DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
71 71 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime()) };
72 72 }
73 73
74 74 void TimeWidget::onTimeUpdateRequested()
75 75 {
76 76 auto dateTime = timeRange();
77 emit timeUpdated(std::move(dateTime));
77 emit timeUpdated(dateTime);
78 78 sqpApp->timeController().setDateTimeRange(dateTime);
79 79 }
80 80
81 81 void TimeWidget::dragEnterEvent(QDragEnterEvent* event)
82 82 {
83 83 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE))
84 84 {
85 85 event->acceptProposedAction();
86 86 setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}");
87 87 }
88 88 else
89 89 {
90 90 event->ignore();
91 91 }
92 92 }
93 93
94 94 void TimeWidget::dragLeaveEvent(QDragLeaveEvent* event)
95 95 {
96 96 setStyleSheet(QString {});
97 97 }
98 98
99 99 void TimeWidget::dropEvent(QDropEvent* event)
100 100 {
101 101 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE))
102 102 {
103 103 auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE);
104 104 auto timeRange = TimeController::timeRangeForMimeData(mimeData);
105 105
106 106 setTimeRange(timeRange);
107 107 }
108 108 else
109 109 {
110 110 event->ignore();
111 111 }
112 112
113 113 setStyleSheet(QString {});
114 114 }
115 115
116 116
117 117 void TimeWidget::mousePressEvent(QMouseEvent* event)
118 118 {
119 119 if (event->button() == Qt::LeftButton)
120 120 {
121 121 impl->m_DragStartPosition = event->pos();
122 122 }
123 123
124 124 QWidget::mousePressEvent(event);
125 125 }
126 126
127 127 void TimeWidget::mouseMoveEvent(QMouseEvent* event)
128 128 {
129 129 if (!(event->buttons() & Qt::LeftButton))
130 130 {
131 131 return;
132 132 }
133 133
134 134 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
135 135 < QApplication::startDragDistance())
136 136 {
137 137 return;
138 138 }
139 139
140 140 // Note: The management of the drag object is done by Qt
141 141 auto drag = new QDrag { this };
142 142
143 143 auto mimeData = new QMimeData;
144 144 auto timeData = TimeController::mimeDataForTimeRange(timeRange());
145 145 mimeData->setData(MIME_TYPE_TIME_RANGE, timeData);
146 146
147 147 drag->setMimeData(mimeData);
148 148
149 149 auto pixmap = QPixmap { ":/icones/time.png" };
150 150 drag->setPixmap(pixmap.scaledToWidth(22));
151 151
152 152 sqpApp->dragDropGuiController().resetDragAndDrop();
153 153
154 154 // Note: The exec() is blocking on windows but not on linux and macOS
155 155 drag->exec(Qt::MoveAction | Qt::CopyAction);
156 156
157 157 QWidget::mouseMoveEvent(event);
158 158 }
@@ -1,545 +1,603
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 #include <cmath>
12 13
13 14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
14 15
15 16 namespace
16 17 {
17 18
18 19 class SqpDataContainer : public QCPGraphDataContainer
19 20 {
20 21 public:
21 22 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
22 23 };
23 24
24 25 /**
25 26 * Struct used to create plottables, depending on the type of the data series from which to create
26 27 * them
27 28 * @tparam T the data series' type
28 29 * @remarks Default implementation can't create plottables
29 30 */
30 31 template <typename T, typename Enabled = void>
31 32 struct PlottablesCreator
32 33 {
33 34 static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries)
34 35 {
35 36 return {};
36 37 }
37 38 };
38 39
39 40 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
40 41 {
41 42 PlottablesMap result {};
42 43
43 44 // Creates {nbGraphs} QCPGraph to add to the plot
44 45 for (auto i = 0; i < nbGraphs; ++i)
45 46 {
46 47 auto graph = plot.addGraph();
47 48 result.insert({ i, graph });
48 49 }
49 50
50 51 plot.replot();
51 52
52 53 return result;
53 54 }
54 55
55 56 /**
56 57 * Specialization of PlottablesCreator for scalars
57 58 * @sa ScalarSeries
58 59 */
59 60 template <typename T>
60 61 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
61 62 {
62 63 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
63 64 {
64 65 return createGraphs(plot, 1);
65 66 }
66 67 };
67 68
68 69 /**
69 70 * Specialization of PlottablesCreator for vectors
70 71 * @sa VectorSeries
71 72 */
72 73 template <typename T>
73 74 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
74 75 {
75 76 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
76 77 {
77 78 return createGraphs(plot, 3);
78 79 }
79 80 };
80 81
81 82 /**
82 83 * Specialization of PlottablesCreator for MultiComponentTimeSeries
83 84 * @sa VectorSeries
84 85 */
85 86 template <typename T>
86 87 struct PlottablesCreator<T,
87 88 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
88 89 {
89 90 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
90 91 {
91 92 return createGraphs(plot, dataSeries->size(1));
92 93 }
93 94 };
94 95
95 96 /**
96 97 * Specialization of PlottablesCreator for spectrograms
97 98 * @sa SpectrogramSeries
98 99 */
99 100 template <typename T>
100 101 struct PlottablesCreator<T,
101 102 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
102 103 {
103 104 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
104 105 {
105 106 PlottablesMap result {};
106 107 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
107 108
108 109 plot.replot();
109 110
110 111 return result;
111 112 }
112 113 };
113 114
114 115 /**
115 116 * Struct used to update plottables, depending on the type of the data series from which to update
116 117 * them
117 118 * @tparam T the data series' type
118 119 * @remarks Default implementation can't update plottables
119 120 */
120 121 template <typename T, typename Enabled = void>
121 122 struct PlottablesUpdater
122 123 {
123 124 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
124 125 {
125 126 qCCritical(LOG_VisualizationGraphHelper())
126 127 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
127 128 }
128 129
129 130 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
130 131 {
131 132 qCCritical(LOG_VisualizationGraphHelper())
132 133 << QObject::tr("Can't update plottables: unmanaged data series type");
133 134 }
134 135 };
135 136
136 137 /**
137 138 * Specialization of PlottablesUpdater for scalars and vectors
138 139 * @sa ScalarSeries
139 140 * @sa VectorSeries
140 141 */
141 142 template <typename T>
142 143 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
143 144 {
144 145 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
145 146 {
146 147 auto minValue = 0., maxValue = 0.;
147 148 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
148 149 {
149 150 if (serie->size())
150 151 {
151 152 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
152 153 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
153 154 }
154 155 }
155 156 plot.yAxis->setRange(QCPRange { minValue, maxValue });
156 157 }
157 158
158 159 static void updatePlottables(
159 160 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
160 161 {
161 162
162 163 // For each plottable to update, resets its data
163 164 for (const auto& plottable : plottables)
164 165 {
165 166 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
166 167 {
167 168 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
168 169 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
169 170 {
170 171 std::for_each(
171 172 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
172 173 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
173 174 });
174 175 }
175 176 graph->setData(dataContainer);
176 177 }
177 178 }
178 179
179 180 if (!plottables.empty())
180 181 {
181 182 auto plot = plottables.begin()->second->parentPlot();
182 183
183 184 if (rescaleAxes)
184 185 {
185 186 plot->rescaleAxes();
186 187 }
187 188 }
188 189 }
189 190 };
190 191
191 192
192 193 template <typename T>
193 194 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
194 195 {
195 196 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
196 197 {
197 198 double minValue = 0., maxValue = 0.;
198 199 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
199 200 {
200 201 std::for_each(
201 202 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
202 203 minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z });
203 204 maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z });
204 205 });
205 206 }
206 207
207 208 plot.yAxis->setRange(QCPRange { minValue, maxValue });
208 209 }
209 210
210 211 static void updatePlottables(
211 212 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
212 213 {
213 214
214 215 // For each plottable to update, resets its data
215 216 for (const auto& plottable : plottables)
216 217 {
217 218 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
218 219 {
219 220 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
220 221 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
221 222 {
222 223 switch (plottable.first)
223 224 {
224 225 case 0:
225 226 std::for_each(std::begin(*serie), std::end(*serie),
226 227 [&dataContainer](const auto& value) {
227 228 dataContainer->appendGraphData(
228 229 QCPGraphData(value.t(), value.v().x));
229 230 });
230 231 break;
231 232 case 1:
232 233 std::for_each(std::begin(*serie), std::end(*serie),
233 234 [&dataContainer](const auto& value) {
234 235 dataContainer->appendGraphData(
235 236 QCPGraphData(value.t(), value.v().y));
236 237 });
237 238 break;
238 239 case 2:
239 240 std::for_each(std::begin(*serie), std::end(*serie),
240 241 [&dataContainer](const auto& value) {
241 242 dataContainer->appendGraphData(
242 243 QCPGraphData(value.t(), value.v().z));
243 244 });
244 245 break;
245 246 default:
246 247 break;
247 248 }
248 249 }
249 250 graph->setData(dataContainer);
250 251 }
251 252 }
252 253
253 254 if (!plottables.empty())
254 255 {
255 256 auto plot = plottables.begin()->second->parentPlot();
256 257
257 258 if (rescaleAxes)
258 259 {
259 260 plot->rescaleAxes();
260 261 }
261 262 }
262 263 }
263 264 };
264 265
265 266
266 267 template <typename T>
267 268 struct PlottablesUpdater<T,
268 269 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
269 270 {
270 271 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
271 272 {
272 273 double minValue = 0., maxValue = 0.;
273 274 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
274 275 {
275 std::for_each(
276 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
277 minValue = std::min(minValue, std::min_element(v.begin(), v.end())->v());
278 maxValue = std::max(maxValue, std::max_element(v.begin(), v.end())->v());
279 });
276 std::for_each(std::begin(*serie), std::end(*serie), [&minValue, &maxValue](auto& v) {
277 minValue = std::min(minValue, std::min_element(v.begin(), v.end())->v());
278 maxValue = std::max(maxValue, std::max_element(v.begin(), v.end())->v());
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 /*=============================================================*/
318 // TODO move this to dedicated srcs
319 /*=============================================================*/
320 struct ColomapProperties
321 {
322 int h_size_px;
323 int v_size_px;
324 double h_resolutuon;
325 double v_resolutuon;
326 };
327
328 inline ColomapProperties CMAxisAnalysis(const TimeSeriesUtils::axis_properties& xAxisProperties,
329 const TimeSeriesUtils::axis_properties& yAxisProperties)
330 {
331 int colormap_h_size
332 = std::min(32000, static_cast<int>(xAxisProperties.range / xAxisProperties.max_resolution));
333 int colormap_v_size = static_cast<int>(yAxisProperties.range / yAxisProperties.max_resolution);
334 double colormap_h_resolution = xAxisProperties.range / static_cast<double>(colormap_h_size);
335 double colormap_v_resolution = yAxisProperties.range / static_cast<double>(colormap_v_size);
336 return ColomapProperties { colormap_h_size, colormap_v_size, colormap_h_resolution,
337 colormap_v_resolution };
338 }
339
340 inline std::vector<std::pair<int, int>> build_access_pattern(const std::vector<double>& axis,
341 const TimeSeriesUtils::axis_properties& axisProperties,
342 const ColomapProperties& colormap_properties)
343 {
344 std::vector<std::pair<int, int>> access_pattern;
345 for (int index = 0, cel_index = 0; index < colormap_properties.v_size_px; index++)
346 {
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, axis.size() - 1 - cel_index });
351 }
352 return access_pattern;
353 }
354
355 /*=============================================================*/
356
317 357 /**
318 358 * Specialization of PlottablesUpdater for spectrograms
319 359 * @sa SpectrogramSeries
320 360 */
321 361 template <typename T>
322 362 struct PlottablesUpdater<T,
323 363 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
324 364 {
325 365 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
326 366 {
327 367 auto [minValue, maxValue] = dataSeries.axis_range(1);
328 368 std::cout << "min=" << minValue << " max=" << maxValue << std::endl;
329 369 plot.yAxis->setRange(QCPRange { minValue, maxValue });
330 370 }
331 371
332 372 static void updatePlottables(
333 373 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
334 374 {
335 375 if (plottables.empty())
336 376 {
337 377 qCDebug(LOG_VisualizationGraphHelper())
338 378 << QObject::tr("Can't update spectrogram: no colormap has been associated");
339 379 return;
340 380 }
341 381
342 382 // Gets the colormap to update (normally there is only one colormap)
343 383 Q_ASSERT(plottables.size() == 1);
344 384 auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
345 385 Q_ASSERT(colormap != nullptr);
346 386 auto plot = colormap->parentPlot();
347 387 auto [minValue, maxValue] = dataSeries.axis_range(1);
348 388 plot->yAxis->setRange(QCPRange { minValue, maxValue });
349 389 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
350 390 {
351 391 if (serie->size(0) > 2)
352 392 {
353 393 const auto& xAxis = serie->axis(0);
354 394 auto yAxis = serie->axis(1); // copy for in place reverse order
355 395 std::reverse(std::begin(yAxis), std::end(yAxis));
356 396 auto xAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLinear,
357 TimeSeriesUtils::CheckMedian>(xAxis);
397 TimeSeriesUtils::CheckMedian>(xAxis, serie->min_sampling);
358 398 auto yAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLog,
359 399 TimeSeriesUtils::DontCheckMedian>(yAxis);
400 auto colormap_properties = CMAxisAnalysis(xAxisProperties, yAxisProperties);
360 401
361 int colormap_h_size = std::min(32000,
362 static_cast<int>(xAxisProperties.range / xAxisProperties.max_resolution));
363 auto colormap_v_size
364 = static_cast<int>(yAxisProperties.range / yAxisProperties.max_resolution);
365
366 colormap->data()->setSize(colormap_h_size, colormap_v_size);
402 colormap->data()->setSize(
403 colormap_properties.h_size_px, colormap_properties.v_size_px);
367 404 colormap->data()->setRange(
368 QCPRange { serie->begin()->t(), (serie->end() - 1)->t() },
369 { minValue, maxValue });
405 QCPRange { xAxisProperties.min, xAxisProperties.max }, { minValue, maxValue });
370 406
371 std::vector<std::pair<int, int>> y_access_pattern;
372 for (int y_index = 0, cel_index = 0; y_index < colormap_v_size; y_index++)
373 {
374 double current_y = pow(
375 10., (yAxisProperties.max_resolution * y_index) + std::log10(minValue));
376 if (current_y > yAxis[cel_index])
377 cel_index++;
378 y_access_pattern.push_back({ y_index, yAxis.size() - 1 - cel_index });
379 }
407 auto y_access_pattern
408 = build_access_pattern(yAxis, yAxisProperties, colormap_properties);
380 409
381 410 auto line = serie->begin();
382 double current_time = xAxis[0];
411 auto next_line = line + 1;
412 double current_time = xAxisProperties.min;
383 413 int x_index = 0;
384
385 while (x_index < colormap_h_size)
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 bool has_nan = false;
419 while (x_index < colormap_properties.h_size_px)
386 420 {
387 if (current_time > (line + 1)->t())
421 if (next_line != std::end(*serie) and current_time >= next_line->t())
388 422 {
389 line++;
423 line = next_line;
424 next_line++;
390 425 }
391 if ((current_time - xAxis[0])
392 > (x_index * xAxisProperties.range / colormap_h_size))
426 if ((current_time - xAxisProperties.min)
427 > (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon))
393 428 {
429 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
430 [&colormap, &line_values, x_index, avg_coef](const auto& acc) {
431 colormap->data()->setCell(
432 x_index, acc.first, line_values[acc.second] / avg_coef);
433 });
434 std::fill(std::begin(line_values), std::end(line_values), 0.);
394 435 x_index++;
436 avg_coef = 0.;
437 has_nan = false;
395 438 }
396 if (line->t() <= (current_time + xAxisProperties.max_resolution))
439 if (line->t() + x_min_resolution > current_time)
397 440 {
398 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
399 [&colormap, &line, x_index](const auto& acc) {
400 colormap->data()->setCell(x_index, acc.first, (*line)[acc.second]);
401 });
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 {
450 std::transform(std::begin(*line), std::end(*line),
451 std::cbegin(line_values), std::begin(line_values),
452 [](const auto& input, auto output) { return input.v() + output; });
453 }
454 avg_coef += 1.;
402 455 }
403 456 else
404 457 {
405 for (int y_index = 0; y_index < colormap_v_size; y_index++)
458 for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++)
406 459 {
407 colormap->data()->setCell(x_index, y_index, std::nan(""));
460 if (avg_coef > 0.)
461 {
462 has_nan = true;
463 std::fill(
464 std::begin(line_values), std::end(line_values), std::nan(""));
465 }
408 466 }
409 467 }
410 468 current_time += xAxisProperties.max_resolution;
411 469 }
412 470 }
413 471 colormap->rescaleDataRange(true);
414 472 if (rescaleAxes)
415 473 {
416 474 plot->rescaleAxes();
417 475 }
418 476 }
419 477 }
420 478 };
421 479
422 480 /**
423 481 * Helper used to create/update plottables
424 482 */
425 483 struct IPlottablesHelper
426 484 {
427 485 virtual ~IPlottablesHelper() noexcept = default;
428 486 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
429 487 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
430 488 virtual void update(
431 489 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
432 490 };
433 491
434 492 /**
435 493 * Default implementation of IPlottablesHelper, which takes data series to create/update
436 494 * plottables
437 495 * @tparam T the data series' type
438 496 */
439 497 template <typename T>
440 498 struct PlottablesHelper : public IPlottablesHelper
441 499 {
442 500 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
443 501
444 502 PlottablesMap create(QCustomPlot& plot) const override
445 503 {
446 504 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
447 505 }
448 506
449 507 void update(
450 508 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
451 509 {
452 510 if (m_DataSeries)
453 511 {
454 512 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
455 513 }
456 514 else
457 515 {
458 516 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
459 517 "between the type of data series and the "
460 518 "type supposed";
461 519 }
462 520 }
463 521
464 522 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
465 523 {
466 524 if (m_DataSeries)
467 525 {
468 526 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
469 527 }
470 528 else
471 529 {
472 530 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
473 531 "between the type of data series and the "
474 532 "type supposed";
475 533 }
476 534 }
477 535
478 536 std::shared_ptr<T> m_DataSeries;
479 537 };
480 538
481 539 /// Creates IPlottablesHelper according to the type of data series a variable holds
482 540 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
483 541 {
484 542 switch (variable->type())
485 543 {
486 544 case DataSeriesType::SCALAR:
487 545 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
488 546 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
489 547 case DataSeriesType::SPECTROGRAM:
490 548 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
491 549 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
492 550 case DataSeriesType::VECTOR:
493 551 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
494 552 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
495 553 case DataSeriesType::MULTICOMPONENT:
496 554 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
497 555 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
498 556 default:
499 557 // Creates default helper
500 558 break;
501 559 }
502 560
503 561 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
504 562 }
505 563
506 564 } // namespace
507 565
508 566 PlottablesMap VisualizationGraphHelper::create(
509 567 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
510 568 {
511 569 if (variable)
512 570 {
513 571 auto helper = createHelper(variable);
514 572 auto plottables = helper->create(plot);
515 573 return plottables;
516 574 }
517 575 else
518 576 {
519 577 qCDebug(LOG_VisualizationGraphHelper())
520 578 << QObject::tr("Can't create graph plottables : the variable is null");
521 579 return PlottablesMap {};
522 580 }
523 581 }
524 582
525 583 void VisualizationGraphHelper::setYAxisRange(
526 584 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
527 585 {
528 586 if (variable)
529 587 {
530 588 auto helper = createHelper(variable);
531 589 helper->setYAxisRange(variable->range(), plot);
532 590 }
533 591 else
534 592 {
535 593 qCDebug(LOG_VisualizationGraphHelper())
536 594 << QObject::tr("Can't set y-axis range of plot: the variable is null");
537 595 }
538 596 }
539 597
540 598 void VisualizationGraphHelper::updateData(
541 599 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
542 600 {
543 601 auto helper = createHelper(variable);
544 602 helper->update(plottables, dateTime);
545 603 }
@@ -1,98 +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 min_sampling = float(var.meta.get("DATASET_MIN_SAMPLING","nan"))
36 max_sampling = float(var.meta.get("DATASET_MAX_SAMPLING","nan"))
35 37 if "PARAMETER_TABLE_MIN_VALUES[1]" in var.meta:
36 38 min_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MIN_VALUES[1]"].split(',') ])
37 39 max_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MAX_VALUES[1]"].split(',') ])
38 40 y = (max_v + min_v)/2.
39 41 elif "PARAMETER_TABLE_MIN_VALUES[0]" in var.meta:
40 42 min_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MIN_VALUES[0]"].split(',') ])
41 43 max_v = np.array([ float(v) for v in var.meta["PARAMETER_TABLE_MAX_VALUES[0]"].split(',') ])
42 44 y = (max_v + min_v)/2.
43 45 else:
44 46 y = np.logspace(1,3,var.data.shape[1])[::-1]
45 return pysciqlopcore.SpectrogramTimeSerie(var.time,y,var.data)
47 return pysciqlopcore.SpectrogramTimeSerie(var.time,y,var.data,min_sampling,max_sampling)
46 48
47 49 def amda_get_sample(metadata,start,stop):
48 50 ts_type = amda_make_scalar
49 51 try:
50 52 param_id = None
51 53 for key,value in metadata:
52 54 if key == 'xml:id':
53 55 param_id = value
54 56 elif key == 'type':
55 57 if value == 'vector':
56 58 ts_type = amda_make_vector
57 59 elif value == 'multicomponent':
58 60 ts_type = amda_make_multi_comp
59 61 elif value == 'spectrogram':
60 62 ts_type = amda_make_spectro
61 63 tstart=datetime.fromtimestamp(start, tz=timezone.utc)
62 64 tend=datetime.fromtimestamp(stop, tz=timezone.utc)
63 65 var = amda.get_parameter(start_time=tstart, stop_time=tend, parameter_id=param_id, method="REST")
64 66 return ts_type(var)
65 67 except Exception as e:
66 68 print(traceback.format_exc())
67 69 print("Error in amda.py ",str(e))
68 70 return ts_type()
69 71
70 72
71 73 if len(amda.component) is 0:
72 74 amda.update_inventory()
73 75 parameters = copy.deepcopy(amda.parameter)
74 76 for name,component in amda.component.items():
75 77 if 'components' in parameters[component['parameter']]:
76 78 parameters[component['parameter']]['components'].append(component)
77 79 else:
78 80 parameters[component['parameter']]['components']=[component]
79 81
80 82 products = []
81 83 for key,parameter in parameters.items():
82 84 path = f"/AMDA/{parameter['mission']}/{parameter.get('observatory','')}/{parameter['instrument']}/{parameter['dataset']}/{parameter['name']}"
83 85 components = [component['name'] for component in parameter.get('components',[])]
84 86 metadata = [ (key,item) for key,item in parameter.items() if key is not 'components' ]
85 87 n_components = parameter.get('size',0)
86 88 if n_components == '3':
87 89 metadata.append(("type","vector"))
88 90 elif parameter.get('display_type','')=="spectrogram":
89 91 metadata.append(("type","spectrogram"))
90 92 elif n_components !=0:
91 93 metadata.append(("type","multicomponent"))
92 94 else:
93 95 metadata.append(("type","scalar"))
94 96 products.append( (path, components, metadata))
95 97
96 98 PythonProviders.register_product(products, amda_get_sample)
97 99
98 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)
67 ts_type = lambda t,values: pysciqlopcore.SpectrogramTimeSerie(t,np.logspace(1,3,32)[::-1],values,np.nan,np.nan)
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