1<?php
2/**
3 * pData - Simplifying data population for pChart
4 * @copyright 2008 Jean-Damien POGOLOTTI
5 * @version 2.0
6 *
7 * http://pchart.sourceforge.net
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 1,2,3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23require_once dirname(__FILE__).'/DataDescription.php';
24require_once dirname(__FILE__).'/CSVImporter.php';
25
26class pData {
27	private $Data = array();
28	private $dataDescription = array();
29
30	/**
31	 * An entry for each series giving the maximum value in that
32	 * series, if we've previously calculated it
33	 */
34	private $seriesMinimums = array();
35
36	/**
37	 * An entry for each series giving the minimum value in that
38	 * series, if we've previously calculated it
39	 */
40	private $seriesMaximums = array();
41
42	public function __construct() {
43		$this->dataDescription = new DataDescription('Name',
44													 'number', 'number',
45													 null, null);
46	}
47
48	/**
49	 * Add a single point to the data set
50	 */
51	public function addPoint($value, $series = "Series1", $Description = "") {
52		if (is_array($value)) {
53			throw new InvalidArgumentException("Can't pass an array to addPoint()");
54		}
55
56		return $this->addPoints(array($value),
57								$series,
58								$Description);
59	}
60
61	/**
62	 * Add an array of one or more points to the data set.
63	 *
64	 * @param $Value If this is an associative array the key values
65	 * are ignored. The members of the array are added sequentially to
66	 * the data set, taking on auto-incremented ID values based on the
67	 * current state of the data set.
68	 */
69	public function addPoints(array $Value, $Serie = "Series1", $Description = "") {
70		$ID = 0;
71		for($i = 0; $i < count ( $this->Data ); $i ++) {
72			if (isset ( $this->Data [$i] [$Serie] )) {
73				$ID = $i + 1;
74			}
75		}
76
77		foreach ( $Value as $Val ) {
78			$this->Data [$ID] [$Serie] = $Val;
79			if ($Description != "") {
80				$this->Data[$ID]["Name"] = $Description;
81			}
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($this->seriesMinimums[$seriesName],
185						$valueSet[$seriesName]);
186			}
187		}
188
189		return $this->seriesMinimums[$seriesName];
190	}
191
192	/**
193	 * @brief Get the maximum data value in the specified series
194	 */
195	public function getSeriesMax($seriesName) {
196		$this->seriesMaximums[$seriesName] = $this->Data[0][$seriesName];
197
198		foreach ($this->Data as $valueSet) {
199			if (isset($valueSet[$seriesName])) {
200				$this->seriesMaximums[$seriesName] =
201					max($this->seriesMaximums[$seriesName],
202						$valueSet[$seriesName]);
203			}
204		}
205
206		return $this->seriesMaximums[$seriesName];
207	}
208
209	/**
210	 * Get the numeric X and Y values, for a given series.
211	 *
212	 * Ugly interface, but this is a step towards refactoring
213	 * duplicated code
214	 *
215	 * For some reason, the data set is assumed to start at (0, 0).
216	 *
217	 * @param[out] $xIn   Returns an array of numeric X values
218	 * @param[out] $yIn   Returns an array of Y values, corresponding
219	 *   to the array of X values. Non-numeric values are omitted
220	 *
221	 * @param $index Returns the number of values in the specified
222	 *   data set, including any non-numeric values (thus this is not
223	 *   necessarily equal to the size of the $xIn or $yIn arrays), minus
224	 *   one (to account for the bogus (0, 0) value added to the X and Y
225	 *   arrays?)
226	 *
227	 * @param $missing  Returns the X values for which no Y value is
228	 *   available. The missing keys form the keys of the $missing array,
229	 *   and the corresponding value in the associative array is always
230	 *   true
231	 *
232	 * @return Null
233	 */
234	public function getXYMap($colName, array &$xIn, array & $yIn, array & $missing, & $index) {
235		$xIn [0] = 0;
236		$yIn [0] = 0;
237
238		$index = 1;
239
240		foreach (array_keys($this->Data) as $Key) {
241			if (isset ( $this->Data[$Key] [$colName] )) {
242				$Value = $this->Data[$Key] [$colName];
243				$xIn [$index] = $index;
244				$yIn [$index] = $Value;
245				if (! is_numeric ( $Value )) {
246					$missing [$index] = TRUE;
247				}
248				$index ++;
249			}
250		}
251		$index --;
252	}
253}