source: trunk/client/modules/Elezioni/grafici/jpgraph_legend.inc.php@ 398

Last change on this file since 398 was 284, checked in by roby, 6 years ago
File size: 17.6 KB
RevLine 
[2]1<?php
2//=======================================================================
[284]3// File: JPGRAPH_LEGEND.INC.PHP
4// Description: Class to handle the legend box in the graph that gives
5// names on the data series. The number of rows and columns
6// in the legend are user specifyable.
7// Created: 2001-01-08 (Refactored to separate file 2008-08-01)
8// Ver: $Id: jpgraph_legend.inc.php 1926 2010-01-11 16:33:07Z ljp $
[2]9//
[284]10// Copyright (c) Asial Corporation. All rights reserved.
[2]11//========================================================================
12
13DEFINE('_DEFAULT_LPM_SIZE',8); // Default Legend Plot Mark size
14
[284]15
[2]16//===================================================
17// CLASS Legend
18// Description: Responsible for drawing the box containing
19// all the legend text for the graph
20//===================================================
21
22class Legend {
23 public $txtcol=array();
[284]24 public $font_family=FF_DEFAULT,$font_style=FS_NORMAL,$font_size=8; // old. 12
25 private $color=array(120,120,120); // Default frame color
26 private $fill_color=array(245,245,245); // Default fill color
27 private $shadow=false; // Shadow around legend "box"
28 private $shadow_color='darkgray';
[2]29 private $mark_abs_hsize=_DEFAULT_LPM_SIZE,$mark_abs_vsize=_DEFAULT_LPM_SIZE;
[284]30 private $xmargin=10,$ymargin=0,$shadow_width=2;
31 private $xlmargin=4;
32 private $ylinespacing=5;
33
34 // We need a separate margin since the baseline of the last text would coincide with the bottom otherwise
35 private $ybottom_margin = 8;
36
[2]37 private $xpos=0.05, $ypos=0.15, $xabspos=-1, $yabspos=-1;
38 private $halign="right", $valign="top";
39 private $font_color='black';
40 private $hide=false,$layout_n=1;
41 private $weight=1,$frameweight=1;
42 private $csimareas='';
43 private $reverse = false ;
[284]44 private $bkg_gradtype=-1, $bkg_gradfrom='lightgray', $bkg_gradto='gray';
[2]45
[284]46 //---------------
47 // CONSTRUCTOR
48 function __construct() {
49 // Empty
[2]50 }
[284]51 //---------------
52 // PUBLIC METHODS
[2]53 function Hide($aHide=true) {
[284]54 $this->hide=$aHide;
[2]55 }
[284]56
[2]57 function SetHColMargin($aXMarg) {
[284]58 $this->xmargin = $aXMarg;
[2]59 }
60
61 function SetVColMargin($aSpacing) {
[284]62 $this->ylinespacing = $aSpacing ;
[2]63 }
64
65 function SetLeftMargin($aXMarg) {
[284]66 $this->xlmargin = $aXMarg;
[2]67 }
68
69 // Synonym
70 function SetLineSpacing($aSpacing) {
[284]71 $this->ylinespacing = $aSpacing ;
[2]72 }
73
[284]74 function SetShadow($aShow='gray',$aWidth=4) {
75 if( is_string($aShow) ) {
76 $this->shadow_color = $aShow;
77 $this->shadow=true;
78 }
79 else {
80 $this->shadow = $aShow;
81 }
82 $this->shadow_width = $aWidth;
[2]83 }
84
85 function SetMarkAbsSize($aSize) {
[284]86 $this->mark_abs_vsize = $aSize ;
87 $this->mark_abs_hsize = $aSize ;
[2]88 }
89
90 function SetMarkAbsVSize($aSize) {
[284]91 $this->mark_abs_vsize = $aSize ;
[2]92 }
93
94 function SetMarkAbsHSize($aSize) {
[284]95 $this->mark_abs_hsize = $aSize ;
[2]96 }
97
98 function SetLineWeight($aWeight) {
[284]99 $this->weight = $aWeight;
[2]100 }
101
102 function SetFrameWeight($aWeight) {
[284]103 $this->frameweight = $aWeight;
[2]104 }
[284]105
[2]106 function SetLayout($aDirection=LEGEND_VERT) {
[284]107 $this->layout_n = $aDirection==LEGEND_VERT ? 1 : 99 ;
[2]108 }
[284]109
[2]110 function SetColumns($aCols) {
[284]111 $this->layout_n = $aCols ;
[2]112 }
113
114 function SetReverse($f=true) {
[284]115 $this->reverse = $f ;
[2]116 }
117
118 // Set color on frame around box
119 function SetColor($aFontColor,$aColor='black') {
[284]120 $this->font_color=$aFontColor;
121 $this->color=$aColor;
[2]122 }
[284]123
[2]124 function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
[284]125 $this->font_family = $aFamily;
126 $this->font_style = $aStyle;
127 $this->font_size = $aSize;
[2]128 }
[284]129
130 function SetPos($aX,$aY,$aHAlign='right',$aVAlign='top') {
131 $this->Pos($aX,$aY,$aHAlign,$aVAlign);
[2]132 }
133
[284]134 function SetAbsPos($aX,$aY,$aHAlign='right',$aVAlign='top') {
135 $this->xabspos=$aX;
136 $this->yabspos=$aY;
137 $this->halign=$aHAlign;
138 $this->valign=$aVAlign;
[2]139 }
140
[284]141 function Pos($aX,$aY,$aHAlign='right',$aVAlign='top') {
142 if( !($aX<1 && $aY<1) ) {
143 JpGraphError::RaiseL(25120);//(" Position for legend must be given as percentage in range 0-1");
144 }
145 $this->xpos=$aX;
146 $this->ypos=$aY;
147 $this->halign=$aHAlign;
148 $this->valign=$aVAlign;
[2]149 }
150
151 function SetFillColor($aColor) {
[284]152 $this->fill_color=$aColor;
[2]153 }
[284]154
155 function Clear() {
156 $this->txtcol = array();
157 }
158
[2]159 function Add($aTxt,$aColor,$aPlotmark='',$aLinestyle=0,$csimtarget='',$csimalt='',$csimwintarget='') {
[284]160 $this->txtcol[]=array($aTxt,$aColor,$aPlotmark,$aLinestyle,$csimtarget,$csimalt,$csimwintarget);
[2]161 }
162
163 function GetCSIMAreas() {
[284]164 return $this->csimareas;
[2]165 }
166
[284]167 function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=2) {
168 $this->bkg_gradtype=$aGradType;
169 $this->bkg_gradfrom = $aFrom;
170 $this->bkg_gradto = $aTo;
171 }
[2]172
[284]173 function HasItems() {
174 return (boolean)(count($this->txtcol));
175 }
[2]176
[284]177 function Stroke($aImg) {
178 // Constant
179 $fillBoxFrameWeight=1;
[2]180
[284]181 if( $this->hide ) return;
[2]182
[284]183 $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
184
185 if( $this->reverse ) {
186 $this->txtcol = array_reverse($this->txtcol);
187 }
188
189 $n=count($this->txtcol);
190 if( $n == 0 ) return;
191
192 // Find out the max width and height of each column to be able
[2]193 // to size the legend box.
[284]194 $numcolumns = ($n > $this->layout_n ? $this->layout_n : $n);
195 for( $i=0; $i < $numcolumns; ++$i ) {
196 $colwidth[$i] = $aImg->GetTextWidth($this->txtcol[$i][0]) +
197 2*$this->xmargin + 2*$this->mark_abs_hsize;
198 $colheight[$i] = 0;
[2]199
[284]200 }
[2]201
[284]202 // Find our maximum height in each row
203 $rows = 0 ; $rowheight[0] = 0;
204 for( $i=0; $i < $n; ++$i ) {
205 $h = max($this->mark_abs_vsize,$aImg->GetTextHeight($this->txtcol[$i][0]))+$this->ylinespacing;
[2]206
[284]207 // Makes sure we always have a minimum of 1/4 (1/2 on each side) of the mark as space
208 // between two vertical legend entries
209 //$h = round(max($h,$this->mark_abs_vsize+$this->ymargin));
210 //echo "Textheight #$i: tetxheight=".$aImg->GetTextHeight($this->txtcol[$i][0]).', ';
211 //echo "h=$h ({$this->mark_abs_vsize},{$this->ymargin})<br>";
212 if( $i % $numcolumns == 0 ) {
213 $rows++;
214 $rowheight[$rows-1] = 0;
215 }
216 $rowheight[$rows-1] = max($rowheight[$rows-1],$h)+1;
217 }
[2]218
[284]219 $abs_height = 0;
220 for( $i=0; $i < $rows; ++$i ) {
221 $abs_height += $rowheight[$i] ;
222 }
[2]223
[284]224 // Make sure that the height is at least as high as mark size + ymargin
225 $abs_height = max($abs_height,$this->mark_abs_vsize);
226 $abs_height += $this->ybottom_margin;
[2]227
[284]228 // Find out the maximum width in each column
229 for( $i=$numcolumns; $i < $n; ++$i ) {
230 $colwidth[$i % $numcolumns] = max(
231 $aImg->GetTextWidth($this->txtcol[$i][0])+2*$this->xmargin+2*$this->mark_abs_hsize,
232 $colwidth[$i % $numcolumns]);
233 }
[2]234
[284]235 // Get the total width
236 $mtw = 0;
237 for( $i=0; $i < $numcolumns; ++$i ) {
238 $mtw += $colwidth[$i] ;
239 }
[2]240
[284]241 // remove the last rows interpace margin (since there is no next row)
242 $abs_height -= $this->ylinespacing;
[2]243
244
[284]245 // Find out maximum width we need for legend box
246 $abs_width = $mtw+$this->xlmargin+($numcolumns-1)*$this->mark_abs_hsize;
[2]247
[284]248 if( $this->xabspos === -1 && $this->yabspos === -1 ) {
249 $this->xabspos = $this->xpos*$aImg->width ;
250 $this->yabspos = $this->ypos*$aImg->height ;
251 }
[2]252
[284]253 // Positioning of the legend box
254 if( $this->halign == 'left' ) {
255 $xp = $this->xabspos;
256 }
257 elseif( $this->halign == 'center' ) {
258 $xp = $this->xabspos - $abs_width/2;
259 }
260 else {
261 $xp = $aImg->width - $this->xabspos - $abs_width;
262 }
[2]263
[284]264 $yp=$this->yabspos;
265 if( $this->valign == 'center' ) {
266 $yp-=$abs_height/2;
267 }
268 elseif( $this->valign == 'bottom' ) {
269 $yp-=$abs_height;
270 }
[2]271
[284]272 // Stroke legend box
273 $aImg->SetColor($this->color);
274 $aImg->SetLineWeight($this->frameweight);
275 $aImg->SetLineStyle('solid');
[2]276
[284]277 if( $this->shadow ) {
278 $aImg->ShadowRectangle($xp,$yp,
279 $xp+$abs_width+$this->shadow_width+2,
280 $yp+$abs_height+$this->shadow_width+2,
281 $this->fill_color,$this->shadow_width+2,$this->shadow_color);
282 }
283 else {
284 $aImg->SetColor($this->fill_color);
285 $aImg->FilledRectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
286 $aImg->SetColor($this->color);
287 $aImg->Rectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
288 }
[2]289
[284]290 if( $this->bkg_gradtype >= 0 ) {
291 $grad = new Gradient($aImg);
292 $grad->FilledRectangle($xp+1, $yp+1,
293 $xp+$abs_width-3, $yp+$abs_height-3,
294 $this->bkg_gradfrom, $this->bkg_gradto,
295 $this->bkg_gradtype);
296 }
[2]297
[284]298 // x1,y1 is the position for the legend marker + text
299 // The vertical position is the baseline position for the text
300 // and every marker is adjusted acording to that.
[2]301
[284]302 // For multiline texts this get more complicated.
[2]303
[284]304 $x1 = $xp + $this->xlmargin;
305 $y1 = $yp + $rowheight[0] - $this->ylinespacing + 2 ; // The ymargin is included in rowheight
306
307 // Now, y1 is the bottom vertical position of the first legend, i.e if
308 // the legend has multiple lines it is the bottom line.
309
310 $grad = new Gradient($aImg);
311 $patternFactory = null;
312
313 // Now stroke each legend in turn
314 // Each plot has added the following information to the legend
315 // p[0] = Legend text
316 // p[1] = Color,
317 // p[2] = For markers a reference to the PlotMark object
318 // p[3] = For lines the line style, for gradient the negative gradient style
319 // p[4] = CSIM target
320 // p[5] = CSIM Alt text
321 $i = 1 ; $row = 0;
322 foreach($this->txtcol as $p) {
323
324 // STROKE DEBUG BOX
325 if( _JPG_DEBUG ) {
326 $aImg->SetLineWeight(1);
327 $aImg->SetColor('red');
328 $aImg->SetLineStyle('solid');
329 $aImg->Rectangle($x1,$y1,$xp+$abs_width-1,$y1-$rowheight[$row]);
330 }
331
332 $aImg->SetLineWeight($this->weight);
333 $x1 = round($x1)+1; // We add one to not collide with the border
334 $y1=round($y1);
335
336 // This is the center offset up from the baseline which is
337 // considered the "center" of the marks. This gets slightly complicated since
338 // we need to consider if the text is a multiline paragraph or if it is only
339 // a single line. The reason is that for single line the y1 corresponds to the baseline
340 // and that is fine. However for a multiline paragraph there is no single baseline
341 // and in that case the y1 corresponds to the lowest y for the bounding box. In that
342 // case we center the mark in the middle of the paragraph
343 if( !preg_match('/\n/',$p[0]) ) {
344 // Single line
345 $marky = ceil($y1-$this->mark_abs_vsize/2)-1;
346 } else {
347 // Paragraph
348 $marky = $y1 - $aImg->GetTextHeight($p[0])/2;
349
350 // echo "y1=$y1, p[o]={$p[0]}, marky=$marky<br>";
351 }
352
353 //echo "<br>Mark #$i: marky=$marky<br>";
354
355 $x1 += $this->mark_abs_hsize;
356
357 if ( !empty($p[2]) && $p[2]->GetType() > -1 ) {
358
359
360 // Make a plot mark legend. This is constructed with a mark which
361 // is run through with a line
362
363 // First construct a bit of the line that looks exactly like the
364 // line in the plot
365 $aImg->SetColor($p[1]);
366 if( is_string($p[3]) || $p[3]>0 ) {
367 $aImg->SetLineStyle($p[3]);
368 $aImg->StyleLine($x1-$this->mark_abs_hsize,$marky,$x1+$this->mark_abs_hsize,$marky);
369 }
370
371 // Stroke a mark using image
372 if( $p[2]->GetType() == MARK_IMG ) {
373 $p[2]->Stroke($aImg,$x1,$marky);
374 }
375
376 // Stroke a mark with the standard size
377 // (As long as it is not an image mark )
378 if( $p[2]->GetType() != MARK_IMG ) {
379
380 // Clear any user callbacks since we ont want them called for
381 // the legend marks
382 $p[2]->iFormatCallback = '';
383 $p[2]->iFormatCallback2 = '';
384
385 // Since size for circles is specified as the radius
386 // this means that we must half the size to make the total
387 // width behave as the other marks
388 if( $p[2]->GetType() == MARK_FILLEDCIRCLE || $p[2]->GetType() == MARK_CIRCLE ) {
389 $p[2]->SetSize(min($this->mark_abs_vsize,$this->mark_abs_hsize)/2);
390 $p[2]->Stroke($aImg,$x1,$marky);
391 }
392 else {
393 $p[2]->SetSize(min($this->mark_abs_vsize,$this->mark_abs_hsize));
394 $p[2]->Stroke($aImg,$x1,$marky);
395 }
396 }
397 }
398 elseif ( !empty($p[2]) && (is_string($p[3]) || $p[3]>0 ) ) {
399 // Draw a styled line
400 $aImg->SetColor($p[1]);
401 $aImg->SetLineStyle($p[3]);
402 $aImg->StyleLine($x1-$this->mark_abs_hsize,$marky,$x1+$this->mark_abs_hsize,$marky);
403 $aImg->StyleLine($x1-$this->mark_abs_hsize,$marky+1,$x1+$this->mark_abs_hsize,$marky+1);
404 }
405 else {
406 // Draw a colored box
407 $color = $p[1] ;
408
409 // We make boxes slightly larger to better show
410 $boxsize = max($this->mark_abs_vsize,$this->mark_abs_hsize) + 2 ;
411
412 $ym = $marky-ceil($boxsize/2) ; // Marker y-coordinate
413
414 // We either need to plot a gradient or a
415 // pattern. To differentiate we use a kludge.
416 // Patterns have a p[3] value of < -100
417 if( $p[3] < -100 ) {
418 // p[1][0] == iPattern, p[1][1] == iPatternColor, p[1][2] == iPatternDensity
419 if( $patternFactory == null ) {
420 $patternFactory = new RectPatternFactory();
421 }
422 $prect = $patternFactory->Create($p[1][0],$p[1][1],1);
423 $prect->SetBackground($p[1][3]);
424 $prect->SetDensity($p[1][2]+1);
425 $prect->SetPos(new Rectangle($x1,$ym,$boxsize,$boxsize));
426 $prect->Stroke($aImg);
427 $prect=null;
428 }
429 else {
430 if( is_array($color) && count($color)==2 ) {
431 // The client want a gradient color
432 $grad->FilledRectangle($x1-$boxsize/2,$ym,
433 $x1+$boxsize/2,$ym+$boxsize,
434 $color[0],$color[1],-$p[3]);
435 }
436 else {
437 $aImg->SetColor($p[1]);
438 $aImg->FilledRectangle($x1-$boxsize/2,$ym, $x1+$boxsize/2,$ym+$boxsize);
439 }
440
441 // Draw a plot frame line
442 $aImg->SetColor($this->color);
443 $aImg->SetLineWeight($fillBoxFrameWeight);
444 $aImg->Rectangle($x1-$boxsize/2,$ym,
445 $x1+$boxsize/2,$ym+$boxsize);
446 }
447 }
448 $aImg->SetColor($this->font_color);
449 $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
450 $aImg->SetTextAlign('left','baseline');
451
452 $debug=false;
453 $aImg->StrokeText($x1+$this->mark_abs_hsize+$this->xmargin,$y1,$p[0],
454 0,'left',$debug);
455
456 // Add CSIM for Legend if defined
457 if( !empty($p[4]) ) {
458
459 $xs = $x1 - $this->mark_abs_hsize ;
460 $ys = $y1 + 1 ;
461 $xe = $x1 + $aImg->GetTextWidth($p[0]) + $this->mark_abs_hsize + $this->xmargin ;
462 $ye = $y1-$rowheight[$row]+1;
463 $coords = "$xs,$ys,$xe,$y1,$xe,$ye,$xs,$ye";
464 if( ! empty($p[4]) ) {
465 $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".htmlentities($p[4])."\"";
466
467 if( !empty($p[6]) ) {
468 $this->csimareas .= " target=\"".$p[6]."\"";
469 }
470
471 if( !empty($p[5]) ) {
472 $tmp=sprintf($p[5],$p[0]);
473 $this->csimareas .= " title=\"$tmp\" alt=\"$tmp\" ";
474 }
475 $this->csimareas .= " />\n";
476 }
477 }
478
479 if( $i >= $this->layout_n ) {
480 $x1 = $xp+$this->xlmargin;
481 $row++;
482 if( !empty($rowheight[$row]) )
483 $y1 += $rowheight[$row];
484 $i = 1;
485 }
486 else {
487 $x1 += $colwidth[($i-1) % $numcolumns] ;
488 ++$i;
489 }
490 }
[2]491 }
492} // Class
[284]493
[2]494?>
Note: See TracBrowser for help on using the repository browser.