[3594] | 1 | <?php |
---|
| 2 | /*======================================================================= |
---|
| 3 | // File: JPGRAPH_PIE.PHP |
---|
| 4 | // Description: Pie plot extension for JpGraph |
---|
| 5 | // Created: 2001-02-14 |
---|
| 6 | // Author: Johan Persson (johanp@aditus.nu) |
---|
| 7 | // Ver: $Id: jpgraph_pie.php 18250 2005-05-07 14:13:43Z ralfbecker $ |
---|
| 8 | // |
---|
| 9 | // License: This code is released under GPL 2.0 |
---|
| 10 | // Copyright (C) 2001 Johan Persson |
---|
| 11 | //======================================================================== |
---|
| 12 | */ |
---|
| 13 | |
---|
| 14 | //=================================================== |
---|
| 15 | // CLASS PiePlot |
---|
| 16 | // Description: |
---|
| 17 | //=================================================== |
---|
| 18 | class PiePlot { |
---|
| 19 | var $posx=0.5,$posy=0.5; |
---|
| 20 | var $radius=0.3; |
---|
| 21 | var $explode_radius=array(),$explode_all=false,$explode_r=20; |
---|
| 22 | var $labels, $legends=null; |
---|
| 23 | var $csimtargets=null; // Array of targets for CSIM |
---|
| 24 | var $csimareas=''; // Generated CSIM text |
---|
| 25 | var $csimalts=null; // ALT tags for corresponding target |
---|
| 26 | var $data=null; |
---|
| 27 | var $title; |
---|
| 28 | var $startangle=0; |
---|
| 29 | var $weight=1, $color="black"; |
---|
| 30 | var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$font_color="black"; |
---|
| 31 | var $legend_margin=6,$show_labels=true; |
---|
| 32 | var $precision=1,$show_psign=true; |
---|
| 33 | var $themearr=array( |
---|
| 34 | "earth" => array(10,34,40,45,46,62,63,134,74,77,120,136,141,168,180,209,218,346,395,89,430), |
---|
| 35 | "pastel" => array(27,38,42,58,66,79,105,110,128,147,152,230,236,240,331,337,405,415), |
---|
| 36 | "water" => array(8,370,10,40,335,56,213,237,268,14,326,387,24,388), |
---|
| 37 | "sand" => array(27,168,34,170,19,50,65,72,131,209,46,393)); |
---|
| 38 | var $theme="earth"; |
---|
| 39 | var $setslicecolors=array(); |
---|
| 40 | var $labelformat="%01.0f"; // Default format for labels |
---|
| 41 | var $labeltype=0; // Default to percentage |
---|
| 42 | var $pie_border=true,$pie_interior_border=true; |
---|
| 43 | |
---|
| 44 | //--------------- |
---|
| 45 | // CONSTRUCTOR |
---|
| 46 | function PiePlot(&$data) { |
---|
| 47 | $this->data = $data; |
---|
| 48 | $this->title = new Text(""); |
---|
| 49 | $this->title->SetFont(FF_FONT1,FS_BOLD); |
---|
| 50 | } |
---|
| 51 | |
---|
| 52 | //--------------- |
---|
| 53 | // PUBLIC METHODS |
---|
| 54 | function SetCenter($x,$y=0.5) { |
---|
| 55 | $this->posx = $x; |
---|
| 56 | $this->posy = $y; |
---|
| 57 | } |
---|
| 58 | |
---|
| 59 | function SetCSIMTargets(&$targets,$alts=null) { |
---|
| 60 | $this->csimtargets=$targets; |
---|
| 61 | $this->csimalts=$alts; |
---|
| 62 | } |
---|
| 63 | |
---|
| 64 | function GetCSIMareas() { |
---|
| 65 | return $this->csimareas; |
---|
| 66 | } |
---|
| 67 | |
---|
| 68 | function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) { //Slice number, ellipse centre (x,y), height, width, start angle, end angle |
---|
| 69 | |
---|
| 70 | //add coordinates of the centre to the map |
---|
| 71 | $coords = "$xc, $yc"; |
---|
| 72 | |
---|
| 73 | //add coordinates of the first point on the arc to the map |
---|
| 74 | $xp = floor(($radius*cos($sa))+$xc); |
---|
| 75 | $yp = floor($yc-$radius*sin($sa)); |
---|
| 76 | $coords.= ", $xp, $yp"; |
---|
| 77 | |
---|
| 78 | //add coordinates every 0.2 radians |
---|
| 79 | $a=$sa+0.2; |
---|
| 80 | while ($a<$ea) { |
---|
| 81 | $xp = floor($radius*cos($a)+$xc); |
---|
| 82 | $yp = floor($yc-$radius*sin($a)); |
---|
| 83 | $coords.= ", $xp, $yp"; |
---|
| 84 | $a += 0.2; |
---|
| 85 | } |
---|
| 86 | |
---|
| 87 | //Add the last point on the arc |
---|
| 88 | $xp = floor($radius*cos($ea)+$xc); |
---|
| 89 | $yp = floor($yc-$radius*sin($ea)); |
---|
| 90 | $coords.= ", $xp, $yp"; |
---|
| 91 | if( !empty($this->csimtargets[$i]) ) |
---|
| 92 | $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\""; |
---|
| 93 | if( !empty($this->csimalts[$i]) ) { |
---|
| 94 | $tmp=sprintf($this->csimalts[$i],$this->data[$i]); |
---|
| 95 | $this->csimareas .= " alt=\"$tmp\""; |
---|
| 96 | } |
---|
| 97 | $this->csimareas .= ">\r\n"; |
---|
| 98 | } |
---|
| 99 | |
---|
| 100 | |
---|
| 101 | function SetTheme($t) { |
---|
| 102 | if( in_array($t,array_keys($this->themearr)) ) |
---|
| 103 | $this->theme = $t; |
---|
| 104 | else |
---|
| 105 | JpGraphError::Raise("JpGraph Error: Unknown theme: $t"); |
---|
| 106 | } |
---|
| 107 | |
---|
| 108 | function ExplodeSlice($e) { |
---|
| 109 | $this->explode_radius[$e]=20; |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | function ExplodeAll($radius=-1) { |
---|
| 113 | $this->explode_all=true; |
---|
| 114 | if( $radius==-1 ) |
---|
| 115 | $this->explode_r = 20; |
---|
| 116 | else |
---|
| 117 | $this->explode_r = $radius; |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | function Explode($radarr) { |
---|
| 121 | $this->explode_radius = $radarr; |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | function SetSliceColors($c) { |
---|
| 125 | $this->setslicecolors = $c; |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | function SetStartAngle($a) { |
---|
| 129 | assert($a>=0 && $a<2*M_PI); |
---|
| 130 | $this->startangle = $a; |
---|
| 131 | } |
---|
| 132 | |
---|
| 133 | function SetFont($family,$style=FS_NORMAL,$size=10) { |
---|
| 134 | $this->font_family=$family; |
---|
| 135 | $this->font_style=$style; |
---|
| 136 | $this->font_size=$size; |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | // Size in percentage |
---|
| 140 | function SetSize($size) { |
---|
| 141 | if( ($size>0 && $size<=0.5) || ($size>10 && $size<1000) ) |
---|
| 142 | $this->radius = $size; |
---|
| 143 | else |
---|
| 144 | JpGraphError::Raise("JpGraph Error: Size (radius) for pie must either be specified as a fraction |
---|
| 145 | [0, 0.5] of the size of the image or as an absolute size in pixels |
---|
| 146 | in the range [10, 1000]"); |
---|
| 147 | } |
---|
| 148 | |
---|
| 149 | function SetFontColor($color) { |
---|
| 150 | $this->font_color = $color; |
---|
| 151 | } |
---|
| 152 | |
---|
| 153 | // Set label arrays |
---|
| 154 | function SetLegends($l) { |
---|
| 155 | $this->legends = $l; |
---|
| 156 | } |
---|
| 157 | |
---|
| 158 | // Should the values be displayed? |
---|
| 159 | function HideLabels($f=true) { |
---|
| 160 | $this->show_labels = !$f; |
---|
| 161 | } |
---|
| 162 | |
---|
| 163 | // Specify label format as a "C" printf string |
---|
| 164 | function SetLabelFormat($f) { |
---|
| 165 | $this->labelformat=$f; |
---|
| 166 | // If format is specified don't add any %-sign |
---|
| 167 | $this->show_psign=0; |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | // Should we display actual value or percentage? |
---|
| 171 | function SetLabelType($t) { |
---|
| 172 | if( $t<0 || $t>1 ) |
---|
| 173 | JpGraphError::Raise("JpGraph Error: Label type for pie plots must be 0 or 1 (not $t)."); |
---|
| 174 | $this->labeltype=$t; |
---|
| 175 | // Don't show percentage value when displaying absolute values |
---|
| 176 | if( $t==1 ) |
---|
| 177 | $this->show_psign=0; |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | // Should the circle around a pie plot be displayed |
---|
| 181 | function ShowBorder($exterior=true,$interior=true) { |
---|
| 182 | $this->pie_border = $exterior; |
---|
| 183 | $this->pie_interior_border = $interior; |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | // Setup the legends |
---|
| 187 | function Legend(&$graph) { |
---|
| 188 | $colors = array_keys($graph->img->rgb->rgb_table); |
---|
| 189 | sort($colors); |
---|
| 190 | $ta=$this->themearr[$this->theme]; |
---|
| 191 | |
---|
| 192 | if( $this->setslicecolors==null ) |
---|
| 193 | $numcolors=count($ta); |
---|
| 194 | else |
---|
| 195 | $numcolors=count($this->setslicecolors); |
---|
| 196 | |
---|
| 197 | $sum=0; |
---|
| 198 | for($i=0; $i<count($this->data); ++$i) |
---|
| 199 | $sum += $this->data[$i]; |
---|
| 200 | |
---|
| 201 | $i=0; |
---|
| 202 | if( count($this->legends)>0 ) { |
---|
| 203 | foreach( $this->legends as $l ) { |
---|
| 204 | |
---|
| 205 | // Replace possible format with actual values |
---|
| 206 | if( $this->labeltype==0 ) |
---|
| 207 | $l = sprintf($l,100*$this->data[$i]/$sum); |
---|
| 208 | else |
---|
| 209 | $l = sprintf($l,$this->data[$i]); |
---|
| 210 | |
---|
| 211 | if( $this->setslicecolors==null ) |
---|
| 212 | $graph->legend->Add($l,$colors[$ta[$i%$numcolors]]); |
---|
| 213 | else |
---|
| 214 | $graph->legend->Add($l,$this->setslicecolors[$i%$numcolors]); |
---|
| 215 | ++$i; |
---|
| 216 | |
---|
| 217 | // Breakout if there are more legends then values |
---|
| 218 | if( $i==count($this->data) ) return; |
---|
| 219 | } |
---|
| 220 | } |
---|
| 221 | } |
---|
| 222 | |
---|
| 223 | // Specify precision for labels. This is almost a deprecated function |
---|
| 224 | // nowadays since the introduction of SetLabelFormat() |
---|
| 225 | function SetPrecision($p,$psign=true) { |
---|
| 226 | if( $p<0 || $p>8 ) |
---|
| 227 | JpGraphError::Raise("JpGraph Error: Pie label Precision must be between 0 and 8"); |
---|
| 228 | $this->labelformat="%01.".$p."f"; |
---|
| 229 | $this->show_psign=$psign; |
---|
| 230 | } |
---|
| 231 | |
---|
| 232 | function Stroke(&$img) { |
---|
| 233 | |
---|
| 234 | $colors = array_keys($img->rgb->rgb_table); |
---|
| 235 | sort($colors); |
---|
| 236 | $ta=$this->themearr[$this->theme]; |
---|
| 237 | |
---|
| 238 | if( $this->setslicecolors==null ) |
---|
| 239 | $numcolors=count($ta); |
---|
| 240 | else |
---|
| 241 | $numcolors=count($this->setslicecolors); |
---|
| 242 | |
---|
| 243 | // Draw the slices |
---|
| 244 | $sum=0; |
---|
| 245 | for($i=0; $i<count($this->data); ++$i) |
---|
| 246 | $sum += $this->data[$i]; |
---|
| 247 | |
---|
| 248 | // Format the titles for each slice |
---|
| 249 | for( $i=0; $i<count($this->data); ++$i) { |
---|
| 250 | if( $this->labeltype==0 ) |
---|
| 251 | if( $sum != 0 ) |
---|
| 252 | $l = round(100*$this->data[$i]/$sum,$this->precision); |
---|
| 253 | else |
---|
| 254 | $l = 0; |
---|
| 255 | else |
---|
| 256 | $l = $this->data[$i]; |
---|
| 257 | $l = sprintf($this->labelformat,$l); |
---|
| 258 | if( $this->show_psign ) $l .= "%"; |
---|
| 259 | $this->labels[$i]=$l; |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | // Set up the pic-circle |
---|
| 263 | if( $this->radius < 1 ) |
---|
| 264 | $radius = floor($this->radius*min($img->width,$img->height)); |
---|
| 265 | else |
---|
| 266 | $radius = $this->radius; |
---|
| 267 | $xc = $this->posx*$img->width; |
---|
| 268 | $yc = $this->posy*$img->height; |
---|
| 269 | |
---|
| 270 | $accsum=0; |
---|
| 271 | $angle2 = $this->startangle; |
---|
| 272 | $img->SetColor($this->color); |
---|
| 273 | |
---|
| 274 | if( $this->explode_all ) |
---|
| 275 | for($i=0;$i<count($this->data);++$i) |
---|
| 276 | $this->explode_radius[$i]=$this->explode_r; |
---|
| 277 | |
---|
| 278 | for($i=0; $sum>0 && $i<count($this->data); ++$i) { |
---|
| 279 | $d = $this->data[$i]; |
---|
| 280 | $angle1 = $angle2; |
---|
| 281 | $accsum += $d; |
---|
| 282 | $angle2 = $this->startangle+2.0*M_PI*$accsum/$sum; |
---|
| 283 | |
---|
| 284 | if( $this->setslicecolors==null ) |
---|
| 285 | $slicecolor=$colors[$ta[$i%$numcolors]]; |
---|
| 286 | else |
---|
| 287 | $slicecolor=$this->setslicecolors[$i%$numcolors]; |
---|
| 288 | |
---|
| 289 | if( $this->pie_interior_border ) |
---|
| 290 | $img->SetColor($this->color); |
---|
| 291 | else |
---|
| 292 | $img->SetColor($slicecolor); |
---|
| 293 | |
---|
| 294 | $arccolor = $this->pie_border ? $this->color : $slicecolor; |
---|
| 295 | |
---|
| 296 | $la = abs($angle2-$angle1)/2.0+$angle1; |
---|
| 297 | if( empty($this->explode_radius[$i]) ) |
---|
| 298 | $this->explode_radius[$i]=0; |
---|
| 299 | $xcm = $xc + $this->explode_radius[$i]*cos($la); |
---|
| 300 | $ycm = $yc - $this->explode_radius[$i]*sin($la); |
---|
| 301 | |
---|
| 302 | $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,$angle1,$angle2, |
---|
| 303 | $slicecolor,$arccolor); |
---|
| 304 | |
---|
| 305 | if( $this->show_labels ) |
---|
| 306 | $this->StrokeLabels($this->labels[$i],$img,$xc,$yc,$la, |
---|
| 307 | $radius+$this->explode_radius[$i]); |
---|
| 308 | |
---|
| 309 | if ($this->csimtargets) { |
---|
| 310 | $this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2); |
---|
| 311 | } |
---|
| 312 | |
---|
| 313 | } |
---|
| 314 | // Adjust title position |
---|
| 315 | $this->title->Pos($xc,$yc-$img->GetFontHeight()-$radius,"center","bottom"); |
---|
| 316 | $this->title->Stroke($img); |
---|
| 317 | |
---|
| 318 | } |
---|
| 319 | |
---|
| 320 | //--------------- |
---|
| 321 | // PRIVATE METHODS |
---|
| 322 | |
---|
| 323 | // Position the labels of each slice |
---|
| 324 | function StrokeLabels($label,$img,$xc,$yc,$a,$r) { |
---|
| 325 | $img->SetFont($this->font_family,$this->font_style,$this->font_size); |
---|
| 326 | $img->SetColor($this->font_color); |
---|
| 327 | $img->SetTextAlign("left","top"); |
---|
| 328 | $marg=6; |
---|
| 329 | $r += $img->GetFontHeight()/2; |
---|
| 330 | $xt=round($r*cos($a)+$xc); |
---|
| 331 | $yt=round($yc-$r*sin($a)); |
---|
| 332 | |
---|
| 333 | // Position the axis title. |
---|
| 334 | // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text |
---|
| 335 | // that intersects with the extension of the corresponding axis. The code looks a little |
---|
| 336 | // bit messy but this is really the only way of having a reasonable position of the |
---|
| 337 | // axis titles. |
---|
| 338 | $h=$img->GetTextHeight($label); |
---|
| 339 | $w=$img->GetTextWidth($label); |
---|
| 340 | while( $a > 2*M_PI ) $a -= 2*M_PI; |
---|
| 341 | if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0; |
---|
| 342 | if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; |
---|
| 343 | if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1; |
---|
| 344 | if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI); |
---|
| 345 | |
---|
| 346 | if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI; |
---|
| 347 | if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI); |
---|
| 348 | if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1; |
---|
| 349 | if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI); |
---|
| 350 | if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0; |
---|
| 351 | |
---|
| 352 | $img->StrokeText($xt-$dx*$w,$yt-$dy*$h,$label); |
---|
| 353 | } |
---|
| 354 | } // Class |
---|
| 355 | |
---|
| 356 | |
---|
| 357 | |
---|
| 358 | //=================================================== |
---|
| 359 | // CLASS PieGraph |
---|
| 360 | // Description: |
---|
| 361 | //=================================================== |
---|
| 362 | class PieGraph extends Graph { |
---|
| 363 | var $posx, $posy, $radius; |
---|
| 364 | var $legends=array(); |
---|
| 365 | var $plots=array(); |
---|
| 366 | //--------------- |
---|
| 367 | // CONSTRUCTOR |
---|
| 368 | function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) { |
---|
| 369 | $this->Graph($width,$height,$cachedName,$timeout,$inline); |
---|
| 370 | $this->posx=$width/2; |
---|
| 371 | $this->posy=$height/2; |
---|
| 372 | $this->SetColor(array(255,255,255)); |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | //--------------- |
---|
| 376 | // PUBLIC METHODS |
---|
| 377 | function Add(&$pie) { |
---|
| 378 | $this->plots[] = $pie; |
---|
| 379 | } |
---|
| 380 | |
---|
| 381 | function SetColor($c) { |
---|
| 382 | $this->SetMarginColor($c); |
---|
| 383 | } |
---|
| 384 | |
---|
| 385 | // Method description |
---|
| 386 | function Stroke($aStrokeFileName="") { |
---|
| 387 | |
---|
| 388 | $this->StrokeFrame(); |
---|
| 389 | |
---|
| 390 | for($i=0; $i<count($this->plots); ++$i) |
---|
| 391 | $this->plots[$i]->Stroke($this->img); |
---|
| 392 | |
---|
| 393 | foreach( $this->plots as $p) |
---|
| 394 | $p->Legend($this); |
---|
| 395 | |
---|
| 396 | $this->legend->Stroke($this->img); |
---|
| 397 | $this->title->Center($this->img->left_margin,$this->img->width-$this->img->right_margin,5); |
---|
| 398 | $this->title->Stroke($this->img); |
---|
| 399 | |
---|
| 400 | // Stroke texts |
---|
| 401 | if( $this->texts != null ) |
---|
| 402 | foreach( $this->texts as $t) |
---|
| 403 | $t->Stroke($this->img); |
---|
| 404 | |
---|
| 405 | |
---|
| 406 | if ($this->showcsim) { |
---|
| 407 | foreach($this->plots as $p ) { |
---|
| 408 | $csim.= $p->GetCSIMareas(); |
---|
| 409 | } |
---|
| 410 | //$csim.= $this->legend->GetCSIMareas(); |
---|
| 411 | if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) { |
---|
| 412 | $this->img->SetColor($this->csimcolor); |
---|
| 413 | for ($i=0; $i<count($coords[0]); $i++) { |
---|
| 414 | if ($coords[1][$i]=="poly") { |
---|
| 415 | preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts); |
---|
| 416 | $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]); |
---|
| 417 | for ($j=0; $j<count($pts[0]); $j++) { |
---|
| 418 | $this->img->LineTo($pts[1][$j],$pts[2][$j]); |
---|
| 419 | } |
---|
| 420 | } else if ($coords[1][$i]=="rect") { |
---|
| 421 | $pts = preg_split('/,/', $coords[2][$i]); |
---|
| 422 | $this->img->SetStartPoint($pts[0],$pts[1]); |
---|
| 423 | $this->img->LineTo($pts[2],$pts[1]); |
---|
| 424 | $this->img->LineTo($pts[2],$pts[3]); |
---|
| 425 | $this->img->LineTo($pts[0],$pts[3]); |
---|
| 426 | $this->img->LineTo($pts[0],$pts[1]); |
---|
| 427 | |
---|
| 428 | } |
---|
| 429 | } |
---|
| 430 | } |
---|
| 431 | } |
---|
| 432 | |
---|
| 433 | // Finally output the image |
---|
| 434 | $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName); |
---|
| 435 | } |
---|
| 436 | } // Class |
---|
| 437 | |
---|
| 438 | /* EOF */ |
---|
| 439 | ?> |
---|