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