##// END OF EJS Templates
More fixes on spectrograms...
jeandet -
r1471:d48510a1cc4a
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit 1e3f92d40b0ec24443e067aaa9e7bca385ec27b2
1 Subproject commit 70d6748a06620f865b683c86c89cc27e1cb0d4b9
@@ -1,603 +1,591
1 #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 #include <cmath>
13
13
14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
15
15
16 namespace
16 namespace
17 {
17 {
18
18
19 class SqpDataContainer : public QCPGraphDataContainer
19 class SqpDataContainer : public QCPGraphDataContainer
20 {
20 {
21 public:
21 public:
22 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
22 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
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 * Struct used to create plottables, depending on the type of the data series from which to create
27 * them
27 * them
28 * @tparam T the data series' type
28 * @tparam T the data series' type
29 * @remarks Default implementation can't create plottables
29 * @remarks Default implementation can't create plottables
30 */
30 */
31 template <typename T, typename Enabled = void>
31 template <typename T, typename Enabled = void>
32 struct PlottablesCreator
32 struct PlottablesCreator
33 {
33 {
34 static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries)
34 static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries)
35 {
35 {
36 return {};
36 return {};
37 }
37 }
38 };
38 };
39
39
40 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
40 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
41 {
41 {
42 PlottablesMap result {};
42 PlottablesMap result {};
43
43
44 // Creates {nbGraphs} QCPGraph to add to the plot
44 // Creates {nbGraphs} QCPGraph to add to the plot
45 for (auto i = 0; i < nbGraphs; ++i)
45 for (auto i = 0; i < nbGraphs; ++i)
46 {
46 {
47 auto graph = plot.addGraph();
47 auto graph = plot.addGraph();
48 result.insert({ i, graph });
48 result.insert({ i, graph });
49 }
49 }
50
50
51 plot.replot();
51 plot.replot();
52
52
53 return result;
53 return result;
54 }
54 }
55
55
56 /**
56 /**
57 * Specialization of PlottablesCreator for scalars
57 * Specialization of PlottablesCreator for scalars
58 * @sa ScalarSeries
58 * @sa ScalarSeries
59 */
59 */
60 template <typename T>
60 template <typename T>
61 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>>
62 {
62 {
63 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
63 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
64 {
64 {
65 return createGraphs(plot, 1);
65 return createGraphs(plot, 1);
66 }
66 }
67 };
67 };
68
68
69 /**
69 /**
70 * Specialization of PlottablesCreator for vectors
70 * Specialization of PlottablesCreator for vectors
71 * @sa VectorSeries
71 * @sa VectorSeries
72 */
72 */
73 template <typename T>
73 template <typename T>
74 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>>
75 {
75 {
76 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
76 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
77 {
77 {
78 return createGraphs(plot, 3);
78 return createGraphs(plot, 3);
79 }
79 }
80 };
80 };
81
81
82 /**
82 /**
83 * Specialization of PlottablesCreator for MultiComponentTimeSeries
83 * Specialization of PlottablesCreator for MultiComponentTimeSeries
84 * @sa VectorSeries
84 * @sa VectorSeries
85 */
85 */
86 template <typename T>
86 template <typename T>
87 struct PlottablesCreator<T,
87 struct PlottablesCreator<T,
88 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>>
89 {
89 {
90 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
90 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
91 {
91 {
92 return createGraphs(plot, dataSeries->size(1));
92 return createGraphs(plot, dataSeries->size(1));
93 }
93 }
94 };
94 };
95
95
96 /**
96 /**
97 * Specialization of PlottablesCreator for spectrograms
97 * Specialization of PlottablesCreator for spectrograms
98 * @sa SpectrogramSeries
98 * @sa SpectrogramSeries
99 */
99 */
100 template <typename T>
100 template <typename T>
101 struct PlottablesCreator<T,
101 struct PlottablesCreator<T,
102 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>>
103 {
103 {
104 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
104 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
105 {
105 {
106 PlottablesMap result {};
106 PlottablesMap result {};
107 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
107 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
108
108
109 plot.replot();
109 plot.replot();
110
110
111 return result;
111 return result;
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 * Struct used to update plottables, depending on the type of the data series from which to update
117 * them
117 * them
118 * @tparam T the data series' type
118 * @tparam T the data series' type
119 * @remarks Default implementation can't update plottables
119 * @remarks Default implementation can't update plottables
120 */
120 */
121 template <typename T, typename Enabled = void>
121 template <typename T, typename Enabled = void>
122 struct PlottablesUpdater
122 struct PlottablesUpdater
123 {
123 {
124 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
124 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
125 {
125 {
126 qCCritical(LOG_VisualizationGraphHelper())
126 qCCritical(LOG_VisualizationGraphHelper())
127 << 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");
128 }
128 }
129
129
130 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
130 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
131 {
131 {
132 qCCritical(LOG_VisualizationGraphHelper())
132 qCCritical(LOG_VisualizationGraphHelper())
133 << QObject::tr("Can't update plottables: unmanaged data series type");
133 << QObject::tr("Can't update plottables: unmanaged data series type");
134 }
134 }
135 };
135 };
136
136
137 /**
137 /**
138 * Specialization of PlottablesUpdater for scalars and vectors
138 * Specialization of PlottablesUpdater for scalars and vectors
139 * @sa ScalarSeries
139 * @sa ScalarSeries
140 * @sa VectorSeries
140 * @sa VectorSeries
141 */
141 */
142 template <typename T>
142 template <typename T>
143 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>>
144 {
144 {
145 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
145 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
146 {
146 {
147 auto minValue = 0., maxValue = 0.;
147 auto minValue = 0., maxValue = 0.;
148 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
148 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
149 {
149 {
150 if (serie->size())
150 if (serie->size())
151 {
151 {
152 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
152 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
153 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
153 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
154 }
154 }
155 }
155 }
156 plot.yAxis->setRange(QCPRange { minValue, maxValue });
156 plot.yAxis->setRange(QCPRange { minValue, maxValue });
157 }
157 }
158
158
159 static void updatePlottables(
159 static void updatePlottables(
160 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
160 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
161 {
161 {
162
162
163 // For each plottable to update, resets its data
163 // For each plottable to update, resets its data
164 for (const auto& plottable : plottables)
164 for (const auto& plottable : plottables)
165 {
165 {
166 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
166 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
167 {
167 {
168 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
168 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
169 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
169 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
170 {
170 {
171 std::for_each(
171 std::for_each(
172 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
172 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
173 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
173 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
174 });
174 });
175 }
175 }
176 graph->setData(dataContainer);
176 graph->setData(dataContainer);
177 }
177 }
178 }
178 }
179
179
180 if (!plottables.empty())
180 if (!plottables.empty())
181 {
181 {
182 auto plot = plottables.begin()->second->parentPlot();
182 auto plot = plottables.begin()->second->parentPlot();
183
183
184 if (rescaleAxes)
184 if (rescaleAxes)
185 {
185 {
186 plot->rescaleAxes();
186 plot->rescaleAxes();
187 }
187 }
188 }
188 }
189 }
189 }
190 };
190 };
191
191
192
192
193 template <typename T>
193 template <typename T>
194 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>>
195 {
195 {
196 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
196 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
197 {
197 {
198 double minValue = 0., maxValue = 0.;
198 double minValue = 0., maxValue = 0.;
199 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
199 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
200 {
200 {
201 std::for_each(
201 std::for_each(
202 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
202 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
203 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 });
204 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 });
205 });
205 });
206 }
206 }
207
207
208 plot.yAxis->setRange(QCPRange { minValue, maxValue });
208 plot.yAxis->setRange(QCPRange { minValue, maxValue });
209 }
209 }
210
210
211 static void updatePlottables(
211 static void updatePlottables(
212 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
212 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
213 {
213 {
214
214
215 // For each plottable to update, resets its data
215 // For each plottable to update, resets its data
216 for (const auto& plottable : plottables)
216 for (const auto& plottable : plottables)
217 {
217 {
218 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
218 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
219 {
219 {
220 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
220 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
221 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
221 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
222 {
222 {
223 switch (plottable.first)
223 switch (plottable.first)
224 {
224 {
225 case 0:
225 case 0:
226 std::for_each(std::begin(*serie), std::end(*serie),
226 std::for_each(std::begin(*serie), std::end(*serie),
227 [&dataContainer](const auto& value) {
227 [&dataContainer](const auto& value) {
228 dataContainer->appendGraphData(
228 dataContainer->appendGraphData(
229 QCPGraphData(value.t(), value.v().x));
229 QCPGraphData(value.t(), value.v().x));
230 });
230 });
231 break;
231 break;
232 case 1:
232 case 1:
233 std::for_each(std::begin(*serie), std::end(*serie),
233 std::for_each(std::begin(*serie), std::end(*serie),
234 [&dataContainer](const auto& value) {
234 [&dataContainer](const auto& value) {
235 dataContainer->appendGraphData(
235 dataContainer->appendGraphData(
236 QCPGraphData(value.t(), value.v().y));
236 QCPGraphData(value.t(), value.v().y));
237 });
237 });
238 break;
238 break;
239 case 2:
239 case 2:
240 std::for_each(std::begin(*serie), std::end(*serie),
240 std::for_each(std::begin(*serie), std::end(*serie),
241 [&dataContainer](const auto& value) {
241 [&dataContainer](const auto& value) {
242 dataContainer->appendGraphData(
242 dataContainer->appendGraphData(
243 QCPGraphData(value.t(), value.v().z));
243 QCPGraphData(value.t(), value.v().z));
244 });
244 });
245 break;
245 break;
246 default:
246 default:
247 break;
247 break;
248 }
248 }
249 }
249 }
250 graph->setData(dataContainer);
250 graph->setData(dataContainer);
251 }
251 }
252 }
252 }
253
253
254 if (!plottables.empty())
254 if (!plottables.empty())
255 {
255 {
256 auto plot = plottables.begin()->second->parentPlot();
256 auto plot = plottables.begin()->second->parentPlot();
257
257
258 if (rescaleAxes)
258 if (rescaleAxes)
259 {
259 {
260 plot->rescaleAxes();
260 plot->rescaleAxes();
261 }
261 }
262 }
262 }
263 }
263 }
264 };
264 };
265
265
266
266
267 template <typename T>
267 template <typename T>
268 struct PlottablesUpdater<T,
268 struct PlottablesUpdater<T,
269 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>>
270 {
270 {
271 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
271 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
272 {
272 {
273 double minValue = 0., maxValue = 0.;
273 double minValue = 0., maxValue = 0.;
274 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
274 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
275 {
275 {
276 std::for_each(std::begin(*serie), std::end(*serie), [&minValue, &maxValue](auto& v) {
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());
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 /*=============================================================*/
317 /*=============================================================*/
318 // TODO move this to dedicated srcs
318 // TODO move this to dedicated srcs
319 /*=============================================================*/
319 /*=============================================================*/
320 struct ColomapProperties
320 struct ColomapProperties
321 {
321 {
322 int h_size_px;
322 int h_size_px;
323 int v_size_px;
323 int v_size_px;
324 double h_resolutuon;
324 double h_resolutuon;
325 double v_resolutuon;
325 double v_resolutuon;
326 };
326 };
327
327
328 inline ColomapProperties CMAxisAnalysis(const TimeSeriesUtils::axis_properties& xAxisProperties,
328 inline ColomapProperties CMAxisAnalysis(const TimeSeriesUtils::axis_properties& xAxisProperties,
329 const TimeSeriesUtils::axis_properties& yAxisProperties)
329 const TimeSeriesUtils::axis_properties& yAxisProperties)
330 {
330 {
331 int colormap_h_size
331 int colormap_h_size
332 = std::min(32000, static_cast<int>(xAxisProperties.range / xAxisProperties.max_resolution));
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);
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);
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);
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,
336 return ColomapProperties { colormap_h_size, colormap_v_size, colormap_h_resolution,
337 colormap_v_resolution };
337 colormap_v_resolution };
338 }
338 }
339
339
340 inline std::vector<std::pair<int, int>> build_access_pattern(const std::vector<double>& axis,
340 inline std::vector<std::pair<int, int>> build_access_pattern(const std::vector<double>& axis,
341 const TimeSeriesUtils::axis_properties& axisProperties,
341 const TimeSeriesUtils::axis_properties& axisProperties,
342 const ColomapProperties& colormap_properties)
342 const ColomapProperties& colormap_properties)
343 {
343 {
344 std::vector<std::pair<int, int>> access_pattern;
344 std::vector<std::pair<int, int>> access_pattern;
345 for (int index = 0, cel_index = 0; index < colormap_properties.v_size_px; index++)
345 for (int index = 0, cel_index = axis.size() - 1; index < colormap_properties.v_size_px; index++)
346 {
346 {
347 double current_y = pow(10., (axisProperties.max_resolution * index) + axisProperties.min);
347 double current_y = pow(10., (axisProperties.max_resolution * index) + axisProperties.min);
348 if (current_y > axis[cel_index])
348 if (current_y > axis[cel_index])
349 cel_index++;
349 cel_index--;
350 access_pattern.push_back({ index, axis.size() - 1 - cel_index });
350 access_pattern.push_back({ index, cel_index });
351 }
351 }
352 return access_pattern;
352 return access_pattern;
353 }
353 }
354
354
355 /*=============================================================*/
355 /*=============================================================*/
356
356
357 /**
357 /**
358 * Specialization of PlottablesUpdater for spectrograms
358 * Specialization of PlottablesUpdater for spectrograms
359 * @sa SpectrogramSeries
359 * @sa SpectrogramSeries
360 */
360 */
361 template <typename T>
361 template <typename T>
362 struct PlottablesUpdater<T,
362 struct PlottablesUpdater<T,
363 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>>
364 {
364 {
365 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
365 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
366 {
366 {
367 auto [minValue, maxValue] = dataSeries.axis_range(1);
367 auto [minValue, maxValue] = dataSeries.axis_range(1);
368 std::cout << "min=" << minValue << " max=" << maxValue << std::endl;
368 std::cout << "min=" << minValue << " max=" << maxValue << std::endl;
369 plot.yAxis->setRange(QCPRange { minValue, maxValue });
369 plot.yAxis->setRange(QCPRange { minValue, maxValue });
370 }
370 }
371
371
372 static void updatePlottables(
372 static void updatePlottables(
373 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
373 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
374 {
374 {
375 if (plottables.empty())
375 if (plottables.empty())
376 {
376 {
377 qCDebug(LOG_VisualizationGraphHelper())
377 qCDebug(LOG_VisualizationGraphHelper())
378 << QObject::tr("Can't update spectrogram: no colormap has been associated");
378 << QObject::tr("Can't update spectrogram: no colormap has been associated");
379 return;
379 return;
380 }
380 }
381
381
382 // Gets the colormap to update (normally there is only one colormap)
382 // Gets the colormap to update (normally there is only one colormap)
383 Q_ASSERT(plottables.size() == 1);
383 Q_ASSERT(plottables.size() == 1);
384 auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
384 auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
385 Q_ASSERT(colormap != nullptr);
385 Q_ASSERT(colormap != nullptr);
386 auto plot = colormap->parentPlot();
386 auto plot = colormap->parentPlot();
387 auto [minValue, maxValue] = dataSeries.axis_range(1);
387 auto [minValue, maxValue] = dataSeries.axis_range(1);
388 plot->yAxis->setRange(QCPRange { minValue, maxValue });
388 plot->yAxis->setRange(QCPRange { minValue, maxValue });
389 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
389 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
390 {
390 {
391 if (serie->size(0) > 2)
391 if (serie->size(0) > 2)
392 {
392 {
393 const auto& xAxis = serie->axis(0);
393 const auto& xAxis = serie->axis(0);
394 auto yAxis = serie->axis(1); // copy for in place reverse order
394 auto yAxis = serie->axis(1); // copy for in place reverse order
395 std::reverse(std::begin(yAxis), std::end(yAxis));
395 std::reverse(std::begin(yAxis), std::end(yAxis));
396 auto xAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLinear,
396 auto xAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLinear,
397 TimeSeriesUtils::CheckMedian>(xAxis, serie->min_sampling);
397 TimeSeriesUtils::CheckMedian>(xAxis, serie->min_sampling);
398 auto yAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLog,
398 auto yAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLog,
399 TimeSeriesUtils::DontCheckMedian>(yAxis);
399 TimeSeriesUtils::DontCheckMedian>(yAxis);
400 auto colormap_properties = CMAxisAnalysis(xAxisProperties, yAxisProperties);
400 auto colormap_properties = CMAxisAnalysis(xAxisProperties, yAxisProperties);
401
401
402 colormap->data()->setSize(
402 colormap->data()->setSize(
403 colormap_properties.h_size_px, colormap_properties.v_size_px);
403 colormap_properties.h_size_px, colormap_properties.v_size_px);
404 colormap->data()->setRange(
404 colormap->data()->setRange(
405 QCPRange { xAxisProperties.min, xAxisProperties.max }, { minValue, maxValue });
405 QCPRange { xAxisProperties.min, xAxisProperties.max }, { minValue, maxValue });
406
406
407 auto y_access_pattern
407 auto y_access_pattern
408 = build_access_pattern(yAxis, yAxisProperties, colormap_properties);
408 = build_access_pattern(serie->axis(1), yAxisProperties, colormap_properties);
409
409
410 auto line = serie->begin();
410 auto line = serie->begin();
411 auto next_line = line + 1;
411 auto next_line = line + 1;
412 double current_time = xAxisProperties.min;
412 double current_time = xAxisProperties.min;
413 int x_index = 0;
413 int x_index = 0;
414 auto x_min_resolution
414 auto x_min_resolution
415 = std::fmin(2. * serie->max_sampling, xAxisProperties.max_resolution * 100.);
415 = std::fmin(2. * serie->max_sampling, xAxisProperties.max_resolution * 100.);
416 std::vector<double> line_values(serie->size(1));
416 std::vector<double> line_values(serie->size(1));
417 double avg_coef = 0.;
417 double avg_coef = 0.;
418 bool has_nan = false;
419 while (x_index < colormap_properties.h_size_px)
418 while (x_index < colormap_properties.h_size_px)
420 {
419 {
421 if (next_line != std::end(*serie) and current_time >= next_line->t())
420 if (next_line != std::end(*serie) and current_time >= next_line->t())
422 {
421 {
423 line = next_line;
422 line = next_line;
424 next_line++;
423 next_line++;
425 }
424 }
426 if ((current_time - xAxisProperties.min)
425 if ((current_time - xAxisProperties.min)
427 > (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon))
426 > (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon))
428 {
427 {
429 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
428 std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern),
430 [&colormap, &line_values, x_index, avg_coef](const auto& acc) {
429 [&colormap, &line_values, x_index, avg_coef](const auto& acc) {
431 colormap->data()->setCell(
430 colormap->data()->setCell(
432 x_index, acc.first, line_values[acc.second] / avg_coef);
431 x_index, acc.first, line_values[acc.second] / avg_coef);
433 });
432 });
434 std::fill(std::begin(line_values), std::end(line_values), 0.);
433 std::fill(std::begin(line_values), std::end(line_values), 0.);
435 x_index++;
434 x_index++;
436 avg_coef = 0.;
435 avg_coef = 0.;
437 has_nan = false;
438 }
436 }
439 if (line->t() + x_min_resolution > current_time)
437 if (line->t() + x_min_resolution > current_time)
440 {
438 {
441 if (has_nan)
442 {
443 std::transform(std::begin(*line), std::end(*line),
444 std::begin(line_values),
445 [](const auto& input) { return input.v(); });
446 has_nan = false;
447 }
448 else
449 {
439 {
450 std::transform(std::begin(*line), std::end(*line),
440 std::transform(std::begin(*line), std::end(*line),
451 std::cbegin(line_values), std::begin(line_values),
441 std::cbegin(line_values), std::begin(line_values),
452 [](const auto& input, auto output) { return input.v() + output; });
442 [](const auto& input, auto output) { return input.v() + output; });
453 }
443 }
454 avg_coef += 1.;
444 avg_coef += 1.;
455 }
445 }
456 else
446 else
457 {
447 {
458 for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++)
448 for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++)
459 {
449 {
460 if (avg_coef > 0.)
450 if (avg_coef > 0.)
461 {
451 {
462 has_nan = true;
452 std::fill(std::begin(line_values), std::end(line_values), 0);
463 std::fill(
464 std::begin(line_values), std::end(line_values), std::nan(""));
465 }
453 }
466 }
454 }
467 }
455 }
468 current_time += xAxisProperties.max_resolution;
456 current_time += xAxisProperties.max_resolution * 0.9;
469 }
457 }
470 }
458 }
471 colormap->rescaleDataRange(true);
459 colormap->rescaleDataRange(true);
472 if (rescaleAxes)
460 if (rescaleAxes)
473 {
461 {
474 plot->rescaleAxes();
462 plot->rescaleAxes();
475 }
463 }
476 }
464 }
477 }
465 }
478 };
466 };
479
467
480 /**
468 /**
481 * Helper used to create/update plottables
469 * Helper used to create/update plottables
482 */
470 */
483 struct IPlottablesHelper
471 struct IPlottablesHelper
484 {
472 {
485 virtual ~IPlottablesHelper() noexcept = default;
473 virtual ~IPlottablesHelper() noexcept = default;
486 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
474 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
487 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
475 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
488 virtual void update(
476 virtual void update(
489 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
477 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
490 };
478 };
491
479
492 /**
480 /**
493 * Default implementation of IPlottablesHelper, which takes data series to create/update
481 * Default implementation of IPlottablesHelper, which takes data series to create/update
494 * plottables
482 * plottables
495 * @tparam T the data series' type
483 * @tparam T the data series' type
496 */
484 */
497 template <typename T>
485 template <typename T>
498 struct PlottablesHelper : public IPlottablesHelper
486 struct PlottablesHelper : public IPlottablesHelper
499 {
487 {
500 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
488 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
501
489
502 PlottablesMap create(QCustomPlot& plot) const override
490 PlottablesMap create(QCustomPlot& plot) const override
503 {
491 {
504 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
492 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
505 }
493 }
506
494
507 void update(
495 void update(
508 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
496 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
509 {
497 {
510 if (m_DataSeries)
498 if (m_DataSeries)
511 {
499 {
512 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
500 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
513 }
501 }
514 else
502 else
515 {
503 {
516 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
504 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
517 "between the type of data series and the "
505 "between the type of data series and the "
518 "type supposed";
506 "type supposed";
519 }
507 }
520 }
508 }
521
509
522 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
510 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
523 {
511 {
524 if (m_DataSeries)
512 if (m_DataSeries)
525 {
513 {
526 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
514 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
527 }
515 }
528 else
516 else
529 {
517 {
530 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
518 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
531 "between the type of data series and the "
519 "between the type of data series and the "
532 "type supposed";
520 "type supposed";
533 }
521 }
534 }
522 }
535
523
536 std::shared_ptr<T> m_DataSeries;
524 std::shared_ptr<T> m_DataSeries;
537 };
525 };
538
526
539 /// Creates IPlottablesHelper according to the type of data series a variable holds
527 /// Creates IPlottablesHelper according to the type of data series a variable holds
540 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
528 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
541 {
529 {
542 switch (variable->type())
530 switch (variable->type())
543 {
531 {
544 case DataSeriesType::SCALAR:
532 case DataSeriesType::SCALAR:
545 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
533 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
546 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
534 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
547 case DataSeriesType::SPECTROGRAM:
535 case DataSeriesType::SPECTROGRAM:
548 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
536 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
549 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
537 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
550 case DataSeriesType::VECTOR:
538 case DataSeriesType::VECTOR:
551 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
539 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
552 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
540 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
553 case DataSeriesType::MULTICOMPONENT:
541 case DataSeriesType::MULTICOMPONENT:
554 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
542 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
555 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
543 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
556 default:
544 default:
557 // Creates default helper
545 // Creates default helper
558 break;
546 break;
559 }
547 }
560
548
561 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
549 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
562 }
550 }
563
551
564 } // namespace
552 } // namespace
565
553
566 PlottablesMap VisualizationGraphHelper::create(
554 PlottablesMap VisualizationGraphHelper::create(
567 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
555 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
568 {
556 {
569 if (variable)
557 if (variable)
570 {
558 {
571 auto helper = createHelper(variable);
559 auto helper = createHelper(variable);
572 auto plottables = helper->create(plot);
560 auto plottables = helper->create(plot);
573 return plottables;
561 return plottables;
574 }
562 }
575 else
563 else
576 {
564 {
577 qCDebug(LOG_VisualizationGraphHelper())
565 qCDebug(LOG_VisualizationGraphHelper())
578 << QObject::tr("Can't create graph plottables : the variable is null");
566 << QObject::tr("Can't create graph plottables : the variable is null");
579 return PlottablesMap {};
567 return PlottablesMap {};
580 }
568 }
581 }
569 }
582
570
583 void VisualizationGraphHelper::setYAxisRange(
571 void VisualizationGraphHelper::setYAxisRange(
584 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
572 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
585 {
573 {
586 if (variable)
574 if (variable)
587 {
575 {
588 auto helper = createHelper(variable);
576 auto helper = createHelper(variable);
589 helper->setYAxisRange(variable->range(), plot);
577 helper->setYAxisRange(variable->range(), plot);
590 }
578 }
591 else
579 else
592 {
580 {
593 qCDebug(LOG_VisualizationGraphHelper())
581 qCDebug(LOG_VisualizationGraphHelper())
594 << QObject::tr("Can't set y-axis range of plot: the variable is null");
582 << QObject::tr("Can't set y-axis range of plot: the variable is null");
595 }
583 }
596 }
584 }
597
585
598 void VisualizationGraphHelper::updateData(
586 void VisualizationGraphHelper::updateData(
599 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
587 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
600 {
588 {
601 auto helper = createHelper(variable);
589 auto helper = createHelper(variable);
602 helper->update(plottables, dateTime);
590 helper->update(plottables, dateTime);
603 }
591 }
General Comments 0
You need to be logged in to leave comments. Login now