1 | /* |
2 | * GoogleChart.hpp |
3 | * |
4 | * Created on: Jan 9, 2016 |
5 | * Author: i-bird |
6 | */ |
7 | |
8 | #ifndef OPENFPM_DATA_SRC_PLOT_GOOGLECHART_HPP_ |
9 | #define OPENFPM_DATA_SRC_PLOT_GOOGLECHART_HPP_ |
10 | |
11 | #include <fstream> |
12 | #include "Vector/map_vector.hpp" |
13 | #include <cmath> |
14 | |
15 | #define GGRAPH_COLUMS 1 |
16 | #define GGRAPH_POINTS 2 |
17 | |
18 | #define GC_ZOOM std::string("explorer: {actions: ['dragToZoom', 'rightClickToReset'],axis: 'horizontal,vertical',keepInBounds: true, maxZoomIn: 128.0}") |
19 | #define GC_X_LOG std::string("hAxis: { logScale: true }") |
20 | #define GC_Y_LOG std::string("vAxis: { logScale: true }") |
21 | |
22 | /*! \brief Google chart options |
23 | * |
24 | */ |
25 | struct GCoptions |
26 | { |
27 | //! Title of the chart |
28 | std::string title; |
29 | //! Y axis name |
30 | std::string yAxis; |
31 | //! X axis name |
32 | std::string xAxis; |
33 | |
34 | //! Type of chart (list of the option can be founded in Google Chart API for seriesType) |
35 | //! Possible options are: |
36 | //! 'line', 'area', 'bars', 'candlesticks', and 'steppedArea' |
37 | //! default: line |
38 | std::string stype; |
39 | |
40 | //! Extended series options |
41 | //! Example {5: {type: 'line'}} specify that the series number 5 must be represented |
42 | //! with a line |
43 | std::string stypeext; |
44 | |
45 | //! width of the graph in pixels |
46 | size_t width=900; |
47 | |
48 | //! height of the graph in pixels |
49 | size_t heigh=500; |
50 | |
51 | //! Flag that specify if the colums are stacked |
52 | //! Check in Google Chart for is stacked option |
53 | bool isStacked = false; |
54 | |
55 | //! Width of the line |
56 | size_t lineWidth = 4; |
57 | |
58 | //! Style for all the intervals |
59 | //! Check Google Chart API intervals option |
60 | std::string intervalsext; |
61 | |
62 | //! Style for each interval |
63 | //! Check Google Chart API interval option |
64 | std::string intervalext; |
65 | |
66 | //! more |
67 | std::string more; |
68 | |
69 | //! curve type |
70 | std::string curveType = "function" ; |
71 | |
72 | //! barWD |
73 | bool barWD = false; |
74 | |
75 | /*! \brief copy operator |
76 | * |
77 | * \param opt object to copy |
78 | * |
79 | * \return itself |
80 | * |
81 | */ |
82 | GCoptions & operator=(const GCoptions & opt) |
83 | { |
84 | title = opt.title; |
85 | yAxis = opt.yAxis; |
86 | xAxis = opt.xAxis; |
87 | stype = opt.stype; |
88 | stypeext = opt.stypeext; |
89 | width=opt.width; |
90 | heigh=opt.heigh; |
91 | |
92 | lineWidth = opt.lineWidth; |
93 | intervalsext = opt.intervalsext; |
94 | more = opt.more; |
95 | |
96 | return *this; |
97 | } |
98 | }; |
99 | |
100 | /*! \brief Google Graph |
101 | * |
102 | */ |
103 | struct GGraph |
104 | { |
105 | //! TypeOfGraph |
106 | size_t type; |
107 | |
108 | //! data |
109 | std::string data; |
110 | |
111 | //! option |
112 | std::string option; |
113 | |
114 | //! view in case we need a view |
115 | std::string view; |
116 | |
117 | //! Google chart option |
118 | GCoptions opt; |
119 | }; |
120 | |
121 | /////////////////// Constants strings usefull to construct the HTML page ////////// |
122 | |
123 | const std::string begin_data ="<html>\n\ |
124 | <head>\n\ |
125 | <script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>\n\ |
126 | <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js\"></script>\n\ |
127 | <script type=\"text/javascript\">\n\ |
128 | google.charts.load('current', {'packages':['corechart']});\n\ |
129 | google.charts.setOnLoadCallback(drawVisualization);\n\ |
130 | \n\ |
131 | function exportToSVG(i)\n\ |
132 | {\n\ |
133 | var e = document.getElementById('chart_div'+i);\n\ |
134 | var svg = e.getElementsByTagName('svg')[0].parentNode.innerHTML;\n\ |
135 | var pos = svg.lastIndexOf(\"</svg>\");\n\ |
136 | pos += 6;\n\ |
137 | svg = svg.substring(0,4) + \" xmlns='http://www.w3.org/2000/svg' xmlns:xlink= 'http://www.w3.org/1999/xlink' \" + svg.substring(4,pos);\n\ |
138 | svgData = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);\n\ |
139 | $(this).attr({'href': svgData,'target': '_blank'});\n\ |
140 | }\n\ |
141 | \n\ |
142 | function drawVisualization() {\n" ; |
143 | |
144 | const std::string end_data="]);\n\n" ; |
145 | |
146 | const std::string begin_div = "}</script>\n\ |
147 | </head>\n\ |
148 | <body>\n" ; |
149 | |
150 | const std::string div_end = "</body>\n\ |
151 | </html>\n" ; |
152 | |
153 | const std::string saving_javascript = "function save(i)\n\ |
154 | var e = document.getElementById('chart_')\n\ |
155 | e.getElementsByTagName('svg')[0].parentNode.innerHTML" ; |
156 | |
157 | template<typename T> |
158 | struct check_nan |
159 | { |
160 | static bool check(const T & n) |
161 | { |
162 | return false; |
163 | } |
164 | }; |
165 | |
166 | template<> |
167 | struct check_nan<float> |
168 | { |
169 | static bool check(const float & n) |
170 | { |
171 | return std::isnan(n); |
172 | } |
173 | }; |
174 | |
175 | template<> |
176 | struct check_nan<double> |
177 | { |
178 | static bool check(const double & n) |
179 | { |
180 | return std::isnan(n); |
181 | } |
182 | }; |
183 | |
184 | ///////////////////////////////////////////////////////////////////// |
185 | |
186 | /*! \brief Small class to produce graph with Google chart in HTML |
187 | * |
188 | * This Class can produce several graph using google chart |
189 | * |
190 | * ### Create Histogram graph |
191 | * |
192 | * \image html g_graph_hist.jpg |
193 | * |
194 | * This code produce the graph above |
195 | * |
196 | * \snippet Plot_unit_tests.hpp Producing an Histogram graph |
197 | * |
198 | * ### Create Lines |
199 | * |
200 | * \image html g_graph_plot2.jpg |
201 | * |
202 | * This code produce the graph above |
203 | * |
204 | * \snippet Plot_unit_tests.hpp Producing lines |
205 | * |
206 | * ### Create lines with different styles |
207 | * |
208 | * \image html g_graph_plot.jpg |
209 | * |
210 | * This code produce the graph above |
211 | * |
212 | * \snippet Plot_unit_tests.hpp Producing lines graph with style |
213 | * |
214 | */ |
215 | class GoogleChart |
216 | { |
217 | template<typename arg_0, typename ... args> |
218 | struct get_value_type |
219 | { |
220 | typedef typename arg_0::value_type type; |
221 | }; |
222 | |
223 | //! set of graphs |
224 | openfpm::vector<GGraph> set_of_graphs; |
225 | |
226 | //! set inject HTML; |
227 | openfpm::vector<std::string> injectHTML; |
228 | |
229 | //! Data has holes |
230 | bool holes = false; |
231 | |
232 | /*! \brief Recursively sort variadic template of vectors |
233 | * |
234 | * \param x vector with x |
235 | * \param y vector with y |
236 | * \param xy all the other vectors |
237 | * |
238 | */ |
239 | template<typename X, typename ... Xs> |
240 | void recursive_sort(X & x,X & y,Xs& ... xy) |
241 | { |
242 | struct srt_xy |
243 | { |
244 | typename X::value_type x; |
245 | unsigned int id; |
246 | |
247 | bool operator<(const srt_xy & tmp) const |
248 | { |
249 | return x < tmp.x; |
250 | } |
251 | }; |
252 | |
253 | openfpm::vector<srt_xy> reord; |
254 | X y_tmp; |
255 | |
256 | reord.resize(x.size()); |
257 | y_tmp.resize(x.size()); |
258 | |
259 | for (size_t i = 0 ; i < x.size() ; i++) |
260 | { |
261 | reord.get(i).x = x.get(i); |
262 | reord.get(i).id = i; |
263 | } |
264 | |
265 | reord.sort(); |
266 | |
267 | // reorder x and y |
268 | |
269 | for (size_t i = 0 ; i < x.size() ; i++) |
270 | { |
271 | x.get(i) = reord.get(i).x; |
272 | y_tmp.get(i) = y.get(reord.get(i).id); |
273 | } |
274 | |
275 | y_tmp.swap(y); |
276 | |
277 | // sort x |
278 | |
279 | recursive_sort(xy ...); |
280 | } |
281 | |
282 | //! terminator for recursive sort |
283 | void recursive_sort() |
284 | {} |
285 | |
286 | /*! \brief Recursively sort variadic template of vectors |
287 | * |
288 | * \param counters indexes |
289 | * \param x vector with x |
290 | * \param y vector with y |
291 | * \param xy all the other vectors |
292 | * |
293 | */ |
294 | template<typename X, typename ... Xs> |
295 | bool isNext(size_t * counters,X & x,X & y,Xs& ... xy) |
296 | { |
297 | if (counters[0] < x.size()) |
298 | { |
299 | return true; |
300 | } |
301 | return isNext(&counters[1],xy ...); |
302 | } |
303 | |
304 | /*! \brief Recursively sort variadic template of vectors |
305 | * |
306 | * \param counters indexes |
307 | * |
308 | */ |
309 | bool isNext(size_t * counters) |
310 | { |
311 | return false; |
312 | } |
313 | |
314 | |
315 | /*! \brief Recursively sort variadic template of vectors |
316 | * |
317 | * \param counters indexes |
318 | * \param x vector with x |
319 | * \param y vector with y |
320 | * \param xy all the other vectors |
321 | * |
322 | */ |
323 | template<typename T, typename X, typename ... Xs> |
324 | T get_low(size_t * counters,X & x,X & y,Xs& ... xy) |
325 | { |
326 | if (sizeof...(Xs) != 0) |
327 | { |
328 | T low1; |
329 | if (counters[0] >= x.size()) |
330 | {low1 = std::numeric_limits<typename X::value_type>::infinity();} |
331 | else |
332 | {low1 = x.get(counters[0]);} |
333 | |
334 | T low2 = get_low<T>(&counters[1],xy ...); |
335 | return (low1 < low2)?low1:low2; |
336 | } |
337 | |
338 | if (counters[0] >= x.size()) |
339 | {return std::numeric_limits<typename X::value_type>::infinity();} |
340 | return x.get(counters[0]); |
341 | } |
342 | |
343 | /*! \brief Recursively sort variadic template of vectors |
344 | * |
345 | * \param counters indexes |
346 | * |
347 | */ |
348 | template<typename T> |
349 | T get_low(size_t * counters) |
350 | { |
351 | return 0.0; |
352 | } |
353 | |
354 | /*! \brief Recursively sort variadic template of vectors |
355 | * |
356 | * \param x vector with x |
357 | * \param y vector with y |
358 | * \param xy all the other vectors |
359 | * |
360 | */ |
361 | template<typename X, typename ... Xs> |
362 | void get_point(typename X::value_type low, |
363 | typename X::value_type * point, |
364 | size_t * counters, |
365 | X & x, |
366 | X & y, |
367 | Xs& ... xy) |
368 | { |
369 | if (low == x.get(counters[0])) |
370 | { |
371 | point[0] = y.get(counters[0]); |
372 | counters[0]++; |
373 | } |
374 | else |
375 | { |
376 | point[0] = std::numeric_limits<typename X::value_type>::quiet_NaN(); |
377 | } |
378 | |
379 | get_point(low,&point[1],&counters[1],xy...); |
380 | } |
381 | |
382 | /*! \brief Recursively sort variadic template of vectors |
383 | * |
384 | * \param x vector with x |
385 | * \param y vector with y |
386 | * \param xy all the other vectors |
387 | * |
388 | */ |
389 | template<typename T> |
390 | void get_point(T low, |
391 | T * point, |
392 | size_t * counters) |
393 | { |
394 | return; |
395 | } |
396 | |
397 | /*! \brief Recursively sort variadic template of vectors |
398 | * |
399 | * \param x vector with x |
400 | * \param y vector with y |
401 | * \param xy all the other vectors |
402 | * |
403 | */ |
404 | template<typename ... Xs> |
405 | bool get_v(typename get_value_type<Xs...>::type & x, |
406 | typename get_value_type<Xs...>::type * point, |
407 | size_t * counters, |
408 | Xs& ... xy) |
409 | { |
410 | // if exist the next element |
411 | if (isNext(counters,xy...) == false) |
412 | {return false;} |
413 | |
414 | // get lowest x |
415 | typename get_value_type<Xs...>::type low = get_low<typename get_value_type<Xs...>::type>(counters,xy...); |
416 | |
417 | x = low; |
418 | |
419 | get_point(low,point,counters,xy...); |
420 | |
421 | return true; |
422 | } |
423 | |
424 | /*! \brief Given X and Y vector return the string representing the data section of the Google Chart |
425 | * |
426 | * \tparam X type for the X coordinates |
427 | * \tparam Y type for the Y coordinates |
428 | * |
429 | * \param x vector of points on x |
430 | * \param y vector of points on y |
431 | * \param yn vector containing the name of each graph |
432 | * \param opt options to draw the graph |
433 | * \param i index of the graph we are drawing |
434 | * |
435 | * \return string with the data section |
436 | * |
437 | */ |
438 | template<typename X, typename Y> std::string get_points_plot_data(const openfpm::vector<X> & x, const openfpm::vector<Y> & y, const openfpm::vector<std::string> & yn, const GCoptions & opt, size_t i) |
439 | { |
440 | std::stringstream data; |
441 | |
442 | size_t interval = 0; |
443 | |
444 | // we require that the number of x elements are the same as y elements |
445 | |
446 | if (x.size() != y.size()) |
447 | {std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector x and the vector y must have the same number of elements " << x.size() << "!=" << y.size() << "\n" ;} |
448 | |
449 | // Google chart visualization |
450 | data << "var data" << i << " = new google.visualization.DataTable();\n" ; |
451 | if (std::is_same<X,typename std::string>::value == true) |
452 | data << "data" << i << ".addColumn(" << "'string'" << "," << "'" << opt.xAxis <<"');\n" ; |
453 | else |
454 | data << "data" << i << ".addColumn(" << "'number'" << "," << "'" << opt.xAxis <<"');\n" ; |
455 | |
456 | for (size_t j = 0 ; j < y.last().size() ; j++) |
457 | { |
458 | if (yn.get(j) == std::string("interval" )) |
459 | { |
460 | data << "data" << i << ".addColumn({id:'i" << interval/2 << "', type:'number', role:'interval'});\n" ; |
461 | interval++; |
462 | } |
463 | else |
464 | data << "data" << i << ".addColumn(" << "'number'" << "," << "'" << yn.get(j) <<"');\n" ; |
465 | } |
466 | |
467 | data << "data" << i << ".addRows([\n" ; |
468 | for (size_t i = 0 ; i < y.size() && x.size() ; i++) |
469 | { |
470 | |
471 | for (size_t j = 0 ; j < y.get(i).size()+1 ; j++) |
472 | { |
473 | // the first is x |
474 | if (j == 0) |
475 | { |
476 | if (std::is_same<X,typename std::string>::value == true) |
477 | data << "['" << x.get(i) << "'" ; |
478 | else |
479 | data << "[" << x.get(i); |
480 | } |
481 | else |
482 | { |
483 | if (check_nan<typename Y::value_type>::check(y.get(i).get(j-1)) == false) |
484 | { |
485 | data << "," << y.get(i).get(j-1); |
486 | } |
487 | else |
488 | { |
489 | holes = true; |
490 | data << "," << "null" ; |
491 | } |
492 | } |
493 | } |
494 | data << "],\n" ; |
495 | } |
496 | |
497 | return data.str(); |
498 | } |
499 | |
500 | /*! \brief Construct a view option |
501 | * |
502 | * \param opt GoogleChart option |
503 | * |
504 | * \return the string |
505 | * |
506 | */ |
507 | std::string get_view_bar_option(const GCoptions & opt, size_t n_col) |
508 | { |
509 | if (opt.barWD == false) |
510 | return std::string(); |
511 | |
512 | std::stringstream str; |
513 | |
514 | str << "[0" << std::endl; |
515 | |
516 | for (size_t i = 1 ; i < n_col ; i++) |
517 | { |
518 | str << "," << i << ",{ calc: \"stringify\"," << std::endl; |
519 | str << "sourceColumn: " << i << "," << std::endl; |
520 | str << "type: \"string\"," << std::endl; |
521 | str << "role: \"annotation\" }" << std::endl; |
522 | } |
523 | |
524 | str << "]" << std::endl; |
525 | |
526 | return str.str(); |
527 | } |
528 | |
529 | std::string get_colums_bar_option(const GCoptions & opt) |
530 | { |
531 | std::stringstream str; |
532 | str << "title : '" << opt.title << "'" ; |
533 | str << ",\nvAxis: {title: '" << opt.yAxis << "'}" ; |
534 | str << ",\nhAxis: {title: '" << opt.xAxis << "'}" ; |
535 | str << ",\nseriesType: '" << opt.stype << "'" ; |
536 | if (opt.stypeext.size() != 0) |
537 | str << ",\nseries: " << opt.stypeext; |
538 | if (opt.more.size() != 0) |
539 | str << ",\n" <<opt.more; |
540 | |
541 | return str.str(); |
542 | } |
543 | |
544 | std::string get_points_plot_option(const GCoptions & opt) |
545 | { |
546 | std::stringstream str; |
547 | str << "title : '" << opt.title << "'" ; |
548 | str << ",\nvAxis: {title: '" << opt.yAxis << "'}" ; |
549 | str << ",\nhAxis: {title: '" << opt.xAxis << "'}" ; |
550 | str << ",\ncurveType: '" << opt.curveType << "'" ; |
551 | |
552 | str << ",\nlineWidth: " << opt.lineWidth; |
553 | if (opt.intervalsext.size() != 0) |
554 | str << ",\nintervals: " << opt.intervalsext; |
555 | else |
556 | str << ",\nintervals: " << "{ 'style':'area' }" ; |
557 | |
558 | if (opt.intervalext.size() != 0) |
559 | str << ",\ninterval: " << opt.intervalext << "\n" ; |
560 | |
561 | if (opt.more.size() != 0) |
562 | str << ",\n" << opt.more; |
563 | |
564 | return str.str(); |
565 | } |
566 | |
567 | /*! \brief Add a graph data variable |
568 | * |
569 | * \param of file out |
570 | * \param i id |
571 | * \param data string |
572 | * |
573 | */ |
574 | void addData(std::ofstream & of, size_t i, const std::string & data) |
575 | { |
576 | |
577 | of << data; |
578 | of << "]);\n" ; |
579 | } |
580 | |
581 | /*! \brief Add an option data variable |
582 | * |
583 | * \param of file out |
584 | * \param i id |
585 | * \param opt string |
586 | * |
587 | */ |
588 | void addOption(std::ofstream & of, size_t i, const std::string & opt) |
589 | { |
590 | of << "var options" ; |
591 | of << i; |
592 | of << "= {\n" ; |
593 | if (holes == true) |
594 | {of << "interpolateNulls : true,\n" ;} |
595 | of << opt; |
596 | of << "};\n" ; |
597 | } |
598 | |
599 | /*! \brief Add a view data variable |
600 | * |
601 | * \param of file out |
602 | * \param i id |
603 | * \param view string |
604 | * |
605 | */ |
606 | void addView(std::ofstream & of, size_t i, std::string view) |
607 | { |
608 | if (view.size() == 0) |
609 | return; |
610 | |
611 | of << "var view" << i << " = new google.visualization.DataView(data" << i << ");" << std::endl; |
612 | of << "view" << i << ".setColumns(" ; |
613 | of << view << ");" << std::endl; |
614 | } |
615 | |
616 | /*! \brief Add a draw div section |
617 | * |
618 | * \param of file out |
619 | * \param i id |
620 | * \param draw_view draw a chart(true) or view(false) |
621 | * |
622 | */ |
623 | void addDrawDiv(std::ofstream & of, size_t i, bool draw_view) |
624 | { |
625 | of << "$(\"#export_svg" << i << "\").on(\"click\", function (event) {exportToSVG.apply(this,[" << i << "]);});\n" ; |
626 | of << "var chart = new google.visualization.ComboChart(document.getElementById('chart_div" ; |
627 | of << i; |
628 | of << "'));" << std::endl; |
629 | if (draw_view == true) |
630 | { |
631 | of << "chart.draw(data" ; |
632 | of << i; |
633 | } |
634 | else |
635 | { |
636 | of << "chart.draw(view" ; |
637 | of << i; |
638 | } |
639 | of << ", options" ; |
640 | of << i; |
641 | of << ");\n" ; |
642 | } |
643 | |
644 | /*! \brief Add a div section |
645 | * |
646 | * \param of file ofstream |
647 | * \param i id of the graph |
648 | * \param gc GoogleChart option |
649 | * |
650 | */ |
651 | void addDiv(std::ofstream & of, size_t i, const GCoptions & gc) |
652 | { |
653 | of << "<a href=\"#\" download=\"graph1.svg\" id=\"export_svg" << i << "\"><button>Export data into svg</button></a>" ; |
654 | of << "<div id=\"chart_div" ; |
655 | of << i; |
656 | of << "\" style=\"width: " ; |
657 | of << gc.width; |
658 | of << "px; height: " ; |
659 | of << gc.heigh; |
660 | of << "px;\"></div>\n" ; |
661 | } |
662 | |
663 | public: |
664 | |
665 | GoogleChart() |
666 | { |
667 | injectHTML.add(); |
668 | } |
669 | |
670 | /*! \brief Add an histogram graph |
671 | * |
672 | * \param y A vector of vectors the size of y indicate how many values we have on x |
673 | * each x value can have multiple values or datasets |
674 | * |
675 | */ |
676 | template<typename Y> void AddHistGraph(openfpm::vector<Y> & y) |
677 | { |
678 | openfpm::vector<std::string> x; |
679 | x.resize(y.size()); |
680 | |
681 | AddHistGraph<std::string,Y>(x,y); |
682 | } |
683 | |
684 | /*! \brief Add an histogram graph |
685 | * |
686 | * \param y A vector of vectors the size of y indicate how many values we have on x |
687 | * each x value can have multiple values or datasets |
688 | * |
689 | * \param x Give a name or number to each colums. Can be a string or a number |
690 | * |
691 | */ |
692 | template<typename X, typename Y> void AddHistGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y) |
693 | { |
694 | GCoptions opt; |
695 | |
696 | openfpm::vector<std::string> yn; |
697 | |
698 | if (y.size() != 0) |
699 | yn.resize(y.get(0).size()); |
700 | |
701 | AddHistGraph<X,Y,std::string>(x,y,yn,opt); |
702 | } |
703 | |
704 | /*! \brief Add an histogram graph |
705 | * |
706 | * \param y A vector of vectors the size of y indicate how many values we have on x |
707 | * each x value can have multiple values or datasets |
708 | * |
709 | * \param x Give a name or number to each colums. Can be a string or a number |
710 | * |
711 | * \param yn Give a name to each dataset |
712 | * |
713 | */ |
714 | template<typename X, typename Y, typename Yn> void AddHistGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y, openfpm::vector<Yn> & yn) |
715 | { |
716 | GCoptions opt; |
717 | |
718 | AddHistGraph(x,y,yn,opt); |
719 | } |
720 | |
721 | /*! \brief Add an histogram graph |
722 | * |
723 | * \param y A vector of vectors the size of y indicate how many values we have on x |
724 | * each x value can have multiple values or datasets |
725 | * |
726 | * \param x Give a name or number to each colums. Can be a string or a number |
727 | * |
728 | * \param yn Give a name to each dataset |
729 | * |
730 | * \param opt Graph options |
731 | * |
732 | */ |
733 | template<typename X, typename Y, typename Yn> void AddHistGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y, openfpm::vector<Yn> & yn , const GCoptions & opt) |
734 | { |
735 | set_of_graphs.add(); |
736 | injectHTML.add(); |
737 | |
738 | // Check that all the internal vector has the same number of elements |
739 | |
740 | if (y.size() != 0) |
741 | { |
742 | size_t sz = y.get(0).size(); |
743 | for (size_t i = 0; i < y.size() ; i++) |
744 | { |
745 | if (y.get(i).size() != sz) |
746 | std::cerr << __FILE__ << ":" << __LINE__ << " error all the elements in the y vector must have the same numbers, element " << i << ": " << y.get(i).size() << " " << " mismatch the numbers of elements at 0: " << sz << "/n" ; |
747 | } |
748 | } |
749 | |
750 | set_of_graphs.last().type = GGRAPH_COLUMS; |
751 | set_of_graphs.last().data = get_points_plot_data(x,y,yn,opt,set_of_graphs.size()-1); |
752 | set_of_graphs.last().option = get_colums_bar_option(opt); |
753 | set_of_graphs.last().view = get_view_bar_option(opt,y.get(0).size()); |
754 | set_of_graphs.last().opt = opt; |
755 | } |
756 | |
757 | |
758 | /*! \brief Add lines graph |
759 | * |
760 | * \param xy list of vectors like openfpm::vector<float> or openfpm::vector<double>. Suppose you have a dataset of points x1,y1 |
761 | * and a dataset x2,y2 and you want to display all these points in one graph. Than you call this function with |
762 | * AddLines(x1,y1,x2,y2,opt) |
763 | * |
764 | * \param opt Graph options |
765 | * |
766 | */ |
767 | template<typename ... X> void AddLines(const openfpm::vector<std::string> & yn, |
768 | const GCoptions & opt, |
769 | X ... xy) |
770 | { |
771 | // first we sort the vectors |
772 | recursive_sort(xy ... ); |
773 | |
774 | size_t counters[sizeof...(X)]; |
775 | memset(&counters,0,sizeof(counters)); |
776 | |
777 | typename get_value_type<X...>::type point[sizeof...(X)]; |
778 | typename get_value_type<X...>::type xe; |
779 | |
780 | openfpm::vector<typename get_value_type<X...>::type> x; |
781 | |
782 | openfpm::vector<openfpm::vector<typename get_value_type<X...>::type>> y; |
783 | |
784 | |
785 | while (get_v(xe,point,counters,xy...)) |
786 | { |
787 | y.add(); |
788 | x.add(xe); |
789 | |
790 | for (size_t i = 0 ; i < sizeof...(X)/2 ; i++) |
791 | { |
792 | y.last().add(point[i]); |
793 | } |
794 | } |
795 | |
796 | AddLinesGraph(x,y,yn,opt); |
797 | } |
798 | |
799 | /*! \brief Add lines graph |
800 | * |
801 | * If the x vector contain 0.0,0.1,0.2,0.3,0.4 and you want to draw two lines you specifying two y values for each of the x points |
802 | * you create an openfpm::vector<openfpm::vector<float>> y such that |
803 | * |
804 | * \verbatim |
805 | ------ j ------- | |
806 | y.get(i).get(j) = 3.2 3.4 5.4 1.3 2.3 i |
807 | 1.4 5.3 3.2 2.1 1.1 | |
808 | |
809 | \endverbatim |
810 | * |
811 | * \param y A vector of vectors of values. each vector contain the graph points, compared to AddLinesGraph the points are stored |
812 | * |
813 | * |
814 | * \param x axis values |
815 | * |
816 | * \param opt Graph options |
817 | * |
818 | */ |
819 | template<typename X, typename Y> void AddLinesGraphT(openfpm::vector<X> & x, openfpm::vector<Y> & y , const GCoptions & opt) |
820 | { |
821 | openfpm::vector<std::string> yn; |
822 | |
823 | if (y.size() == 0) |
824 | { |
825 | std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector y must be filled" << std::endl; |
826 | return; |
827 | } |
828 | |
829 | for (size_t i = 0 ; i < y.last().size() ; i++) |
830 | yn.add(std::string("line" ) + std::to_string(i)); |
831 | |
832 | if (y.size() == 0) |
833 | return; |
834 | |
835 | // number of points |
836 | size_t np = y.last().size(); |
837 | |
838 | for (size_t j = 0 ; j < y.size() ; j++) |
839 | { |
840 | if (y.get(j).size() != np) |
841 | std::cerr << __FILE__ << ":" << __LINE__ << " Error all the graph must have the same number of points " << np << "!=" << y.get(j).size() << std::endl; |
842 | } |
843 | |
844 | openfpm::vector<openfpm::vector<X>> swap; |
845 | |
846 | |
847 | // swap the vector |
848 | // Each vector is a graph |
849 | // It is different from the other call where each vector |
850 | // has multiple value for the same point |
851 | for (size_t i = 0 ; i < np ; i++) |
852 | { |
853 | swap.add(); |
854 | for (size_t j = 0 ; j < y.size() ; j++) |
855 | { |
856 | swap.last().add(y.get(j).get(i)); |
857 | } |
858 | } |
859 | |
860 | AddLinesGraph(x,swap,yn,opt); |
861 | } |
862 | |
863 | /*! \brief Add a simple lines graph |
864 | * |
865 | * If the x vector contain 0.0,0.1,0.2,0.3,0.4 and you want to draw two lines you specifying two y values for each of the x points |
866 | * you create an openfpm::vector<openfpm::vector<float>> y such that |
867 | * |
868 | * \verbatim |
869 | -- j -- |
870 | y.get(i).get(j) = 3.2 3.4 |
871 | 1.4 5.3 | |
872 | 5.4 3.2 i |
873 | 1.3 2.2 | |
874 | 2.3 1.1 |
875 | \endverbatim |
876 | * |
877 | * \param y A vector of vectors of values. The size of y indicate how many x values |
878 | * we have, while the internal vector can store multiple value of the same point, |
879 | * for example error bar |
880 | * |
881 | * \param x Give a name or number to each x value, so can be a string or a number |
882 | * |
883 | * \param opt Graph options |
884 | * |
885 | */ |
886 | template<typename X, typename Y> void AddLinesGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y , const GCoptions & opt) |
887 | { |
888 | openfpm::vector<std::string> yn; |
889 | |
890 | if (y.size() == 0) |
891 | { |
892 | std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector y must be filled" ; |
893 | return; |
894 | } |
895 | |
896 | for (size_t i = 0 ; i < y.last().size() ; i++) |
897 | yn.add(std::string("line" ) + std::to_string(i)); |
898 | |
899 | AddLinesGraph(x,y,yn,opt); |
900 | } |
901 | |
902 | /*! \brief Add a simple plot graph |
903 | * |
904 | * \param y A vector of vector of values (numbers) the size of y indicate how many x values |
905 | * or colums we have, while the internal vector store multiple lines, |
906 | * or error bars |
907 | * |
908 | * \param x Give a name or number to each colums, so can be a string or a number |
909 | * |
910 | * \param yn Give a name to each line, or specify an error bar |
911 | * |
912 | * \param opt Graph options |
913 | * |
914 | */ |
915 | template<typename X, typename Y> void AddLinesGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y , const openfpm::vector<std::string> & yn, const GCoptions & opt) |
916 | { |
917 | if (y.size() == 0) |
918 | { |
919 | std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector y must be filled\n" ; |
920 | return; |
921 | } |
922 | |
923 | set_of_graphs.add(); |
924 | injectHTML.add(); |
925 | |
926 | // Check that all the internal vectors has the same number of elements |
927 | |
928 | if (y.size() != 0) |
929 | { |
930 | size_t sz = y.get(0).size(); |
931 | for (size_t i = 0; i < y.size() ; i++) |
932 | { |
933 | if (y.get(i).size() != sz) |
934 | std::cerr << __FILE__ << ":" << __LINE__ << " error all the elements in the y vector must have the same numbers of elements " << y.get(i).size() << " != " << sz << "\n" ; |
935 | } |
936 | } |
937 | |
938 | set_of_graphs.last().type = GGRAPH_POINTS; |
939 | set_of_graphs.last().data = get_points_plot_data(x,y,yn,opt,set_of_graphs.size()-1); |
940 | set_of_graphs.last().option = get_points_plot_option(opt); |
941 | set_of_graphs.last().opt = opt; |
942 | } |
943 | |
944 | /*! \brief Add HTML text |
945 | * |
946 | * \param html add html text in the page |
947 | * |
948 | */ |
949 | void addHTML(const std::string & html) |
950 | { |
951 | injectHTML.last() = html; |
952 | } |
953 | |
954 | /*! \brief It write the graphs on file in html format using Google charts |
955 | * |
956 | * \param file output file |
957 | * |
958 | */ |
959 | void write(std::string file) |
960 | { |
961 | // Open a file |
962 | |
963 | std::ofstream of(file); |
964 | |
965 | // Check if the file is open |
966 | if (of.is_open() == false) |
967 | {std::cerr << "Error cannot create the HTML file: " + file + "\n" ;} |
968 | |
969 | // write the file |
970 | |
971 | of << begin_data; |
972 | |
973 | for (size_t i = 0 ; i < set_of_graphs.size() ; i++) |
974 | addData(of,i,set_of_graphs.get(i).data); |
975 | |
976 | for (size_t i = 0 ; i < set_of_graphs.size() ; i++) |
977 | addOption(of,i,set_of_graphs.get(i).option); |
978 | |
979 | for (size_t i = 0 ; i < set_of_graphs.size() ; i++) |
980 | addView(of,i,set_of_graphs.get(i).view); |
981 | |
982 | for (size_t i = 0 ; i < set_of_graphs.size() ; i++) |
983 | addDrawDiv(of,i,set_of_graphs.get(i).view.size() == 0); |
984 | |
985 | of << begin_div; |
986 | |
987 | of << injectHTML.get(0); |
988 | |
989 | for (size_t i = 0 ; i < set_of_graphs.size() ; i++) |
990 | { |
991 | addDiv(of,i,set_of_graphs.get(i).opt); |
992 | of << injectHTML.get(i+1); |
993 | } |
994 | |
995 | of << div_end; |
996 | |
997 | of.close(); |
998 | } |
999 | }; |
1000 | |
1001 | #endif /* OPENFPM_DATA_SRC_PLOT_GOOGLECHART_HPP_ */ |
1002 | |