PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.17
E2Pdf – Export Pdf Tool for WordPress v1.32.17
1.32.49 1.32.48 1.32.43 1.32.40 1.32.34 1.32.32 1.32.31 1.32.26 1.32.22 1.32.23 1.32.18 1.32.17 1.32.15 trunk 1.00.00 1.00.13 1.01.01 1.02.02 1.03.07 1.04.07 1.05.03 1.06.02 1.07.11 1.08.00 1.08.06 All 72 releases
e2pdf / vendors / svggraph / Histogram.php

Histogram.php in E2Pdf – Export Pdf Tool for WordPress 1.32.17, at vendors/svggraph/Histogram.php

251 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright (C) 2015-2022 Graham Breach
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19 * For more information, please contact <graham@goat1000.com>
20 */
21
22 namespace Goat1000\SVGGraph;
23
24 class Histogram extends BarGraph {
25
26 private $scaling = 1;
27 private $increment = 1;
28
29 public function __construct($w, $h, array $settings, array $fixed_settings = [])
30 {
31 $fs = [
32 'repeated_keys' => 'accept',
33 'label_centre' => false,
34
35 // disable datetime conversion
36 'datetime_keys' => false,
37 ];
38 $fs = array_merge($fs, $fixed_settings);
39 parent::__construct($w, $h, $settings, $fs);
40 }
41
42 /**
43 * Process the values
44 */
45 public function values($values)
46 {
47 if(!empty($values)) {
48 parent::values($values);
49 if($this->values->error)
50 return;
51 $values = [];
52
53 // find min, max, strip out nulls
54 $min = $max = null;
55 $dataset = $this->getOption(['dataset', 0], 0);
56 foreach($this->values[$dataset] as $item) {
57 if($item->value !== null) {
58 if(!is_numeric($item->value)) {
59 $this->values->error = 'Non-numeric value';
60 return;
61 }
62
63 if($min === null || $item->value < $min)
64 $min = $item->value;
65 if($max === null || $item->value > $max)
66 $max = $item->value;
67 $values[] = $item->value;
68 }
69 }
70
71 // calculate or clean up increment
72 $inc = $this->getOption('increment');
73 if($inc <= 0) {
74 $diff = $max - $min;
75 if($diff <= 0) {
76 $inc = 1;
77 } else {
78 $scale = floor(log10($diff));
79 $inc = pow(10, $scale);
80 $d1 = $diff / $inc;
81 if(($inc != 1 || !is_integer($diff)) && $d1 < 4) {
82 if($d1 < 3)
83 $inc *= 0.2;
84 else
85 $inc *= 0.5;
86 }
87 }
88 }
89
90 // need to scale if $inc not an integer
91 $s = 1;
92 while($inc < 1 || ($inc != floor($inc))) {
93 $s *= 10;
94 $inc *= 10;
95 }
96 if($s > 1) {
97 $this->scaling = $s;
98 $max *= $s;
99 $min *= $s;
100 $diff = $max - $min;
101 }
102 $this->increment = $inc;
103
104 // prefill the map with nulls
105 $map = [];
106 $start = $this->interval($min);
107 $end = $this->interval($max, true) + $this->increment / 2;
108
109 Number::setup($this->getOption('precision'), $this->getOption('decimal'),
110 $this->getOption('thousands'));
111 for($i = $start; $i < $end; $i += $this->increment) {
112 $key = (int)$i;
113 $map[$key] = 0;
114 }
115
116 foreach($values as $val) {
117 $val *= $this->scaling;
118 $k = (int)$this->interval($val);
119 if(!array_key_exists($k, $map))
120 $map[$k] = 1;
121 else
122 $map[$k]++;
123 }
124
125 if($this->getOption('percentage')) {
126 $total = count($values);
127 $pmap = [];
128 foreach($map as $k => $v)
129 $pmap[$k] = 100 * $v / $total;
130 $values = $pmap;
131 } else {
132 $values = $map;
133 }
134
135 // turn into structured data
136 $new_values = [];
137 foreach($values as $k => $v)
138 $new_values[] = [$k, $v];
139 $values = $new_values;
140
141 // make structure use the same dataset number
142 $structure = [ 'key' => 0, 'value' => 1 ];
143 if($dataset !== 0) {
144 $structure['value'] = array_fill(0, $dataset, 2);
145 $structure['value'][$dataset] = 1;
146 }
147
148 $this->setOption('structure', $structure);
149 $this->setOption('structured_data', true);
150
151 // set up options to make bar graph class draw the histogram properly
152 $this->setOption('minimum_units_y', 1);
153 $this->setOption('subdivision_h', $this->increment); // no subdiv below bar size
154 $this->setOption('grid_division_h', $this->increment); //max($increment, $this->grid_division_h));
155
156 $amh = $this->getOption('axis_min_h');
157 if(empty($amh))
158 $this->setOption('axis_min_h', $start);
159 if($this->scaling !== 1) {
160 $this->setOption('axis_text_callback_x', function($v) {
161 $s = $this->scaling;
162 $p = log10($this->scaling) + 1;
163 $n = new Number($v / $s);
164 return $n->format(null, $p);
165 });
166 }
167 }
168 parent::values($values);
169 }
170
171 /**
172 * Returns the start (or next) interval for a value
173 */
174 public function interval($value, $next = false)
175 {
176 $n = floor($value / $this->increment);
177 if($next)
178 ++$n;
179 return $n * $this->increment;
180 }
181
182 /**
183 * Sets up the colour class with corrected number of colours
184 */
185 protected function colourSetup($count, $datasets = null, $reverse = false)
186 {
187 // $count is off by 1 because the divisions are numbered
188 return parent::colourSetup($count - 1, $datasets, $reverse);
189 }
190
191 /**
192 * Override because of the shifted numbering
193 */
194 protected function gridPosition($item, $ikey)
195 {
196 $position = null;
197 $zero = -0.01; // catch values close to 0
198 $axis = $this->x_axes[$this->main_x_axis];
199 $offset = $axis->position($item->key);
200 $g_limit = $this->g_width - ($axis->unit() / 2);
201 if($offset >= $zero && floor($offset) <= $g_limit)
202 $position = $this->pad_left + $offset;
203
204 return $position;
205 }
206
207 /**
208 * Returns the width of a bar
209 */
210 protected function barWidth()
211 {
212 $bar_width = $this->getOption('bar_width');
213 if(is_numeric($bar_width) && $bar_width >= 1)
214 return $bar_width;
215 $unit_w = $this->increment *
216 $this->x_axes[$this->main_x_axis]->unit();
217 return max(1, $unit_w - $this->getOption('bar_space'));
218 }
219
220 /**
221 * Returns the space before a bar
222 */
223 protected function barSpace($bar_width)
224 {
225 $uwidth = $this->increment *
226 $this->x_axes[$this->main_x_axis]->unit();
227 return max(0, ($uwidth - $bar_width) / 2);
228 }
229
230 /**
231 * Override to prevent drawing an entry past the last bar
232 */
233 protected function setLegendEntry($dataset, $index, $item, $style_info)
234 {
235 // the last entry is a blank to wangle the numbering
236 if($item->key >= $this->getMaxKey())
237 return;
238 parent::setLegendEntry($dataset, $index, $item, $style_info);
239 }
240
241 /**
242 * Override to pass in the modified Average class to use
243 */
244 protected function calcAverages($cls = 'Goat1000\SVGGraph\HistogramAverage')
245 {
246 return parent::calcAverages('Goat1000\SVGGraph\HistogramAverage');
247 }
248
249 }
250
251