##// END OF EJS Templates
Some added fake specro and switched to new spwc getting rid of DataFrames...
jeandet -
r1464:dce5077d4598
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit 39bf3ff40b41fc01170241f3e471c708c866118b
1 Subproject commit 698d7cfa01b05427c2377ce2799f1290b9eab2ca
@@ -1,523 +1,556
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/VectorTimeSerie.h>
6 #include <Data/VectorTimeSerie.h>
7
7
8 #include <Variable/Variable2.h>
8 #include <Variable/Variable2.h>
9
9
10 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
10 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11
11
12 namespace
12 namespace
13 {
13 {
14
14
15 class SqpDataContainer : public QCPGraphDataContainer
15 class SqpDataContainer : public QCPGraphDataContainer
16 {
16 {
17 public:
17 public:
18 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
18 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
19 };
19 };
20
20
21 /**
21 /**
22 * Struct used to create plottables, depending on the type of the data series from which to create
22 * Struct used to create plottables, depending on the type of the data series from which to create
23 * them
23 * them
24 * @tparam T the data series' type
24 * @tparam T the data series' type
25 * @remarks Default implementation can't create plottables
25 * @remarks Default implementation can't create plottables
26 */
26 */
27 template <typename T, typename Enabled = void>
27 template <typename T, typename Enabled = void>
28 struct PlottablesCreator
28 struct PlottablesCreator
29 {
29 {
30 static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries)
30 static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries)
31 {
31 {
32 return {};
32 return {};
33 }
33 }
34 };
34 };
35
35
36 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
36 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
37 {
37 {
38 PlottablesMap result {};
38 PlottablesMap result {};
39
39
40 // Creates {nbGraphs} QCPGraph to add to the plot
40 // Creates {nbGraphs} QCPGraph to add to the plot
41 for (auto i = 0; i < nbGraphs; ++i)
41 for (auto i = 0; i < nbGraphs; ++i)
42 {
42 {
43 auto graph = plot.addGraph();
43 auto graph = plot.addGraph();
44 result.insert({ i, graph });
44 result.insert({ i, graph });
45 }
45 }
46
46
47 plot.replot();
47 plot.replot();
48
48
49 return result;
49 return result;
50 }
50 }
51
51
52 /**
52 /**
53 * Specialization of PlottablesCreator for scalars
53 * Specialization of PlottablesCreator for scalars
54 * @sa ScalarSeries
54 * @sa ScalarSeries
55 */
55 */
56 template <typename T>
56 template <typename T>
57 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
57 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
58 {
58 {
59 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
59 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
60 {
60 {
61 return createGraphs(plot, 1);
61 return createGraphs(plot, 1);
62 }
62 }
63 };
63 };
64
64
65 /**
65 /**
66 * Specialization of PlottablesCreator for vectors
66 * Specialization of PlottablesCreator for vectors
67 * @sa VectorSeries
67 * @sa VectorSeries
68 */
68 */
69 template <typename T>
69 template <typename T>
70 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
70 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
71 {
71 {
72 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
72 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
73 {
73 {
74 return createGraphs(plot, 3);
74 return createGraphs(plot, 3);
75 }
75 }
76 };
76 };
77
77
78 /**
78 /**
79 * Specialization of PlottablesCreator for MultiComponentTimeSeries
79 * Specialization of PlottablesCreator for MultiComponentTimeSeries
80 * @sa VectorSeries
80 * @sa VectorSeries
81 */
81 */
82 template <typename T>
82 template <typename T>
83 struct PlottablesCreator<T,
83 struct PlottablesCreator<T,
84 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
84 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
85 {
85 {
86 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
86 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
87 {
87 {
88 return createGraphs(plot, dataSeries->size(1));
88 return createGraphs(plot, dataSeries->size(1));
89 }
89 }
90 };
90 };
91
91
92 /**
92 /**
93 * Specialization of PlottablesCreator for spectrograms
93 * Specialization of PlottablesCreator for spectrograms
94 * @sa SpectrogramSeries
94 * @sa SpectrogramSeries
95 */
95 */
96 template <typename T>
96 template <typename T>
97 struct PlottablesCreator<T,
97 struct PlottablesCreator<T,
98 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
98 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
99 {
99 {
100 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
100 static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries)
101 {
101 {
102 PlottablesMap result {};
102 PlottablesMap result {};
103 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
103 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
104
104
105 plot.replot();
105 plot.replot();
106
106
107 return result;
107 return result;
108 }
108 }
109 };
109 };
110
110
111 /**
111 /**
112 * Struct used to update plottables, depending on the type of the data series from which to update
112 * Struct used to update plottables, depending on the type of the data series from which to update
113 * them
113 * them
114 * @tparam T the data series' type
114 * @tparam T the data series' type
115 * @remarks Default implementation can't update plottables
115 * @remarks Default implementation can't update plottables
116 */
116 */
117 template <typename T, typename Enabled = void>
117 template <typename T, typename Enabled = void>
118 struct PlottablesUpdater
118 struct PlottablesUpdater
119 {
119 {
120 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
120 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
121 {
121 {
122 qCCritical(LOG_VisualizationGraphHelper())
122 qCCritical(LOG_VisualizationGraphHelper())
123 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
123 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
124 }
124 }
125
125
126 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
126 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
127 {
127 {
128 qCCritical(LOG_VisualizationGraphHelper())
128 qCCritical(LOG_VisualizationGraphHelper())
129 << QObject::tr("Can't update plottables: unmanaged data series type");
129 << QObject::tr("Can't update plottables: unmanaged data series type");
130 }
130 }
131 };
131 };
132
132
133 /**
133 /**
134 * Specialization of PlottablesUpdater for scalars and vectors
134 * Specialization of PlottablesUpdater for scalars and vectors
135 * @sa ScalarSeries
135 * @sa ScalarSeries
136 * @sa VectorSeries
136 * @sa VectorSeries
137 */
137 */
138 template <typename T>
138 template <typename T>
139 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
139 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
140 {
140 {
141 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
141 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
142 {
142 {
143 auto minValue = 0., maxValue = 0.;
143 auto minValue = 0., maxValue = 0.;
144 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
144 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
145 {
145 {
146 if (serie->size())
146 if (serie->size())
147 {
147 {
148 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
148 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
149 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
149 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
150 }
150 }
151 }
151 }
152 plot.yAxis->setRange(QCPRange { minValue, maxValue });
152 plot.yAxis->setRange(QCPRange { minValue, maxValue });
153 }
153 }
154
154
155 static void updatePlottables(
155 static void updatePlottables(
156 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
156 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
157 {
157 {
158
158
159 // For each plottable to update, resets its data
159 // For each plottable to update, resets its data
160 for (const auto& plottable : plottables)
160 for (const auto& plottable : plottables)
161 {
161 {
162 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
162 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
163 {
163 {
164 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
164 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
165 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
165 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
166 {
166 {
167 std::for_each(
167 std::for_each(
168 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
168 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
169 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
169 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
170 });
170 });
171 }
171 }
172 graph->setData(dataContainer);
172 graph->setData(dataContainer);
173 }
173 }
174 }
174 }
175
175
176 if (!plottables.empty())
176 if (!plottables.empty())
177 {
177 {
178 auto plot = plottables.begin()->second->parentPlot();
178 auto plot = plottables.begin()->second->parentPlot();
179
179
180 if (rescaleAxes)
180 if (rescaleAxes)
181 {
181 {
182 plot->rescaleAxes();
182 plot->rescaleAxes();
183 }
183 }
184 }
184 }
185 }
185 }
186 };
186 };
187
187
188
188
189 template <typename T>
189 template <typename T>
190 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
190 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
191 {
191 {
192 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
192 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
193 {
193 {
194 double minValue = 0., maxValue = 0.;
194 double minValue = 0., maxValue = 0.;
195 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
195 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
196 {
196 {
197 std::for_each(
197 std::for_each(
198 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
198 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
199 minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z });
199 minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z });
200 maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z });
200 maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z });
201 });
201 });
202 }
202 }
203
203
204 plot.yAxis->setRange(QCPRange { minValue, maxValue });
204 plot.yAxis->setRange(QCPRange { minValue, maxValue });
205 }
205 }
206
206
207 static void updatePlottables(
207 static void updatePlottables(
208 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
208 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
209 {
209 {
210
210
211 // For each plottable to update, resets its data
211 // For each plottable to update, resets its data
212 for (const auto& plottable : plottables)
212 for (const auto& plottable : plottables)
213 {
213 {
214 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
214 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
215 {
215 {
216 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
216 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
217 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
217 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
218 {
218 {
219 switch (plottable.first)
219 switch (plottable.first)
220 {
220 {
221 case 0:
221 case 0:
222 std::for_each(std::begin(*serie), std::end(*serie),
222 std::for_each(std::begin(*serie), std::end(*serie),
223 [&dataContainer](const auto& value) {
223 [&dataContainer](const auto& value) {
224 dataContainer->appendGraphData(
224 dataContainer->appendGraphData(
225 QCPGraphData(value.t(), value.v().x));
225 QCPGraphData(value.t(), value.v().x));
226 });
226 });
227 break;
227 break;
228 case 1:
228 case 1:
229 std::for_each(std::begin(*serie), std::end(*serie),
229 std::for_each(std::begin(*serie), std::end(*serie),
230 [&dataContainer](const auto& value) {
230 [&dataContainer](const auto& value) {
231 dataContainer->appendGraphData(
231 dataContainer->appendGraphData(
232 QCPGraphData(value.t(), value.v().y));
232 QCPGraphData(value.t(), value.v().y));
233 });
233 });
234 break;
234 break;
235 case 2:
235 case 2:
236 std::for_each(std::begin(*serie), std::end(*serie),
236 std::for_each(std::begin(*serie), std::end(*serie),
237 [&dataContainer](const auto& value) {
237 [&dataContainer](const auto& value) {
238 dataContainer->appendGraphData(
238 dataContainer->appendGraphData(
239 QCPGraphData(value.t(), value.v().z));
239 QCPGraphData(value.t(), value.v().z));
240 });
240 });
241 break;
241 break;
242 default:
242 default:
243 break;
243 break;
244 }
244 }
245 }
245 }
246 graph->setData(dataContainer);
246 graph->setData(dataContainer);
247 }
247 }
248 }
248 }
249
249
250 if (!plottables.empty())
250 if (!plottables.empty())
251 {
251 {
252 auto plot = plottables.begin()->second->parentPlot();
252 auto plot = plottables.begin()->second->parentPlot();
253
253
254 if (rescaleAxes)
254 if (rescaleAxes)
255 {
255 {
256 plot->rescaleAxes();
256 plot->rescaleAxes();
257 }
257 }
258 }
258 }
259 }
259 }
260 };
260 };
261
261
262
262
263 template <typename T>
263 template <typename T>
264 struct PlottablesUpdater<T,
264 struct PlottablesUpdater<T,
265 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
265 typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>>
266 {
266 {
267 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
267 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
268 {
268 {
269 double minValue = 0., maxValue = 0.;
269 double minValue = 0., maxValue = 0.;
270 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
270 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
271 {
271 {
272 std::for_each(
272 std::for_each(
273 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
273 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
274 minValue = std::min( minValue, std::min_element(v.begin(), v.end())->v() );
274 minValue = std::min(minValue, std::min_element(v.begin(), v.end())->v());
275 maxValue = std::max( maxValue, std::max_element(v.begin(), v.end())->v() );
275 maxValue = std::max(maxValue, std::max_element(v.begin(), v.end())->v());
276 });
276 });
277 }
277 }
278 plot.yAxis->setRange(QCPRange { minValue, maxValue });
278 plot.yAxis->setRange(QCPRange { minValue, maxValue });
279 }
279 }
280
280
281 static void updatePlottables(
281 static void updatePlottables(
282 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
282 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
283 {
283 {
284 for (const auto& plottable : plottables)
284 for (const auto& plottable : plottables)
285 {
285 {
286 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
286 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
287 {
287 {
288 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
288 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
289 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
289 if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries))
290 {
290 {
291 // TODO
291 // TODO
292 std::for_each(std::begin(*serie), std::end(*serie),
292 std::for_each(std::begin(*serie), std::end(*serie),
293 [&dataContainer, component = plottable.first](const auto& value) {
293 [&dataContainer, component = plottable.first](const auto& value) {
294 dataContainer->appendGraphData(
294 dataContainer->appendGraphData(
295 QCPGraphData(value.t(), value[component]));
295 QCPGraphData(value.t(), value[component]));
296 });
296 });
297 }
297 }
298 graph->setData(dataContainer);
298 graph->setData(dataContainer);
299 }
299 }
300 }
300 }
301
301
302 if (!plottables.empty())
302 if (!plottables.empty())
303 {
303 {
304 auto plot = plottables.begin()->second->parentPlot();
304 auto plot = plottables.begin()->second->parentPlot();
305
305
306 if (rescaleAxes)
306 if (rescaleAxes)
307 {
307 {
308 plot->rescaleAxes();
308 plot->rescaleAxes();
309 }
309 }
310 }
310 }
311 }
311 }
312 };
312 };
313
313
314 /**
314 /**
315 * Specialization of PlottablesUpdater for spectrograms
315 * Specialization of PlottablesUpdater for spectrograms
316 * @sa SpectrogramSeries
316 * @sa SpectrogramSeries
317 */
317 */
318 template <typename T>
318 template <typename T>
319 struct PlottablesUpdater<T,
319 struct PlottablesUpdater<T,
320 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
320 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
321 {
321 {
322 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
322 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
323 {
323 {
324 // TODO
324 // TODO
325 // double min, max;
325 // double min, max;
326 // std::tie(min, max) = dataSeries.yBounds();
326 // std::tie(min, max) = dataSeries.yBounds();
327
327
328 // if (!std::isnan(min) && !std::isnan(max))
328 // if (!std::isnan(min) && !std::isnan(max))
329 // {
329 // {
330 // plot.yAxis->setRange(QCPRange { min, max });
330 // plot.yAxis->setRange(QCPRange { min, max });
331 // }
331 // }
332 double minValue = 0., maxValue = 0.;
333 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
334 {
335 auto& yAxis = serie->axis(1);
336 if (yAxis.size())
337 {
338 minValue = *std::min_element(std::cbegin(yAxis), std::cend(yAxis));
339 maxValue = *std::max_element(std::cbegin(yAxis), std::cend(yAxis));
340 }
341 }
342 plot.yAxis->setRange(QCPRange { minValue, maxValue });
332 }
343 }
333
344
334 static void updatePlottables(
345 static void updatePlottables(
335 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
346 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
336 {
347 {
337 // TODO
348 // TODO
338 // if (plottables.empty())
349 if (plottables.empty())
339 // {
350 {
340 // qCDebug(LOG_VisualizationGraphHelper())
351 qCDebug(LOG_VisualizationGraphHelper())
341 // << QObject::tr("Can't update spectrogram: no colormap has been
352 << QObject::tr("Can't update spectrogram: no colormap has been associated");
342 // associated");
353 return;
343 // return;
354 }
344 // }
345
355
346 // // Gets the colormap to update (normally there is only one colormap)
347 // Q_ASSERT(plottables.size() == 1);
348 // auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
349 // Q_ASSERT(colormap != nullptr);
350
356
357 // // Gets the colormap to update (normally there is only one colormap)
358 Q_ASSERT(plottables.size() == 1);
359 auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
360 Q_ASSERT(colormap != nullptr);
361 if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries))
362 {
363 colormap->data()->setSize(serie->shape()[0], serie->shape()[1]);
364 if (serie->size(0))
365 {
366 colormap->data()->setRange(
367 QCPRange { serie->begin()->t(), (serie->end() - 1)->t() },
368 QCPRange { 1., 1000. });
369 for (int x_index = 0; x_index < serie->shape()[0]; x_index++)
370 {
371 auto pixline = (*serie)[x_index];
372 for (int y_index = 0; y_index < serie->shape()[1]; y_index++)
373 {
374 auto value = pixline[y_index];
375 colormap->data()->setCell(x_index, y_index, value);
376 if (std::isnan(value))
377 {
378 colormap->data()->setAlpha(x_index, y_index, 0);
379 }
380 }
381 }
382 }
383 }
351 // dataSeries.lockRead();
384 // dataSeries.lockRead();
352
385
353 // // Processing spectrogram data for display in QCustomPlot
386 // // Processing spectrogram data for display in QCustomPlot
354 // auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
387 // auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
355
388
356 // // Computes logarithmic y-axis resolution for the spectrogram
389 // // Computes logarithmic y-axis resolution for the spectrogram
357 // auto yData = its.first->y();
390 // auto yData = its.first->y();
358 // auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
391 // auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
359
392
360 // // Generates mesh for colormap
393 // // Generates mesh for colormap
361 // auto mesh = DataSeriesUtils::regularMesh(its.first, its.second,
394 // auto mesh = DataSeriesUtils::regularMesh(its.first, its.second,
362 // DataSeriesUtils::Resolution { dataSeries.xResolution() }, yResolution);
395 // DataSeriesUtils::Resolution { dataSeries.xResolution() }, yResolution);
363
396
364 // dataSeries.unlock();
397 // dataSeries.unlock();
365
398
366 // colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
399 // colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
367 // if (!mesh.isEmpty())
400 // if (!mesh.isEmpty())
368 // {
401 // {
369 // colormap->data()->setRange(QCPRange { mesh.m_XMin, mesh.xMax() },
402 // colormap->data()->setRange(QCPRange { mesh.m_XMin, mesh.xMax() },
370 // // y-axis range is converted to linear values
403 // // y-axis range is converted to linear values
371 // QCPRange { std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax()) });
404 // QCPRange { std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax()) });
372
405
373 // // Sets values
406 // // Sets values
374 // auto index = 0;
407 // auto index = 0;
375 // for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it,
408 // for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it,
376 // ++index)
409 // ++index)
377 // {
410 // {
378 // auto xIndex = index % mesh.m_NbX;
411 // auto xIndex = index % mesh.m_NbX;
379 // auto yIndex = index / mesh.m_NbX;
412 // auto yIndex = index / mesh.m_NbX;
380
413
381 // colormap->data()->setCell(xIndex, yIndex, *it);
414 // colormap->data()->setCell(xIndex, yIndex, *it);
382
415
383 // // Makes the NaN values to be transparent in the colormap
416 // // Makes the NaN values to be transparent in the colormap
384 // if (std::isnan(*it))
417 // if (std::isnan(*it))
385 // {
418 // {
386 // colormap->data()->setAlpha(xIndex, yIndex, 0);
419 // colormap->data()->setAlpha(xIndex, yIndex, 0);
387 // }
420 // }
388 // }
421 // }
389 // }
422 // }
390
423
391 // // Rescales axes
424 // // Rescales axes
392 // auto plot = colormap->parentPlot();
425 auto plot = colormap->parentPlot();
393
426 setPlotYAxisRange(dataSeries, {}, *plot);
394 // if (rescaleAxes)
427 if (rescaleAxes)
395 // {
428 {
396 // plot->rescaleAxes();
429 plot->rescaleAxes();
397 // }
430 }
398 }
431 }
399 };
432 };
400
433
401 /**
434 /**
402 * Helper used to create/update plottables
435 * Helper used to create/update plottables
403 */
436 */
404 struct IPlottablesHelper
437 struct IPlottablesHelper
405 {
438 {
406 virtual ~IPlottablesHelper() noexcept = default;
439 virtual ~IPlottablesHelper() noexcept = default;
407 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
440 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
408 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
441 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
409 virtual void update(
442 virtual void update(
410 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
443 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
411 };
444 };
412
445
413 /**
446 /**
414 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
447 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
415 * @tparam T the data series' type
448 * @tparam T the data series' type
416 */
449 */
417 template <typename T>
450 template <typename T>
418 struct PlottablesHelper : public IPlottablesHelper
451 struct PlottablesHelper : public IPlottablesHelper
419 {
452 {
420 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
453 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
421
454
422 PlottablesMap create(QCustomPlot& plot) const override
455 PlottablesMap create(QCustomPlot& plot) const override
423 {
456 {
424 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
457 return PlottablesCreator<T>::createPlottables(plot, m_DataSeries);
425 }
458 }
426
459
427 void update(
460 void update(
428 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
461 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
429 {
462 {
430 if (m_DataSeries)
463 if (m_DataSeries)
431 {
464 {
432 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
465 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
433 }
466 }
434 else
467 else
435 {
468 {
436 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
469 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
437 "between the type of data series and the "
470 "between the type of data series and the "
438 "type supposed";
471 "type supposed";
439 }
472 }
440 }
473 }
441
474
442 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
475 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
443 {
476 {
444 if (m_DataSeries)
477 if (m_DataSeries)
445 {
478 {
446 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
479 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
447 }
480 }
448 else
481 else
449 {
482 {
450 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
483 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
451 "between the type of data series and the "
484 "between the type of data series and the "
452 "type supposed";
485 "type supposed";
453 }
486 }
454 }
487 }
455
488
456 std::shared_ptr<T> m_DataSeries;
489 std::shared_ptr<T> m_DataSeries;
457 };
490 };
458
491
459 /// Creates IPlottablesHelper according to the type of data series a variable holds
492 /// Creates IPlottablesHelper according to the type of data series a variable holds
460 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
493 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
461 {
494 {
462 switch (variable->type())
495 switch (variable->type())
463 {
496 {
464 case DataSeriesType::SCALAR:
497 case DataSeriesType::SCALAR:
465 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
498 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
466 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
499 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
467 case DataSeriesType::SPECTROGRAM:
500 case DataSeriesType::SPECTROGRAM:
468 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
501 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
469 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
502 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
470 case DataSeriesType::VECTOR:
503 case DataSeriesType::VECTOR:
471 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
504 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
472 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
505 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
473 case DataSeriesType::MULTICOMPONENT:
506 case DataSeriesType::MULTICOMPONENT:
474 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
507 return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>(
475 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
508 std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data()));
476 default:
509 default:
477 // Creates default helper
510 // Creates default helper
478 break;
511 break;
479 }
512 }
480
513
481 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
514 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
482 }
515 }
483
516
484 } // namespace
517 } // namespace
485
518
486 PlottablesMap VisualizationGraphHelper::create(
519 PlottablesMap VisualizationGraphHelper::create(
487 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
520 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
488 {
521 {
489 if (variable)
522 if (variable)
490 {
523 {
491 auto helper = createHelper(variable);
524 auto helper = createHelper(variable);
492 auto plottables = helper->create(plot);
525 auto plottables = helper->create(plot);
493 return plottables;
526 return plottables;
494 }
527 }
495 else
528 else
496 {
529 {
497 qCDebug(LOG_VisualizationGraphHelper())
530 qCDebug(LOG_VisualizationGraphHelper())
498 << QObject::tr("Can't create graph plottables : the variable is null");
531 << QObject::tr("Can't create graph plottables : the variable is null");
499 return PlottablesMap {};
532 return PlottablesMap {};
500 }
533 }
501 }
534 }
502
535
503 void VisualizationGraphHelper::setYAxisRange(
536 void VisualizationGraphHelper::setYAxisRange(
504 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
537 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
505 {
538 {
506 if (variable)
539 if (variable)
507 {
540 {
508 auto helper = createHelper(variable);
541 auto helper = createHelper(variable);
509 helper->setYAxisRange(variable->range(), plot);
542 helper->setYAxisRange(variable->range(), plot);
510 }
543 }
511 else
544 else
512 {
545 {
513 qCDebug(LOG_VisualizationGraphHelper())
546 qCDebug(LOG_VisualizationGraphHelper())
514 << QObject::tr("Can't set y-axis range of plot: the variable is null");
547 << QObject::tr("Can't set y-axis range of plot: the variable is null");
515 }
548 }
516 }
549 }
517
550
518 void VisualizationGraphHelper::updateData(
551 void VisualizationGraphHelper::updateData(
519 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
552 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
520 {
553 {
521 auto helper = createHelper(variable);
554 auto helper = createHelper(variable);
522 helper->update(plottables, dateTime);
555 helper->update(plottables, dateTime);
523 }
556 }
@@ -1,65 +1,65
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 pandas as pds
8 import requests
7 import requests
9 import copy
8 import copy
10 from spwc.amda import AMDA
9 from spwc.amda import AMDA
11
10
12 amda = AMDA()
11 amda = AMDA()
13
12
14 def get_sample(metadata,start,stop):
13 def get_sample(metadata,start,stop):
15 ts_type = pysciqlopcore.ScalarTimeSerie
14 ts_type = pysciqlopcore.ScalarTimeSerie
16 default_ctor_args = 1
15 default_ctor_args = 1
17 try:
16 try:
18 param_id = None
17 param_id = None
19 for key,value in metadata:
18 for key,value in metadata:
20 if key == 'xml:id':
19 if key == 'xml:id':
21 param_id = value
20 param_id = value
22 elif key == 'type':
21 elif key == 'type':
23 if value == 'vector':
22 if value == 'vector':
24 ts_type = pysciqlopcore.VectorTimeSerie
23 ts_type = pysciqlopcore.VectorTimeSerie
25 elif value == 'multicomponent':
24 elif value == 'multicomponent':
26 ts_type = pysciqlopcore.MultiComponentTimeSerie
25 ts_type = pysciqlopcore.MultiComponentTimeSerie
27 default_ctor_args = (0,2)
26 default_ctor_args = (0,2)
28 tstart=datetime.fromtimestamp(start, tz=timezone.utc)
27 tstart=datetime.fromtimestamp(start, tz=timezone.utc)
29 tend=datetime.fromtimestamp(stop, tz=timezone.utc)
28 tend=datetime.fromtimestamp(stop, tz=timezone.utc)
30 df = amda.get_parameter(start_time=tstart, stop_time=tend, parameter_id=param_id, method="REST")
29 var = amda.get_parameter(start_time=tstart, stop_time=tend, parameter_id=param_id, method="REST")
31 t = np.array([d.timestamp() for d in df.index])
30 return ts_type(var.time,var.data)
32 values = df.values
33 return ts_type(t,values)
34 except Exception as e:
31 except Exception as e:
35 print(traceback.format_exc())
32 print(traceback.format_exc())
36 print("Error in amda.py ",str(e))
33 print("Error in amda.py ",str(e))
37 return ts_type(default_ctor_args)
34 return ts_type(default_ctor_args)
38
35
39
36
40 if len(amda.component) is 0:
37 if len(amda.component) is 0:
41 amda.update_inventory()
38 amda.update_inventory()
42 parameters = copy.deepcopy(amda.parameter)
39 parameters = copy.deepcopy(amda.parameter)
43 for name,component in amda.component.items():
40 for name,component in amda.component.items():
44 if 'components' in parameters[component['parameter']]:
41 if 'components' in parameters[component['parameter']]:
45 parameters[component['parameter']]['components'].append(component)
42 parameters[component['parameter']]['components'].append(component)
46 else:
43 else:
47 parameters[component['parameter']]['components']=[component]
44 parameters[component['parameter']]['components']=[component]
48
45
49 products = []
46 products = []
50 for key,parameter in parameters.items():
47 for key,parameter in parameters.items():
51 path = f"/AMDA/{parameter['mission']}/{parameter.get('observatory','')}/{parameter['instrument']}/{parameter['dataset']}/{parameter['name']}"
48 path = f"/AMDA/{parameter['mission']}/{parameter.get('observatory','')}/{parameter['instrument']}/{parameter['dataset']}/{parameter['name']}"
52 components = [component['name'] for component in parameter.get('components',[])]
49 components = [component['name'] for component in parameter.get('components',[])]
53 metadata = [ (key,item) for key,item in parameter.items() if key is not 'components' ]
50 metadata = [ (key,item) for key,item in parameter.items() if key is not 'components' ]
54 n_components = parameter.get('size',0)
51 n_components = parameter.get('size',0)
55 if n_components is '3':
52 if n_components is '3':
56 metadata.append(("type","vector"))
53 metadata.append(("type","vector"))
57 elif n_components !=0:
54 elif n_components !=0:
58 metadata.append(("type","multicomponent"))
55 if parameter.get('display_type','')=="spectrogram":
56 metadata.append(("type","spectrogram"))
57 else:
58 metadata.append(("type","multicomponent"))
59 else:
59 else:
60 metadata.append(("type","scalar"))
60 metadata.append(("type","scalar"))
61 products.append( (path, components, metadata))
61 products.append( (path, components, metadata))
62
62
63 PythonProviders.register_product(products, get_sample)
63 PythonProviders.register_product(products, get_sample)
64
64
65
65
@@ -1,87 +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
11 from spwc.common.variable import SpwcVariable
12 someglobal = 1
13
12
14 def make_scalar(x):
13 def make_scalar(x):
15 y = np.cos(x/10.)
14 y = np.cos(x/10.)
16 return pds.DataFrame(index=[datetime.fromtimestamp(t, tz=timezone.utc) for t in x], data=y)
15 return SpwcVariable(time=x, data=y)
17
16
18 def make_vector(x):
17 def make_vector(x):
19 v=np.ones((len(x),3))
18 v=np.ones((len(x),3))
20 for i in range(3):
19 for i in range(3):
21 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)))
22 return pds.DataFrame(index=[datetime.fromtimestamp(t, tz=timezone.utc) for t in x], data=v)
21 return SpwcVariable(time=x, data=v)
23
22
24
23
25 def make_multicomponent(x):
24 def make_multicomponent(x):
26 v=np.ones((len(x),4))
25 v=np.ones((len(x),4))
27 for i in range(4):
26 for i in range(4):
28 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))
29 return pds.DataFrame(index=[datetime.fromtimestamp(t, tz=timezone.utc) for t in x], data=v)
28 return SpwcVariable(time=x, data=v)
29
30 def make_spectrogram(x):
31 v=np.ones((len(x),32))
32 for i in range(32):
33 v.transpose()[:][i] = 100.*(2.+ float(i+1) * np.cos(x/1024. + float(i)))
34 return SpwcVariable(time=x, data=v)
30
35
31
36
32 def _get_data(p_type, start, stop):
37 def _get_data(p_type, start, stop):
33 if type(start) is datetime:
38 if type(start) is datetime:
34 start = start.timestamp()
39 start = start.timestamp()
35 stop = stop.timestamp()
40 stop = stop.timestamp()
36 x = np.arange(math.ceil(start), math.floor(stop))
41 x = np.arange(math.ceil(start), math.floor(stop))
37 if p_type == 'scalar':
42 if p_type == 'scalar':
38 return make_scalar(x)
43 return make_scalar(x)
39 if p_type == 'vector':
44 if p_type == 'vector':
40 return make_vector(x)
45 return make_vector(x)
41 if p_type == 'multicomponent':
46 if p_type == 'multicomponent':
42 return make_multicomponent(x)
47 return make_multicomponent(x)
48 if p_type == 'spectrogram':
49 return make_spectrogram(np.arange(math.ceil(start), math.floor(stop),15.))
43 return None
50 return None
44
51
45 def get_data(metadata,start,stop):
52 def get_data(metadata,start,stop):
46 ts_type = pysciqlopcore.ScalarTimeSerie
53 ts_type = pysciqlopcore.ScalarTimeSerie
47 default_ctor_args = 1
54 default_ctor_args = 1
48 use_cache = False
55 use_cache = False
49 p_type = 'scalar'
56 p_type = 'scalar'
50 try:
57 try:
51 for key,value in metadata:
58 for key,value in metadata:
52 if key == 'type':
59 if key == 'type':
53 p_type = value
60 p_type = value
54 if value == 'vector':
61 if value == 'vector':
55 ts_type = pysciqlopcore.VectorTimeSerie
62 ts_type = pysciqlopcore.VectorTimeSerie
56 elif value == 'multicomponent':
63 elif value == 'multicomponent':
57 ts_type = pysciqlopcore.MultiComponentTimeSerie
64 ts_type = pysciqlopcore.MultiComponentTimeSerie
58 default_ctor_args = (0,2)
65 default_ctor_args = (0,2)
66 elif value == 'spectrogram':
67 ts_type = lambda t,values: pysciqlopcore.SpectrogramTimeSerie(t,np.logspace(1,3,32),values)
68 default_ctor_args = (0,2)
59 if key == 'cache' and value == 'true':
69 if key == 'cache' and value == 'true':
60 use_cache = True
70 use_cache = True
61 if use_cache:
71 if use_cache:
62 cache_product = f"tests/{p_type}"
72 cache_product = f"tests/{p_type}"
63 df = _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)),
64 partial(_get_data, p_type),
74 partial(_get_data, p_type),
65 fragment_hours=24)
75 fragment_hours=24)
66 else:
76 else:
67 print("No Cache")
77 print("No Cache")
68 df = _get_data(p_type, start, stop)
78 var = _get_data(p_type, start, stop)
69 t = np.array([d.timestamp() for d in df.index])
79 return ts_type(var.time,var.data)
70 values = df.values
71 return ts_type(t,values)
72 except Exception as e:
80 except Exception as e:
73 print(traceback.format_exc())
81 print(traceback.format_exc())
74 print("Error in test.py ",str(e))
82 print("Error in test.py ",str(e))
75 return ts_type(default_ctor_args)
83 return ts_type(default_ctor_args)
76
84
77 products = [
85 products = [
78 ("/tests/without_cache/scalar",[],[("type","scalar")]),
86 ("/tests/without_cache/scalar",[],[("type","scalar")]),
79 ("/tests/without_cache/vector",[],[("type","vector")]),
87 ("/tests/without_cache/vector",[],[("type","vector")]),
80 ("/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')]),
81 ("/tests/with_cache/scalar",[],[("type","scalar"), ("cache","true")]),
90 ("/tests/with_cache/scalar",[],[("type","scalar"), ("cache","true")]),
82 ("/tests/with_cache/vector",[],[("type","vector"), ("cache","true")]),
91 ("/tests/with_cache/vector",[],[("type","vector"), ("cache","true")]),
83 ("/tests/with_cache/multicomponent",[],[("type","multicomponent"),('size','4'), ("cache","true")])
92 ("/tests/with_cache/multicomponent",[],[("type","multicomponent"),('size','4'), ("cache","true")])
84 ]
93 ]
85
94
86
95
87 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