| 1 | <?php
 | 
|---|
| 2 | /*=======================================================================
 | 
|---|
| 3 |  // File:        JPGRAPH_UTILS.INC
 | 
|---|
| 4 |  // Description: Collection of non-essential "nice to have" utilities
 | 
|---|
| 5 |  // Created:     2005-11-20
 | 
|---|
| 6 |  // Ver:         $Id: jpgraph_utils.inc.php 1777 2009-08-23 17:34:36Z ljp $
 | 
|---|
| 7 |  //
 | 
|---|
| 8 |  // Copyright (c) Asial Corporation. All rights reserved.
 | 
|---|
| 9 |  //========================================================================
 | 
|---|
| 10 |  */
 | 
|---|
| 11 | 
 | 
|---|
| 12 | //===================================================
 | 
|---|
| 13 | // CLASS FuncGenerator
 | 
|---|
| 14 | // Description: Utility class to help generate data for function plots.
 | 
|---|
| 15 | // The class supports both parametric and regular functions.
 | 
|---|
| 16 | //===================================================
 | 
|---|
| 17 | class FuncGenerator {
 | 
|---|
| 18 |     private $iFunc='',$iXFunc='',$iMin,$iMax,$iStepSize;
 | 
|---|
| 19 | 
 | 
|---|
| 20 |     function __construct($aFunc,$aXFunc='') {
 | 
|---|
| 21 |         $this->iFunc = $aFunc;
 | 
|---|
| 22 |         $this->iXFunc = $aXFunc;
 | 
|---|
| 23 |     }
 | 
|---|
| 24 | 
 | 
|---|
| 25 |     function E($aXMin,$aXMax,$aSteps=50) {
 | 
|---|
| 26 |         $this->iMin = $aXMin;
 | 
|---|
| 27 |         $this->iMax = $aXMax;
 | 
|---|
| 28 |         $this->iStepSize = ($aXMax-$aXMin)/$aSteps;
 | 
|---|
| 29 | 
 | 
|---|
| 30 |         if( $this->iXFunc != '' )
 | 
|---|
| 31 |         $t = 'for($i='.$aXMin.'; $i<='.$aXMax.'; $i += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]='.$this->iXFunc.';}';
 | 
|---|
| 32 |         elseif( $this->iFunc != '' )
 | 
|---|
| 33 |         $t = 'for($x='.$aXMin.'; $x<='.$aXMax.'; $x += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]=$x;} $x='.$aXMax.';$ya[]='.$this->iFunc.';$xa[]=$x;';
 | 
|---|
| 34 |         else
 | 
|---|
| 35 |         JpGraphError::RaiseL(24001);//('FuncGenerator : No function specified. ');
 | 
|---|
| 36 | 
 | 
|---|
| 37 |         @eval($t);
 | 
|---|
| 38 | 
 | 
|---|
| 39 |         // If there is an error in the function specifcation this is the only
 | 
|---|
| 40 |         // way we can discover that.
 | 
|---|
| 41 |         if( empty($xa) || empty($ya) )
 | 
|---|
| 42 |         JpGraphError::RaiseL(24002);//('FuncGenerator : Syntax error in function specification ');
 | 
|---|
| 43 | 
 | 
|---|
| 44 |         return array($xa,$ya);
 | 
|---|
| 45 |     }
 | 
|---|
| 46 | }
 | 
|---|
| 47 | 
 | 
|---|
| 48 | 
 | 
|---|
| 49 | //=============================================================================
 | 
|---|
| 50 | // CLASS DateScaleUtils
 | 
|---|
| 51 | // Description: Help to create a manual date scale
 | 
|---|
| 52 | //=============================================================================
 | 
|---|
| 53 | define('DSUTILS_MONTH',1); // Major and minor ticks on a monthly basis
 | 
|---|
| 54 | define('DSUTILS_MONTH1',1); // Major and minor ticks on a monthly basis
 | 
|---|
| 55 | define('DSUTILS_MONTH2',2); // Major ticks on a bi-monthly basis
 | 
|---|
| 56 | define('DSUTILS_MONTH3',3); // Major icks on a tri-monthly basis
 | 
|---|
| 57 | define('DSUTILS_MONTH6',4); // Major on a six-monthly basis
 | 
|---|
| 58 | define('DSUTILS_WEEK1',5); // Major ticks on a weekly basis
 | 
|---|
| 59 | define('DSUTILS_WEEK2',6); // Major ticks on a bi-weekly basis
 | 
|---|
| 60 | define('DSUTILS_WEEK4',7); // Major ticks on a quod-weekly basis
 | 
|---|
| 61 | define('DSUTILS_DAY1',8); // Major ticks on a daily basis
 | 
|---|
| 62 | define('DSUTILS_DAY2',9); // Major ticks on a bi-daily basis
 | 
|---|
| 63 | define('DSUTILS_DAY4',10); // Major ticks on a qoud-daily basis
 | 
|---|
| 64 | define('DSUTILS_YEAR1',11); // Major ticks on a yearly basis
 | 
|---|
| 65 | define('DSUTILS_YEAR2',12); // Major ticks on a bi-yearly basis
 | 
|---|
| 66 | define('DSUTILS_YEAR5',13); // Major ticks on a five-yearly basis
 | 
|---|
| 67 | 
 | 
|---|
| 68 | 
 | 
|---|
| 69 | class DateScaleUtils {
 | 
|---|
| 70 |     public static $iMin=0, $iMax=0;
 | 
|---|
| 71 | 
 | 
|---|
| 72 |     private static $starthour,$startmonth, $startday, $startyear;
 | 
|---|
| 73 |     private static $endmonth, $endyear, $endday;
 | 
|---|
| 74 |     private static $tickPositions=array(),$minTickPositions=array();
 | 
|---|
| 75 |     private static $iUseWeeks = true;
 | 
|---|
| 76 | 
 | 
|---|
| 77 |     static function UseWeekFormat($aFlg) {
 | 
|---|
| 78 |         self::$iUseWeeks = $aFlg;
 | 
|---|
| 79 |     }
 | 
|---|
| 80 | 
 | 
|---|
| 81 |     static function doYearly($aType,$aMinor=false) {
 | 
|---|
| 82 |         $i=0; $j=0;
 | 
|---|
| 83 |         $m = self::$startmonth;
 | 
|---|
| 84 |         $y = self::$startyear;
 | 
|---|
| 85 | 
 | 
|---|
| 86 |         if( self::$startday == 1 ) {
 | 
|---|
| 87 |             self::$tickPositions[$i++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 88 |         }
 | 
|---|
| 89 |         ++$m;
 | 
|---|
| 90 | 
 | 
|---|
| 91 | 
 | 
|---|
| 92 |         switch( $aType ) {
 | 
|---|
| 93 |             case DSUTILS_YEAR1:
 | 
|---|
| 94 |                 for($y=self::$startyear; $y <= self::$endyear; ++$y ) {
 | 
|---|
| 95 |                     if( $aMinor ) {
 | 
|---|
| 96 |                         while( $m <= 12 ) {
 | 
|---|
| 97 |                             if( !($y == self::$endyear && $m > self::$endmonth) ) {
 | 
|---|
| 98 |                                 self::$minTickPositions[$j++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 99 |                             }
 | 
|---|
| 100 |                             ++$m;
 | 
|---|
| 101 |                         }
 | 
|---|
| 102 |                         $m=1;
 | 
|---|
| 103 |                     }
 | 
|---|
| 104 |                     self::$tickPositions[$i++] = mktime(0,0,0,1,1,$y);
 | 
|---|
| 105 |                 }
 | 
|---|
| 106 |                 break;
 | 
|---|
| 107 |             case DSUTILS_YEAR2:
 | 
|---|
| 108 |                 $y=self::$startyear;
 | 
|---|
| 109 |                 while( $y <= self::$endyear ) {
 | 
|---|
| 110 |                     self::$tickPositions[$i++] = mktime(0,0,0,1,1,$y);
 | 
|---|
| 111 |                     for($k=0; $k < 1; ++$k ) {
 | 
|---|
| 112 |                         ++$y;
 | 
|---|
| 113 |                         if( $aMinor ) {
 | 
|---|
| 114 |                             self::$minTickPositions[$j++] = mktime(0,0,0,1,1,$y);
 | 
|---|
| 115 |                         }
 | 
|---|
| 116 |                     }
 | 
|---|
| 117 |                     ++$y;
 | 
|---|
| 118 |                 }
 | 
|---|
| 119 |                 break;
 | 
|---|
| 120 |             case DSUTILS_YEAR5:
 | 
|---|
| 121 |                 $y=self::$startyear;
 | 
|---|
| 122 |                 while( $y <= self::$endyear ) {
 | 
|---|
| 123 |                     self::$tickPositions[$i++] = mktime(0,0,0,1,1,$y);
 | 
|---|
| 124 |                     for($k=0; $k < 4; ++$k ) {
 | 
|---|
| 125 |                         ++$y;
 | 
|---|
| 126 |                         if( $aMinor ) {
 | 
|---|
| 127 |                             self::$minTickPositions[$j++] = mktime(0,0,0,1,1,$y);
 | 
|---|
| 128 |                         }
 | 
|---|
| 129 |                     }
 | 
|---|
| 130 |                     ++$y;
 | 
|---|
| 131 |                 }
 | 
|---|
| 132 |                 break;
 | 
|---|
| 133 |         }
 | 
|---|
| 134 |     }
 | 
|---|
| 135 | 
 | 
|---|
| 136 |     static function doDaily($aType,$aMinor=false) {
 | 
|---|
| 137 |         $m = self::$startmonth;
 | 
|---|
| 138 |         $y = self::$startyear;
 | 
|---|
| 139 |         $d = self::$startday;
 | 
|---|
| 140 |         $h = self::$starthour;
 | 
|---|
| 141 |         $i=0;$j=0;
 | 
|---|
| 142 | 
 | 
|---|
| 143 |         if( $h == 0 ) {
 | 
|---|
| 144 |             self::$tickPositions[$i++] = mktime(0,0,0,$m,$d,$y);
 | 
|---|
| 145 |         }
 | 
|---|
| 146 |         $t = mktime(0,0,0,$m,$d,$y);
 | 
|---|
| 147 | 
 | 
|---|
| 148 |         switch($aType) {
 | 
|---|
| 149 |             case DSUTILS_DAY1:
 | 
|---|
| 150 |                 while( $t <= self::$iMax ) {
 | 
|---|
| 151 |                     $t = strtotime('+1 day',$t);
 | 
|---|
| 152 |                     self::$tickPositions[$i++] = $t;
 | 
|---|
| 153 |                     if( $aMinor ) {
 | 
|---|
| 154 |                         self::$minTickPositions[$j++] = strtotime('+12 hours',$t);
 | 
|---|
| 155 |                     }
 | 
|---|
| 156 |                 }
 | 
|---|
| 157 |                 break;
 | 
|---|
| 158 |             case DSUTILS_DAY2:
 | 
|---|
| 159 |                 while( $t <= self::$iMax ) {
 | 
|---|
| 160 |                     $t = strtotime('+1 day',$t);
 | 
|---|
| 161 |                     if( $aMinor ) {
 | 
|---|
| 162 |                         self::$minTickPositions[$j++] = $t;
 | 
|---|
| 163 |                     }
 | 
|---|
| 164 |                     $t = strtotime('+1 day',$t);
 | 
|---|
| 165 |                     self::$tickPositions[$i++] = $t;
 | 
|---|
| 166 |                 }
 | 
|---|
| 167 |                 break;
 | 
|---|
| 168 |             case DSUTILS_DAY4:
 | 
|---|
| 169 |                 while( $t <= self::$iMax ) {
 | 
|---|
| 170 |                     for($k=0; $k < 3; ++$k ) {
 | 
|---|
| 171 |                         $t = strtotime('+1 day',$t);
 | 
|---|
| 172 |                         if( $aMinor ) {
 | 
|---|
| 173 |                             self::$minTickPositions[$j++] = $t;
 | 
|---|
| 174 |                         }
 | 
|---|
| 175 |                     }
 | 
|---|
| 176 |                     $t = strtotime('+1 day',$t);
 | 
|---|
| 177 |                     self::$tickPositions[$i++] = $t;
 | 
|---|
| 178 |                 }
 | 
|---|
| 179 |                 break;
 | 
|---|
| 180 |         }
 | 
|---|
| 181 |     }
 | 
|---|
| 182 | 
 | 
|---|
| 183 |     static function doWeekly($aType,$aMinor=false) {
 | 
|---|
| 184 |         $hpd = 3600*24;
 | 
|---|
| 185 |         $hpw = 3600*24*7;
 | 
|---|
| 186 |         // Find out week number of min date
 | 
|---|
| 187 |         $thursday = self::$iMin + $hpd * (3 - (date('w', self::$iMin) + 6) % 7);
 | 
|---|
| 188 |         $week = 1 + (date('z', $thursday) - (11 - date('w', mktime(0, 0, 0, 1, 1, date('Y', $thursday)))) % 7) / 7;
 | 
|---|
| 189 |         $daynumber = date('w',self::$iMin);
 | 
|---|
| 190 |         if( $daynumber == 0 ) $daynumber = 7;
 | 
|---|
| 191 |         $m = self::$startmonth;
 | 
|---|
| 192 |         $y = self::$startyear;
 | 
|---|
| 193 |         $d = self::$startday;
 | 
|---|
| 194 |         $i=0;$j=0;
 | 
|---|
| 195 |         // The assumption is that the weeks start on Monday. If the first day
 | 
|---|
| 196 |         // is later in the week then the first week tick has to be on the following
 | 
|---|
| 197 |         // week.
 | 
|---|
| 198 |         if( $daynumber == 1 ) {
 | 
|---|
| 199 |             self::$tickPositions[$i++] = mktime(0,0,0,$m,$d,$y);
 | 
|---|
| 200 |             $t = mktime(0,0,0,$m,$d,$y) + $hpw;
 | 
|---|
| 201 |         }
 | 
|---|
| 202 |         else {
 | 
|---|
| 203 |             $t = mktime(0,0,0,$m,$d,$y) + $hpd*(8-$daynumber);
 | 
|---|
| 204 |         }
 | 
|---|
| 205 | 
 | 
|---|
| 206 |         switch($aType) {
 | 
|---|
| 207 |             case DSUTILS_WEEK1:
 | 
|---|
| 208 |                 $cnt=0;
 | 
|---|
| 209 |                 break;
 | 
|---|
| 210 |             case DSUTILS_WEEK2:
 | 
|---|
| 211 |                 $cnt=1;
 | 
|---|
| 212 |                 break;
 | 
|---|
| 213 |             case DSUTILS_WEEK4:
 | 
|---|
| 214 |                 $cnt=3;
 | 
|---|
| 215 |                 break;
 | 
|---|
| 216 |         }
 | 
|---|
| 217 |         while( $t <= self::$iMax ) {
 | 
|---|
| 218 |             self::$tickPositions[$i++] = $t;
 | 
|---|
| 219 |             for($k=0; $k < $cnt; ++$k ) {
 | 
|---|
| 220 |                 $t += $hpw;
 | 
|---|
| 221 |                 if( $aMinor ) {
 | 
|---|
| 222 |                     self::$minTickPositions[$j++] = $t;
 | 
|---|
| 223 |                 }
 | 
|---|
| 224 |             }
 | 
|---|
| 225 |             $t += $hpw;
 | 
|---|
| 226 |         }
 | 
|---|
| 227 |     }
 | 
|---|
| 228 | 
 | 
|---|
| 229 |     static function doMonthly($aType,$aMinor=false) {
 | 
|---|
| 230 |         $monthcount=0;
 | 
|---|
| 231 |         $m = self::$startmonth;
 | 
|---|
| 232 |         $y = self::$startyear;
 | 
|---|
| 233 |         $i=0; $j=0;
 | 
|---|
| 234 | 
 | 
|---|
| 235 |         // Skip the first month label if it is before the startdate
 | 
|---|
| 236 |         if( self::$startday == 1 ) {
 | 
|---|
| 237 |             self::$tickPositions[$i++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 238 |             $monthcount=1;
 | 
|---|
| 239 |         }
 | 
|---|
| 240 |         if( $aType == 1 ) {
 | 
|---|
| 241 |             if( self::$startday < 15 ) {
 | 
|---|
| 242 |                 self::$minTickPositions[$j++] = mktime(0,0,0,$m,15,$y);
 | 
|---|
| 243 |             }
 | 
|---|
| 244 |         }
 | 
|---|
| 245 |         ++$m;
 | 
|---|
| 246 | 
 | 
|---|
| 247 |         // Loop through all the years included in the scale
 | 
|---|
| 248 |         for($y=self::$startyear; $y <= self::$endyear; ++$y ) {
 | 
|---|
| 249 |             // Loop through all the months. There are three cases to consider:
 | 
|---|
| 250 |             // 1. We are in the first year and must start with the startmonth
 | 
|---|
| 251 |             // 2. We are in the end year and we must stop at last month of the scale
 | 
|---|
| 252 |             // 3. A year in between where we run through all the 12 months
 | 
|---|
| 253 |             $stopmonth = $y == self::$endyear ? self::$endmonth : 12;
 | 
|---|
| 254 |             while( $m <= $stopmonth ) {
 | 
|---|
| 255 |                 switch( $aType ) {
 | 
|---|
| 256 |                     case DSUTILS_MONTH1:
 | 
|---|
| 257 |                         // Set minor tick at the middle of the month
 | 
|---|
| 258 |                         if( $aMinor ) {
 | 
|---|
| 259 |                             if( $m <= $stopmonth ) {
 | 
|---|
| 260 |                                 if( !($y==self::$endyear && $m==$stopmonth && self::$endday < 15) )
 | 
|---|
| 261 |                                 self::$minTickPositions[$j++] = mktime(0,0,0,$m,15,$y);
 | 
|---|
| 262 |                             }
 | 
|---|
| 263 |                         }
 | 
|---|
| 264 |                         // Major at month
 | 
|---|
| 265 |                         // Get timestamp of first hour of first day in each month
 | 
|---|
| 266 |                         self::$tickPositions[$i++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 267 | 
 | 
|---|
| 268 |                         break;
 | 
|---|
| 269 |                     case DSUTILS_MONTH2:
 | 
|---|
| 270 |                         if( $aMinor ) {
 | 
|---|
| 271 |                             // Set minor tick at start of each month
 | 
|---|
| 272 |                             self::$minTickPositions[$j++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 273 |                         }
 | 
|---|
| 274 | 
 | 
|---|
| 275 |                         // Major at every second month
 | 
|---|
| 276 |                         // Get timestamp of first hour of first day in each month
 | 
|---|
| 277 |                         if( $monthcount % 2 == 0 ) {
 | 
|---|
| 278 |                             self::$tickPositions[$i++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 279 |                         }
 | 
|---|
| 280 |                         break;
 | 
|---|
| 281 |                     case DSUTILS_MONTH3:
 | 
|---|
| 282 |                         if( $aMinor ) {
 | 
|---|
| 283 |                             // Set minor tick at start of each month
 | 
|---|
| 284 |                             self::$minTickPositions[$j++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 285 |                         }
 | 
|---|
| 286 |                         // Major at every third month
 | 
|---|
| 287 |                         // Get timestamp of first hour of first day in each month
 | 
|---|
| 288 |                         if( $monthcount % 3 == 0 ) {
 | 
|---|
| 289 |                             self::$tickPositions[$i++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 290 |                         }
 | 
|---|
| 291 |                         break;
 | 
|---|
| 292 |                     case DSUTILS_MONTH6:
 | 
|---|
| 293 |                         if( $aMinor ) {
 | 
|---|
| 294 |                             // Set minor tick at start of each month
 | 
|---|
| 295 |                             self::$minTickPositions[$j++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 296 |                         }
 | 
|---|
| 297 |                         // Major at every third month
 | 
|---|
| 298 |                         // Get timestamp of first hour of first day in each month
 | 
|---|
| 299 |                         if( $monthcount % 6 == 0 ) {
 | 
|---|
| 300 |                             self::$tickPositions[$i++] = mktime(0,0,0,$m,1,$y);
 | 
|---|
| 301 |                         }
 | 
|---|
| 302 |                         break;
 | 
|---|
| 303 |                 }
 | 
|---|
| 304 |                 ++$m;
 | 
|---|
| 305 |                 ++$monthcount;
 | 
|---|
| 306 |             }
 | 
|---|
| 307 |             $m=1;
 | 
|---|
| 308 |         }
 | 
|---|
| 309 | 
 | 
|---|
| 310 |         // For the case where all dates are within the same month
 | 
|---|
| 311 |         // we want to make sure we have at least two ticks on the scale
 | 
|---|
| 312 |         // since the scale want work properly otherwise
 | 
|---|
| 313 |         if(self::$startmonth == self::$endmonth && self::$startyear == self::$endyear && $aType==1 ) {
 | 
|---|
| 314 |             self::$tickPositions[$i++] = mktime(0 ,0 ,0, self::$startmonth + 1, 1, self::$startyear);
 | 
|---|
| 315 |         }
 | 
|---|
| 316 | 
 | 
|---|
| 317 |         return array(self::$tickPositions,self::$minTickPositions);
 | 
|---|
| 318 |     }
 | 
|---|
| 319 | 
 | 
|---|
| 320 |     static function GetTicks($aData,$aType=1,$aMinor=false,$aEndPoints=false) {
 | 
|---|
| 321 |         $n = count($aData);
 | 
|---|
| 322 |         return self::GetTicksFromMinMax($aData[0],$aData[$n-1],$aType,$aMinor,$aEndPoints);
 | 
|---|
| 323 |     }
 | 
|---|
| 324 | 
 | 
|---|
| 325 |     static function GetAutoTicks($aMin,$aMax,$aMaxTicks=10,$aMinor=false) {
 | 
|---|
| 326 |         $diff = $aMax - $aMin;
 | 
|---|
| 327 |         $spd = 3600*24;
 | 
|---|
| 328 |         $spw = $spd*7;
 | 
|---|
| 329 |         $spm = $spd*30;
 | 
|---|
| 330 |         $spy = $spd*352;
 | 
|---|
| 331 | 
 | 
|---|
| 332 |         if( self::$iUseWeeks )
 | 
|---|
| 333 |         $w = 'W';
 | 
|---|
| 334 |         else
 | 
|---|
| 335 |         $w = 'd M';
 | 
|---|
| 336 | 
 | 
|---|
| 337 |         // Decision table for suitable scales
 | 
|---|
| 338 |         // First value: Main decision point
 | 
|---|
| 339 |         // Second value: Array of formatting depending on divisor for wanted max number of ticks. <divisor><formatting><format-string>,..
 | 
|---|
| 340 |         $tt = array(
 | 
|---|
| 341 |             array($spw, array(1,DSUTILS_DAY1,'d M',2,DSUTILS_DAY2,'d M',-1,DSUTILS_DAY4,'d M')),
 | 
|---|
| 342 |             array($spm, array(1,DSUTILS_DAY1,'d M',2,DSUTILS_DAY2,'d M',4,DSUTILS_DAY4,'d M',7,DSUTILS_WEEK1,$w,-1,DSUTILS_WEEK2,$w)),
 | 
|---|
| 343 |             array($spy, array(1,DSUTILS_DAY1,'d M',2,DSUTILS_DAY2,'d M',4,DSUTILS_DAY4,'d M',7,DSUTILS_WEEK1,$w,14,DSUTILS_WEEK2,$w,30,DSUTILS_MONTH1,'M',60,DSUTILS_MONTH2,'M',-1,DSUTILS_MONTH3,'M')),
 | 
|---|
| 344 |             array(-1, array(30,DSUTILS_MONTH1,'M-Y',60,DSUTILS_MONTH2,'M-Y',90,DSUTILS_MONTH3,'M-Y',180,DSUTILS_MONTH6,'M-Y',352,DSUTILS_YEAR1,'Y',704,DSUTILS_YEAR2,'Y',-1,DSUTILS_YEAR5,'Y')));
 | 
|---|
| 345 | 
 | 
|---|
| 346 |         $ntt = count($tt);
 | 
|---|
| 347 |         $nd = floor($diff/$spd);
 | 
|---|
| 348 |         for($i=0; $i < $ntt; ++$i ) {
 | 
|---|
| 349 |             if( $diff <= $tt[$i][0] || $i==$ntt-1) {
 | 
|---|
| 350 |                 $t = $tt[$i][1];
 | 
|---|
| 351 |                 $n = count($t)/3;
 | 
|---|
| 352 |                 for( $j=0; $j < $n; ++$j ) {
 | 
|---|
| 353 |                     if( $nd/$t[3*$j] <= $aMaxTicks || $j==$n-1) {
 | 
|---|
| 354 |                         $type = $t[3*$j+1];
 | 
|---|
| 355 |                         $fs = $t[3*$j+2];
 | 
|---|
| 356 |                         list($tickPositions,$minTickPositions) = self::GetTicksFromMinMax($aMin,$aMax,$type,$aMinor);
 | 
|---|
| 357 |                         return array($fs,$tickPositions,$minTickPositions,$type);
 | 
|---|
| 358 |                     }
 | 
|---|
| 359 |                 }
 | 
|---|
| 360 |             }
 | 
|---|
| 361 |         }
 | 
|---|
| 362 |     }
 | 
|---|
| 363 | 
 | 
|---|
| 364 |     static function GetTicksFromMinMax($aMin,$aMax,$aType,$aMinor=false,$aEndPoints=false) {
 | 
|---|
| 365 |         self::$starthour = date('G',$aMin);
 | 
|---|
| 366 |         self::$startmonth = date('n',$aMin);
 | 
|---|
| 367 |         self::$startday = date('j',$aMin);
 | 
|---|
| 368 |         self::$startyear = date('Y',$aMin);
 | 
|---|
| 369 |         self::$endmonth = date('n',$aMax);
 | 
|---|
| 370 |         self::$endyear = date('Y',$aMax);
 | 
|---|
| 371 |         self::$endday = date('j',$aMax);
 | 
|---|
| 372 |         self::$iMin = $aMin;
 | 
|---|
| 373 |         self::$iMax = $aMax;
 | 
|---|
| 374 | 
 | 
|---|
| 375 |         if( $aType <= DSUTILS_MONTH6 ) {
 | 
|---|
| 376 |             self::doMonthly($aType,$aMinor);
 | 
|---|
| 377 |         }
 | 
|---|
| 378 |         elseif( $aType <= DSUTILS_WEEK4 ) {
 | 
|---|
| 379 |             self::doWeekly($aType,$aMinor);
 | 
|---|
| 380 |         }
 | 
|---|
| 381 |         elseif( $aType <= DSUTILS_DAY4 ) {
 | 
|---|
| 382 |             self::doDaily($aType,$aMinor);
 | 
|---|
| 383 |         }
 | 
|---|
| 384 |         elseif( $aType <= DSUTILS_YEAR5 ) {
 | 
|---|
| 385 |             self::doYearly($aType,$aMinor);
 | 
|---|
| 386 |         }
 | 
|---|
| 387 |         else {
 | 
|---|
| 388 |             JpGraphError::RaiseL(24003);
 | 
|---|
| 389 |         }
 | 
|---|
| 390 |         // put a label at the very left data pos
 | 
|---|
| 391 |         if( $aEndPoints ) {
 | 
|---|
| 392 |             $tickPositions[$i++] = $aData[0];
 | 
|---|
| 393 |         }
 | 
|---|
| 394 | 
 | 
|---|
| 395 |         // put a label at the very right data pos
 | 
|---|
| 396 |         if( $aEndPoints ) {
 | 
|---|
| 397 |             $tickPositions[$i] = $aData[$n-1];
 | 
|---|
| 398 |         }
 | 
|---|
| 399 | 
 | 
|---|
| 400 |         return array(self::$tickPositions,self::$minTickPositions);
 | 
|---|
| 401 |     }
 | 
|---|
| 402 | }
 | 
|---|
| 403 | 
 | 
|---|
| 404 | //=============================================================================
 | 
|---|
| 405 | // Class ReadFileData
 | 
|---|
| 406 | //=============================================================================
 | 
|---|
| 407 | Class ReadFileData {
 | 
|---|
| 408 |     //----------------------------------------------------------------------------
 | 
|---|
| 409 |     // Desciption:
 | 
|---|
| 410 |     // Read numeric data from a file.
 | 
|---|
| 411 |     // Each value should be separated by either a new line or by a specified
 | 
|---|
| 412 |     // separator character (default is ',').
 | 
|---|
| 413 |     // Before returning the data each value is converted to a proper float
 | 
|---|
| 414 |     // value. The routine is robust in the sense that non numeric data in the
 | 
|---|
| 415 |     // file will be discarded.
 | 
|---|
| 416 |     //
 | 
|---|
| 417 |     // Returns:
 | 
|---|
| 418 |     // The number of data values read on success, FALSE on failure
 | 
|---|
| 419 |     //----------------------------------------------------------------------------
 | 
|---|
| 420 |     static function FromCSV($aFile,&$aData,$aSepChar=',',$aMaxLineLength=1024) {
 | 
|---|
| 421 |         $rh = @fopen($aFile,'r');
 | 
|---|
| 422 |         if( $rh === false ) {
 | 
|---|
| 423 |                 return false;
 | 
|---|
| 424 |         }
 | 
|---|
| 425 |         $tmp = array();
 | 
|---|
| 426 |         $lineofdata = fgetcsv($rh, 1000, ',');
 | 
|---|
| 427 |         while ( $lineofdata !== FALSE) {
 | 
|---|
| 428 |             $tmp = array_merge($tmp,$lineofdata);
 | 
|---|
| 429 |             $lineofdata = fgetcsv($rh, $aMaxLineLength, $aSepChar);
 | 
|---|
| 430 |         }
 | 
|---|
| 431 |         fclose($rh);
 | 
|---|
| 432 | 
 | 
|---|
| 433 |         // Now make sure that all data is numeric. By default
 | 
|---|
| 434 |         // all data is read as strings
 | 
|---|
| 435 |         $n = count($tmp);
 | 
|---|
| 436 |         $aData = array();
 | 
|---|
| 437 |         $cnt=0;
 | 
|---|
| 438 |         for($i=0; $i < $n; ++$i) {
 | 
|---|
| 439 |             if( $tmp[$i] !== "" ) {
 | 
|---|
| 440 |                 $aData[$cnt++] = floatval($tmp[$i]);
 | 
|---|
| 441 |             }
 | 
|---|
| 442 |         }
 | 
|---|
| 443 |         return $cnt;
 | 
|---|
| 444 |     }
 | 
|---|
| 445 | 
 | 
|---|
| 446 |     //----------------------------------------------------------------------------
 | 
|---|
| 447 |     // Desciption:
 | 
|---|
| 448 |     // Read numeric data from a file.
 | 
|---|
| 449 |     // Each value should be separated by either a new line or by a specified
 | 
|---|
| 450 |     // separator character (default is ',').
 | 
|---|
| 451 |     // Before returning the data each value is converted to a proper float
 | 
|---|
| 452 |     // value. The routine is robust in the sense that non numeric data in the
 | 
|---|
| 453 |     // file will be discarded.
 | 
|---|
| 454 |     //
 | 
|---|
| 455 |     // Options:
 | 
|---|
| 456 |     // 'separator'     => ',',
 | 
|---|
| 457 |     // 'enclosure'     => '"',
 | 
|---|
| 458 |     // 'readlength'    => 1024,
 | 
|---|
| 459 |     // 'ignore_first'  => false,
 | 
|---|
| 460 |     // 'first_as_key'  => false
 | 
|---|
| 461 |     // 'escape'        => '\',   # PHP >= 5.3 only
 | 
|---|
| 462 |     //
 | 
|---|
| 463 |     // Returns:
 | 
|---|
| 464 |     // The number of lines read on success, FALSE on failure
 | 
|---|
| 465 |     //----------------------------------------------------------------------------
 | 
|---|
| 466 |     static function FromCSV2($aFile, &$aData, $aOptions = array()) {
 | 
|---|
| 467 |         $aDefaults = array(
 | 
|---|
| 468 |             'separator'     => ',',
 | 
|---|
| 469 |             'enclosure'     => chr(34),
 | 
|---|
| 470 |             'escape'        => chr(92),
 | 
|---|
| 471 |             'readlength'    => 1024,
 | 
|---|
| 472 |             'ignore_first'  => false,
 | 
|---|
| 473 |             'first_as_key'  => false
 | 
|---|
| 474 |             );
 | 
|---|
| 475 | 
 | 
|---|
| 476 |         $aOptions = array_merge(
 | 
|---|
| 477 |             $aDefaults, is_array($aOptions) ? $aOptions : array());
 | 
|---|
| 478 | 
 | 
|---|
| 479 |         if( $aOptions['first_as_key'] ) {
 | 
|---|
| 480 |             $aOptions['ignore_first'] =  true;
 | 
|---|
| 481 |         }
 | 
|---|
| 482 | 
 | 
|---|
| 483 |         $rh = @fopen($aFile, 'r');
 | 
|---|
| 484 | 
 | 
|---|
| 485 |         if( $rh === false ) {
 | 
|---|
| 486 |             return false;
 | 
|---|
| 487 |         }
 | 
|---|
| 488 | 
 | 
|---|
| 489 |         $aData  = array();
 | 
|---|
| 490 |         $aLine  = fgetcsv($rh,
 | 
|---|
| 491 |                           $aOptions['readlength'],
 | 
|---|
| 492 |                           $aOptions['separator'],
 | 
|---|
| 493 |                           $aOptions['enclosure']
 | 
|---|
| 494 |                           /*, $aOptions['escape']     # PHP >= 5.3 only */
 | 
|---|
| 495 |                           );
 | 
|---|
| 496 | 
 | 
|---|
| 497 |         // Use numeric array keys for the columns by default
 | 
|---|
| 498 |         // If specified use first lines values as assoc keys instead
 | 
|---|
| 499 |         $keys = array_keys($aLine);
 | 
|---|
| 500 |         if( $aOptions['first_as_key'] ) {
 | 
|---|
| 501 |             $keys = array_values($aLine);
 | 
|---|
| 502 |         }
 | 
|---|
| 503 | 
 | 
|---|
| 504 |         $num_lines = 0;
 | 
|---|
| 505 |         $num_cols  = count($aLine);
 | 
|---|
| 506 | 
 | 
|---|
| 507 |         while ($aLine !== false) {
 | 
|---|
| 508 |             if( is_array($aLine) && count($aLine) != $num_cols ) {
 | 
|---|
| 509 |                 JpGraphError::RaiseL(24004);
 | 
|---|
| 510 |                 // 'ReadCSV2: Column count mismatch in %s line %d'
 | 
|---|
| 511 |             }
 | 
|---|
| 512 | 
 | 
|---|
| 513 |             // fgetcsv returns NULL for empty lines
 | 
|---|
| 514 |             if( !is_null($aLine) ) {
 | 
|---|
| 515 |                 $num_lines++;
 | 
|---|
| 516 | 
 | 
|---|
| 517 |                 if( !($aOptions['ignore_first'] && $num_lines == 1) && is_numeric($aLine[0]) ) {
 | 
|---|
| 518 |                     for( $i = 0; $i < $num_cols; $i++ ) {
 | 
|---|
| 519 |                         $aData[ $keys[$i] ][] = floatval($aLine[$i]);
 | 
|---|
| 520 |                     }
 | 
|---|
| 521 |                 }
 | 
|---|
| 522 |             }
 | 
|---|
| 523 | 
 | 
|---|
| 524 |             $aLine = fgetcsv($rh,
 | 
|---|
| 525 |                              $aOptions['readlength'],
 | 
|---|
| 526 |                              $aOptions['separator'],
 | 
|---|
| 527 |                              $aOptions['enclosure']
 | 
|---|
| 528 |                              /*, $aOptions['escape']     # PHP >= 5.3 only*/
 | 
|---|
| 529 |                 );
 | 
|---|
| 530 |         }
 | 
|---|
| 531 | 
 | 
|---|
| 532 |         fclose($rh);
 | 
|---|
| 533 | 
 | 
|---|
| 534 |         if( $aOptions['ignore_first'] ) {
 | 
|---|
| 535 |             $num_lines--;
 | 
|---|
| 536 |         }
 | 
|---|
| 537 | 
 | 
|---|
| 538 |         return $num_lines;
 | 
|---|
| 539 |     }
 | 
|---|
| 540 | 
 | 
|---|
| 541 |     // Read data from two columns in a plain text file
 | 
|---|
| 542 |     static function From2Col($aFile, $aCol1, $aCol2, $aSepChar=' ') {
 | 
|---|
| 543 |         $lines = @file($aFile,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
 | 
|---|
| 544 |         if( $lines === false ) {
 | 
|---|
| 545 |                 return false;
 | 
|---|
| 546 |         }
 | 
|---|
| 547 |         $s = '/[\s]+/';
 | 
|---|
| 548 |         if( $aSepChar == ',' ) {
 | 
|---|
| 549 |                         $s = '/[\s]*,[\s]*/';
 | 
|---|
| 550 |         }
 | 
|---|
| 551 |         elseif( $aSepChar == ';' ) {
 | 
|---|
| 552 |                         $s = '/[\s]*;[\s]*/';
 | 
|---|
| 553 |         }
 | 
|---|
| 554 |         foreach( $lines as $line => $datarow ) {
 | 
|---|
| 555 |                 $split = preg_split($s,$datarow);
 | 
|---|
| 556 |                 $aCol1[] = floatval(trim($split[0]));
 | 
|---|
| 557 |                 $aCol2[] = floatval(trim($split[1]));
 | 
|---|
| 558 |         }
 | 
|---|
| 559 | 
 | 
|---|
| 560 |         return count($lines);
 | 
|---|
| 561 |     }
 | 
|---|
| 562 | 
 | 
|---|
| 563 |     // Read data from one columns in a plain text file
 | 
|---|
| 564 |     static function From1Col($aFile, $aCol1) {
 | 
|---|
| 565 |         $lines = @file($aFile,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
 | 
|---|
| 566 |         if( $lines === false ) {
 | 
|---|
| 567 |                 return false;
 | 
|---|
| 568 |         }
 | 
|---|
| 569 |         foreach( $lines as $line => $datarow ) {
 | 
|---|
| 570 |                 $aCol1[] = floatval(trim($datarow));
 | 
|---|
| 571 |         }
 | 
|---|
| 572 | 
 | 
|---|
| 573 |         return count($lines);
 | 
|---|
| 574 |     }
 | 
|---|
| 575 | 
 | 
|---|
| 576 |     static function FromMatrix($aFile,$aSepChar=' ') {
 | 
|---|
| 577 |         $lines = @file($aFile,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
 | 
|---|
| 578 |         if( $lines === false ) {
 | 
|---|
| 579 |                 return false;
 | 
|---|
| 580 |         }
 | 
|---|
| 581 |         $mat = array();
 | 
|---|
| 582 |         $reg = '/'.$aSepChar.'/';
 | 
|---|
| 583 |         foreach( $lines as $line => $datarow ) {
 | 
|---|
| 584 |                 $row = preg_split($reg,trim($datarow));
 | 
|---|
| 585 |                 foreach ($row as $key => $cell ) {
 | 
|---|
| 586 |                         $row[$key] = floatval(trim($cell));
 | 
|---|
| 587 |                 }
 | 
|---|
| 588 |                 $mat[] = $row;
 | 
|---|
| 589 |         }
 | 
|---|
| 590 |         return $mat;
 | 
|---|
| 591 |     }
 | 
|---|
| 592 | 
 | 
|---|
| 593 | 
 | 
|---|
| 594 | }
 | 
|---|
| 595 | 
 | 
|---|
| 596 | define('__LR_EPSILON', 1.0e-8);
 | 
|---|
| 597 | //=============================================================================
 | 
|---|
| 598 | // Class LinearRegression
 | 
|---|
| 599 | //=============================================================================
 | 
|---|
| 600 | class LinearRegression {
 | 
|---|
| 601 |         private $ix=array(),$iy=array();
 | 
|---|
| 602 |         private $ib=0, $ia=0;
 | 
|---|
| 603 |         private $icalculated=false;
 | 
|---|
| 604 |         public $iDet=0, $iCorr=0, $iStdErr=0;
 | 
|---|
| 605 | 
 | 
|---|
| 606 |         public function __construct($aDataX,$aDataY) {
 | 
|---|
| 607 |                 if( count($aDataX) !== count($aDataY) ) {
 | 
|---|
| 608 |                         JpGraph::Raise('LinearRegression: X and Y data array must be of equal length.');
 | 
|---|
| 609 |                 }
 | 
|---|
| 610 |                 $this->ix = $aDataX;
 | 
|---|
| 611 |                 $this->iy = $aDataY;
 | 
|---|
| 612 |         }
 | 
|---|
| 613 | 
 | 
|---|
| 614 |         public function Calc() {
 | 
|---|
| 615 | 
 | 
|---|
| 616 |                 $this->icalculated = true;
 | 
|---|
| 617 | 
 | 
|---|
| 618 |                 $n = count($this->ix);
 | 
|---|
| 619 |                 $sx2 = 0 ;
 | 
|---|
| 620 |                 $sy2 = 0 ;
 | 
|---|
| 621 |                 $sxy = 0 ;
 | 
|---|
| 622 |                 $sx = 0 ;
 | 
|---|
| 623 |                 $sy = 0 ;
 | 
|---|
| 624 | 
 | 
|---|
| 625 |                 for( $i=0; $i < $n; ++$i ) {
 | 
|---|
| 626 |                         $sx2 += $this->ix[$i] * $this->ix[$i];
 | 
|---|
| 627 |                         $sy2 += $this->iy[$i] * $this->iy[$i];
 | 
|---|
| 628 |                         $sxy += $this->ix[$i] * $this->iy[$i];
 | 
|---|
| 629 |                         $sx += $this->ix[$i];
 | 
|---|
| 630 |                         $sy += $this->iy[$i];
 | 
|---|
| 631 |                 }
 | 
|---|
| 632 | 
 | 
|---|
| 633 |                 if( $n*$sx2 - $sx*$sx > __LR_EPSILON ) {
 | 
|---|
| 634 |                         $this->ib = ($n*$sxy - $sx*$sy) / ( $n*$sx2 - $sx*$sx );
 | 
|---|
| 635 |                         $this->ia = ( $sy - $this->ib*$sx ) / $n;
 | 
|---|
| 636 | 
 | 
|---|
| 637 |                         $sx = $this->ib * ( $sxy - $sx*$sy/$n );
 | 
|---|
| 638 |                         $sy2 = $sy2 - $sy*$sy/$n;
 | 
|---|
| 639 |                         $sy = $sy2 - $sx;
 | 
|---|
| 640 | 
 | 
|---|
| 641 |                         $this->iDet = $sx / $sy2;
 | 
|---|
| 642 |                         $this->iCorr = sqrt($this->iDet);
 | 
|---|
| 643 |                         if( $n > 2 ) {
 | 
|---|
| 644 |                                 $this->iStdErr = sqrt( $sy / ($n-2) );
 | 
|---|
| 645 |                         }
 | 
|---|
| 646 |                         else {
 | 
|---|
| 647 |                                 $this->iStdErr = NAN ;
 | 
|---|
| 648 |                         }
 | 
|---|
| 649 |                 }
 | 
|---|
| 650 |                 else {
 | 
|---|
| 651 |                         $this->ib = 0;
 | 
|---|
| 652 |                         $this->ia = 0;
 | 
|---|
| 653 |                 }
 | 
|---|
| 654 | 
 | 
|---|
| 655 |         }
 | 
|---|
| 656 | 
 | 
|---|
| 657 |         public function GetAB() {
 | 
|---|
| 658 |                 if( $this->icalculated == false )
 | 
|---|
| 659 |                         $this->Calc();
 | 
|---|
| 660 |                 return array($this->ia, $this->ib);
 | 
|---|
| 661 |         }
 | 
|---|
| 662 | 
 | 
|---|
| 663 |         public function GetStat() {
 | 
|---|
| 664 |                 if( $this->icalculated == false )
 | 
|---|
| 665 |                         $this->Calc();
 | 
|---|
| 666 |                 return array($this->iStdErr, $this->iCorr, $this->iDet);
 | 
|---|
| 667 |         }
 | 
|---|
| 668 | 
 | 
|---|
| 669 |         public function GetY($aMinX, $aMaxX, $aStep=1) {
 | 
|---|
| 670 |                 if( $this->icalculated == false )
 | 
|---|
| 671 |                         $this->Calc();
 | 
|---|
| 672 | 
 | 
|---|
| 673 |                 $yy = array();
 | 
|---|
| 674 |                 $i = 0;
 | 
|---|
| 675 |                 for( $x=$aMinX; $x <= $aMaxX; $x += $aStep ) {
 | 
|---|
| 676 |                         $xx[$i  ] = $x;
 | 
|---|
| 677 |                         $yy[$i++] = $this->ia + $this->ib * $x;
 | 
|---|
| 678 |                 }
 | 
|---|
| 679 | 
 | 
|---|
| 680 |                 return array($xx,$yy);
 | 
|---|
| 681 |         }
 | 
|---|
| 682 | 
 | 
|---|
| 683 | }
 | 
|---|
| 684 | 
 | 
|---|
| 685 | ?>
 | 
|---|