| 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 |  |