dolgraph.class.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. <?php
  2. /* Copyright (c) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (c) 2004-2015 Laurent Destailleur <eldy@users.sourceforge.net>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /**
  19. * \file htdocs/core/class/dolgraph.class.php
  20. * \ingroup core
  21. * \brief File for class to generate graph
  22. */
  23. /**
  24. * Class to build graphs.
  25. * Usage is:
  26. * $dolgraph=new DolGraph();
  27. * $dolgraph->SetTitle($langs->transnoentities('MyTitle').'<br>'.$langs->transnoentities('MyTitlePercent').'%');
  28. * $dolgraph->SetMaxValue(50);
  29. * $dolgraph->SetData($data);
  30. * $dolgraph->setShowLegend(1);
  31. * $dolgraph->setShowPercent(1);
  32. * $dolgraph->SetType(array('pie'));
  33. * $dolgraph->setWidth('100%');
  34. * $dolgraph->draw('idofgraph');
  35. * print $dolgraph->show($total?0:1);
  36. */
  37. class DolGraph
  38. {
  39. public $type=array(); // Array with type of each series. Example: array('bars', 'lines', ...)
  40. public $mode='side'; // Mode bars graph: side, depth
  41. private $_library='jflot'; // Graphic library to use (jflot, artichow)
  42. //! Array of data
  43. public $data; // Data of graph: array(array('abs1',valA1,valB1), array('abs2',valA2,valB2), ...)
  44. public $title; // Title of graph
  45. public $cssprefix=''; // To add into css styles
  46. public $width=380;
  47. public $height=200;
  48. public $MaxValue=0;
  49. public $MinValue=0;
  50. public $SetShading=0;
  51. public $PrecisionY=-1;
  52. public $horizTickIncrement=-1;
  53. public $SetNumXTicks=-1;
  54. public $labelInterval=-1;
  55. public $hideXGrid=false;
  56. public $hideYGrid=false;
  57. public $Legend=array();
  58. public $LegendWidthMin=0;
  59. public $showlegend=1;
  60. public $showpointvalue=1;
  61. public $showpercent=0;
  62. public $combine=0; // 0.05 if you want to combine records < 5% into "other"
  63. public $graph; // Objet Graph (Artichow, Phplot...)
  64. /**
  65. * @var string Error code (or message)
  66. */
  67. public $error='';
  68. public $bordercolor; // array(R,G,B)
  69. public $bgcolor; // array(R,G,B)
  70. public $bgcolorgrid=array(255,255,255); // array(R,G,B)
  71. public $datacolor; // array(array(R,G,B),...)
  72. private $stringtoshow; // To store string to output graph into HTML page
  73. /**
  74. * Constructor
  75. *
  76. * @param string $library 'jflot' (default) or 'artichow' (no more supported)
  77. */
  78. function __construct($library='jflot')
  79. {
  80. global $conf;
  81. global $theme_bordercolor, $theme_datacolor, $theme_bgcolor, $theme_bgcoloronglet;
  82. // To use old feature
  83. if ($library == 'artichow')
  84. {
  85. $this->_library='artichow';
  86. // Test if module GD present
  87. $modules_list = get_loaded_extensions();
  88. $isgdinstalled=0;
  89. foreach ($modules_list as $module)
  90. {
  91. if ($module == 'gd') $isgdinstalled=1;
  92. }
  93. if (! $isgdinstalled)
  94. {
  95. $this->error="Error: PHP GD module is not available. It is required to build graphics.";
  96. return -1;
  97. }
  98. }
  99. $this->bordercolor = array(235,235,224);
  100. $this->datacolor = array(array(120,130,150), array(160,160,180), array(190,190,220));
  101. $this->bgcolor = array(235,235,224);
  102. $color_file = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/graph-color.php';
  103. if (is_readable($color_file))
  104. {
  105. include_once $color_file;
  106. if (isset($theme_bordercolor)) $this->bordercolor = $theme_bordercolor;
  107. if (isset($theme_datacolor)) $this->datacolor = $theme_datacolor;
  108. if (isset($theme_bgcolor)) $this->bgcolor = $theme_bgcolor;
  109. }
  110. //print 'bgcolor: '.join(',',$this->bgcolor).'<br>';
  111. }
  112. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  113. /**
  114. * Set Y precision
  115. *
  116. * @param float $which_prec Precision
  117. * @return boolean
  118. */
  119. function SetPrecisionY($which_prec)
  120. {
  121. // phpcs:enable
  122. $this->PrecisionY = $which_prec;
  123. return true;
  124. }
  125. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  126. /**
  127. * Utiliser SetNumTicks ou SetHorizTickIncrement mais pas les 2
  128. *
  129. * @param float $xi Xi
  130. * @return boolean True
  131. */
  132. function SetHorizTickIncrement($xi)
  133. {
  134. // phpcs:enable
  135. $this->horizTickIncrement = $xi;
  136. return true;
  137. }
  138. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  139. /**
  140. * Utiliser SetNumTicks ou SetHorizTickIncrement mais pas les 2
  141. *
  142. * @param float $xt Xt
  143. * @return boolean True
  144. */
  145. function SetNumXTicks($xt)
  146. {
  147. // phpcs:enable
  148. $this->SetNumXTicks = $xt;
  149. return true;
  150. }
  151. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  152. /**
  153. * Set label interval to reduce number of labels
  154. *
  155. * @param float $x Label interval
  156. * @return boolean True
  157. */
  158. function SetLabelInterval($x)
  159. {
  160. // phpcs:enable
  161. $this->labelInterval = $x;
  162. return true;
  163. }
  164. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  165. /**
  166. * Hide X grid
  167. *
  168. * @param boolean $bool XGrid or not
  169. * @return boolean true
  170. */
  171. function SetHideXGrid($bool)
  172. {
  173. // phpcs:enable
  174. $this->hideXGrid = $bool;
  175. return true;
  176. }
  177. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  178. /**
  179. * Hide Y grid
  180. *
  181. * @param boolean $bool YGrid or not
  182. * @return boolean true
  183. */
  184. function SetHideYGrid($bool)
  185. {
  186. // phpcs:enable
  187. $this->hideYGrid = $bool;
  188. return true;
  189. }
  190. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  191. /**
  192. * Set y label
  193. *
  194. * @param string $label Y label
  195. * @return boolean|null True
  196. */
  197. function SetYLabel($label)
  198. {
  199. // phpcs:enable
  200. $this->YLabel = $label;
  201. }
  202. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  203. /**
  204. * Set width
  205. *
  206. * @param int $w Width
  207. * @return boolean|null True
  208. */
  209. function SetWidth($w)
  210. {
  211. // phpcs:enable
  212. $this->width = $w;
  213. }
  214. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  215. /**
  216. * Set title
  217. *
  218. * @param string $title Title
  219. * @return void
  220. */
  221. function SetTitle($title)
  222. {
  223. // phpcs:enable
  224. $this->title = $title;
  225. }
  226. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  227. /**
  228. * Set data
  229. *
  230. * @param array $data Data
  231. * @return void
  232. * @see draw_jflot for syntax of data array
  233. */
  234. function SetData($data)
  235. {
  236. // phpcs:enable
  237. $this->data = $data;
  238. }
  239. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  240. /**
  241. * Set data
  242. *
  243. * @param array $datacolor Data color array(array(R,G,B),array(R,G,B)...)
  244. * @return void
  245. */
  246. function SetDataColor($datacolor)
  247. {
  248. // phpcs:enable
  249. $this->datacolor = $datacolor;
  250. }
  251. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  252. /**
  253. * Set type
  254. *
  255. * @param array $type Array with type for each serie. Example: array('pie'), array('lines',...,'bars')
  256. * @return void
  257. */
  258. function SetType($type)
  259. {
  260. // phpcs:enable
  261. $this->type = $type;
  262. }
  263. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  264. /**
  265. * Set legend
  266. *
  267. * @param array $legend Legend. Example: array('seriename1','seriname2',...)
  268. * @return void
  269. */
  270. function SetLegend($legend)
  271. {
  272. // phpcs:enable
  273. $this->Legend = $legend;
  274. }
  275. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  276. /**
  277. * Set min width
  278. *
  279. * @param int $legendwidthmin Min width
  280. * @return void
  281. */
  282. function SetLegendWidthMin($legendwidthmin)
  283. {
  284. // phpcs:enable
  285. $this->LegendWidthMin = $legendwidthmin;
  286. }
  287. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  288. /**
  289. * Set max value
  290. *
  291. * @param int $max Max value
  292. * @return void
  293. */
  294. function SetMaxValue($max)
  295. {
  296. // phpcs:enable
  297. $this->MaxValue = $max;
  298. }
  299. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  300. /**
  301. * Get max value
  302. *
  303. * @return int Max value
  304. */
  305. function GetMaxValue()
  306. {
  307. // phpcs:enable
  308. return $this->MaxValue;
  309. }
  310. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  311. /**
  312. * Set min value
  313. *
  314. * @param int $min Min value
  315. * @return void
  316. */
  317. function SetMinValue($min)
  318. {
  319. // phpcs:enable
  320. $this->MinValue = $min;
  321. }
  322. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  323. /**
  324. * Get min value
  325. *
  326. * @return int Max value
  327. */
  328. function GetMinValue()
  329. {
  330. // phpcs:enable
  331. return $this->MinValue;
  332. }
  333. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  334. /**
  335. * Set height
  336. *
  337. * @param int $h Height
  338. * @return void
  339. */
  340. function SetHeight($h)
  341. {
  342. // phpcs:enable
  343. $this->height = $h;
  344. }
  345. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  346. /**
  347. * Set shading
  348. *
  349. * @param string $s Shading
  350. * @return void
  351. */
  352. function SetShading($s)
  353. {
  354. // phpcs:enable
  355. $this->SetShading = $s;
  356. }
  357. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  358. /**
  359. * Set shading
  360. *
  361. * @param string $s Shading
  362. * @return void
  363. */
  364. function SetCssPrefix($s)
  365. {
  366. // phpcs:enable
  367. $this->cssprefix = $s;
  368. }
  369. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  370. /**
  371. * Reset bg color
  372. *
  373. * @return void
  374. */
  375. function ResetBgColor()
  376. {
  377. // phpcs:enable
  378. unset($this->bgcolor);
  379. }
  380. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  381. /**
  382. * Reset bgcolorgrid
  383. *
  384. * @return void
  385. */
  386. function ResetBgColorGrid()
  387. {
  388. // phpcs:enable
  389. unset($this->bgcolorgrid);
  390. }
  391. /**
  392. * Is graph ko
  393. *
  394. * @return string Error
  395. */
  396. function isGraphKo()
  397. {
  398. return $this->error;
  399. }
  400. /**
  401. * Show legend or not
  402. *
  403. * @param int $showlegend 1=Show legend (default), 0=Hide legend
  404. * @return void
  405. */
  406. function setShowLegend($showlegend)
  407. {
  408. $this->showlegend=$showlegend;
  409. }
  410. /**
  411. * Show pointvalue or not
  412. *
  413. * @param int $showpointvalue 1=Show value for each point, as tooltip or inline (default), 0=Hide value
  414. * @return void
  415. */
  416. function setShowPointValue($showpointvalue)
  417. {
  418. $this->showpointvalue=$showpointvalue;
  419. }
  420. /**
  421. * Show percent or not
  422. *
  423. * @param int $showpercent 1=Show percent for each point, as tooltip or inline, 0=Hide percent (default)
  424. * @return void
  425. */
  426. function setShowPercent($showpercent)
  427. {
  428. $this->showpercent=$showpercent;
  429. }
  430. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  431. /**
  432. * Define background color of complete image
  433. *
  434. * @param array $bg_color array(R,G,B) ou 'onglet' ou 'default'
  435. * @return void
  436. */
  437. function SetBgColor($bg_color = array(255,255,255))
  438. {
  439. // phpcs:enable
  440. global $theme_bgcolor,$theme_bgcoloronglet;
  441. if (! is_array($bg_color))
  442. {
  443. if ($bg_color == 'onglet')
  444. {
  445. //print 'ee'.join(',',$theme_bgcoloronglet);
  446. $this->bgcolor = $theme_bgcoloronglet;
  447. }
  448. else
  449. {
  450. $this->bgcolor = $theme_bgcolor;
  451. }
  452. }
  453. else
  454. {
  455. $this->bgcolor = $bg_color;
  456. }
  457. }
  458. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  459. /**
  460. * Define background color of grid
  461. *
  462. * @param array $bg_colorgrid array(R,G,B) ou 'onglet' ou 'default'
  463. * @return void
  464. */
  465. function SetBgColorGrid($bg_colorgrid = array(255,255,255))
  466. {
  467. // phpcs:enable
  468. global $theme_bgcolor,$theme_bgcoloronglet;
  469. if (! is_array($bg_colorgrid))
  470. {
  471. if ($bg_colorgrid == 'onglet')
  472. {
  473. //print 'ee'.join(',',$theme_bgcoloronglet);
  474. $this->bgcolorgrid = $theme_bgcoloronglet;
  475. }
  476. else
  477. {
  478. $this->bgcolorgrid = $theme_bgcolor;
  479. }
  480. }
  481. else
  482. {
  483. $this->bgcolorgrid = $bg_colorgrid;
  484. }
  485. }
  486. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  487. /**
  488. * Reset data color
  489. *
  490. * @return void
  491. */
  492. function ResetDataColor()
  493. {
  494. // phpcs:enable
  495. unset($this->datacolor);
  496. }
  497. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  498. /**
  499. * Get max value
  500. *
  501. * @return int Max value
  502. */
  503. function GetMaxValueInData()
  504. {
  505. // phpcs:enable
  506. $k = 0;
  507. $vals = array();
  508. $nblines = count($this->data);
  509. $nbvalues = count($this->data[0]) - 1;
  510. for ($j = 0 ; $j < $nblines ; $j++)
  511. {
  512. for ($i = 0 ; $i < $nbvalues ; $i++)
  513. {
  514. $vals[$k] = $this->data[$j][$i+1];
  515. $k++;
  516. }
  517. }
  518. rsort($vals);
  519. return $vals[0];
  520. }
  521. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  522. /**
  523. * Return min value of all data
  524. *
  525. * @return int Min value of all data
  526. */
  527. function GetMinValueInData()
  528. {
  529. // phpcs:enable
  530. $k = 0;
  531. $vals = array();
  532. $nblines = count($this->data);
  533. $nbvalues = count($this->data[0]) - 1;
  534. for ($j = 0 ; $j < $nblines ; $j++)
  535. {
  536. for ($i = 0 ; $i < $nbvalues ; $i++)
  537. {
  538. $vals[$k] = $this->data[$j][$i+1];
  539. $k++;
  540. }
  541. }
  542. sort($vals);
  543. return $vals[0];
  544. }
  545. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  546. /**
  547. * Return max value of all data
  548. *
  549. * @return int Max value of all data
  550. */
  551. function GetCeilMaxValue()
  552. {
  553. // phpcs:enable
  554. $max = $this->GetMaxValueInData();
  555. if ($max != 0) $max++;
  556. $size=dol_strlen(abs(ceil($max)));
  557. $factor=1;
  558. for ($i=0; $i < ($size-1); $i++)
  559. {
  560. $factor*=10;
  561. }
  562. $res=0;
  563. if (is_numeric($max)) $res=ceil($max/$factor)*$factor;
  564. //print "max=".$max." res=".$res;
  565. return $res;
  566. }
  567. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  568. /**
  569. * Return min value of all data
  570. *
  571. * @return double Max value of all data
  572. */
  573. function GetFloorMinValue()
  574. {
  575. // phpcs:enable
  576. $min = $this->GetMinValueInData();
  577. if ($min == '') $min=0;
  578. if ($min != 0) $min--;
  579. $size=dol_strlen(abs(floor($min)));
  580. $factor=1;
  581. for ($i=0; $i < ($size-1); $i++)
  582. {
  583. $factor*=10;
  584. }
  585. $res=floor($min/$factor)*$factor;
  586. //print "min=".$min." res=".$res;
  587. return $res;
  588. }
  589. /**
  590. * Build a graph into memory using correct library (may also be wrote on disk, depending on library used)
  591. *
  592. * @param string $file Image file name to use to save onto disk (also used as javascript unique id)
  593. * @param string $fileurl Url path to show image if saved onto disk
  594. * @return integer|null
  595. */
  596. function draw($file, $fileurl='')
  597. {
  598. if (empty($file))
  599. {
  600. $this->error="Call to draw method was made with empty value for parameter file.";
  601. dol_syslog(get_class($this)."::draw ".$this->error, LOG_ERR);
  602. return -2;
  603. }
  604. if (! is_array($this->data))
  605. {
  606. $this->error="Call to draw method was made but SetData was not called or called with an empty dataset for parameters";
  607. dol_syslog(get_class($this)."::draw ".$this->error, LOG_ERR);
  608. return -1;
  609. }
  610. if (count($this->data) < 1)
  611. {
  612. $this->error="Call to draw method was made but SetData was is an empty dataset";
  613. dol_syslog(get_class($this)."::draw ".$this->error, LOG_WARNING);
  614. }
  615. $call = "draw_".$this->_library;
  616. call_user_func_array(array($this,$call), array($file,$fileurl));
  617. }
  618. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  619. /**
  620. * Build a graph onto disk using Artichow library and return img string to it
  621. *
  622. * @param string $file Image file name to use if we save onto disk
  623. * @param string $fileurl Url path to show image if saved onto disk
  624. * @return void
  625. */
  626. private function draw_artichow($file,$fileurl)
  627. {
  628. // phpcs:enable
  629. global $artichow_defaultfont;
  630. dol_syslog(get_class($this)."::draw_artichow this->type=".join(',',$this->type));
  631. if (! defined('SHADOW_RIGHT_TOP')) define('SHADOW_RIGHT_TOP',3);
  632. if (! defined('LEGEND_BACKGROUND')) define('LEGEND_BACKGROUND',2);
  633. if (! defined('LEGEND_LINE')) define('LEGEND_LINE',1);
  634. // Create graph
  635. $classname='';
  636. if (! isset($this->type[0]) || $this->type[0] == 'bars') $classname='BarPlot'; // Only one type (first one) is supported by artichow
  637. else if ($this->type[0] == 'lines' || $this->type[0] == 'linesnopoint') $classname='LinePlot';
  638. else $classname='TypeUnknown';
  639. include_once ARTICHOW_PATH.$classname.'.class.php';
  640. // Definition de couleurs
  641. $bgcolor=new Color($this->bgcolor[0],$this->bgcolor[1],$this->bgcolor[2]);
  642. $bgcolorgrid=new Color($this->bgcolorgrid[0],$this->bgcolorgrid[1],$this->bgcolorgrid[2]);
  643. $colortrans=new Color(0,0,0,100);
  644. $colorsemitrans=new Color(255,255,255,60);
  645. $colorgradient= new LinearGradient(new Color(235, 235, 235),new Color(255, 255, 255),0);
  646. $colorwhite=new Color(255,255,255);
  647. // Graph
  648. $graph = new Graph($this->width, $this->height);
  649. $graph->border->hide();
  650. $graph->setAntiAliasing(true);
  651. if (isset($this->title))
  652. {
  653. $graph->title->set($this->title);
  654. //print $artichow_defaultfont;exit;
  655. $graph->title->setFont(new $artichow_defaultfont(10));
  656. }
  657. if (is_array($this->bgcolor)) $graph->setBackgroundColor($bgcolor);
  658. else $graph->setBackgroundGradient($colorgradient);
  659. $group = new PlotGroup;
  660. //$group->setSpace(5, 5, 0, 0);
  661. $paddleft=50;
  662. $paddright=10;
  663. $strl=dol_strlen(max(abs($this->MaxValue),abs($this->MinValue)));
  664. if ($strl > 6) $paddleft += ($strl * 4);
  665. $group->setPadding($paddleft, $paddright); // Width on left and right for Y axis values
  666. $group->legend->setSpace(0);
  667. $group->legend->setPadding(2,2,2,2);
  668. $group->legend->setPosition(null, 0.1);
  669. $group->legend->setBackgroundColor($colorsemitrans);
  670. if (is_array($this->bgcolorgrid)) $group->grid->setBackgroundColor($bgcolorgrid);
  671. else $group->grid->setBackgroundColor($colortrans);
  672. if ($this->hideXGrid) $group->grid->hideVertical(true);
  673. if ($this->hideYGrid) $group->grid->hideHorizontal(true);
  674. // On boucle sur chaque lot de donnees
  675. $legends=array();
  676. $i=0;
  677. $nblot=count($this->data[0])-1;
  678. while ($i < $nblot)
  679. {
  680. $x=0;
  681. $values=array();
  682. foreach($this->data as $key => $valarray)
  683. {
  684. $legends[$x] = $valarray[0];
  685. $values[$x] = $valarray[$i+1];
  686. $x++;
  687. }
  688. // We fix unknown values to null
  689. $newvalues=array();
  690. foreach($values as $val)
  691. {
  692. $newvalues[]=(is_numeric($val) ? $val : null);
  693. }
  694. if ($this->type[0] == 'bars')
  695. {
  696. //print "Lot de donnees $i<br>";
  697. //print_r($values);
  698. //print '<br>';
  699. $color=new Color($this->datacolor[$i][0],$this->datacolor[$i][1],$this->datacolor[$i][2],20);
  700. $colorbis=new Color(min($this->datacolor[$i][0]+50,255),min($this->datacolor[$i][1]+50,255),min($this->datacolor[$i][2]+50,255),50);
  701. $colorgrey=new Color(100,100,100);
  702. $colorborder=new Color($this->datacolor[$i][0],$this->datacolor[$i][1],$this->datacolor[$i][2]);
  703. if ($this->mode == 'side') $plot = new BarPlot($newvalues, $i+1, $nblot);
  704. if ($this->mode == 'depth') $plot = new BarPlot($newvalues, 1, 1, ($nblot-$i-1)*5);
  705. $plot->barBorder->setColor($colorgrey);
  706. //$plot->setBarColor($color);
  707. $plot->setBarGradient(new LinearGradient($colorbis, $color, 90));
  708. if ($this->mode == 'side') $plot->setBarPadding(0.1, 0.1);
  709. if ($this->mode == 'depth') $plot->setBarPadding(0.1, 0.4);
  710. if ($this->mode == 'side') $plot->setBarSpace(5);
  711. if ($this->mode == 'depth') $plot->setBarSpace(2);
  712. $plot->barShadow->setSize($this->SetShading);
  713. $plot->barShadow->setPosition(SHADOW_RIGHT_TOP);
  714. $plot->barShadow->setColor(new Color(160, 160, 160, 50));
  715. $plot->barShadow->smooth(true);
  716. //$plot->setSize(1, 0.96);
  717. //$plot->setCenter(0.5, 0.52);
  718. // Le mode automatique est plus efficace
  719. $plot->SetYMax($this->MaxValue);
  720. $plot->SetYMin($this->MinValue);
  721. }
  722. if ($this->type[0] == 'lines' || $this->type[0] == 'linesnopoint')
  723. {
  724. $color=new Color($this->datacolor[$i][0],$this->datacolor[$i][1],$this->datacolor[$i][2],20);
  725. $colorbis=new Color(min($this->datacolor[$i][0]+20,255),min($this->datacolor[$i][1]+20,255),min($this->datacolor[$i][2]+20,255),60);
  726. $colorter=new Color(min($this->datacolor[$i][0]+50,255),min($this->datacolor[$i][1]+50,255),min($this->datacolor[$i][2]+50,255),90);
  727. $plot = new LinePlot($newvalues);
  728. //$plot->setSize(1, 0.96);
  729. //$plot->setCenter(0.5, 0.52);
  730. $plot->setColor($color);
  731. $plot->setThickness(1);
  732. // Set line background gradient
  733. $plot->setFillGradient(new LinearGradient($colorter, $colorbis, 90));
  734. $plot->xAxis->setLabelText($legends);
  735. // Le mode automatique est plus efficace
  736. $plot->SetYMax($this->MaxValue);
  737. $plot->SetYMin($this->MinValue);
  738. //$plot->setYAxis(0);
  739. //$plot->hideLine(true);
  740. }
  741. //$plot->reduce(80); // Evite temps d'affichage trop long et nombre de ticks absisce satures
  742. $group->legend->setTextFont(new $artichow_defaultfont(10)); // This is to force Artichow to use awFileFontDriver to
  743. // solve a bug in Artichow with UTF8
  744. if (count($this->Legend))
  745. {
  746. if ($this->type[0] == 'bars') $group->legend->add($plot, $this->Legend[$i], LEGEND_BACKGROUND);
  747. if ($this->type[0] == 'lines' || $this->type[0] == 'linesnopoint') $group->legend->add($plot, $this->Legend[$i], LEGEND_LINE);
  748. }
  749. $group->add($plot);
  750. $i++;
  751. }
  752. $group->axis->bottom->setLabelText($legends);
  753. $group->axis->bottom->label->setFont(new $artichow_defaultfont(7));
  754. //print $group->axis->bottom->getLabelNumber();
  755. if ($this->labelInterval > 0) $group->axis->bottom->setLabelInterval($this->labelInterval);
  756. $graph->add($group);
  757. // Generate file
  758. $graph->draw($file);
  759. $this->stringtoshow='<!-- Build using '.$this->_library.' --><img src="'.$fileurl.'" title="'.dol_escape_htmltag($this->title?$this->title:$this->YLabel).'" alt="'.dol_escape_htmltag($this->title?$this->title:$this->YLabel).'">';
  760. }
  761. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  762. /**
  763. * Build a graph using JFlot library. Input when calling this method should be:
  764. * $this->data = array(array(0=>'labelxA',1=>yA), array('labelxB',yB));
  765. * $this->data = array(array(0=>'labelxA',1=>yA1,...,n=>yAn), array('labelxB',yB1,...yBn)); // or when there is n series to show for each x
  766. * $this->data = array(array('label'=>'labelxA','data'=>yA), array('labelxB',yB)); // Syntax deprecated
  767. * $this->legend= array("Val1",...,"Valn"); // list of n series name
  768. * $this->type = array('bars',...'lines'); or array('pie')
  769. * $this->mode = 'depth' ???
  770. * $this->bgcolorgrid
  771. * $this->datacolor
  772. * $this->shownodatagraph
  773. *
  774. * @param string $file Image file name to use to save onto disk (also used as javascript unique id)
  775. * @param string $fileurl Url path to show image if saved onto disk. Never used here.
  776. * @return void
  777. */
  778. private function draw_jflot($file, $fileurl)
  779. {
  780. // phpcs:enable
  781. global $artichow_defaultfont;
  782. dol_syslog(get_class($this)."::draw_jflot this->type=".join(',',$this->type)." this->MaxValue=".$this->MaxValue);
  783. if (empty($this->width) && empty($this->height))
  784. {
  785. print 'Error width or height not set';
  786. return;
  787. }
  788. $legends=array();
  789. $nblot=count($this->data[0])-1; // -1 to remove legend
  790. if ($nblot < 0) dol_syslog('Bad value for property ->data. Must be set by mydolgraph->SetData before calling mydolgrapgh->draw', LOG_WARNING);
  791. $firstlot=0;
  792. // Works with line but not with bars
  793. //if ($nblot > 2) $firstlot = ($nblot - 2); // We limit nblot to 2 because jflot can't manage more than 2 bars on same x
  794. $i=$firstlot;
  795. $serie=array();
  796. while ($i < $nblot) // Loop on each serie
  797. {
  798. $values=array(); // Array with horizontal y values (specific values of a serie) for each abscisse x
  799. $serie[$i]="var d".$i." = [];\n";
  800. // Fill array $values
  801. $x=0;
  802. foreach($this->data as $valarray) // Loop on each x
  803. {
  804. $legends[$x] = $valarray[0];
  805. $values[$x] = (is_numeric($valarray[$i+1]) ? $valarray[$i+1] : null);
  806. $x++;
  807. }
  808. // TODO Avoid push by adding generated long array...
  809. if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'pie')
  810. {
  811. foreach($values as $x => $y) {
  812. if (isset($y)) $serie[$i].='d'.$i.'.push({"label":"'.dol_escape_js($legends[$x]).'", "data":'.$y.'});'."\n";
  813. }
  814. }
  815. else
  816. {
  817. foreach($values as $x => $y) {
  818. if (isset($y)) $serie[$i].='d'.$i.'.push(['.$x.', '.$y.']);'."\n";
  819. }
  820. }
  821. unset($values);
  822. $i++;
  823. }
  824. $tag=dol_escape_htmltag(dol_string_unaccent(dol_string_nospecial(basename($file),'_',array('-','.'))));
  825. $this->stringtoshow ='<!-- Build using '.$this->_library.' -->'."\n";
  826. if (! empty($this->title)) $this->stringtoshow.='<div align="center" class="dolgraphtitle'.(empty($this->cssprefix)?'':' dolgraphtitle'.$this->cssprefix).'">'.$this->title.'</div>';
  827. if (! empty($this->shownographyet))
  828. {
  829. $this->stringtoshow.='<div style="width:'.$this->width.'px;height:'.$this->height.'px;" class="nographyet"></div>';
  830. $this->stringtoshow.='<div class="nographyettext">'.$langs->trans("NotEnoughDataYet").'</div>';
  831. return;
  832. }
  833. $this->stringtoshow.='<div id="placeholder_'.$tag.'" style="width:'.$this->width.'px;height:'.$this->height.'px;" class="dolgraph'.(empty($this->cssprefix)?'':' dolgraph'.$this->cssprefix).'"></div>'."\n";
  834. $this->stringtoshow.='<script id="'.$tag.'">'."\n";
  835. $this->stringtoshow.='$(function () {'."\n";
  836. $i=$firstlot;
  837. if ($nblot < 0)
  838. {
  839. $this->stringtoshow.='<!-- No series of data -->';
  840. }
  841. else
  842. {
  843. while ($i < $nblot)
  844. {
  845. $this->stringtoshow.=$serie[$i];
  846. $i++;
  847. }
  848. }
  849. $this->stringtoshow.="\n";
  850. // Special case for Graph of type 'pie'
  851. if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'pie')
  852. {
  853. $datacolor=array();
  854. foreach($this->datacolor as $val) $datacolor[]="#".sprintf("%02x%02x%02x",$val[0],$val[1],$val[2]);
  855. $urltemp=''; // TODO Add support for url link into labels
  856. $showlegend=$this->showlegend;
  857. $showpointvalue=$this->showpointvalue;
  858. $showpercent=$this->showpercent;
  859. $this->stringtoshow.= '
  860. function plotWithOptions_'.$tag.'() {
  861. $.plot($("#placeholder_'.$tag.'"), d0,
  862. {
  863. series: {
  864. pie: {
  865. show: true,
  866. radius: 0.8,
  867. '.($this->combine ? '
  868. combine: {
  869. threshold: '.$this->combine.'
  870. },' : '') . '
  871. label: {
  872. show: true,
  873. radius: 0.9,
  874. formatter: function(label, series) {
  875. var percent=Math.round(series.percent);
  876. var number=series.data[0][1];
  877. return \'';
  878. $this->stringtoshow.='<span style="font-size:8pt;text-align:center;padding:2px;color:black;">';
  879. if ($urltemp) $this->stringtoshow.='<a style="color: #FFFFFF;" border="0" href="'.$urltemp.'">';
  880. $this->stringtoshow.='\'+';
  881. $this->stringtoshow.=($showlegend?'':'label+\' \'+'); // Hide label if already shown in legend
  882. $this->stringtoshow.=($showpointvalue?'number+':'');
  883. $this->stringtoshow.=($showpercent?'\'<br/>\'+percent+\'%\'+':'');
  884. $this->stringtoshow.='\'';
  885. if ($urltemp) $this->stringtoshow.='</a>';
  886. $this->stringtoshow.='</span>\';
  887. },
  888. background: {
  889. opacity: 0.0,
  890. color: \'#000000\'
  891. }
  892. }
  893. }
  894. },
  895. zoom: {
  896. interactive: true
  897. },
  898. pan: {
  899. interactive: true
  900. },';
  901. if (count($datacolor))
  902. {
  903. $this->stringtoshow.='colors: '.(! empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)).',';
  904. }
  905. $this->stringtoshow.='legend: {show: '.($showlegend?'true':'false').', position: \'ne\' }
  906. });
  907. }'."\n";
  908. }
  909. // Other cases, graph of type 'bars', 'lines'
  910. else
  911. {
  912. // Add code to support tooltips
  913. // TODO: remove js css and use graph-tooltip-inner class instead by adding css in each themes
  914. $this->stringtoshow.='
  915. function showTooltip_'.$tag.'(x, y, contents) {
  916. $(\'<div class="graph-tooltip-inner" id="tooltip_'.$tag.'">\' + contents + \'</div>\').css({
  917. position: \'absolute\',
  918. display: \'none\',
  919. top: y + 10,
  920. left: x + 15,
  921. border: \'1px solid #000\',
  922. padding: \'5px\',
  923. \'background-color\': \'#000\',
  924. \'color\': \'#fff\',
  925. \'font-weight\': \'bold\',
  926. width: 200,
  927. opacity: 0.80
  928. }).appendTo("body").fadeIn(100);
  929. }
  930. var previousPoint = null;
  931. $("#placeholder_'.$tag.'").bind("plothover", function (event, pos, item) {
  932. $("#x").text(pos.x.toFixed(2));
  933. $("#y").text(pos.y.toFixed(2));
  934. if (item) {
  935. if (previousPoint != item.dataIndex) {
  936. previousPoint = item.dataIndex;
  937. $("#tooltip").remove();
  938. /* console.log(item); */
  939. var x = item.datapoint[0].toFixed(2);
  940. var y = item.datapoint[1].toFixed(2);
  941. var z = item.series.xaxis.ticks[item.dataIndex].label;
  942. ';
  943. if ($this->showpointvalue > 0) $this->stringtoshow.='
  944. showTooltip_'.$tag.'(item.pageX, item.pageY, item.series.label + "<br>" + z + " => " + y);
  945. ';
  946. $this->stringtoshow.='
  947. }
  948. }
  949. else {
  950. $("#tooltip_'.$tag.'").remove();
  951. previousPoint = null;
  952. }
  953. });
  954. ';
  955. $this->stringtoshow.='var stack = null, steps = false;'."\n";
  956. $this->stringtoshow.='function plotWithOptions_'.$tag.'() {'."\n";
  957. $this->stringtoshow.='$.plot($("#placeholder_'.$tag.'"), [ '."\n";
  958. $i=$firstlot;
  959. while ($i < $nblot)
  960. {
  961. if ($i > $firstlot) $this->stringtoshow.=', '."\n";
  962. $color=sprintf("%02x%02x%02x",$this->datacolor[$i][0],$this->datacolor[$i][1],$this->datacolor[$i][2]);
  963. $this->stringtoshow.='{ ';
  964. if (! isset($this->type[$i]) || $this->type[$i] == 'bars') $this->stringtoshow.='bars: { lineWidth: 1, show: true, align: "'.($i==$firstlot?'center':'left').'", barWidth: 0.5 }, ';
  965. if (isset($this->type[$i]) && ($this->type[$i] == 'lines' || $this->type[$i] == 'linesnopoint')) $this->stringtoshow.='lines: { show: true, fill: false }, points: { show: '.($this->type[$i] == 'linesnopoint' ? 'false' : 'true').' }, ';
  966. $this->stringtoshow.='color: "#'.$color.'", label: "'.(isset($this->Legend[$i]) ? dol_escape_js($this->Legend[$i]) : '').'", data: d'.$i.' }';
  967. $i++;
  968. }
  969. // shadowSize: 0 -> Drawing is faster without shadows
  970. $this->stringtoshow.="\n".' ], { series: { shadowSize: 0, stack: stack, lines: { fill: false, steps: steps }, bars: { barWidth: 0.6 } }'."\n";
  971. // Xaxis
  972. $this->stringtoshow.=', xaxis: { ticks: ['."\n";
  973. $x=0;
  974. foreach($this->data as $key => $valarray)
  975. {
  976. if ($x > 0) $this->stringtoshow.=', '."\n";
  977. $this->stringtoshow.= ' ['.$x.', "'.$valarray[0].'"]';
  978. $x++;
  979. }
  980. $this->stringtoshow.='] }'."\n";
  981. // Yaxis
  982. $this->stringtoshow.=', yaxis: { min: '.$this->MinValue.', max: '.($this->MaxValue).' }'."\n";
  983. // Background color
  984. $color1=sprintf("%02x%02x%02x",$this->bgcolorgrid[0],$this->bgcolorgrid[0],$this->bgcolorgrid[2]);
  985. $color2=sprintf("%02x%02x%02x",$this->bgcolorgrid[0],$this->bgcolorgrid[1],$this->bgcolorgrid[2]);
  986. $this->stringtoshow.=', grid: { hoverable: true, backgroundColor: { colors: ["#'.$color1.'", "#'.$color2.'"] }, borderWidth: 1, borderColor: \'#e6e6e6\', tickColor : \'#e6e6e6\' }'."\n";
  987. //$this->stringtoshow.=', shadowSize: 20'."\n"; TODO Uncommet this
  988. $this->stringtoshow.='});'."\n";
  989. $this->stringtoshow.='}'."\n";
  990. }
  991. $this->stringtoshow.='plotWithOptions_'.$tag.'();'."\n";
  992. $this->stringtoshow.='});'."\n";
  993. $this->stringtoshow.='</script>'."\n";
  994. }
  995. /**
  996. * Output HTML string to show graph
  997. *
  998. * @param int $shownographyet Show graph to say there is not enough data
  999. * @return string HTML string to show graph
  1000. */
  1001. function show($shownographyet=0)
  1002. {
  1003. global $langs;
  1004. if ($shownographyet)
  1005. {
  1006. $s= '<div class="nographyet" style="width:'.(preg_match('/%/',$this->width)?$this->width:$this->width.'px').'; height:'.(preg_match('/%/',$this->height)?$this->height:$this->height.'px').';"></div>';
  1007. $s.='<div class="nographyettext">'.$langs->trans("NotEnoughDataYet").'</div>';
  1008. return $s;
  1009. }
  1010. return $this->stringtoshow;
  1011. }
  1012. /**
  1013. * getDefaultGraphSizeForStats
  1014. *
  1015. * @param string $direction 'width' or 'height'
  1016. * @param string $defaultsize Value we want as default size
  1017. * @return int Value of width or height to use by default
  1018. */
  1019. static function getDefaultGraphSizeForStats($direction,$defaultsize='')
  1020. {
  1021. global $conf;
  1022. if ($direction == 'width')
  1023. {
  1024. if (empty($conf->dol_optimize_smallscreen)) return ($defaultsize ? $defaultsize : '500');
  1025. else return (empty($_SESSION['dol_screen_width']) ? '280' : ($_SESSION['dol_screen_width']-40));
  1026. }
  1027. if ($direction == 'height')
  1028. {
  1029. return (empty($conf->dol_optimize_smallscreen)?($defaultsize?$defaultsize:'200'):'160');
  1030. }
  1031. return 0;
  1032. }
  1033. }