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 */
25struct 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 */
103struct 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
123const 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\
131function exportToSVG(i)\n\
132{\n\
133var e = document.getElementById('chart_div'+i);\n\
134var svg = e.getElementsByTagName('svg')[0].parentNode.innerHTML;\n\
135var pos = svg.lastIndexOf(\"</svg>\");\n\
136pos += 6;\n\
137svg = svg.substring(0,4) + \" xmlns='http://www.w3.org/2000/svg' xmlns:xlink= 'http://www.w3.org/1999/xlink' \" + svg.substring(4,pos);\n\
138svgData = '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
144const std::string end_data="]);\n\n";
145
146const std::string begin_div = "}</script>\n\
147</head>\n\
148<body>\n";
149
150const std::string div_end = "</body>\n\
151</html>\n";
152
153const std::string saving_javascript = "function save(i)\n\
154 var e = document.getElementById('chart_')\n\
155 e.getElementsByTagName('svg')[0].parentNode.innerHTML";
156
157template<typename T>
158struct check_nan
159{
160 static bool check(const T & n)
161 {
162 return false;
163 }
164};
165
166template<>
167struct check_nan<float>
168{
169 static bool check(const float & n)
170 {
171 return std::isnan(n);
172 }
173};
174
175template<>
176struct 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 */
215class 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
663public:
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