Subversion Repositories Applications.annuaire

Rev

Rev 66 | Details | Compare with Previous | Last modification | View Log | RSS feed

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