source: trunk/xgraph/jpgraph/jpgraph_bar.php

Last change on this file was 42, checked in by marrucho, 10 years ago
File size: 47.4 KB
Line 
1<?php
2/*=======================================================================
3 // File:        JPGRAPH_BAR.PHP
4 // Description: Bar plot extension for JpGraph
5 // Created:     2001-01-08
6 // Ver:         $Id: jpgraph_bar.php 1905 2009-10-06 18:00:21Z ljp $
7 //
8 // Copyright (c) Asial Corporation. All rights reserved.
9 //========================================================================
10 */
11
12require_once('jpgraph_plotband.php');
13
14// Pattern for Bars
15DEFINE('PATTERN_DIAG1',1);
16DEFINE('PATTERN_DIAG2',2);
17DEFINE('PATTERN_DIAG3',3);
18DEFINE('PATTERN_DIAG4',4);
19DEFINE('PATTERN_CROSS1',5);
20DEFINE('PATTERN_CROSS2',6);
21DEFINE('PATTERN_CROSS3',7);
22DEFINE('PATTERN_CROSS4',8);
23DEFINE('PATTERN_STRIPE1',9);
24DEFINE('PATTERN_STRIPE2',10);
25
26//===================================================
27// CLASS BarPlot
28// Description: Main code to produce a bar plot
29//===================================================
30class BarPlot extends Plot {
31    public $fill=false,$fill_color="lightblue"; // Default is to fill with light blue
32    public $iPattern=-1,$iPatternDensity=80,$iPatternColor='black';
33    public $valuepos='top';
34    public $grad=false,$grad_style=1;
35    public $grad_fromcolor=array(50,50,200),$grad_tocolor=array(255,255,255);
36    public $ymin=0;
37    protected $width=0.4; // in percent of major ticks
38    protected $abswidth=-1; // Width in absolute pixels
39    protected $ybase=0; // Bars start at 0
40    protected $align="center";
41    protected $bar_shadow=false;
42    protected $bar_shadow_color="black";
43    protected $bar_shadow_hsize=3,$bar_shadow_vsize=3;
44    protected $bar_3d=false;
45    protected $bar_3d_hsize=3,$bar_3d_vsize=3;
46
47    //---------------
48    // CONSTRUCTOR
49    function __construct($datay,$datax=false) {
50        parent::__construct($datay,$datax);
51        ++$this->numpoints;
52    }
53
54    //---------------
55    // PUBLIC METHODS
56
57    // Set a drop shadow for the bar (or rather an "up-right" shadow)
58    function SetShadow($aColor="black",$aHSize=3,$aVSize=3,$aShow=true) {
59        $this->bar_shadow=$aShow;
60        $this->bar_shadow_color=$aColor;
61        $this->bar_shadow_vsize=$aVSize;
62        $this->bar_shadow_hsize=$aHSize;
63
64        // Adjust the value margin to compensate for shadow
65        $this->value->margin += $aVSize;
66    }
67
68    function Set3D($aHSize=3,$aVSize=3,$aShow=true) {
69        $this->bar_3d=$aShow;
70        $this->bar_3d_vsize=$aVSize;
71        $this->bar_3d_hsize=$aHSize;
72
73        $this->value->margin += $aVSize;
74    }
75
76    // DEPRECATED use SetYBase instead
77    function SetYMin($aYStartValue) {
78        //die("JpGraph Error: Deprecated function SetYMin. Use SetYBase() instead.");
79        $this->ybase=$aYStartValue;
80    }
81
82    // Specify the base value for the bars
83    function SetYBase($aYStartValue) {
84        $this->ybase=$aYStartValue;
85    }
86
87    // The method will take the specified pattern anre
88    // return a pattern index that corresponds to the original
89    // patterm being rotated 90 degreees. This is needed when plottin
90    // Horizontal bars
91    function RotatePattern($aPat,$aRotate=true) {
92        $rotate = array(1 => 2, 2 => 1, 3 => 3, 4 => 5, 5 => 4, 6 => 6, 7 => 7, 8 => 8);
93        if( $aRotate ) {
94            return $rotate[$aPat];
95        }
96        else {
97            return $aPat;
98        }
99    }
100
101    function Legend($graph) {
102        if( $this->grad && $this->legend!="" && !$this->fill ) {
103            $color=array($this->grad_fromcolor,$this->grad_tocolor);
104            // In order to differentiate between gradients and cooors specified as an RGB triple
105            $graph->legend->Add($this->legend,$color,"",-$this->grad_style,
106            $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
107        }
108        elseif( $this->legend!="" && ($this->iPattern > -1 || is_array($this->iPattern)) ) {
109            if( is_array($this->iPattern) ) {
110                $p1 = $this->RotatePattern( $this->iPattern[0], $graph->img->a == 90 );
111                $p2 = $this->iPatternColor[0];
112                $p3 = $this->iPatternDensity[0];
113            }
114            else {
115                $p1 = $this->RotatePattern( $this->iPattern, $graph->img->a == 90 );
116                $p2 = $this->iPatternColor;
117                $p3 = $this->iPatternDensity;
118            }
119            if( $p3 < 90 ) $p3 += 5;
120            $color = array($p1,$p2,$p3,$this->fill_color);
121            // A kludge: Too mark that we add a pattern we use a type value of < 100
122            $graph->legend->Add($this->legend,$color,"",-101,
123                                $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
124        }
125        elseif( $this->fill_color && $this->legend!="" ) {
126            if( is_array($this->fill_color) ) {
127                $graph->legend->Add($this->legend,$this->fill_color[0],"",0,
128                $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
129            }
130            else {
131                $graph->legend->Add($this->legend,$this->fill_color,"",0,
132                $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
133            }
134        }
135    }
136
137    // Gets called before any axis are stroked
138    function PreStrokeAdjust($graph) {
139        parent::PreStrokeAdjust($graph);
140
141        // If we are using a log Y-scale we want the base to be at the
142        // minimum Y-value unless the user have specifically set some other
143        // value than the default.
144        if( substr($graph->axtype,-3,3)=="log" && $this->ybase==0 )
145        $this->ybase = $graph->yaxis->scale->GetMinVal();
146
147        // For a "text" X-axis scale we will adjust the
148        // display of the bars a little bit.
149        if( substr($graph->axtype,0,3)=="tex" ) {
150            // Position the ticks between the bars
151            $graph->xaxis->scale->ticks->SetXLabelOffset(0.5,0);
152
153            // Center the bars
154            if( $this->abswidth > -1 ) {
155                $graph->SetTextScaleAbsCenterOff($this->abswidth);
156            }
157            else {
158                if( $this->align == "center" )
159                $graph->SetTextScaleOff(0.5-$this->width/2);
160                elseif( $this->align == "right" )
161                $graph->SetTextScaleOff(1-$this->width);
162            }
163        }
164        elseif( ($this instanceof AccBarPlot) || ($this instanceof GroupBarPlot) ) {
165            // We only set an absolute width for linear and int scale
166            // for text scale the width will be set to a fraction of
167            // the majstep width.
168            if( $this->abswidth == -1 ) {
169                // Not set
170                // set width to a visuable sensible default
171                $this->abswidth = $graph->img->plotwidth/(2*$this->numpoints);
172            }
173        }
174    }
175
176    function Min() {
177        $m = parent::Min();
178        if( $m[1] >= $this->ybase ) $m[1] = $this->ybase;
179        return $m;
180    }
181
182    function Max() {
183        $m = parent::Max();
184        if( $m[1] <= $this->ybase ) $m[1] = $this->ybase;
185        return $m;
186    }
187
188    // Specify width as fractions of the major stepo size
189    function SetWidth($aWidth) {
190        if( $aWidth > 1 ) {
191            // Interpret this as absolute width
192            $this->abswidth=$aWidth;
193        }
194        else {
195            $this->width=$aWidth;
196        }
197    }
198
199    // Specify width in absolute pixels. If specified this
200    // overrides SetWidth()
201    function SetAbsWidth($aWidth) {
202        $this->abswidth=$aWidth;
203    }
204
205    function SetAlign($aAlign) {
206        $this->align=$aAlign;
207    }
208
209    function SetNoFill() {
210        $this->grad = false;
211        $this->fill_color=false;
212        $this->fill=false;
213    }
214
215    function SetFillColor($aColor) {
216        // Do an extra error check if the color is specified as an RGB array triple
217        // In that case convert it to a hex string since it will otherwise be
218        // interpretated as an array of colors for each individual bar.
219
220        $aColor = RGB::tryHexConversion($aColor);
221        $this->fill = true ;
222        $this->fill_color=$aColor;
223
224    }
225
226    function SetFillGradient($aFromColor,$aToColor=null,$aStyle=null) {
227        $this->grad = true;
228        $this->grad_fromcolor = $aFromColor;
229        $this->grad_tocolor   = $aToColor;
230        $this->grad_style     = $aStyle;
231    }
232
233    function SetValuePos($aPos) {
234        $this->valuepos = $aPos;
235    }
236
237    function SetPattern($aPattern, $aColor='black'){
238        if( is_array($aPattern) ) {
239            $n = count($aPattern);
240            $this->iPattern = array();
241            $this->iPatternDensity = array();
242            if( is_array($aColor) ) {
243                $this->iPatternColor = array();
244                if( count($aColor) != $n ) {
245                    JpGraphError::RaiseL(2001);//('NUmber of colors is not the same as the number of patterns in BarPlot::SetPattern()');
246                }
247            }
248            else {
249                $this->iPatternColor = $aColor;
250            }
251            for( $i=0; $i < $n; ++$i ) {
252                $this->_SetPatternHelper($aPattern[$i], $this->iPattern[$i], $this->iPatternDensity[$i]);
253                if( is_array($aColor) ) {
254                    $this->iPatternColor[$i] = $aColor[$i];
255                }
256            }
257        }
258        else {
259            $this->_SetPatternHelper($aPattern, $this->iPattern, $this->iPatternDensity);
260            $this->iPatternColor = $aColor;
261        }
262    }
263
264    function _SetPatternHelper($aPattern, &$aPatternValue, &$aDensity){
265        switch( $aPattern ) {
266            case PATTERN_DIAG1:
267                $aPatternValue= 1;
268                $aDensity = 92;
269                break;
270            case PATTERN_DIAG2:
271                $aPatternValue= 1;
272                $aDensity = 78;
273                break;
274            case PATTERN_DIAG3:
275                $aPatternValue= 2;
276                $aDensity = 92;
277                break;
278            case PATTERN_DIAG4:
279                $aPatternValue= 2;
280                $aDensity = 78;
281                break;
282            case PATTERN_CROSS1:
283                $aPatternValue= 8;
284                $aDensity = 90;
285                break;
286            case PATTERN_CROSS2:
287                $aPatternValue= 8;
288                $aDensity = 78;
289                break;
290            case PATTERN_CROSS3:
291                $aPatternValue= 8;
292                $aDensity = 65;
293                break;
294            case PATTERN_CROSS4:
295                $aPatternValue= 7;
296                $aDensity = 90;
297                break;
298            case PATTERN_STRIPE1:
299                $aPatternValue= 5;
300                $aDensity = 94;
301                break;
302            case PATTERN_STRIPE2:
303                $aPatternValue= 5;
304                $aDensity = 85;
305                break;
306            default:
307                JpGraphError::RaiseL(2002);
308                //('Unknown pattern specified in call to BarPlot::SetPattern()');
309        }
310    }
311
312    function Stroke($img,$xscale,$yscale) {
313
314        $numpoints = count($this->coords[0]);
315        if( isset($this->coords[1]) ) {
316            if( count($this->coords[1])!=$numpoints ) {
317                JpGraphError::RaiseL(2003,count($this->coords[1]),$numpoints);
318            //"Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])."Number of Y-points:$numpoints");
319            }
320            else {
321                $exist_x = true;
322            }
323        }
324        else {
325            $exist_x = false;
326        }
327
328
329        $numbars=count($this->coords[0]);
330
331        // Use GetMinVal() instead of scale[0] directly since in the case
332        // of log scale we get a correct value. Log scales will have negative
333        // values for values < 1 while still not representing negative numbers.
334        if( $yscale->GetMinVal() >= 0 )
335        $zp=$yscale->scale_abs[0];
336        else {
337            $zp=$yscale->Translate(0);
338        }
339
340        if( $this->abswidth > -1 ) {
341            $abswidth=$this->abswidth;
342        }
343        else {
344            $abswidth=round($this->width*$xscale->scale_factor,0);
345        }
346
347        // Count pontetial pattern array to avoid doing the count for each iteration
348        if( is_array($this->iPattern) ) {
349            $np = count($this->iPattern);
350        }
351
352        $grad = null;
353        for($i=0; $i < $numbars; ++$i) {
354
355            // If value is NULL, or 0 then don't draw a bar at all
356            if ($this->coords[0][$i] === null || $this->coords[0][$i] === '' )
357            continue;
358
359            if( $exist_x ) {
360                $x=$this->coords[1][$i];
361            }
362            else {
363                $x=$i;
364            }
365
366            $x=$xscale->Translate($x);
367
368            // Comment Note: This confuses the positioning when using acc together with
369            // grouped bars. Workaround for fixing #191
370            /*
371            if( !$xscale->textscale ) {
372            if($this->align=="center")
373            $x -= $abswidth/2;
374            elseif($this->align=="right")
375            $x -= $abswidth;
376            }
377            */
378            // Stroke fill color and fill gradient
379            $pts=array(
380            $x,$zp,
381            $x,$yscale->Translate($this->coords[0][$i]),
382            $x+$abswidth,$yscale->Translate($this->coords[0][$i]),
383            $x+$abswidth,$zp);
384            if( $this->grad ) {
385                if( $grad === null ) {
386                    $grad = new Gradient($img);
387                }
388                if( is_array($this->grad_fromcolor) ) {
389                    // The first argument (grad_fromcolor) can be either an array or a single color. If it is an array
390                    // then we have two choices. It can either a) be a single color specified as an RGB triple or it can be
391                    // an array to specify both (from, to style) for each individual bar. The way to know the difference is
392                    // to investgate the first element. If this element is an integer [0,255] then we assume it is an RGB
393                    // triple.
394                    $ng = count($this->grad_fromcolor);
395                    if( $ng === 3 ) {
396                        if( is_numeric($this->grad_fromcolor[0]) && $this->grad_fromcolor[0] > 0 && $this->grad_fromcolor[0] < 256 ) {
397                            // RGB Triple
398                            $fromcolor = $this->grad_fromcolor;
399                            $tocolor = $this->grad_tocolor;
400                            $style = $this->grad_style;
401                        }
402                        else {
403                            $fromcolor = $this->grad_fromcolor[$i % $ng][0];
404                            $tocolor = $this->grad_fromcolor[$i % $ng][1];
405                            $style = $this->grad_fromcolor[$i % $ng][2];
406                        }
407                    }
408                    else {
409                        $fromcolor = $this->grad_fromcolor[$i % $ng][0];
410                        $tocolor = $this->grad_fromcolor[$i % $ng][1];
411                        $style = $this->grad_fromcolor[$i % $ng][2];
412                    }
413                    $grad->FilledRectangle($pts[2],$pts[3],
414                                           $pts[6],$pts[7],
415                                           $fromcolor,$tocolor,$style);
416                }
417                else {
418                    $grad->FilledRectangle($pts[2],$pts[3],
419                    $pts[6],$pts[7],
420                    $this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
421                }
422            }
423            elseif( !empty($this->fill_color) ) {
424                if(is_array($this->fill_color)) {
425                    $img->PushColor($this->fill_color[$i % count($this->fill_color)]);
426                } else {
427                    $img->PushColor($this->fill_color);
428                }
429                $img->FilledPolygon($pts);
430                $img->PopColor();
431            }
432
433/////////////////////////kokorahen rectangle polygon//////////////////////
434
435            // Remember value of this bar
436            $val=$this->coords[0][$i];
437
438            if( !empty($val) && !is_numeric($val) ) {
439                JpGraphError::RaiseL(2004,$i,$val);
440                //'All values for a barplot must be numeric. You have specified value['.$i.'] == \''.$val.'\'');
441            }
442
443            // Determine the shadow
444            if( $this->bar_shadow && $val != 0) {
445
446                $ssh = $this->bar_shadow_hsize;
447                $ssv = $this->bar_shadow_vsize;
448                // Create points to create a "upper-right" shadow
449                if( $val > 0 ) {
450                    $sp[0]=$pts[6];  $sp[1]=$pts[7];
451                    $sp[2]=$pts[4];  $sp[3]=$pts[5];
452                    $sp[4]=$pts[2];  $sp[5]=$pts[3];
453                    $sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
454                    $sp[8]=$pts[4]+$ssh; $sp[9]=$pts[5]-$ssv;
455                    $sp[10]=$pts[6]+$ssh; $sp[11]=$pts[7]-$ssv;
456                }
457                elseif( $val < 0 ) {
458                    $sp[0]=$pts[4];  $sp[1]=$pts[5];
459                    $sp[2]=$pts[6];  $sp[3]=$pts[7];
460                    $sp[4]=$pts[0];  $sp[5]=$pts[1];
461                    $sp[6]=$pts[0]+$ssh; $sp[7]=$pts[1]-$ssv;
462                    $sp[8]=$pts[6]+$ssh; $sp[9]=$pts[7]-$ssv;
463                    $sp[10]=$pts[4]+$ssh; $sp[11]=$pts[5]-$ssv;
464                }
465                if( is_array($this->bar_shadow_color) ) {
466                    $numcolors = count($this->bar_shadow_color);
467                    if( $numcolors == 0 ) {
468                        JpGraphError::RaiseL(2005);//('You have specified an empty array for shadow colors in the bar plot.');
469                    }
470                    $img->PushColor($this->bar_shadow_color[$i % $numcolors]);
471                }
472                else {
473                    $img->PushColor($this->bar_shadow_color);
474                }
475                $img->FilledPolygon($sp);
476                $img->PopColor();
477
478            } elseif( $this->bar_3d && $val != 0) {
479              // Determine the 3D
480
481                $ssh = $this->bar_3d_hsize;
482                $ssv = $this->bar_3d_vsize;
483
484                // Create points to create a "upper-right" shadow
485                if( $val > 0 ) {
486                    $sp1[0]=$pts[6];  $sp1[1]=$pts[7];
487                    $sp1[2]=$pts[4];  $sp1[3]=$pts[5];
488                    $sp1[4]=$pts[4]+$ssh; $sp1[5]=$pts[5]-$ssv;
489                    $sp1[6]=$pts[6]+$ssh; $sp1[7]=$pts[7]-$ssv;
490
491                    $sp2[0]=$pts[4];  $sp2[1]=$pts[5];
492                    $sp2[2]=$pts[2];  $sp2[3]=$pts[3];
493                    $sp2[4]=$pts[2]+$ssh; $sp2[5]=$pts[3]-$ssv;
494                    $sp2[6]=$pts[4]+$ssh; $sp2[7]=$pts[5]-$ssv;
495
496                }
497                elseif( $val < 0 ) {
498                    $sp1[0]=$pts[4];  $sp1[1]=$pts[5];
499                    $sp1[2]=$pts[6];  $sp1[3]=$pts[7];
500                    $sp1[4]=$pts[6]+$ssh; $sp1[5]=$pts[7]-$ssv;
501                    $sp1[6]=$pts[4]+$ssh; $sp1[7]=$pts[5]-$ssv;
502
503                    $sp2[0]=$pts[6];  $sp2[1]=$pts[7];
504                    $sp2[2]=$pts[0];  $sp2[3]=$pts[1];
505                    $sp2[4]=$pts[0]+$ssh; $sp2[5]=$pts[1]-$ssv;
506                    $sp2[6]=$pts[6]+$ssh; $sp2[7]=$pts[7]-$ssv;
507                }
508
509                $base_color = $this->fill_color;
510
511                $img->PushColor($base_color . ':0.7');
512                $img->FilledPolygon($sp1);
513                $img->PopColor();
514
515                $img->PushColor($base_color . ':1.1');
516                $img->FilledPolygon($sp2);
517                $img->PopColor();
518            }
519
520            // Stroke the pattern
521            if( is_array($this->iPattern) ) {
522                $f = new RectPatternFactory();
523                if( is_array($this->iPatternColor) ) {
524                    $pcolor = $this->iPatternColor[$i % $np];
525                }
526                else {
527                    $pcolor = $this->iPatternColor;
528                }
529                $prect = $f->Create($this->iPattern[$i % $np],$pcolor,1);
530                $prect->SetDensity($this->iPatternDensity[$i % $np]);
531
532                if( $val < 0 ) {
533                    $rx = $pts[0];
534                    $ry = $pts[1];
535                }
536                else {
537                    $rx = $pts[2];
538                    $ry = $pts[3];
539                }
540                $width = abs($pts[4]-$pts[0])+1;
541                $height = abs($pts[1]-$pts[3])+1;
542                $prect->SetPos(new Rectangle($rx,$ry,$width,$height));
543                $prect->Stroke($img);
544            }
545            else {
546                if( $this->iPattern > -1 ) {
547                    $f = new RectPatternFactory();
548                    $prect = $f->Create($this->iPattern,$this->iPatternColor,1);
549                    $prect->SetDensity($this->iPatternDensity);
550                    if( $val < 0 ) {
551                        $rx = $pts[0];
552                        $ry = $pts[1];
553                    }
554                    else {
555                        $rx = $pts[2];
556                        $ry = $pts[3];
557                    }
558                    $width = abs($pts[4]-$pts[0])+1;
559                    $height = abs($pts[1]-$pts[3])+1;
560                    $prect->SetPos(new Rectangle($rx,$ry,$width,$height));
561                    $prect->Stroke($img);
562                }
563            }
564
565            // Stroke the outline of the bar
566            if( is_array($this->color) ) {
567                $img->SetColor($this->color[$i % count($this->color)]);
568            }
569            else {
570                $img->SetColor($this->color);
571            }
572
573            $pts[] = $pts[0];
574            $pts[] = $pts[1];
575
576            if( $this->weight > 0 ) {
577                $img->SetLineWeight($this->weight);
578                $img->Polygon($pts);
579            }
580
581            // Determine how to best position the values of the individual bars
582            $x=$pts[2]+($pts[4]-$pts[2])/2;
583            $this->value->SetMargin(5);
584
585            if( $this->valuepos=='top' ) {
586                $y=$pts[3];
587                if( $img->a === 90 ) {
588                    if( $val < 0 ) {
589                        $this->value->SetAlign('right','center');
590                    }
591                    else {
592                        $this->value->SetAlign('left','center');
593                    }
594
595                }
596                else {
597                    if( $val < 0 ) {
598                        $this->value->SetMargin(-5);
599                        $y=$pts[1];
600                        $this->value->SetAlign('center','bottom');
601                    }
602                    else {
603                        $this->value->SetAlign('center','bottom');
604                    }
605
606                }
607                $this->value->Stroke($img,$val,$x,$y);
608            }
609            elseif( $this->valuepos=='max' ) {
610                $y=$pts[3];
611                if( $img->a === 90 ) {
612                    if( $val < 0 )
613                    $this->value->SetAlign('left','center');
614                    else
615                    $this->value->SetAlign('right','center');
616                }
617                else {
618                    if( $val < 0 ) {
619                        $this->value->SetAlign('center','bottom');
620                    }
621                    else {
622                        $this->value->SetAlign('center','top');
623                    }
624                }
625                $this->value->SetMargin(-5);
626                $this->value->Stroke($img,$val,$x,$y);
627            }
628            elseif( $this->valuepos=='center' ) {
629                $y = ($pts[3] + $pts[1])/2;
630                $this->value->SetAlign('center','center');
631                $this->value->SetMargin(0);
632                $this->value->Stroke($img,$val,$x,$y);
633            }
634            elseif( $this->valuepos=='bottom' || $this->valuepos=='min' ) {
635                $y=$pts[1];
636                if( $img->a === 90 ) {
637                    if( $val < 0 )
638                    $this->value->SetAlign('right','center');
639                    else
640                    $this->value->SetAlign('left','center');
641                }
642                $this->value->SetMargin(3);
643                $this->value->Stroke($img,$val,$x,$y);
644            }
645            else {
646                JpGraphError::RaiseL(2006,$this->valuepos);
647                //'Unknown position for values on bars :'.$this->valuepos);
648            }
649            // Create the client side image map
650            $rpts = $img->ArrRotate($pts);
651            $csimcoord=round($rpts[0]).", ".round($rpts[1]);
652            for( $j=1; $j < 4; ++$j){
653                $csimcoord .= ", ".round($rpts[2*$j]).", ".round($rpts[2*$j+1]);
654            }
655            if( !empty($this->csimtargets[$i]) ) {
656                $this->csimareas .= '<area shape="poly" coords="'.$csimcoord.'" ';
657                $this->csimareas .= " href=\"".htmlentities($this->csimtargets[$i])."\"";
658
659                if( !empty($this->csimwintargets[$i]) ) {
660                    $this->csimareas .= " target=\"".$this->csimwintargets[$i]."\" ";
661                }
662
663                $sval='';
664                if( !empty($this->csimalts[$i]) ) {
665                    $sval=sprintf($this->csimalts[$i],$this->coords[0][$i]);
666                    $this->csimareas .= " title=\"$sval\" alt=\"$sval\" ";
667                }
668                $this->csimareas .= " />\n";
669            }
670        }
671        return true;
672    }
673} // Class
674
675//===================================================
676// CLASS GroupBarPlot
677// Description: Produce grouped bar plots
678//===================================================
679class GroupBarPlot extends BarPlot {
680    public $plots;
681    private $nbrplots=0;
682    //---------------
683    // CONSTRUCTOR
684    function GroupBarPlot($plots) {
685        $this->width=0.7;
686        $this->plots = $plots;
687        $this->nbrplots = count($plots);
688        if( $this->nbrplots < 1 ) {
689            JpGraphError::RaiseL(2007);//('Cannot create GroupBarPlot from empty plot array.');
690        }
691        for($i=0; $i < $this->nbrplots; ++$i ) {
692            if( empty($this->plots[$i]) || !isset($this->plots[$i]) ) {
693                JpGraphError::RaiseL(2008,$i);//("Group bar plot element nbr $i is undefined or empty.");
694            }
695        }
696        $this->numpoints = $plots[0]->numpoints;
697        $this->width=0.7;
698    }
699
700    //---------------
701    // PUBLIC METHODS
702    function Legend($graph) {
703        $n = count($this->plots);
704        for($i=0; $i < $n; ++$i) {
705            $c = get_class($this->plots[$i]);
706            if( !($this->plots[$i] instanceof BarPlot) ) {
707                JpGraphError::RaiseL(2009,$c);
708                //('One of the objects submitted to GroupBar is not a BarPlot. Make sure that you create the Group Bar plot from an array of BarPlot or AccBarPlot objects. (Class = '.$c.')');
709            }
710            $this->plots[$i]->DoLegend($graph);
711        }
712    }
713
714    function Min() {
715        list($xmin,$ymin) = $this->plots[0]->Min();
716        $n = count($this->plots);
717        for($i=0; $i < $n; ++$i) {
718            list($xm,$ym) = $this->plots[$i]->Min();
719            $xmin = max($xmin,$xm);
720            $ymin = min($ymin,$ym);
721        }
722        return array($xmin,$ymin);
723    }
724
725    function Max() {
726        list($xmax,$ymax) = $this->plots[0]->Max();
727        $n = count($this->plots);
728        for($i=0; $i < $n; ++$i) {
729            list($xm,$ym) = $this->plots[$i]->Max();
730            $xmax = max($xmax,$xm);
731            $ymax = max($ymax,$ym);
732        }
733        return array($xmax,$ymax);
734    }
735
736    function GetCSIMareas() {
737        $n = count($this->plots);
738        $csimareas='';
739        for($i=0; $i < $n; ++$i) {
740            $csimareas .= $this->plots[$i]->csimareas;
741        }
742        return $csimareas;
743    }
744
745    // Stroke all the bars next to each other
746    function Stroke($img,$xscale,$yscale) {
747        $tmp=$xscale->off;
748        $n = count($this->plots);
749        $subwidth = $this->width/$this->nbrplots ;
750
751        for( $i=0; $i < $n; ++$i ) {
752            $this->plots[$i]->ymin=$this->ybase;
753            $this->plots[$i]->SetWidth($subwidth);
754
755            // If the client have used SetTextTickInterval() then
756            // major_step will be > 1 and the positioning will fail.
757            // If we assume it is always one the positioning will work
758            // fine with a text scale but this will not work with
759            // arbitrary linear scale
760            $xscale->off = $tmp+$i*round($xscale->scale_factor* $subwidth);
761            $this->plots[$i]->Stroke($img,$xscale,$yscale);
762        }
763        $xscale->off=$tmp;
764    }
765} // Class
766
767//===================================================
768// CLASS AccBarPlot
769// Description: Produce accumulated bar plots
770//===================================================
771class AccBarPlot extends BarPlot {
772    public $plots=null;
773    private $nbrplots=0;
774    //---------------
775    // CONSTRUCTOR
776    function __construct($plots) {
777        $this->plots = $plots;
778        $this->nbrplots = count($plots);
779        if( $this->nbrplots < 1 ) {
780            JpGraphError::RaiseL(2010);//('Cannot create AccBarPlot from empty plot array.');
781        }
782        for($i=0; $i < $this->nbrplots; ++$i ) {
783            if( empty($this->plots[$i]) || !isset($this->plots[$i]) ) {
784                JpGraphError::RaiseL(2011,$i);//("Acc bar plot element nbr $i is undefined or empty.");
785            }
786        }
787
788        // We can only allow individual plost which do not have specified X-positions
789        for($i=0; $i < $this->nbrplots; ++$i ) {
790            if( !empty($this->plots[$i]->coords[1]) ) {
791                JpGraphError::RaiseL(2015);
792                //'Individual bar plots in an AccBarPlot or GroupBarPlot can not have specified X-positions.');
793            }
794        }
795
796        // Use 0 weight by default which means that the individual bar
797        // weights will be used per part n the accumulated bar
798        $this->SetWeight(0);
799
800        $this->numpoints = $plots[0]->numpoints;
801        $this->value = new DisplayValue();
802    }
803
804    //---------------
805    // PUBLIC METHODS
806    function Legend($graph) {
807        $n = count($this->plots);
808        for( $i=$n-1; $i >= 0; --$i ) {
809            $c = get_class($this->plots[$i]);
810            if( !($this->plots[$i] instanceof BarPlot) ) {
811                JpGraphError::RaiseL(2012,$c);
812                //('One of the objects submitted to AccBar is not a BarPlot. Make sure that you create the AccBar plot from an array of BarPlot objects.(Class='.$c.')');
813            }
814            $this->plots[$i]->DoLegend($graph);
815        }
816    }
817
818    function Max() {
819        list($xmax) = $this->plots[0]->Max();
820        $nmax=0;
821        for($i=0; $i < count($this->plots); ++$i) {
822            $n = count($this->plots[$i]->coords[0]);
823            $nmax = max($nmax,$n);
824            list($x) = $this->plots[$i]->Max();
825            $xmax = max($xmax,$x);
826        }
827        for( $i = 0; $i < $nmax; $i++ ) {
828            // Get y-value for bar $i by adding the
829            // individual bars from all the plots added.
830            // It would be wrong to just add the
831            // individual plots max y-value since that
832            // would in most cases give to large y-value.
833            $y=0;
834            if( !isset($this->plots[0]->coords[0][$i]) ) {
835                JpGraphError::RaiseL(2014);
836            }
837            if( $this->plots[0]->coords[0][$i] > 0 )
838            $y=$this->plots[0]->coords[0][$i];
839            for( $j = 1; $j < $this->nbrplots; $j++ ) {
840                if( !isset($this->plots[$j]->coords[0][$i]) ) {
841                    JpGraphError::RaiseL(2014);
842                }
843                if( $this->plots[$j]->coords[0][$i] > 0 )
844                $y += $this->plots[$j]->coords[0][$i];
845            }
846            $ymax[$i] = $y;
847        }
848        $ymax = max($ymax);
849
850        // Bar always start at baseline
851        if( $ymax <= $this->ybase )
852        $ymax = $this->ybase;
853        return array($xmax,$ymax);
854    }
855
856    function Min() {
857        $nmax=0;
858        list($xmin,$ysetmin) = $this->plots[0]->Min();
859        for($i=0; $i < count($this->plots); ++$i) {
860            $n = count($this->plots[$i]->coords[0]);
861            $nmax = max($nmax,$n);
862            list($x,$y) = $this->plots[$i]->Min();
863            $xmin = Min($xmin,$x);
864            $ysetmin = Min($y,$ysetmin);
865        }
866        for( $i = 0; $i < $nmax; $i++ ) {
867            // Get y-value for bar $i by adding the
868            // individual bars from all the plots added.
869            // It would be wrong to just add the
870            // individual plots max y-value since that
871            // would in most cases give to large y-value.
872            $y=0;
873            if( $this->plots[0]->coords[0][$i] < 0 )
874            $y=$this->plots[0]->coords[0][$i];
875            for( $j = 1; $j < $this->nbrplots; $j++ ) {
876                if( $this->plots[$j]->coords[0][$i] < 0 )
877                $y += $this->plots[ $j ]->coords[0][$i];
878            }
879            $ymin[$i] = $y;
880        }
881        $ymin = Min($ysetmin,Min($ymin));
882        // Bar always start at baseline
883        if( $ymin >= $this->ybase )
884        $ymin = $this->ybase;
885        return array($xmin,$ymin);
886    }
887
888    // Stroke acc bar plot
889    function Stroke($img,$xscale,$yscale) {
890        $pattern=NULL;
891        $img->SetLineWeight($this->weight);
892        $grad=null;
893        for($i=0; $i < $this->numpoints-1; $i++) {
894            $accy = 0;
895            $accy_neg = 0;
896            for($j=0; $j < $this->nbrplots; ++$j ) {
897                $img->SetColor($this->plots[$j]->color);
898
899                if ( $this->plots[$j]->coords[0][$i] >= 0) {
900                    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
901                    $accyt=$yscale->Translate($accy);
902                    $accy+=$this->plots[$j]->coords[0][$i];
903                }
904                else {
905                    //if ( $this->plots[$j]->coords[0][$i] < 0 || $accy_neg < 0 ) {
906                    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
907                    $accyt=$yscale->Translate($accy_neg);
908                    $accy_neg+=$this->plots[$j]->coords[0][$i];
909                }
910
911                $xt=$xscale->Translate($i);
912
913                if( $this->abswidth > -1 ) {
914                    $abswidth=$this->abswidth;
915                }
916                else {
917                    $abswidth=round($this->width*$xscale->scale_factor,0);
918                }
919
920                $pts=array($xt,$accyt,$xt,$yt,$xt+$abswidth,$yt,$xt+$abswidth,$accyt);
921
922                if( $this->bar_shadow ) {
923                    $ssh = $this->bar_shadow_hsize;
924                    $ssv = $this->bar_shadow_vsize;
925
926                    // We must also differ if we are a positive or negative bar.
927                    if( $j === 0 ) {
928                        // This gets extra complicated since we have to
929                        // see all plots to see if we are negative. It could
930                        // for example be that all plots are 0 until the very
931                        // last one. We therefore need to save the initial setup
932                        // for both the negative and positive case
933
934                        // In case the final bar is positive
935                        $sp[0]=$pts[6]+1; $sp[1]=$pts[7];
936                        $sp[2]=$pts[6]+$ssh; $sp[3]=$pts[7]-$ssv;
937
938                        // In case the final bar is negative
939                        $nsp[0]=$pts[0]; $nsp[1]=$pts[1];
940                        $nsp[2]=$pts[0]+$ssh; $nsp[3]=$pts[1]-$ssv;
941                        $nsp[4]=$pts[6]+$ssh; $nsp[5]=$pts[7]-$ssv;
942                        $nsp[10]=$pts[6]+1; $nsp[11]=$pts[7];
943                    }
944
945                    if( $j === $this->nbrplots-1 ) {
946                        // If this is the last plot of the bar and
947                        // the total value is larger than 0 then we
948                        // add the shadow.
949                        if( is_array($this->bar_shadow_color) ) {
950                            $numcolors = count($this->bar_shadow_color);
951                            if( $numcolors == 0 ) {
952                                JpGraphError::RaiseL(2013);//('You have specified an empty array for shadow colors in the bar plot.');
953                            }
954                            $img->PushColor($this->bar_shadow_color[$i % $numcolors]);
955                        }
956                        else {
957                            $img->PushColor($this->bar_shadow_color);
958                        }
959
960                        if( $accy > 0 ) {
961                            $sp[4]=$pts[4]+$ssh; $sp[5]=$pts[5]-$ssv;
962                            $sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
963                            $sp[8]=$pts[2]; $sp[9]=$pts[3]-1;
964                            $sp[10]=$pts[4]+1; $sp[11]=$pts[5];
965                            $img->FilledPolygon($sp,4);
966                        }
967                        elseif( $accy_neg < 0 ) {
968                            $nsp[6]=$pts[4]+$ssh; $nsp[7]=$pts[5]-$ssv;
969                            $nsp[8]=$pts[4]+1; $nsp[9]=$pts[5];
970                            $img->FilledPolygon($nsp,4);
971                        }
972                        $img->PopColor();
973                    }
974                }
975
976
977                // If value is NULL or 0, then don't draw a bar at all
978                if ($this->plots[$j]->coords[0][$i] == 0 ) continue;
979
980                if( $this->plots[$j]->grad ) {
981                    if( $grad === null ) {
982                        $grad = new Gradient($img);
983                    }
984                    if( is_array($this->plots[$j]->grad_fromcolor) ) {
985                        // The first argument (grad_fromcolor) can be either an array or a single color. If it is an array
986                        // then we have two choices. It can either a) be a single color specified as an RGB triple or it can be
987                        // an array to specify both (from, to style) for each individual bar. The way to know the difference is
988                        // to investgate the first element. If this element is an integer [0,255] then we assume it is an RGB
989                        // triple.
990                        $ng = count($this->plots[$j]->grad_fromcolor);
991                        if( $ng === 3 ) {
992                            if( is_numeric($this->plots[$j]->grad_fromcolor[0]) && $this->plots[$j]->grad_fromcolor[0] > 0 &&
993                                 $this->plots[$j]->grad_fromcolor[0] < 256 ) {
994                                // RGB Triple
995                                $fromcolor = $this->plots[$j]->grad_fromcolor;
996                                $tocolor = $this->plots[$j]->grad_tocolor;
997                                $style = $this->plots[$j]->grad_style;
998                            }
999                            else {
1000                                $fromcolor = $this->plots[$j]->grad_fromcolor[$i % $ng][0];
1001                                $tocolor = $this->plots[$j]->grad_fromcolor[$i % $ng][1];
1002                                $style = $this->plots[$j]->grad_fromcolor[$i % $ng][2];
1003                            }
1004                        }
1005                        else {
1006                            $fromcolor = $this->plots[$j]->grad_fromcolor[$i % $ng][0];
1007                            $tocolor = $this->plots[$j]->grad_fromcolor[$i % $ng][1];
1008                            $style = $this->plots[$j]->grad_fromcolor[$i % $ng][2];
1009                        }
1010                        $grad->FilledRectangle($pts[2],$pts[3],
1011                                               $pts[6],$pts[7],
1012                                               $fromcolor,$tocolor,$style);
1013                    }
1014                    else {
1015                        $grad->FilledRectangle($pts[2],$pts[3],
1016                                               $pts[6],$pts[7],
1017                                               $this->plots[$j]->grad_fromcolor,
1018                                               $this->plots[$j]->grad_tocolor,
1019                                               $this->plots[$j]->grad_style);
1020                    }
1021                } else {
1022                    if (is_array($this->plots[$j]->fill_color) ) {
1023                        $numcolors = count($this->plots[$j]->fill_color);
1024                        $fillcolor = $this->plots[$j]->fill_color[$i % $numcolors];
1025                        // If the bar is specified to be non filled then the fill color is false
1026                        if( $fillcolor !== false ) {
1027                            $img->SetColor($this->plots[$j]->fill_color[$i % $numcolors]);
1028                        }
1029                    }
1030                    else {
1031                        $fillcolor = $this->plots[$j]->fill_color;
1032                        if( $fillcolor !== false ) {
1033                            $img->SetColor($this->plots[$j]->fill_color);
1034                        }
1035                    }
1036                    if( $fillcolor !== false ) {
1037                        $img->FilledPolygon($pts);
1038                    }
1039                }
1040
1041                $img->SetColor($this->plots[$j]->color);
1042
1043                // Stroke the pattern
1044                if( $this->plots[$j]->iPattern > -1 ) {
1045                    if( $pattern===NULL ) {
1046                        $pattern = new RectPatternFactory();
1047                    }
1048
1049                    $prect = $pattern->Create($this->plots[$j]->iPattern,$this->plots[$j]->iPatternColor,1);
1050                    $prect->SetDensity($this->plots[$j]->iPatternDensity);
1051                    if( $this->plots[$j]->coords[0][$i] < 0 ) {
1052                        $rx = $pts[0];
1053                        $ry = $pts[1];
1054                    }
1055                    else {
1056                        $rx = $pts[2];
1057                        $ry = $pts[3];
1058                    }
1059                    $width = abs($pts[4]-$pts[0])+1;
1060                    $height = abs($pts[1]-$pts[3])+1;
1061                    $prect->SetPos(new Rectangle($rx,$ry,$width,$height));
1062                    $prect->Stroke($img);
1063                }
1064
1065
1066                // CSIM array
1067
1068                if( $i < count($this->plots[$j]->csimtargets) ) {
1069                    // Create the client side image map
1070                    $rpts = $img->ArrRotate($pts);
1071                    $csimcoord=round($rpts[0]).", ".round($rpts[1]);
1072                    for( $k=1; $k < 4; ++$k){
1073                        $csimcoord .= ", ".round($rpts[2*$k]).", ".round($rpts[2*$k+1]);
1074                    }
1075                    if( ! empty($this->plots[$j]->csimtargets[$i]) ) {
1076                        $this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
1077                        $this->csimareas.= " href=\"".$this->plots[$j]->csimtargets[$i]."\" ";
1078
1079                        if( ! empty($this->plots[$j]->csimwintargets[$i]) ) {
1080                            $this->csimareas.= " target=\"".$this->plots[$j]->csimwintargets[$i]."\" ";
1081                        }
1082
1083                        $sval='';
1084                        if( !empty($this->plots[$j]->csimalts[$i]) ) {
1085                            $sval=sprintf($this->plots[$j]->csimalts[$i],$this->plots[$j]->coords[0][$i]);
1086                            $this->csimareas .= " title=\"$sval\" ";
1087                        }
1088                        $this->csimareas .= " alt=\"$sval\" />\n";
1089                    }
1090                }
1091
1092                $pts[] = $pts[0];
1093                $pts[] = $pts[1];
1094                $img->SetLineWeight($this->plots[$j]->weight);
1095                $img->Polygon($pts);
1096                $img->SetLineWeight(1);
1097            }
1098
1099            // Daw potential bar around the entire accbar bar
1100            if( $this->weight > 0 ) {
1101                $y=$yscale->Translate(0);
1102                $img->SetColor($this->color);
1103                $img->SetLineWeight($this->weight);
1104                $img->Rectangle($pts[0],$y,$pts[6],$pts[5]);
1105            }
1106
1107            // Draw labels for each acc.bar
1108
1109            $x=$pts[2]+($pts[4]-$pts[2])/2;
1110            if($this->bar_shadow) $x += $ssh;
1111
1112            // First stroke the accumulated value for the entire bar
1113            // This value is always placed at the top/bottom of the bars
1114            if( $accy_neg < 0 ) {
1115                $y=$yscale->Translate($accy_neg);
1116                $this->value->Stroke($img,$accy_neg,$x,$y);
1117            }
1118            else {
1119                $y=$yscale->Translate($accy);
1120                $this->value->Stroke($img,$accy,$x,$y);
1121            }
1122
1123            $accy = 0;
1124            $accy_neg = 0;
1125            for($j=0; $j < $this->nbrplots; ++$j ) {
1126
1127                // We don't print 0 values in an accumulated bar plot
1128                if( $this->plots[$j]->coords[0][$i] == 0 ) continue;
1129
1130                if ($this->plots[$j]->coords[0][$i] > 0) {
1131                    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
1132                    $accyt=$yscale->Translate($accy);
1133                    if(  $this->plots[$j]->valuepos=='center' ) {
1134                        $y = $accyt-($accyt-$yt)/2;
1135                    }
1136                    elseif( $this->plots[$j]->valuepos=='bottom' ) {
1137                        $y = $accyt;
1138                    }
1139                    else { // top or max
1140                        $y = $accyt-($accyt-$yt);
1141                    }
1142                    $accy+=$this->plots[$j]->coords[0][$i];
1143                    if(  $this->plots[$j]->valuepos=='center' ) {
1144                        $this->plots[$j]->value->SetAlign("center","center");
1145                        $this->plots[$j]->value->SetMargin(0);
1146                    }
1147                    elseif( $this->plots[$j]->valuepos=='bottom' ) {
1148                        $this->plots[$j]->value->SetAlign('center','bottom');
1149                        $this->plots[$j]->value->SetMargin(2);
1150                    }
1151                    else {
1152                        $this->plots[$j]->value->SetAlign('center','top');
1153                        $this->plots[$j]->value->SetMargin(1);
1154                    }
1155                } else {
1156                    $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
1157                    $accyt=$yscale->Translate($accy_neg);
1158                    $accy_neg+=$this->plots[$j]->coords[0][$i];
1159                    if(  $this->plots[$j]->valuepos=='center' ) {
1160                        $y = $accyt-($accyt-$yt)/2;
1161                    }
1162                    elseif( $this->plots[$j]->valuepos=='bottom' ) {
1163                        $y = $accyt;
1164                    }
1165                    else {
1166                        $y = $accyt-($accyt-$yt);
1167                    }
1168                    if(  $this->plots[$j]->valuepos=='center' ) {
1169                        $this->plots[$j]->value->SetAlign("center","center");
1170                        $this->plots[$j]->value->SetMargin(0);
1171                    }
1172                    elseif( $this->plots[$j]->valuepos=='bottom' ) {
1173                        $this->plots[$j]->value->SetAlign('center',$j==0 ? 'bottom':'top');
1174                        $this->plots[$j]->value->SetMargin(-2);
1175                    }
1176                    else {
1177                        $this->plots[$j]->value->SetAlign('center','bottom');
1178                        $this->plots[$j]->value->SetMargin(-1);
1179                    }
1180                }
1181                $this->plots[$j]->value->Stroke($img,$this->plots[$j]->coords[0][$i],$x,$y);
1182            }
1183
1184        }
1185        return true;
1186    }
1187} // Class
1188
1189/* EOF */
1190?>
Note: See TracBrowser for help on using the repository browser.