From cf974ef2fe73ae7831d108fa32ff5d86e54022cf Mon Sep 17 00:00:00 2001 From: Pietro Incardona <i-bird@localhost.localdomain> Date: Tue, 19 Jan 2016 16:06:25 -0500 Subject: [PATCH] Adding Google Chart --- src/Plot/GoogleChart.hpp | 199 +++++++++++++++++++++++++++++++++-- src/Plot/Plot_unit_tests.hpp | 98 +++++++++++++++++ 2 files changed, 289 insertions(+), 8 deletions(-) diff --git a/src/Plot/GoogleChart.hpp b/src/Plot/GoogleChart.hpp index f718a69a..36445e19 100644 --- a/src/Plot/GoogleChart.hpp +++ b/src/Plot/GoogleChart.hpp @@ -11,6 +11,7 @@ #include <fstream> #define GGRAPH_COLUMS 1 +#define GGRAPH_POINTS 2 /*! \brief Google chart options * @@ -38,8 +39,34 @@ struct GCoptions 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; } }; @@ -93,14 +120,83 @@ class GoogleChart // set inject HTML; openfpm::vector<std::string> injectHTML; - /*! \brief Given X and Y vector + /*! \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<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) + { + 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<X,typename std::string>::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<X,typename std::string>::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<typename X, typename Y, typename Yn> std::string get_data(const openfpm::vector<X> & x, const openfpm::vector<Y> & y, const openfpm::vector<Yn> & yn, const GCoptions & opt) + template<typename X, typename Y, typename Yn> std::string get_colums_bar_data(const openfpm::vector<X> & x, const openfpm::vector<Y> & y, const openfpm::vector<Yn> & 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()) @@ -127,7 +223,7 @@ class GoogleChart return data.str(); } - std::string get_option(const GCoptions & opt) + std::string get_colums_bar_option(const GCoptions & opt) { std::stringstream str; str << "title : '" << opt.title << "',\n"; @@ -140,6 +236,26 @@ class GoogleChart 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 @@ -149,9 +265,7 @@ class GoogleChart */ void addData(std::ofstream & of, size_t i, const std::string & data) { - of << "var data"; - of << i; - of << " = google.visualization.arrayToDataTable([\n"; + of << data; of << "]);\n"; } @@ -294,8 +408,77 @@ public: } set_of_graphs.last().type = GGRAPH_COLUMS; - set_of_graphs.last().data = get_data(x,y,yn,opt); - set_of_graphs.last().option = get_option(opt); + 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<typename X, typename Y> void AddPointsGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y , const GCoptions & opt) + { + openfpm::vector<std::string> 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<typename X, typename Y> void AddPointsGraph(openfpm::vector<X> & x, openfpm::vector<Y> & y , const openfpm::vector<std::string> & yn, const GCoptions & opt) + { + if (y.size() == 0) + { + std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " vector y must be filled"; + 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; } diff --git a/src/Plot/Plot_unit_tests.hpp b/src/Plot/Plot_unit_tests.hpp index c7591ecf..f3df91bb 100644 --- a/src/Plot/Plot_unit_tests.hpp +++ b/src/Plot/Plot_unit_tests.hpp @@ -284,6 +284,104 @@ BOOST_AUTO_TEST_CASE( google_chart_with_inject_HTML ) BOOST_REQUIRE_EQUAL(true,test); } +BOOST_AUTO_TEST_CASE( google_chart_linear_plot ) +{ + openfpm::vector<std::string> x; + openfpm::vector<openfpm::vector<double>> y; + openfpm::vector<std::string> yn; + + x.add("colum1"); + x.add("colum2"); + x.add("colum3"); + x.add("colum4"); + x.add("colum5"); + x.add("colum6"); + + // Here we specify how many lines we have + // first Line + yn.add("line1"); + + // second line + 2 intervals (Error bands) + yn.add("line2"); + yn.add("interval"); + yn.add("interval"); + yn.add("interval"); + yn.add("interval"); + + // third line + 1 interval (Error bands) + yn.add("line3"); + yn.add("interval"); + yn.add("interval"); + + // Each line can have multiple intervals or error bars + // The first number specify the bottom line + // The last three numbers specify the top line + error band (min, max) + // The middle 5 line specify the middle lines + one external error band + one internal error band + + y.add({0.10,0.20,0.19,0.22,0.195,0.215,0.35,0.34,0.36}); + y.add({0.11,0.21,0.18,0.22,0.19,0.215,0.36,0.35,0.37}); + y.add({0.12,0.22,0.21,0.23,0.215,0.225,0.35,0.34,0.36}); + y.add({0.15,0.25,0.20,0.26,0.22,0.255,0.36,0.35,0.37}); + y.add({0.09,0.29,0.25,0.30,0.26,0.295,0.35,0.34,0.36}); + y.add({0.08,0.28,0.27,0.29,0.275,0.285,0.36,0.35,0.37}); + + // Google charts options + GCoptions options; + + options.title = std::string("Example"); + options.yAxis = std::string("Y Axis"); + options.xAxis = std::string("X Axis"); + options.lineWidth = 1.0; + options.intervalext = std::string("{'i2': { 'color': '#4374E0', 'style':'bars', 'lineWidth':4, 'fillOpacity':1 } }"); + + GoogleChart cg; + cg.AddPointsGraph(x,y,yn,options); + cg.write("gc_plot_out.html"); + + bool test = compare("gc_plot_out.html","gc_plot_out_test.html"); + BOOST_REQUIRE_EQUAL(true,test); +} + +BOOST_AUTO_TEST_CASE( google_chart_linear_plot2 ) +{ + openfpm::vector<std::string> x; + openfpm::vector<openfpm::vector<double>> y; + + x.add("colum1"); + x.add("colum2"); + x.add("colum3"); + x.add("colum4"); + x.add("colum5"); + x.add("colum6"); + + // Each line can have multiple intervals or error bars + // The first number specify the bottom line + // The last three numbers specify the top line + error band (min, max) + // The middle 5 line specify the middle lines + one external error band + one internal error band + + y.add({0.10,0.20,0.19,0.22,0.195,0.215,0.35,0.34,0.36}); + y.add({0.11,0.21,0.18,0.22,0.19,0.215,0.36,0.35,0.37}); + y.add({0.12,0.22,0.21,0.23,0.215,0.225,0.35,0.34,0.36}); + y.add({0.15,0.25,0.20,0.26,0.22,0.255,0.36,0.35,0.37}); + y.add({0.09,0.29,0.25,0.30,0.26,0.295,0.35,0.34,0.36}); + y.add({0.08,0.28,0.27,0.29,0.275,0.285,0.36,0.35,0.37}); + + // Google charts options + GCoptions options; + + options.title = std::string("Example"); + options.yAxis = std::string("Y Axis"); + options.xAxis = std::string("X Axis"); + options.lineWidth = 1.0; + + GoogleChart cg; + cg.AddPointsGraph(x,y,options); + cg.write("gc_plot2_out.html"); + + bool test = compare("gc_plot2_out.html","gc_plot2_out_test.html"); + BOOST_REQUIRE_EQUAL(true,test); +} + BOOST_AUTO_TEST_SUITE_END() #endif /* OPENFPM_DATA_SRC_PLOT_PLOT_UNIT_TESTS_HPP_ */ -- GitLab