/* * GoogleChart.hpp * * Created on: Jan 9, 2016 * Author: i-bird */ #ifndef OPENFPM_DATA_SRC_PLOT_GOOGLECHART_HPP_ #define OPENFPM_DATA_SRC_PLOT_GOOGLECHART_HPP_ #include #define GGRAPH_COLUMS 1 #define GGRAPH_POINTS 2 /*! \brief Google chart options * */ struct GCoptions { //! Title of the chart std::string title; //! Y axis name std::string yAxis; //! X axis name std::string xAxis; //! Type of chart (list of the option can be founded in Google Chart API for seriesType) //! Possible options are: //! 'line', 'area', 'bars', 'candlesticks', and 'steppedArea' //! default: line std::string stype; //! Extended series options //! Example {5: {type: 'line'}} specify that the series number 5 must be represented //! with a line std::string stypeext; size_t width=900; size_t heigh=500; //! Flag that specify if the colums are stacked //! Check in Google Chart for is stacked option bool isStacked = false; //! Width of the line size_t lineWidth = 4; //! Style for all the intervals //! Check Google Chart API intervals option std::string intervalsext; //! Style for each interval //! Check Google Chart API interval option std::string intervalext; GCoptions & operator=(const GCoptions & opt) { title = opt.title; yAxis = opt.yAxis; xAxis = opt.xAxis; stype = opt.stype; stypeext = opt.stypeext; width=opt.width; heigh=opt.heigh; lineWidth = opt.lineWidth; intervalsext = opt.intervalsext; return *this; } }; struct GGraph { // TypeOfGraph size_t type; // data std::string data; // option std::string option; // Google chart option GCoptions opt; }; /////////////////// Constants strings usefull to construct the HTML page ////////// const std::string begin_data ="\n\ \n\ \n\ \n\ \n\ \n"; const std::string div_end = "\n\ \n"; ///////////////////////////////////////////////////////////////////// /*! It convert an array y or a set of two array x,y into a Google chart * */ class GoogleChart { // set of graphs openfpm::vector set_of_graphs; // set inject HTML; openfpm::vector injectHTML; /*! \brief Given X and Y vector return the string representing the data section of the Google Chart * * \param X vector * \param Y vector * \param i counter * * \return string with the data section * */ template std::string get_points_plot_data(const openfpm::vector & x, const openfpm::vector & y, const openfpm::vector & yn, const GCoptions & opt, size_t i) { std::stringstream data; size_t interval = 0; // we require that the number of x elements are the same as y elements if (x.size() != y.size()) std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector x and the vector y must have the same number of elements " << x.size() << "!=" << y.size() << "\n"; // Google chart visualization data << "var data" << i << " = new google.visualization.DataTable();\n"; if (std::is_same::value == true) data << "data" << i << ".addColumn(" << "'string'" << "," << "'" << opt.xAxis <<"');\n"; else data << "data" << i << ".addColumn(" << "'number'" << "," << "'" << opt.xAxis <<"');\n"; for (size_t j = 0 ; j < y.last().size() ; j++) { if (yn.get(j) == std::string("interval")) { data << "data" << i << ".addColumn({id:'i" << interval/2 << "', type:'number', role:'interval'});\n"; interval++; } else data << "data" << i << ".addColumn(" << "'number'" << "," << "'" << yn.get(j) <<"');\n"; } data << "data" << i << ".addRows([\n"; for (size_t i = 0 ; i < y.size() ; i++) { for (size_t j = 0 ; j < y.get(i).size()+1 ; j++) { // the first is x if (j == 0) { if (std::is_same::value == true) data << "['" << x.get(i) << "'"; else data << "[" << x.get(i); } else data << "," << y.get(i).get(j-1); } data << "],\n"; } return data.str(); } /*! \brief Given X and Y vector return the string representing the data section of the Google Chart * * \param X vector * \param Y vector * * \return string with the data section * */ template std::string get_colums_bar_data(const openfpm::vector & x, const openfpm::vector & y, const openfpm::vector & yn, const GCoptions & opt, size_t i) { std::stringstream data; data << "var data"; data << i; data << " = google.visualization.arrayToDataTable([\n"; // we require that the number of x elements are the same as y elements if (x.size() != y.size()) std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector x and the vector y must have the same number of elements " << x.size() << "!=" << y.size() << "\n"; data << "['"; data << opt.xAxis << "'"; for (size_t i = 0 ; i < yn.size() ; i++) data << ",'" << std::to_string(yn.get(i)) << "'"; data << "],\n"; // Write the values for (size_t i = 0 ; i < y.size() ; i++) { data << "["; data << "'" << std::to_string(x.get(i)) << "'"; for (size_t j = 0 ; j < y.get(i).size() ; j++) data << "," << std::to_string(y.get(i).get(j)); data << "],\n"; } return data.str(); } std::string get_colums_bar_option(const GCoptions & opt) { std::stringstream str; str << "title : '" << opt.title << "',\n"; str << "vAxis: {title: '" << opt.yAxis << "'},\n"; str << "hAxis: {title: '" << opt.xAxis << "'},\n"; str << "seriesType: '" << opt.stype << "',\n"; if (opt.stypeext.size() != 0) str << "series: " << opt.stypeext << "\n"; return str.str(); } std::string get_points_plot_option(const GCoptions & opt) { std::stringstream str; str << "title : '" << opt.title << "',\n"; str << "vAxis: {title: '" << opt.yAxis << "'},\n"; str << "hAxis: {title: '" << opt.xAxis << "'},\n"; str << "curveType: 'function',\n"; str << "lineWidth: " << opt.lineWidth << ",\n"; if (opt.intervalsext.size() != 0) str << "intervals: " << opt.intervalsext << ",\n"; else str << "intervals: " << "{ 'style':'area' }" << ",\n"; if (opt.intervalext.size() != 0) str << "interval: " << opt.intervalext << "\n"; return str.str(); } /*! \brief Add a graph data variable * * \param of file out * \param i id * \param data string * */ void addData(std::ofstream & of, size_t i, const std::string & data) { of << data; of << "]);\n"; } /*! \brief Add an option data variable * * \param of file out * \param i id * \param opt string * */ void addOption(std::ofstream & of, size_t i, const std::string & opt) { of << "var options"; of << i; of << "= {\n"; of << opt; of << "};\n"; } /*! \brief Add a draw div section * * \param of file out * \param i id * */ void addDrawDiv(std::ofstream & of, size_t i) { of << "var chart = new google.visualization.ComboChart(document.getElementById('chart_div"; of << i; of << "'));chart.draw(data"; of << i; of << ", options"; of << i; of << ");\n"; } /*! \brief Add a div section * * \param i id * \param gc GoogleChart option * */ void addDiv(std::ofstream & of, size_t i, const GCoptions & gc) { of << "
\n"; } public: GoogleChart() { injectHTML.add(); } /*! \brief Add a colums graph * * \param y A vector of vector of values (numbers) the size of y indicate how many columns * has the graph while the internal vector can store multiple datasets * */ template void AddColumsGraph(openfpm::vector & y) { openfpm::vector x; x.resize(y.size()); AddColumsGraph(x,y); } /*! \brief Add a colums graph * * \param y A vector of vector of values (numbers) the size of y indicate how many columns * has the graph while the internal vector can store multiple datasets * * \param x Give a name or number to each colums, so can be a string or a number * */ template void AddColumsGraph(openfpm::vector & x, openfpm::vector & y) { GCoptions opt; openfpm::vector yn; if (y.size() != 0) yn.resize(y.get(0).size()); AddColumsGraph(x,y,yn,opt); } /*! \brief Add a colums graph * * \param y A vector of vector of values (numbers) the size of y indicate how many columns * has the graph while the internal vector can store multiple datasets * * \param x Give a name or number to each colums, so can be a string or a number * * \param yn Give a name to each dataset * */ template void AddColumsGraph(openfpm::vector & x, openfpm::vector & y, openfpm::vector & yn) { GCoptions opt; AddColumsGraph(x,y,yn,opt); } /*! \brief Add a colums graph * * \param y A vector of vector of values (numbers) the size of y indicate how many columns * has the graph while the internal vector can store multiple datasets * * \param x Give a name or number to each colums, so can be a string or a number * * \param yn Give a name to each dataset * * \param opt Graph options * */ template void AddColumsGraph(openfpm::vector & x, openfpm::vector & y, openfpm::vector & yn , const GCoptions & opt) { set_of_graphs.add(); injectHTML.add(); // Check that all the internal vector has the same number of elements if (y.size() != 0) { size_t sz = y.get(0).size(); for (size_t i = 0; i < y.size() ; i++) { if (y.get(i).size() != sz) 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"; } } set_of_graphs.last().type = GGRAPH_COLUMS; set_of_graphs.last().data = get_colums_bar_data(x,y,yn,opt,set_of_graphs.size()-1); set_of_graphs.last().option = get_colums_bar_option(opt); set_of_graphs.last().opt = opt; } /*! \brief Add a simple plot graph * * \param y A vector of vector of values (numbers) the size of y indicate how many x values * or colums do we have, while the internal vector can store multiple realizations, * or min and max, for error bar * * \param x Give a name or number to each colums, so can be a string or a number * * \param opt Graph options * */ template void AddPointsGraph(openfpm::vector & x, openfpm::vector & y , const GCoptions & opt) { openfpm::vector yn; if (y.size() == 0) { std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector y must be filled"; return; } for (size_t i = 0 ; i < y.last().size() ; i++) yn.add(std::string("line") + std::to_string(i)); AddPointsGraph(x,y,yn,opt); } /*! \brief Add a simple plot graph * * \param y A vector of vector of values (numbers) the size of y indicate how many x values * or colums we have, while the internal vector store multiple lines, * or error bars * * \param x Give a name or number to each colums, so can be a string or a number * * \param yn Give a name to each line, or specify an error bar * * \param opt Graph options * */ template void AddPointsGraph(openfpm::vector & x, openfpm::vector & y , const openfpm::vector & yn, const GCoptions & opt) { if (y.size() == 0) { std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector y must be filled\n"; return; } set_of_graphs.add(); injectHTML.add(); // Check that all the internal vectors has the same number of elements if (y.size() != 0) { size_t sz = y.get(0).size(); for (size_t i = 0; i < y.size() ; i++) { if (y.get(i).size() != sz) 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"; } } set_of_graphs.last().type = GGRAPH_POINTS; set_of_graphs.last().data = get_points_plot_data(x,y,yn,opt,set_of_graphs.size()-1); set_of_graphs.last().option = get_points_plot_option(opt); set_of_graphs.last().opt = opt; } /*! \brief Add HTML text * * \param html add html text in the page * */ void addHTML(const std::string & html) { injectHTML.last() = html; } /*! \brief It write the html file * * \param file output html file * */ void write(std::string file) { // Open a file std::ofstream of(file); // Check if the file is open if (of.is_open() == false) {std::cerr << "Error cannot create the HTML file: " + file + "\n";} // write the file of << begin_data; for (size_t i = 0 ; i < set_of_graphs.size() ; i++) addData(of,i,set_of_graphs.get(i).data); for (size_t i = 0 ; i < set_of_graphs.size() ; i++) addOption(of,i,set_of_graphs.get(i).option); for (size_t i = 0 ; i < set_of_graphs.size() ; i++) addDrawDiv(of,i); of << begin_div; of << injectHTML.get(0); for (size_t i = 0 ; i < set_of_graphs.size() ; i++) { addDiv(of,i,set_of_graphs.get(i).opt); of << injectHTML.get(i+1); } of << div_end; of.close(); } }; #endif /* OPENFPM_DATA_SRC_PLOT_GOOGLECHART_HPP_ */