2 /*=======================================================================
3 // File: JPGRAPH_LINE.PHP
4 // Description: Line plot extension for JpGraph
6 // Author: Johan Persson (johanp@aditus.nu)
7 // Ver: $Id: jpgraph_line.php,v 1.48.2.3 2003/08/23 22:01:56 aditus Exp $
9 // License: This code is released under QPL
10 // Copyright (C) 2001,2002 Johan Persson
11 //========================================================================
14 // constants for the (filled) area
15 DEFINE("LP_AREA_FILLED", true);
16 DEFINE("LP_AREA_NOT_FILLED", false);
17 DEFINE("LP_AREA_BORDER",false);
18 DEFINE("LP_AREA_NO_BORDER",true);
20 //===================================================
23 //===================================================
24 class LinePlot extends Plot{
26 var $fill_color='blue';
28 var $step_style=false, $center=false;
29 var $line_style=1; // Default to solid
30 var $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
31 var $barcenter=false; // When we mix line and bar. Should we center the line in the bar.
32 var $fillFromMin = false ;
33 var $fillgrad=false,$fillgrad_fromcolor='navy',$fillgrad_tocolor='silver',$fillgrad_numcolors=100;
37 function LinePlot(&$datay,$datax=false) {
38 $this->Plot($datay,$datax);
39 $this->mark = new PlotMark();
44 // Set style, filled or open
45 function SetFilled($aFlag=true) {
46 JpGraphError::Raise('LinePlot::SetFilled() is deprecated. Use SetFillColor()');
49 function SetBarCenter($aFlag=true) {
50 $this->barcenter=$aFlag;
53 function SetStyle($aStyle) {
54 $this->line_style=$aStyle;
57 function SetStepStyle($aFlag=true) {
58 $this->step_style = $aFlag;
61 function SetColor($aColor) {
62 parent::SetColor($aColor);
65 function SetFillFromYMin($f=true) {
66 $this->fillFromMin = $f ;
69 function SetFillColor($aColor,$aFilled=true) {
70 $this->fill_color=$aColor;
71 $this->filled=$aFilled;
74 function SetFillGradient($aFromColor,$aToColor,$aNumColors=100,$aFilled=true) {
75 $this->fillgrad_fromcolor = $aFromColor;
76 $this->fillgrad_tocolor = $aToColor;
77 $this->fillgrad_numcolors = $aNumColors;
78 $this->filled = $aFilled;
79 $this->fillgrad = true;
82 function Legend(&$graph) {
83 if( $this->legend!="" ) {
85 $graph->legend->Add($this->legend,
86 $this->fill_color,$this->mark,0,
87 $this->legendcsimtarget,$this->legendcsimalt);
89 $graph->legend->Add($this->legend,
90 $this->color,$this->mark,$this->line_style,
91 $this->legendcsimtarget,$this->legendcsimalt);
96 function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
103 $this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
106 // Gets called before any axis are stroked
107 function PreStrokeAdjust(&$graph) {
109 // If another plot type have already adjusted the
110 // offset we don't touch it.
111 // (We check for empty in case the scale is a log scale
112 // and hence doesn't contain any xlabel_offset)
113 if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
114 $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
115 if( $this->center ) {
121 $graph->xaxis->scale->ticks->SetXLabelOffset($a);
122 $graph->SetTextScaleOff($b);
123 //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
127 function Stroke(&$img,&$xscale,&$yscale) {
128 $numpoints=count($this->coords[0]);
129 if( isset($this->coords[1]) ) {
130 if( count($this->coords[1])!=$numpoints )
131 JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
138 if( $this->barcenter )
139 $textadj = 0.5-$xscale->text_scale_off;
143 // Find the first numeric data point
145 while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) )
148 // Bail out if no data points
149 if( $startpoint == $numpoints )
153 $xs=$this->coords[1][$startpoint];
155 $xs= $textadj+$startpoint;
157 $img->SetStartPoint($xscale->Translate($xs),
158 $yscale->Translate($this->coords[0][$startpoint]));
161 if( $this->filled ) {
162 $cord[] = $xscale->Translate($xs);
163 $min = $yscale->GetMinVal();
164 if( $min > 0 || $this->fillFromMin )
165 $cord[] = $yscale->Translate($min);
167 $cord[] = $yscale->Translate(0);
169 $xt = $xscale->Translate($xs);
170 $yt = $yscale->Translate($this->coords[0][$startpoint]);
175 $this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);
177 $img->SetColor($this->color);
178 $img->SetLineWeight($this->weight);
179 $img->SetLineStyle($this->line_style);
180 for( $pnts=$startpoint+1; $pnts<$numpoints; ++$pnts) {
182 if( $exist_x ) $x=$this->coords[1][$pnts];
183 else $x=$pnts+$textadj;
184 $xt = $xscale->Translate($x);
185 $yt = $yscale->Translate($this->coords[0][$pnts]);
187 $y=$this->coords[0][$pnts];
188 if( $this->step_style && is_numeric($y) ) {
189 $img->StyleLineTo($xt,$yt_old);
190 $img->StyleLineTo($xt,$yt);
200 if( is_numeric($y) || (is_string($y) && $y != "-") ) {
201 $tmp1=$this->coords[0][$pnts];
202 $tmp2=$this->coords[0][$pnts-1];
203 if( is_numeric($tmp1) && (is_numeric($tmp2) || $tmp2=="-" ) ) {
204 $img->StyleLineTo($xt,$yt);
207 $img->SetStartPoint($xt,$yt);
209 if( is_numeric($tmp1) &&
210 (is_numeric($tmp2) || $tmp2=="-" || ($this->filled && $tmp2=='') ) ) {
218 $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
221 if( $this->filled ) {
223 if( $min > 0 || $this->fillFromMin )
224 $cord[] = $yscale->Translate($min);
226 $cord[] = $yscale->Translate(0);
227 if( $this->fillgrad ) {
228 $img->SetLineWeight(1);
229 $grad = new Gradient($img);
230 $grad->SetNumColors($this->fillgrad_numcolors);
231 $grad->FilledFlatPolygon($cord,$this->fillgrad_fromcolor,$this->fillgrad_tocolor);
232 $img->SetLineWeight($this->weight);
235 $img->SetColor($this->fill_color);
236 $img->FilledPolygon($cord);
238 if( $this->line_weight > 0 ) {
239 $img->SetColor($this->color);
240 $img->Polygon($cord);
244 if(!empty($this->filledAreas)) {
246 $minY = $yscale->Translate($yscale->GetMinVal());
247 $factor = ($this->step_style ? 4 : 2);
249 for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
250 // go through all filled area elements ordered by insertion
251 // fill polygon array
252 $areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
253 $areaCoords[] = $minY;
256 array_merge($areaCoords,
258 $this->filledAreas[$i][0] * $factor,
259 ($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1)) * $factor));
260 $areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
261 $areaCoords[] = $minY; // last y
263 if($this->filledAreas[$i][3]) {
264 $img->SetColor($this->filledAreas[$i][2]);
265 $img->FilledPolygon($areaCoords);
266 $img->SetColor($this->color);
268 // Check if we should draw the frame.
269 // If not we still re-draw the line since it might have been
270 // partially overwritten by the filled area and it doesn't look
272 // TODO: The behaviour is undefined if the line does not have
273 // any line at the position of the area.
274 if( $this->filledAreas[$i][4] )
275 $img->Polygon($areaCoords);
277 $img->Polygon($cord);
279 $areaCoords = array();
283 if( $this->mark->type == -1 || $this->mark->show == false )
286 for( $pnts=0; $pnts<$numpoints; ++$pnts) {
288 if( $exist_x ) $x=$this->coords[1][$pnts];
289 else $x=$pnts+$textadj;
290 $xt = $xscale->Translate($x);
291 $yt = $yscale->Translate($this->coords[0][$pnts]);
293 if( is_numeric($this->coords[0][$pnts]) ) {
294 if( !empty($this->csimtargets[$pnts]) ) {
295 $this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
296 $this->mark->SetCSIMAlt($this->csimalts[$pnts]);
299 $x=$this->coords[1][$pnts];
302 $this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
303 $this->mark->Stroke($img,$xt,$yt);
304 $this->csimareas .= $this->mark->GetCSIMAreas();
305 $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
314 //===================================================
317 //===================================================
318 class AccLinePlot extends Plot {
319 var $plots=null,$nbrplots=0,$numpoints=0;
322 function AccLinePlot($plots) {
323 $this->plots = $plots;
324 $this->nbrplots = count($plots);
325 $this->numpoints = $plots[0]->numpoints;
330 function Legend(&$graph) {
331 foreach( $this->plots as $p )
332 $p->DoLegend($graph);
336 list($xmax) = $this->plots[0]->Max();
338 for($i=0; $i<count($this->plots); ++$i) {
339 $n = count($this->plots[$i]->coords[0]);
340 $nmax = max($nmax,$n);
341 list($x) = $this->plots[$i]->Max();
342 $xmax = Max($xmax,$x);
344 for( $i = 0; $i < $nmax; $i++ ) {
345 // Get y-value for line $i by adding the
346 // individual bars from all the plots added.
347 // It would be wrong to just add the
348 // individual plots max y-value since that
349 // would in most cases give to large y-value.
350 $y=$this->plots[0]->coords[0][$i];
351 for( $j = 1; $j < $this->nbrplots; $j++ ) {
352 $y += $this->plots[ $j ]->coords[0][$i];
357 return array($xmax,$ymax);
362 list($xmin,$ysetmin) = $this->plots[0]->Min();
363 for($i=0; $i<count($this->plots); ++$i) {
364 $n = count($this->plots[$i]->coords[0]);
365 $nmax = max($nmax,$n);
366 list($x,$y) = $this->plots[$i]->Min();
367 $xmin = Min($xmin,$x);
368 $ysetmin = Min($y,$ysetmin);
370 for( $i = 0; $i < $nmax; $i++ ) {
371 // Get y-value for line $i by adding the
372 // individual bars from all the plots added.
373 // It would be wrong to just add the
374 // individual plots min y-value since that
375 // would in most cases give to small y-value.
376 $y=$this->plots[0]->coords[0][$i];
377 for( $j = 1; $j < $this->nbrplots; $j++ ) {
378 $y += $this->plots[ $j ]->coords[0][$i];
382 $ymin = Min($ysetmin,Min($ymin));
383 return array($xmin,$ymin);
386 // Gets called before any axis are stroked
387 function PreStrokeAdjust(&$graph) {
389 // If another plot type have already adjusted the
390 // offset we don't touch it.
391 // (We check for empty in case the scale is a log scale
392 // and hence doesn't contain any xlabel_offset)
394 if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
395 $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
396 if( $this->center ) {
402 $graph->xaxis->scale->ticks->SetXLabelOffset($a);
403 $graph->SetTextScaleOff($b);
404 $graph->xaxis->scale->ticks->SupressMinorTickMarks();
409 // To avoid duplicate of line drawing code here we just
410 // change the y-values for each plot and then restore it
411 // after we have made the stroke. We must do this copy since
412 // it wouldn't be possible to create an acc line plot
413 // with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
414 // since this method would have a side effect.
415 function Stroke(&$img,&$xscale,&$yscale) {
416 $img->SetLineWeight($this->weight);
417 $this->numpoints = count($this->plots[0]->coords[0]);
419 $coords[$this->nbrplots][$this->numpoints]=0;
420 for($i=0; $i<$this->numpoints; $i++) {
421 $coords[0][$i]=$this->plots[0]->coords[0][$i];
422 $accy=$coords[0][$i];
423 for($j=1; $j<$this->nbrplots; ++$j ) {
424 $coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy;
425 $accy = $coords[$j][$i];
428 for($j=$this->nbrplots-1; $j>=0; --$j) {
430 for( $i=0; $i<$this->numpoints; ++$i) {
431 $tmp[$i]=$p->coords[0][$i];
432 $p->coords[0][$i]=$coords[$j][$i];
434 $p->Stroke($img,$xscale,$yscale);
435 for( $i=0; $i<$this->numpoints; ++$i)
436 $p->coords[0][$i]=$tmp[$i];
437 $p->coords[0][]=$tmp;