graph_svg.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <?php
  2. //
  3. // vnStat PHP frontend (c)2006-2010 Bjorge Dijkstra (bjd@jooz.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 2 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, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. //
  19. //
  20. // see file COPYING or at http://www.gnu.org/licenses/gpl.html
  21. // for more information.
  22. //
  23. require 'config.php';
  24. require 'localize.php';
  25. require 'vnstat.php';
  26. validate_input();
  27. require "./themes/$style/theme.php";
  28. function svg_create($width, $height)
  29. {
  30. header('Content-type: image/svg+xml');
  31. print "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
  32. print "<svg width=\"$width\" height=\"$height\" version=\"1.2\" baseProfile=\"tiny\" xmlns=\"http://www.w3.org/2000/svg\">\n";
  33. print "<g style=\"shape-rendering: crispEdges\">\n";
  34. }
  35. function svg_end()
  36. {
  37. print "</g>\n";
  38. print "</svg>\n";
  39. }
  40. function svg_options($options)
  41. {
  42. foreach ($options as $key => $value) {
  43. print "$key=\"$value\" ";
  44. }
  45. }
  46. function svg_group($options)
  47. {
  48. print "<g ";
  49. svg_options($options);
  50. print ">\n";
  51. }
  52. function svg_group_end()
  53. {
  54. print "</g>\n";
  55. }
  56. function svg_text($x, $y, $text, $options = array())
  57. {
  58. printf("<text x=\"%F\" y=\"%F\" ", $x, $y);
  59. svg_options($options);
  60. print ">$text</text>\n";
  61. }
  62. function svg_line($x1, $y1, $x2, $y2, $options = array())
  63. {
  64. printf("<line x1=\"%F\" y1=\"%F\" x2=\"%F\" y2=\"%F\" ", $x1, $y1, $x2, $y2);
  65. svg_options($options);
  66. print "/>\n";
  67. }
  68. function svg_rect($x, $y, $w, $h, $options = array())
  69. {
  70. printf("<rect x=\"%F\" y=\"%F\" width=\"%F\" height=\"%F\" ", $x, $y, $w, $h);
  71. svg_options($options);
  72. print "/>\n";
  73. }
  74. function svg_poly($points, $options = array())
  75. {
  76. print "<polygon points=\"";
  77. for ($p = 0; $p < count($points); $p += 2) {
  78. printf("%F,%F ", $points[$p], $points[$p+1]);
  79. }
  80. svg_options($options);
  81. print "\"/>\n";
  82. }
  83. function allocate_color($colors)
  84. {
  85. $col['rgb'] = sprintf("#%02X%02X%02X", $colors[0], $colors[1], $colors[2]);
  86. $col['opacity'] = sprintf("%F", (127 - $colors[3]) / 127);
  87. return $col;
  88. }
  89. function init_image()
  90. {
  91. global $xlm, $xrm, $ytm, $ybm, $iw, $ih,$graph, $cl, $iface, $colorscheme, $style;
  92. if ($graph == 'none')
  93. return;
  94. //
  95. // image object
  96. //
  97. $xlm = 70;
  98. $xrm = 20;
  99. $ytm = 35;
  100. $ybm = 60;
  101. if ($graph == 'small')
  102. {
  103. $iw = 300 + $xrm + $xlm;
  104. $ih = 100 + $ytm + $ybm;
  105. }
  106. else
  107. {
  108. $iw = 600 + $xrm + $xlm;
  109. $ih = 200 + $ytm + $ybm;
  110. }
  111. svg_create($iw, $ih);
  112. //
  113. // colors
  114. //
  115. $cs = $colorscheme;
  116. $cl['image_background'] = allocate_color($cs['image_background']);
  117. $cl['background'] = allocate_color($cs['graph_background']);
  118. $cl['background_2'] = allocate_color($cs['graph_background_2']);
  119. $cl['grid_stipple_1'] = allocate_color($cs['grid_stipple_1']);
  120. $cl['grid_stipple_2'] = allocate_color($cs['grid_stipple_2']);
  121. $cl['text'] = allocate_color($cs['text']);
  122. $cl['border'] = allocate_color($cs['border']);
  123. $cl['rx'] = allocate_color($cs['rx']);
  124. $cl['rx_border'] = allocate_color($cs['rx_border']);
  125. $cl['tx'] = allocate_color($cs['tx']);
  126. $cl['tx_border'] = allocate_color($cs['tx_border']);
  127. svg_rect(0, 0, $iw, $ih, array( 'stroke' => 'none', 'stroke-width' => 0, 'fill' => $cl['image_background']['rgb']) );
  128. svg_rect($xlm, $ytm, $iw-$xrm-$xlm, $ih-$ybm-$ytm, array( 'stroke' => 'none', 'stroke-width' => 0, 'fill' => $cl['background']['rgb']) );
  129. $depth = 12*SVG_DEPTH_SCALING;
  130. svg_group( array( 'stroke' => 'none', 'stroke-width' => 0, 'fill' => $cl['background_2']['rgb'], 'fill-opacity' => $cl['background_2']['opacity']) );
  131. svg_poly(array($xlm, $ytm, $xlm, $ih - $ybm, $xlm - $depth, $ih - $ybm + $depth, $xlm - $depth, $ytm + $depth));
  132. svg_poly(array($xlm, $ih - $ybm, $xlm - $depth, $ih - $ybm + $depth, $iw - $xrm - $depth, $ih - $ybm + $depth, $iw - $xrm, $ih - $ybm));
  133. svg_group_end();
  134. // draw title
  135. $text = T('Traffic data for')." $iface";
  136. svg_text($iw / 2, ($ytm / 2), $text, array( 'stroke' => 'none', 'fill' => $cl['text']['rgb'],'stroke-width' => 0, 'font-family' => SVG_FONT, 'font-weight' => 'bold', 'text-anchor' => 'middle' ));
  137. }
  138. function draw_border()
  139. {
  140. global $cl, $iw, $ih;
  141. svg_rect(1, 1, $iw-2, $ih-2, array( 'stroke' => $cl['border']['rgb'], 'stroke-opacity' => $cl['border']['opacity'], 'stroke-width' => 1, 'fill' => 'none') );
  142. }
  143. function draw_grid($x_ticks, $y_ticks)
  144. {
  145. global $cl, $iw, $ih, $xlm, $xrm, $ytm, $ybm;
  146. $x_step = ($iw - $xlm - $xrm) / ($x_ticks ?: 1);
  147. $y_step = ($ih - $ytm - $ybm) / $y_ticks;
  148. $depth = 12*SVG_DEPTH_SCALING;
  149. svg_group( array( 'stroke' => $cl['grid_stipple_1']['rgb'], 'stroke-opacity' => $cl['grid_stipple_1']['opacity'], 'stroke-width' => '1px', 'stroke-dasharray' => '1,1' ) );
  150. for ($i = $xlm; $i <= ($iw - $xrm); $i += $x_step)
  151. {
  152. svg_line($i, $ytm, $i, $ih-$ybm);
  153. svg_line($i, $ih-$ybm, $i-$depth, $ih-$ybm+$depth);
  154. }
  155. for ($i = $ytm; $i <= ($ih - $ybm); $i += $y_step)
  156. {
  157. svg_line($xlm, $i, $iw - $xrm, $i);
  158. svg_line($xlm, $i, $xlm - $depth, $i + $depth);
  159. }
  160. svg_group_end();
  161. svg_group( array( 'stroke' => $cl['border']['rgb'], 'stroke-width' => '1px', 'stroke-opacity' => $cl['border']['opacity'] ) );
  162. svg_line($xlm, $ytm, $xlm, $ih - $ybm);
  163. svg_line($xlm, $ih - $ybm, $iw - $xrm, $ih - $ybm);
  164. svg_group_end();
  165. }
  166. function draw_data($data)
  167. {
  168. global $cl,$iw,$ih,$xlm,$xrm,$ytm,$ybm;
  169. sort($data);
  170. $x_ticks = count($data);
  171. $y_ticks = 10;
  172. $y_scale = 1;
  173. $prescale = 1;
  174. $unit = 'K';
  175. $offset = 0;
  176. $gr_h = $ih - $ytm - $ybm;
  177. $x_step = ($iw - $xlm - $xrm) / ($x_ticks ?: 1);
  178. $y_step = ($ih - $ytm - $ybm) / $y_ticks;
  179. $bar_w = ($x_step / 2);
  180. // 找出最大值
  181. $low = 99999999999;
  182. $high = 0;
  183. for ($i=0; $i<$x_ticks; $i++)
  184. {
  185. if ($data[$i]['rx'] < $low)
  186. $low = $data[$i]['rx'];
  187. if ($data[$i]['tx'] < $low)
  188. $low = $data[$i]['tx'];
  189. if ($data[$i]['rx'] > $high)
  190. $high = $data[$i]['rx'];
  191. if ($data[$i]['tx'] > $high)
  192. $high = $data[$i]['tx'];
  193. }
  194. // 新的单位转换逻辑
  195. $high = $high * 1.2; // 增加20%余量,避免数据触顶
  196. // 自动选择合适的单位
  197. if ($high < 1024) {
  198. $unit = 'K';
  199. } else if ($high < 1048576) { // 1024 * 1024
  200. $unit = 'M';
  201. $prescale = 1024;
  202. $high = $high / 1024;
  203. } else if ($high < 1073741824) { // 1024 * 1024 * 1024
  204. $unit = 'G';
  205. $prescale = 1048576;
  206. $high = $high / 1048576;
  207. } else {
  208. $unit = 'T';
  209. $prescale = 1073741824;
  210. $high = $high / 1073741824;
  211. }
  212. // 计算合适的刻度
  213. $y_scale = ceil($high / $y_ticks);
  214. // 确保最小刻度
  215. if ($y_scale < 1) {
  216. $y_scale = 1;
  217. }
  218. draw_grid($x_ticks, $y_ticks);
  219. // 图表缩放因子(每像素)
  220. $sf = ($prescale * $y_scale * $y_ticks) / $gr_h;
  221. if (count($data) == 0)
  222. {
  223. $text = T('no data available');
  224. svg_text($iw/2, $ytm + 80, $text, array(
  225. 'stroke' => 'none',
  226. 'fill' => $cl['text']['rgb'],
  227. 'stroke-width' => 0,
  228. 'font-family' => SVG_FONT,
  229. 'font-size' => '16pt',
  230. 'text-anchor' => 'middle'
  231. ));
  232. }
  233. else
  234. {
  235. // 绘制柱状图
  236. for ($i=0; $i<$x_ticks; $i++)
  237. {
  238. $x = $xlm + ($i * $x_step);
  239. $y = $ytm + ($ih - $ytm - $ybm) - (($data[$i]['rx'] - $offset) / $sf);
  240. $depth = ($x_ticks < 20) ? 8*SVG_DEPTH_SCALING : 6*SVG_DEPTH_SCALING;
  241. $space = 0;
  242. $x1 = (int)$x;
  243. $y1 = (int)$y;
  244. $w = (int)($bar_w - $space);
  245. $h = (int)($ih - $ybm - $y);
  246. $x2 = (int)($x + $bar_w - $space);
  247. $y2 = (int)($ih - $ybm);
  248. svg_group( array( 'stroke' => $cl['rx_border']['rgb'], 'stroke-opacity' => $cl['rx_border']['opacity'],
  249. 'stroke-width' => 1, 'stroke-linejoin' => 'round',
  250. 'fill' => $cl['rx']['rgb'], 'fill-opacity' => $cl['rx']['opacity'] ) );
  251. svg_rect($x1, $y1, $w, $h);
  252. svg_rect($x1 - $depth, $y1 + $depth, $w, $h);
  253. svg_poly(array($x1, $y1, $x2, $y1, $x2 - $depth, $y1 + $depth, $x1 - $depth, $y1 + $depth));
  254. svg_poly(array($x2, $y1, $x2, $y2, $x2 - $depth, $y2 + $depth, $x2 - $depth, $y1 + $depth));
  255. svg_group_end();
  256. $y1 = (int)($ytm + ($ih - $ytm - $ybm) - (($data[$i]['tx'] - $offset) / $sf));
  257. $x1 = (int)($x1 + $bar_w);
  258. $x2 = (int)($x2 + $bar_w);
  259. $w = (int)($bar_w - $space);
  260. $h = (int)($ih - $ybm - $y1 - 1);
  261. svg_group( array( 'stroke' => $cl['tx_border']['rgb'], 'stroke-opacity' => $cl['tx_border']['opacity'],
  262. 'stroke-width' => 1, 'stroke-linejoin' => 'round',
  263. 'fill' => $cl['tx']['rgb'], 'fill-opacity' => $cl['tx']['opacity'] ) );
  264. svg_rect($x1, $y1, $w, $h);
  265. svg_rect($x1 - $depth, $y1 + $depth, $w, $h);
  266. svg_poly(array($x1, $y1, $x2, $y1, $x2 - $depth, $y1 + $depth, $x1 - $depth, $y1 + $depth));
  267. svg_poly(array($x2, $y1, $x2, $y2, $x2 - $depth, $y2 + $depth, $x2 - $depth, $y1 + $depth));
  268. svg_group_end();
  269. }
  270. // 绘制坐标轴标签
  271. svg_group(array(
  272. 'fill' => $cl['text']['rgb'],
  273. 'fill-opacity' => $cl['text']['opacity'],
  274. 'stroke-width' => '0',
  275. 'font-family' => SVG_FONT,
  276. 'font-size' => '10pt',
  277. 'text-anchor' => 'end'
  278. ));
  279. // Y轴刻度标签
  280. for ($i=0; $i<=$y_ticks; $i++)
  281. {
  282. $label = ($i * $y_scale).$unit;
  283. $tx = $xlm - 16;
  284. $ty = ($ih - $ybm) - ($i * $y_step) + 8;
  285. svg_text($tx, $ty, $label);
  286. }
  287. svg_group_end();
  288. // X轴标签
  289. svg_group(array(
  290. 'fill' => $cl['text']['rgb'],
  291. 'fill-opacity' => $cl['text']['opacity'],
  292. 'stroke-width' => '0',
  293. 'font-family' => SVG_FONT,
  294. 'font-size' => '10pt',
  295. 'text-anchor' => 'middle'
  296. ));
  297. for ($i=0; $i<$x_ticks; $i++)
  298. {
  299. $label = $data[$i]['img_label'];
  300. svg_text(
  301. $xlm + ($i * $x_step) + ($x_step / 2),
  302. $ih - $ybm + 20,
  303. $label
  304. );
  305. }
  306. svg_group_end();
  307. }
  308. draw_border();
  309. // 图例
  310. svg_rect($xlm, $ih-$ybm+39, 8, 8, array(
  311. 'stroke' => $cl['text']['rgb'],
  312. 'stroke-width' => 1,
  313. 'fill' => $cl['rx']['rgb']
  314. ));
  315. svg_text($xlm+14, $ih-$ybm+48, T('bytes in'), array(
  316. 'fill' => $cl['text']['rgb'],
  317. 'stroke-width' => 0,
  318. 'font-family' => SVG_FONT,
  319. 'font-size' => '8pt'
  320. ));
  321. svg_rect($xlm+120, $ih-$ybm+39, 8, 8, array(
  322. 'stroke' => $cl['text']['rgb'],
  323. 'stroke-width' => 1,
  324. 'fill' => $cl['tx']['rgb']
  325. ));
  326. svg_text($xlm+134, $ih-$ybm+48, T('bytes out'), array(
  327. 'fill' => $cl['text']['rgb'],
  328. 'stroke-width' => 0,
  329. 'font-family' => SVG_FONT,
  330. 'font-size' => '8pt'
  331. ));
  332. }
  333. function output_image()
  334. {
  335. global $page,$hour,$day,$month,$iface;
  336. if ($page == 'summary')
  337. return;
  338. init_image();
  339. if ($page == 'h')
  340. {
  341. draw_data($hour);
  342. }
  343. else if ($page == 'd')
  344. {
  345. draw_data($day);
  346. }
  347. else if ($page == 'm')
  348. {
  349. draw_data($month);
  350. }
  351. svg_end();
  352. }
  353. get_vnstat_data();
  354. output_image();
  355. ?>