Koláčový graf
Trieda na generovanie koláčového grafu pomocou knižnice GD.
<?php /* -*- C -*- */ /* ** ** PHP Class for creating pie charts using the GD library functions. ** ** There is a bug in the GD library somewhere that seems to kick in ** when you try to return images that are larger than 4K. We probably ** need a workaround for this... ** ** Pie charts look a bit shabby. There seems to be one or more ** roundoff errors lurking about making life hard for us. To fix this ** we should perhaps investigate how the Arc-drawing thingey works and ** try to find out how it gets the endpoints. Also the flood-filler ** doesn't quite cope with filling the pieces very well. ** ** Authors: Bjørn Borud, Guardian Networks AS, <borud@guardian.no> ** Original author 1998/02/03 ** ** Jean-Marc Libs <libs@mail.cybercable.tm.fr> ** Version 2.0 2000-09-14 ** Converted gif generation to png generation ** Broke app into separate images for piechart and legend ** (dropped heading generation, as this can be displayed much ** simpler in plain html) ** Cleaned up all the rounding stuff, and corrected bugs ** which produced spilling for some values. ** Added some debugging stuff, which is commented out with ** #-type comments ** Remark: It wouldn't be difficult to merge the "all-in-one image" ** design of version 1.0 with the debugged algorithms of version 2.0 ** but neither I nor Bjørn currently have the free time for that ** */ dl("gd.so"); // This is necessary if you have the GD library as a PHP module. // If your PHP has been compiled with GD support, remove the line. /* {{{ piechart */ /* ** This is a class for creating pie charts. Generally you just have ** to instantiate it, and then make a call to the "init" method to ** set the size and transfer the data. ** ** The data is an array of arrays that consist the following data: ** o numeric value ** o value legend ** o red \ ** o green > the RGB values for the color of the slice/legend ** o blue / ** */ class piechart { /* {{{ attributes */ var $im; var $width, $height; var $data; var $colors; var $angles; var $left=10; var $right=10; var $top=10; var $bottom=10; var $head_top=10; var $head_space=5; var $legend_left=20; var $center_x; var $center_y; var $diameter; /* sum of values */ var $sum; /* font sizes */ var $fx, $fy; var $legend_num = ""; /* }}} */ /* {{{ constants */ var $PI = 3.1415926535897931; /* }}} */ /* {{{ get_xy_factors */ /* ** Calculate the directional vector for the sides of the ** piece of pie. */ function get_xy_factors ($degrees) { $x = cos(deg2rad($degrees)); $y = sin(deg2rad($degrees)); return (array($x, $y)); } /* }}} */ /* {{{ init */ /* ** Initialize the object and draw the pie. This would be the ** constructor in an ordinary OO scenario -- just that we haven't ** got constructors in PHP, now do we? ;-) */ function init ($w, $h, $d) { #if( !($ftrack=fopen("/var/tmp/debug","a+"))) echo "not opened"; #fwrite($ftrack,"Start init()\n"); $this->im = ImageCreate($w, $h); $this->width = $w; $this->height = $h; $this->data = $d; $this->da_width = ($this->width - $this->left - $this->right); $this->da_height = ($this->height - $this->top - $this->bottom); $this->center_x = intval($this->left + ($this->da_width / 2)); $this->center_y = intval($this->top + ($this->da_height / 2)); /* decide the diameter of the pie */ if ($this->da_height > $this->da_width) { $this->diameter = $this->da_width; } else { $this->diameter = $this->da_height; } /* a less agressive kind of white */ // $this->white = ImageColorAllocate($this->im, 255, 255, 255); $this->white = ImageColorAllocate($this->im, 0xF5, 0xF5, 0xF5); $this->black = ImageColorAllocate($this->im, 0, 0, 0); $n = count($this->data); $this->add[0]=0; for ($i = 0; $i < $n; $i++) { if((!$this->data[$i][2])&&(!$this->data[$i][3])&&(!$this->data[$i][4])) { // -> Default color $this->colors[$i] = ImageColorAllocate($this->im, 0x33 ,0x33 , 0x33); } else { $this->colors[$i] = ImageColorAllocate($this->im, $this->data[$i][2], $this->data[$i][3], $this->data[$i][4]); } $this->sum += $this->data[$i][0]; $this->add[$i+1] = $this->sum; } $from = 0; $to = 0; for ($i = 0; $i < $n; $i++) { $from = round(($this->add[$i]*360)/doubleval($this->sum)); $to = round(($this->add[$i+1]*360)/doubleval($this->sum)); $this->angles[$i] = $to-$from; # $to = $from + $this->angles[$i]; $col = $this->colors[$i]; $this->draw_slice($this->center_x, $this->center_y, $from, $to, $this->colors[$i]); #} #fwrite($ftrack,"from: $from to: $to\n"); } ImageArc($this->im,$this->center_x,$this->center_y,$this->diameter,$this->diameter,0,360,$this->black); #fclose($ftrack); } /* }}} */ /* {{{ draw_point */ /* ** This function is just here for debugging purposes. It is ** sometimes very useful to be able to draw an X to check ** coordinates. */ function draw_point($x, $y) { ImageLine($this->im, $x-4, $y-4, $x+4, $y+4, $this->black); ImageLine($this->im, $x-4, $y+4, $x+4, $y-4, $this->black); } /* }}} */ /* {{{ draw_margins */ /* ** Also a debugging function to show where the margins are at */ function draw_margins () { ImageLine($this->im, 0, $this->top, $this->width, $this->top, $this->black); ImageLine($this->im, 0, $this->height - $this->bottom, $this->width, $this->height - $this->bottom, $this->black); ImageLine($this->im, $this->left, 0, $this->left, $this->height, $this->black); ImageLine($this->im, $this->width - $this->right, 0, $this->width - $this->right, $this->height, $this->black); } /* }}} */ /* {{{ draw_slice */ /* ** This function draws a piece of pie centered at x,y starting at ** "from" degrees and ending at "to" degrees using the specified color. */ function draw_slice ($x, $y, $from, $to, $color) { # Awful Kludge!!! if ($to > 360) { $to = 360; } /* to start on top */ $from=$from+270; $to=$to+270; /* Arc */ ImageArc($this->im, $x, $y, $this->diameter, $this->diameter, $from, $to, $color); /* First line */ $axy2 = $this->get_xy_factors($from); $ax2 = round($x + ($axy2[0] * ($this->diameter /2))); $ay2 = round($y + ($axy2[1] * ($this->diameter /2))); ImageLine($this->im, $x, $y, $ax2, $ay2, $color); /* Second line */ $bxy2 = $this->get_xy_factors($to); $bx2 = round($x + ($bxy2[0] * ($this->diameter /2))); $by2 = round($y + ($bxy2[1] * ($this->diameter /2))); ImageLine($this->im, $x, $y, $bx2, $by2, $color); /* Decide if it's worth filling. a(ax2,ay2) and b(bx2,by2) should be at least 2 pixels apart. */ #fwrite($ftrack,"ax2 : ".$ax2."\n"); #fwrite($ftrack,"pow(by2-ay2,2) : ".pow($by2-$ay2,2)."\n"); /* decide where to start filling, then fill, or draw a middle line */ $xy2 = $this->get_xy_factors((($to - $from) / 2) + $from); if (((pow(($bx2-$ax2),2)+pow(($by2-$ay2),2))>=2)||(($to-$from)>90)) { /* fill */ $x2 = round($x + ($xy2[0] * ($this->diameter /2.2))); $y2 = round($y + ($xy2[1] * ($this->diameter /2.2))); ImageFillToBorder($this->im, $x2, $y2, $color, $color); } else { /* middle line */ ImageLine($this->im,$x,$y,$x+($xy2[0] * ($this->diameter /2)),$y+($xy2[1] * ($this->diameter /2)),$color); } } /* }}} */ /* {{{ display */ /* ** Make sure the legends are drawn, then output the image to the ** client */ function display() { Header("Content-type: image/png"); Header("Pragma: no-cache"); Imagepng($this->im); } /* }}} */ } class pielegend { /* {{{ attributes */ var $im; var $width, $height; var $data; var $colors; var $angles; var $left=10; var $right=200; var $top=50; var $bottom=10; var $head_top=10; var $head_space=5; var $legend_left=20; var $center_x; var $center_y; var $diameter; /* sum of values */ var $sum; /* font sizes */ var $fx, $fy; var $legend_num = ""; /* }}} */ /* {{{ constants */ var $PI = 3.1415926535897931; /* }}} */ /* {{{ get_xy_factors */ /* ** Calculate the directional vector for the sides of the ** piece of pie. */ function get_xy_factors ($degrees) { $x = cos(deg2rad($degrees)); $y = sin(deg2rad($degrees)); return (array($x, $y)); } /* }}} */ /* {{{ init */ /* ** Initialize the object and draw the pie. This would be the ** constructor in an ordinary OO scenario -- just that we haven't ** got constructors in PHP, now do we? ;-) */ function init ($w, $h, $d) { $this->im = ImageCreate($w, $h); $this->width = $w; $this->height = $h; $this->data = $d; $this->da_width = ($this->width - $this->left - $this->right); $this->da_height = ($this->height - $this->top - $this->bottom); $this->center_x = intval($this->left + ($this->da_width / 2)); $this->center_y = intval($this->top + ($this->da_height / 2)); /* font sizes */ $this->fx = array(0, 5,6,7,8,9); $this->fy = array(0, 7,8,10,14,11); /* decide the diameter of the pie */ if ($this->da_height > $this->da_width) { $this->diameter = $this->da_width; } else { $this->diameter = $this->da_height; } // $this->white = ImageColorAllocate($this->im, 255, 255, 255); $this->white = ImageColorAllocate($this->im, 0xF5, 0xF5, 0xF5); $this->black = ImageColorAllocate($this->im, 0, 0, 0); $n = count($this->data); for ($i = 0; $i < $n; $i++) { if((!$this->data[$i][2])&&(!$this->data[$i][3])&&(!$this->data[$i][4])) { // Not a country which color was decided on $this->colors[$i] = ImageColorAllocate($this->im, 0x33 ,0x33 , 0x33); } else { $this->colors[$i] = ImageColorAllocate($this->im, $this->data[$i][2], $this->data[$i][3], $this->data[$i][4]); } $this->sum += $this->data[$i][0]; } $from = 0; $to = 0; } /* }}} */ /* {{{ set_legend_percent */ /* utility function to set an attribute so we display percentages */ function set_legend_percent () { $this->legend_num = "p"; } /* }}} */ /* {{{ set_legend_value */ /* utility function to set an attribute so we display values */ function set_legend_value () { $this->legend_num = "v"; } /* }}} */ /* {{{ draw_point */ /* ** This function is just here for debugging purposes. It is ** sometimes very useful to be able to draw an X to check ** coordinates. */ function draw_point($x, $y) { ImageLine($this->im, $x-4, $y-4, $x+4, $y+4, $this->black); ImageLine($this->im, $x-4, $y+4, $x+4, $y-4, $this->black); } /* }}} */ /* {{{ draw_margins */ /* ** Also a debugging function to show where the margins are at */ function draw_margins () { ImageLine($this->im, 0, $this->top, $this->width, $this->top, $this->black); ImageLine($this->im, 0, $this->height - $this->bottom, $this->width, $this->height - $this->bottom, $this->black); ImageLine($this->im, $this->left, 0, $this->left, $this->height, $this->black); ImageLine($this->im, $this->width - $this->right, 0, $this->width - $this->right, $this->height, $this->black); } /* }}} */ /* {{{ draw_legends */ /* ** Draw legends at the right side of the pie chart. This function ** accepts a fontsize and gathers all the other information from ** the multilevel data array */ function draw_legends ($fontsize) { $n = count($this->data); $x1 = $this->width - $this->right + $this->legend_left; $x2 = $x1 + $this->fy[$fontsize];; for ($i = 0; $i < $n; $i++) { /* determine Y coordinates */ $y1 = ($i * $this->fy[$fontsize] * 1.5) + $this->top; $y2 = $y1 + $this->fy[$fontsize]; /* draw the legend color rectangle */ ImageFilledRectangle($this->im, $x1, $y1, $x2, $y2, $this->colors[$i]); ImageRectangle($this->im, $x1, $y1, $x2, $y2, $this->black); $legend = $this->data[$i][1]; /* decide what to show after legend */ switch ($this->legend_num) { case "v": $legend .= sprintf(" (%.2f)", $this->data[$i][0]); break; case "p": $legend .= sprintf(" (%.2f%%)", ($this->data[$i][0] * 100 / doubleval($this->sum))); break; } ImageString($this->im, $fontsize, $x2 + 5, $y1, $legend, $this->black); } } /* }}} */ /* {{{ display */ /* ** Make sure the legends are drawn, then output the image to the ** client */ function display() { $this->draw_legends(2); Imagepng($this->im, "/tmp/pie.png"); Header("Content-type: image/png"); Imagepng($this->im); } /* }}} */ } /* {{{ Test code */ $vals = array( array(100, "First value", 190, 0 ,0), array(100, "Second value", 0, 190, 0), array(100, "Third value", 0, 0 ,190), array(100, "Fourth value", 0, 190 ,190), array(301.2437, "Fifth value", 204, 0 ,204), array(308, "Sixth value", 204,204,0) ); /* For the pie chart uncomment this part */ $pie = new piechart; $pie->init(300, 300, $vals); $pie->display(); /* For the legend uncomment this part */ /* $pie = new pielegend; $pie->init(200, 300, $vals); $pie->set_legend_percent(); $pie->display(); */ /* }}} */ ?>