source: trunk/client/modules/Elezioni/grafici/jpgraph_line.php@ 417

Last change on this file since 417 was 284, checked in by roby, 6 years ago
File size: 23.2 KB
Line 
1<?php
2/*=======================================================================
3 // File: JPGRAPH_LINE.PHP
4 // Description: Line plot extension for JpGraph
5 // Created: 2001-01-08
6 // Ver: $Id: jpgraph_line.php 1921 2009-12-11 11:46:39Z ljp $
7 //
8 // Copyright (c) Asial Corporation. All rights reserved.
9 //========================================================================
10 */
11
12require_once ('jpgraph_plotmark.inc.php');
13
14// constants for the (filled) area
15DEFINE("LP_AREA_FILLED", true);
16DEFINE("LP_AREA_NOT_FILLED", false);
17DEFINE("LP_AREA_BORDER",false);
18DEFINE("LP_AREA_NO_BORDER",true);
19
20//===================================================
21// CLASS LinePlot
22// Description:
23//===================================================
24class LinePlot extends Plot{
25 public $mark=null;
26 protected $filled=false;
27 protected $fill_color='blue';
28 protected $step_style=false, $center=false;
29 protected $line_style=1; // Default to solid
30 protected $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
31 public $barcenter=false; // When we mix line and bar. Should we center the line in the bar.
32 protected $fillFromMin = false, $fillFromMax = false;
33 protected $fillgrad=false,$fillgrad_fromcolor='navy',$fillgrad_tocolor='silver',$fillgrad_numcolors=100;
34 protected $iFastStroke=false;
35
36 //---------------
37 // CONSTRUCTOR
38 function __construct($datay,$datax=false) {
39 parent::__construct($datay,$datax);
40 $this->mark = new PlotMark() ;
41 $this->color = ColorFactory::getColor();
42 $this->fill_color = $this->color;
43 }
44 //---------------
45 // PUBLIC METHODS
46
47 function SetFilled($aFlg=true) {
48 $this->filled = $aFlg;
49 }
50
51 function SetBarCenter($aFlag=true) {
52 $this->barcenter=$aFlag;
53 }
54
55 function SetStyle($aStyle) {
56 $this->line_style=$aStyle;
57 }
58
59 function SetStepStyle($aFlag=true) {
60 $this->step_style = $aFlag;
61 }
62
63 function SetColor($aColor) {
64 parent::SetColor($aColor);
65 }
66
67 function SetFillFromYMin($f=true) {
68 $this->fillFromMin = $f ;
69 }
70
71 function SetFillFromYMax($f=true) {
72 $this->fillFromMax = $f ;
73 }
74
75 function SetFillColor($aColor,$aFilled=true) {
76 //$this->color = $aColor;
77 $this->fill_color=$aColor;
78 $this->filled=$aFilled;
79 }
80
81 function SetFillGradient($aFromColor,$aToColor,$aNumColors=100,$aFilled=true) {
82 $this->fillgrad_fromcolor = $aFromColor;
83 $this->fillgrad_tocolor = $aToColor;
84 $this->fillgrad_numcolors = $aNumColors;
85 $this->filled = $aFilled;
86 $this->fillgrad = true;
87 }
88
89 function Legend($graph) {
90 if( $this->legend!="" ) {
91 if( $this->filled && !$this->fillgrad ) {
92 $graph->legend->Add($this->legend,
93 $this->fill_color,$this->mark,0,
94 $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
95 }
96 elseif( $this->fillgrad ) {
97 $color=array($this->fillgrad_fromcolor,$this->fillgrad_tocolor);
98 // In order to differentiate between gradients and cooors specified as an RGB triple
99 $graph->legend->Add($this->legend,$color,"",-2 /* -GRAD_HOR */,
100 $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
101 } else {
102 $graph->legend->Add($this->legend,
103 $this->color,$this->mark,$this->line_style,
104 $this->legendcsimtarget,$this->legendcsimalt,$this->legendcsimwintarget);
105 }
106 }
107 }
108
109 function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
110 if($aMin > $aMax) {
111 // swap
112 $tmp = $aMin;
113 $aMin = $aMax;
114 $aMax = $tmp;
115 }
116 $this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
117 }
118
119 // Gets called before any axis are stroked
120 function PreStrokeAdjust($graph) {
121
122 // If another plot type have already adjusted the
123 // offset we don't touch it.
124 // (We check for empty in case the scale is a log scale
125 // and hence doesn't contain any xlabel_offset)
126 if( empty($graph->xaxis->scale->ticks->xlabel_offset) || $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
127 if( $this->center ) {
128 ++$this->numpoints;
129 $a=0.5; $b=0.5;
130 } else {
131 $a=0; $b=0;
132 }
133 $graph->xaxis->scale->ticks->SetXLabelOffset($a);
134 $graph->SetTextScaleOff($b);
135 //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
136 }
137 }
138
139 function SetFastStroke($aFlg=true) {
140 $this->iFastStroke = $aFlg;
141 }
142
143 function FastStroke($img,$xscale,$yscale,$aStartPoint=0,$exist_x=true) {
144 // An optimized stroke for many data points with no extra
145 // features but 60% faster. You can't have values or line styles, or null
146 // values in plots.
147 $numpoints=count($this->coords[0]);
148 if( $this->barcenter ) {
149 $textadj = 0.5-$xscale->text_scale_off;
150 }
151 else {
152 $textadj = 0;
153 }
154
155 $img->SetColor($this->color);
156 $img->SetLineWeight($this->weight);
157 $pnts=$aStartPoint;
158 while( $pnts < $numpoints ) {
159 if( $exist_x ) {
160 $x=$this->coords[1][$pnts];
161 }
162 else {
163 $x=$pnts+$textadj;
164 }
165 $xt = $xscale->Translate($x);
166 $y=$this->coords[0][$pnts];
167 $yt = $yscale->Translate($y);
168 if( is_numeric($y) ) {
169 $cord[] = $xt;
170 $cord[] = $yt;
171 }
172 elseif( $y == '-' && $pnts > 0 ) {
173 // Just ignore
174 }
175 else {
176 JpGraphError::RaiseL(10002);//('Plot too complicated for fast line Stroke. Use standard Stroke()');
177 }
178 ++$pnts;
179 } // WHILE
180
181 $img->Polygon($cord,false,true);
182 }
183
184 function Stroke($img,$xscale,$yscale) {
185 $idx=0;
186 $numpoints=count($this->coords[0]);
187 if( isset($this->coords[1]) ) {
188 if( count($this->coords[1])!=$numpoints ) {
189 JpGraphError::RaiseL(2003,count($this->coords[1]),$numpoints);
190 //("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
191 }
192 else {
193 $exist_x = true;
194 }
195 }
196 else {
197 $exist_x = false;
198 }
199
200 if( $this->barcenter ) {
201 $textadj = 0.5-$xscale->text_scale_off;
202 }
203 else {
204 $textadj = 0;
205 }
206
207 // Find the first numeric data point
208 $startpoint=0;
209 while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) ) {
210 ++$startpoint;
211 }
212
213 // Bail out if no data points
214 if( $startpoint == $numpoints ) return;
215
216 if( $this->iFastStroke ) {
217 $this->FastStroke($img,$xscale,$yscale,$startpoint,$exist_x);
218 return;
219 }
220
221 if( $exist_x ) {
222 $xs=$this->coords[1][$startpoint];
223 }
224 else {
225 $xs= $textadj+$startpoint;
226 }
227
228 $img->SetStartPoint($xscale->Translate($xs),
229 $yscale->Translate($this->coords[0][$startpoint]));
230
231 if( $this->filled ) {
232 if( $this->fillFromMax ) {
233 //$max = $yscale->GetMaxVal();
234 $cord[$idx++] = $xscale->Translate($xs);
235 $cord[$idx++] = $yscale->scale_abs[1];
236 }
237 else {
238 $min = $yscale->GetMinVal();
239 if( $min > 0 || $this->fillFromMin ) {
240 $fillmin = $yscale->scale_abs[0];//Translate($min);
241 }
242 else {
243 $fillmin = $yscale->Translate(0);
244 }
245
246 $cord[$idx++] = $xscale->Translate($xs);
247 $cord[$idx++] = $fillmin;
248 }
249 }
250 $xt = $xscale->Translate($xs);
251 $yt = $yscale->Translate($this->coords[0][$startpoint]);
252 $cord[$idx++] = $xt;
253 $cord[$idx++] = $yt;
254 $yt_old = $yt;
255 $xt_old = $xt;
256 $y_old = $this->coords[0][$startpoint];
257
258 $this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);
259
260 $img->SetColor($this->color);
261 $img->SetLineWeight($this->weight);
262 $img->SetLineStyle($this->line_style);
263 $pnts=$startpoint+1;
264 $firstnonumeric = false;
265
266
267 while( $pnts < $numpoints ) {
268
269 if( $exist_x ) {
270 $x=$this->coords[1][$pnts];
271 }
272 else {
273 $x=$pnts+$textadj;
274 }
275 $xt = $xscale->Translate($x);
276 $yt = $yscale->Translate($this->coords[0][$pnts]);
277
278 $y=$this->coords[0][$pnts];
279 if( $this->step_style ) {
280 // To handle null values within step style we need to record the
281 // first non numeric value so we know from where to start if the
282 // non value is '-'.
283 if( is_numeric($y) ) {
284 $firstnonumeric = false;
285 if( is_numeric($y_old) ) {
286 $img->StyleLine($xt_old,$yt_old,$xt,$yt_old);
287 $img->StyleLine($xt,$yt_old,$xt,$yt);
288 }
289 elseif( $y_old == '-' ) {
290 $img->StyleLine($xt_first,$yt_first,$xt,$yt_first);
291 $img->StyleLine($xt,$yt_first,$xt,$yt);
292 }
293 else {
294 $yt_old = $yt;
295 $xt_old = $xt;
296 }
297 $cord[$idx++] = $xt;
298 $cord[$idx++] = $yt_old;
299 $cord[$idx++] = $xt;
300 $cord[$idx++] = $yt;
301 }
302 elseif( $firstnonumeric==false ) {
303 $firstnonumeric = true;
304 $yt_first = $yt_old;
305 $xt_first = $xt_old;
306 }
307 }
308 else {
309 $tmp1=$y;
310 $prev=$this->coords[0][$pnts-1];
311 if( $tmp1==='' || $tmp1===NULL || $tmp1==='X' ) $tmp1 = 'x';
312 if( $prev==='' || $prev===null || $prev==='X' ) $prev = 'x';
313
314 if( is_numeric($y) || (is_string($y) && $y != '-') ) {
315 if( is_numeric($y) && (is_numeric($prev) || $prev === '-' ) ) {
316 $img->StyleLineTo($xt,$yt);
317 }
318 else {
319 $img->SetStartPoint($xt,$yt);
320 }
321 }
322 if( $this->filled && $tmp1 !== '-' ) {
323 if( $tmp1 === 'x' ) {
324 $cord[$idx++] = $cord[$idx-3];
325 $cord[$idx++] = $fillmin;
326 }
327 elseif( $prev === 'x' ) {
328 $cord[$idx++] = $xt;
329 $cord[$idx++] = $fillmin;
330 $cord[$idx++] = $xt;
331 $cord[$idx++] = $yt;
332 }
333 else {
334 $cord[$idx++] = $xt;
335 $cord[$idx++] = $yt;
336 }
337 }
338 else {
339 if( is_numeric($tmp1) && (is_numeric($prev) || $prev === '-' ) ) {
340 $cord[$idx++] = $xt;
341 $cord[$idx++] = $yt;
342 }
343 }
344 }
345 $yt_old = $yt;
346 $xt_old = $xt;
347 $y_old = $y;
348
349 $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
350
351 ++$pnts;
352 }
353
354 if( $this->filled ) {
355 $cord[$idx++] = $xt;
356 if( $this->fillFromMax ) {
357 $cord[$idx++] = $yscale->scale_abs[1];
358 }
359 else {
360 if( $min > 0 || $this->fillFromMin ) {
361 $cord[$idx++] = $yscale->Translate($min);
362 }
363 else {
364 $cord[$idx++] = $yscale->Translate(0);
365 }
366 }
367 if( $this->fillgrad ) {
368 $img->SetLineWeight(1);
369 $grad = new Gradient($img);
370 $grad->SetNumColors($this->fillgrad_numcolors);
371 $grad->FilledFlatPolygon($cord,$this->fillgrad_fromcolor,$this->fillgrad_tocolor);
372 $img->SetLineWeight($this->weight);
373 }
374 else {
375 $img->SetColor($this->fill_color);
376 $img->FilledPolygon($cord);
377 }
378 if( $this->weight > 0 ) {
379 $img->SetLineWeight($this->weight);
380 $img->SetColor($this->color);
381 // Remove first and last coordinate before drawing the line
382 // sine we otherwise get the vertical start and end lines which
383 // doesn't look appropriate
384 $img->Polygon(array_slice($cord,2,count($cord)-4));
385 }
386 }
387
388 if(!empty($this->filledAreas)) {
389
390 $minY = $yscale->Translate($yscale->GetMinVal());
391 $factor = ($this->step_style ? 4 : 2);
392
393 for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
394 // go through all filled area elements ordered by insertion
395 // fill polygon array
396 $areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
397 $areaCoords[] = $minY;
398
399 $areaCoords =
400 array_merge($areaCoords,
401 array_slice($cord,
402 $this->filledAreas[$i][0] * $factor,
403 ($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1)) * $factor));
404 $areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
405 $areaCoords[] = $minY; // last y
406
407 if($this->filledAreas[$i][3]) {
408 $img->SetColor($this->filledAreas[$i][2]);
409 $img->FilledPolygon($areaCoords);
410 $img->SetColor($this->color);
411 }
412 // Check if we should draw the frame.
413 // If not we still re-draw the line since it might have been
414 // partially overwritten by the filled area and it doesn't look
415 // very good.
416 if( $this->filledAreas[$i][4] ) {
417 $img->Polygon($areaCoords);
418 }
419 else {
420 $img->Polygon($cord);
421 }
422
423 $areaCoords = array();
424 }
425 }
426
427 if( $this->mark->type == -1 || $this->mark->show == false )
428 return;
429
430 for( $pnts=0; $pnts<$numpoints; ++$pnts) {
431
432 if( $exist_x ) {
433 $x=$this->coords[1][$pnts];
434 }
435 else {
436 $x=$pnts+$textadj;
437 }
438 $xt = $xscale->Translate($x);
439 $yt = $yscale->Translate($this->coords[0][$pnts]);
440
441 if( is_numeric($this->coords[0][$pnts]) ) {
442 if( !empty($this->csimtargets[$pnts]) ) {
443 if( !empty($this->csimwintargets[$pnts]) ) {
444 $this->mark->SetCSIMTarget($this->csimtargets[$pnts],$this->csimwintargets[$pnts]);
445 }
446 else {
447 $this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
448 }
449 $this->mark->SetCSIMAlt($this->csimalts[$pnts]);
450 }
451 if( $exist_x ) {
452 $x=$this->coords[1][$pnts];
453 }
454 else {
455 $x=$pnts;
456 }
457 $this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
458 $this->mark->Stroke($img,$xt,$yt);
459 $this->csimareas .= $this->mark->GetCSIMAreas();
460 }
461 }
462 }
463} // Class
464
465
466//===================================================
467// CLASS AccLinePlot
468// Description:
469//===================================================
470class AccLinePlot extends Plot {
471 protected $plots=null,$nbrplots=0;
472 private $iStartEndZero=true;
473 //---------------
474 // CONSTRUCTOR
475 function __construct($plots) {
476 $this->plots = $plots;
477 $this->nbrplots = count($plots);
478 $this->numpoints = $plots[0]->numpoints;
479
480 // Verify that all plots have the same number of data points
481 for( $i=1; $i < $this->nbrplots; ++$i ) {
482 if( $plots[$i]->numpoints != $this->numpoints ) {
483 JpGraphError::RaiseL(10003);//('Each plot in an accumulated lineplot must have the same number of data points',0)
484 }
485 }
486
487 for($i=0; $i < $this->nbrplots; ++$i ) {
488 $this->LineInterpolate($this->plots[$i]->coords[0]);
489 }
490 }
491
492 //---------------
493 // PUBLIC METHODS
494 function Legend($graph) {
495 foreach( $this->plots as $p ) {
496 $p->DoLegend($graph);
497 }
498 }
499
500 function Max() {
501 list($xmax) = $this->plots[0]->Max();
502 $nmax=0;
503 $n = count($this->plots);
504 for($i=0; $i < $n; ++$i) {
505 $nc = count($this->plots[$i]->coords[0]);
506 $nmax = max($nmax,$nc);
507 list($x) = $this->plots[$i]->Max();
508 $xmax = Max($xmax,$x);
509 }
510 for( $i = 0; $i < $nmax; $i++ ) {
511 // Get y-value for line $i by adding the
512 // individual bars from all the plots added.
513 // It would be wrong to just add the
514 // individual plots max y-value since that
515 // would in most cases give to large y-value.
516 $y=$this->plots[0]->coords[0][$i];
517 for( $j = 1; $j < $this->nbrplots; $j++ ) {
518 $y += $this->plots[ $j ]->coords[0][$i];
519 }
520 $ymax[$i] = $y;
521 }
522 $ymax = max($ymax);
523 return array($xmax,$ymax);
524 }
525
526 function Min() {
527 $nmax=0;
528 list($xmin,$ysetmin) = $this->plots[0]->Min();
529 $n = count($this->plots);
530 for($i=0; $i < $n; ++$i) {
531 $nc = count($this->plots[$i]->coords[0]);
532 $nmax = max($nmax,$nc);
533 list($x,$y) = $this->plots[$i]->Min();
534 $xmin = Min($xmin,$x);
535 $ysetmin = Min($y,$ysetmin);
536 }
537 for( $i = 0; $i < $nmax; $i++ ) {
538 // Get y-value for line $i by adding the
539 // individual bars from all the plots added.
540 // It would be wrong to just add the
541 // individual plots min y-value since that
542 // would in most cases give to small y-value.
543 $y=$this->plots[0]->coords[0][$i];
544 for( $j = 1; $j < $this->nbrplots; $j++ ) {
545 $y += $this->plots[ $j ]->coords[0][$i];
546 }
547 $ymin[$i] = $y;
548 }
549 $ymin = Min($ysetmin,Min($ymin));
550 return array($xmin,$ymin);
551 }
552
553 // Gets called before any axis are stroked
554 function PreStrokeAdjust($graph) {
555
556 // If another plot type have already adjusted the
557 // offset we don't touch it.
558 // (We check for empty in case the scale is a log scale
559 // and hence doesn't contain any xlabel_offset)
560
561 if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
562 $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
563 if( $this->center ) {
564 ++$this->numpoints;
565 $a=0.5; $b=0.5;
566 } else {
567 $a=0; $b=0;
568 }
569 $graph->xaxis->scale->ticks->SetXLabelOffset($a);
570 $graph->SetTextScaleOff($b);
571 $graph->xaxis->scale->ticks->SupressMinorTickMarks();
572 }
573
574 }
575
576 function SetInterpolateMode($aIntMode) {
577 $this->iStartEndZero=$aIntMode;
578 }
579
580 // Replace all '-' with an interpolated value. We use straightforward
581 // linear interpolation. If the data starts with one or several '-' they
582 // will be replaced by the the first valid data point
583 function LineInterpolate(&$aData) {
584
585 $n=count($aData);
586 $i=0;
587
588 // If first point is undefined we will set it to the same as the first
589 // valid data
590 if( $aData[$i]==='-' ) {
591 // Find the first valid data
592 while( $i < $n && $aData[$i]==='-' ) {
593 ++$i;
594 }
595 if( $i < $n ) {
596 for($j=0; $j < $i; ++$j ) {
597 if( $this->iStartEndZero )
598 $aData[$i] = 0;
599 else
600 $aData[$j] = $aData[$i];
601 }
602 }
603 else {
604 // All '-' => Error
605 return false;
606 }
607 }
608
609 while($i < $n) {
610 while( $i < $n && $aData[$i] !== '-' ) {
611 ++$i;
612 }
613 if( $i < $n ) {
614 $pstart=$i-1;
615
616 // Now see how long this segment of '-' are
617 while( $i < $n && $aData[$i] === '-' ) {
618 ++$i;
619 }
620 if( $i < $n ) {
621 $pend=$i;
622 $size=$pend-$pstart;
623 $k=($aData[$pend]-$aData[$pstart])/$size;
624 // Replace the segment of '-' with a linear interpolated value.
625 for($j=1; $j < $size; ++$j ) {
626 $aData[$pstart+$j] = $aData[$pstart] + $j*$k ;
627 }
628 }
629 else {
630 // There are no valid end point. The '-' goes all the way to the end
631 // In that case we just set all the remaining values the the same as the
632 // last valid data point.
633 for( $j=$pstart+1; $j < $n; ++$j )
634 if( $this->iStartEndZero ) {
635 $aData[$j] = 0;
636 }
637 else {
638 $aData[$j] = $aData[$pstart] ;
639 }
640 }
641 }
642 }
643 return true;
644 }
645
646 // To avoid duplicate of line drawing code here we just
647 // change the y-values for each plot and then restore it
648 // after we have made the stroke. We must do this copy since
649 // it wouldn't be possible to create an acc line plot
650 // with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
651 // since this method would have a side effect.
652 function Stroke($img,$xscale,$yscale) {
653 $img->SetLineWeight($this->weight);
654 $this->numpoints = count($this->plots[0]->coords[0]);
655 // Allocate array
656 $coords[$this->nbrplots][$this->numpoints]=0;
657 for($i=0; $i<$this->numpoints; $i++) {
658 $coords[0][$i]=$this->plots[0]->coords[0][$i];
659 $accy=$coords[0][$i];
660 for($j=1; $j<$this->nbrplots; ++$j ) {
661 $coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy;
662 $accy = $coords[$j][$i];
663 }
664 }
665 for($j=$this->nbrplots-1; $j>=0; --$j) {
666 $p=$this->plots[$j];
667 for( $i=0; $i<$this->numpoints; ++$i) {
668 $tmp[$i]=$p->coords[0][$i];
669 $p->coords[0][$i]=$coords[$j][$i];
670 }
671 $p->Stroke($img,$xscale,$yscale);
672 for( $i=0; $i<$this->numpoints; ++$i) {
673 $p->coords[0][$i]=$tmp[$i];
674 }
675 $p->coords[0][]=$tmp;
676 }
677 }
678} // Class
679
680
681/* EOF */
682?>
Note: See TracBrowser for help on using the repository browser.