1<?php
2/**
3 * pData - Simplifying data population for pChart
4 *
5 * @copyright 2008 Jean-Damien POGOLOTTI
6 * @version 2.0
7 *
8 * http://pchart.sourceforge.net
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 1,2,3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24
25class pData {
26    private $Data = array();
27    private $dataDescription = array();
28
29    /**
30     * An entry for each series giving the maximum value in that
31     * series, if we've previously calculated it
32     */
33    private $seriesMinimums = array();
34
35    /**
36     * An entry for each series giving the minimum value in that
37     * series, if we've previously calculated it
38     */
39    private $seriesMaximums = array();
40
41    public function __construct() {
42        $this->dataDescription = new DataDescription('Name',
43                                                     'number', 'number',
44                                                     null, null);
45    }
46
47    /**
48     * Add a single point to the data set
49     */
50    public function addPoint($value, $series = "Series1", $Description = "") {
51        if(is_array($value)) {
52            throw new InvalidArgumentException("Can't pass an array to addPoint()");
53        }
54
55        return $this->addPoints(
56            array($value),
57            $series,
58            $Description
59        );
60    }
61
62    /**
63     * Add an array of one or more points to the data set.
64     *
65     * @param $Value If this is an associative array the key values
66     * are ignored. The members of the array are added sequentially to
67     * the data set, taking on auto-incremented ID values based on the
68     * current state of the data set.
69     */
70    public function addPoints(array $Value, $Serie = "Series1", $Description = "") {
71        $ID = 0;
72        for($i = 0; $i < count($this->Data); $i++) {
73            if(isset ($this->Data [$i] [$Serie])) {
74                $ID = $i + 1;
75            }
76        }
77
78        foreach($Value as $Val) {
79            $this->Data [$ID] [$Serie] = $Val;
80            if($Description != "") {
81                $this->Data[$ID]["Name"] = $Description;
82            } elseif(!isset ($this->Data [$ID] ["Name"])) {
83                $this->Data [$ID] ["Name"] = $ID;
84            }
85            $ID++;
86        }
87    }
88
89    public function addSeries($SerieName = "Series1") {
90        if(!isset($this->dataDescription->values)) {
91            $this->dataDescription->values[] = $SerieName;
92        } else {
93            $Found = FALSE;
94            foreach($this->dataDescription->values as $Value)
95                if($Value == $SerieName) {
96                    $Found = TRUE;
97                }
98
99            if(!$Found)
100                $this->dataDescription->values[] = $SerieName;
101        }
102    }
103
104    public function addAllSeries() {
105        unset($this->dataDescription->values);
106
107        if(isset ($this->Data [0])) {
108            foreach(array_keys($this->Data [0]) as $Key) {
109                if($Key != "Name") {
110                    $this->dataDescription->values[] = $Key;
111                }
112            }
113        }
114    }
115
116    public function removeSeries($SerieName = "Series1") {
117        if(!isset($this->dataDescription->values))
118            return;
119
120        foreach($this->dataDescription->values as $key => $Value) {
121            if($Value == $SerieName)
122                unset ($this->dataDescription->values[$key]);
123        }
124    }
125
126    public function setAbscissaLabelSeries($seriesName = "Name") {
127        $this->dataDescription->setPosition($seriesName);
128    }
129
130    public function setSeriesName($Name, $SeriesName = "Series1") {
131        $this->dataDescription->description[$SeriesName] = $Name;
132    }
133
134    public function setXAxisName($Name) {
135        $this->dataDescription->setXAxisName($Name);
136    }
137
138    public function setYAxisName($Name) {
139        $this->dataDescription->setYAxisName($Name);
140    }
141
142    public function setSeriesSymbol($Name, $Symbol) {
143        $this->dataDescription->seriesSymbols[$Name] = $Symbol;
144    }
145
146    /**
147     * @param unknown_type $SerieName
148     */
149    public function removeSeriesName($SerieName) {
150        if(isset ($this->dataDescription->description[$SerieName]))
151            unset ($this->dataDescription->description[$SerieName]);
152    }
153
154    public function removeAllSeries() {
155        $this->dataDescription->values = array();
156    }
157
158    public function getData() {
159        return $this->Data;
160    }
161
162    public function getDataDescription() {
163        return $this->dataDescription;
164    }
165
166    /**
167     * @brief Get the minimum data value in the specified series
168     */
169    public function getSeriesMin($seriesName) {
170        if(isset($this->seriesMinimums[$seriesName])) {
171            return $this->seriesMinimums[$seriesName];
172        }
173
174        /**
175         * @todo This algorithm assumes that the data set contains a
176         * value at index 0 for the specified series - but this is the
177         * way it's always worked.
178         */
179        $this->seriesMinimums[$seriesName] = $this->Data[0][$seriesName];
180
181        foreach($this->Data as $valueSet) {
182            if(isset($valueSet[$seriesName])) {
183                $this->seriesMinimums[$seriesName] =
184                    min(
185                        $this->seriesMinimums[$seriesName],
186                        $valueSet[$seriesName]
187                    );
188            }
189        }
190
191        return $this->seriesMinimums[$seriesName];
192    }
193
194    /**
195     * @brief Get the maximum data value in the specified series
196     */
197    public function getSeriesMax($seriesName) {
198        $this->seriesMaximums[$seriesName] = $this->Data[0][$seriesName];
199
200        foreach($this->Data as $valueSet) {
201            if(isset($valueSet[$seriesName])) {
202                $this->seriesMaximums[$seriesName] =
203                    max(
204                        $this->seriesMaximums[$seriesName],
205                        $valueSet[$seriesName]
206                    );
207            }
208        }
209
210        return $this->seriesMaximums[$seriesName];
211    }
212
213    /**
214     * Get the numeric X and Y values, for a given series.
215     *
216     * Ugly interface, but this is a step towards refactoring
217     * duplicated code
218     *
219     * For some reason, the data set is assumed to start at (0, 0).
220     *
221     * @param[out] $xIn   Returns an array of numeric X values
222     * @param[out] $yIn   Returns an array of Y values, corresponding
223     *   to the array of X values. Non-numeric values are omitted
224     *
225     * @param $index Returns the number of values in the specified
226     *   data set, including any non-numeric values (thus this is not
227     *   necessarily equal to the size of the $xIn or $yIn arrays), minus
228     *   one (to account for the bogus (0, 0) value added to the X and Y
229     *   arrays?)
230     *
231     * @param $missing  Returns the X values for which no Y value is
232     *   available. The missing keys form the keys of the $missing array,
233     *   and the corresponding value in the associative array is always
234     *   true
235     *
236     * @return Null
237     */
238    public function getXYMap($colName, array &$xIn, array & $yIn, array & $missing, & $index) {
239        $xIn [0] = 0;
240        $yIn [0] = 0;
241
242        $index = 1;
243
244        foreach(array_keys($this->Data) as $Key) {
245            if(isset ($this->Data[$Key] [$colName])) {
246                $Value        = $this->Data[$Key] [$colName];
247                $xIn [$index] = $index;
248                $yIn [$index] = $Value;
249                if(!is_numeric($Value)) {
250                    $missing [$index] = TRUE;
251                }
252                $index++;
253            }
254        }
255        $index--;
256    }
257}