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

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