4 * Copyright (C) 2000 Afan Ottenheimer. Released under
5 * the GPL and PHP licenses as stated in the the README file which
6 * should have been included with this document.
8 * This is an subclass for phplot.php and should only be
9 * called after phplot.ini has been called. This extends
10 * phplot by adding additional routines that can be used
11 * to modify the data arrays.
13 * Data must be a *numerical* array, this is enforced in SetDataValues()
16 require_once("phplot.php");
18 class PHPlot_Data extends PHPlot
23 function PHPlot_Data($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
25 if (! isset($this->img)) {
26 $this->PHPlot($which_width, $which_height, $which_output_file, $which_input_file);
31 * Will scale all data rows
32 * Maybe later I will do a function that only scales some rows
33 * if $even is TRUE, data will be scaled with "even" factors.
34 * \note Original code by Thiemo Nagel
36 function DoScaleData($even, $show_in_legend)
38 $offset = 0; // We use this not to read labels in text-data
40 if ($this->data_type == 'text-data') {
42 } elseif ($this->data_type != 'data-data') {
43 $this->DrawError('wrong data type!!');
47 // Determine maxima for each data row in array $max
48 // Put maximum of the maxima in $maxmax
50 for($i=0; $i < $this->num_data_rows; $i++) {
51 $rowsize = count($this->data[$i]);
52 for ($j=$offset; $j < $rowsize; $j++) {
53 if ($this->data[$i][$j] > @ $max[$j])
54 $max[$j] = $this->data[$i][$j];
55 if (@ $max[$j] > $maxmax)
60 // determine amplification factor $amplify
61 $end = count($max) + $offset;
62 for ($i=$offset; $i < $end; $i++) {
63 if ($max[$i] == 0 || $max[$i] == $maxmax) {
64 $amplify[$i] = 1; // no divide by zero
67 $amp = pow(10,round(log10($maxmax / $max[$i]))-1);
68 if ($amp * $max[$i] * 5 < $maxmax) {
70 } elseif ($amp * $max[$i] * 2 < $maxmax) {
74 $amp = $maxmax / $max[$i];
75 $digits = floor(log10($amp));
76 $amp = round($amp/pow(10,$digits-1))*pow(10,$digits-1);
80 if ($amplify[$i] != 1 && $show_in_legend)
81 @ $this->legend[$i] .= "*$amplify[$i]";
85 // On my machine, running 1000 iterations over 1000 rows of 12 elements each,
86 // the for loops were 43.2% faster (MBD)
87 for ($i = 0; $i < $this->num_data_rows; $i++) {
88 $rowsize = count($this->data[$i]);
89 for ($j=$offset; $j < $rowsize; $j++) {
90 $this->data[$i][$j] *= $amplify[$j];
94 //Re-Scale Vertical Ticks if not already set
95 if ( ! $this->y_tick_increment) {
96 $this->SetYTickIncrement() ;
100 } //function DoScaleData
104 * Computes a moving average of strength $interval for
105 * data row number $datarow, where 0 denotes the first
108 * \param int datarow Index of the row whereupon to make calculations
109 * \param int interval Number of elements to use in average ("strength")
110 * \param bool show Whether to tell about the moving average in the legend.
111 * \param string color Color for the line to be drawn. This color is darkened.
112 * Can be named or #RRGGBB.
113 * \param int width Width of the line to be drawn.
115 * \note Original idea by Theimo Nagel
117 function DoMovingAverage($datarow, $interval, $show=TRUE, $color=NULL, $width=NULL)
119 $off = 1; // Skip record #0 (data label)
123 if ($interval == 0) {
124 $this->DrawError('DoMovingAverage(): interval can\'t be 0');
128 if ($datarow >= $this->records_per_group) {
129 $this->DrawError("DoMovingAverage(): Data row out of bounds ($datarow >= $this->records_per_group)");
133 if ($this->data_type == 'text-data') {
134 // Ok. No need to set the offset to skip more records.
135 } elseif ($this->data_type == 'data-data') {
136 $off++; // first Y value at $data[][2]
138 $this->DrawError('DoMovingAverage(): wrong data type!!');
144 array_push($this->ndx_data_colors, $this->SetIndexDarkColor($color));
146 array_push($this->ndx_data_colors, $this->SetIndexDarkColor($this->data_colors[$datarow]));
150 array_push($this->line_widths, $width);
152 array_push($this->line_widths, $this->line_widths[$datarow] * 2);
156 $this->legend[$this->records_per_group-1] = "(MA[$datarow]:$interval)";
160 for ($i = 0; $i < $this->num_data_rows; $i++) {
161 $storage[$i % $interval] = @ $this->data[$i][$datarow];
162 $ma = array_sum($storage);
163 $ma /= count($storage);
164 array_push($this->data[$i], $ma); // Push the data onto the array
165 $this->num_recs[$i]++; // Tell the drawing functions it is there
167 $this->records_per_group++;
168 // $this->FindDataLimits();
170 } //function DoMovingAverage()
174 * Computes an exponentially smoothed moving average.
175 * @param int perc "smoothing percentage"
176 * FIXME!!! I haven't checked this.
178 function DoExponentialMovingAverage($datarow, $perc, $show_in_legend)
180 if ($this->data_type == 'text-data') {
182 } elseif ($this->data_type != 'data-data') {
183 $this->DrawError('DoWeightedMovingAverage(): wrong data type!!');
187 if ($show_in_legend) {
188 $this->legend[$datarow] .= " (MA: $interval)";
191 $storage[0] = $this->data[0][$datarow];
192 for ($i=1;$i < $this->num_data_rows; $i++) {
193 $storage[$i] = @ $storage[$i-1] + $perc * ($this->data[$i][$datarow] - $storage[$i-1]);
194 $ma = array_sum($storage);
195 $ma /= count($storage);
196 $this->data[$i][$datarow] = $ma;
199 } // function DoExponentialMovingAverage()
203 * Removes the DataSet of number $index
205 function DoRemoveDataSet($index)
208 if ($this->data_type == 'data-data') {
210 } elseif ($this->data_type != 'text-data') {
211 $this->DrawError('wrong data type!!');
216 foreach ($this->data as $key=>$val) {
217 foreach ($val as $key2=>$val2) {
218 if ($key2 >= $index) {
219 if (isset($this->data[$key][$key2+1])) {
220 $this->data[$key][$key2] = $this->data[$key][$key2+1];
222 unset($this->data[$key][$key2]);
227 } // function DoRemoveDataSet
231 * Computes row x divided by row y, stores the result in row x
234 function DoDivision($x,$y)
237 if ($this->data_type == 'data-data') {
239 } elseif ($this->data_type != 'text-data') {
240 $this->DrawError('wrong data type!!');
244 $x += $offset; $y += $offset;
246 while (list($key, $val) = each($this->data)) {
247 if ($this->data[$key][$y] == 0) {
248 $this->data[$key][$x] = 0;
250 $this->data[$key][$x] /= $this->data[$key][$y];
254 $this->DoRemoveDataSet($y-$offset);
255 } // function DoDivision
257 } // class PHPlot_Data extends PHPlot