[ PHPXref.com ] [ Generated: Sun Jul 20 20:16:32 2008 ] [ Simple Groupware 0.25 ]
[ Index ]     [ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

title

Body

[close]

/src/lib/jpgraph/ -> jpgraph_line.php (source)

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


[ Powered by PHPXref - Served by Debian GNU/Linux ]