1 | <?php |
||
2 | |||
3 | /** |
||
4 | * JPGraph v4.0.3 |
||
5 | */ |
||
6 | |||
7 | namespace Amenadiel\JpGraph\Graph; |
||
8 | |||
9 | use Amenadiel\JpGraph\Image; |
||
10 | use Amenadiel\JpGraph\Plot; |
||
11 | use Amenadiel\JpGraph\Text; |
||
12 | use Amenadiel\JpGraph\Util; |
||
13 | |||
14 | /** |
||
15 | * @class RadarGraph |
||
16 | * // Description: Main container for a radar graph |
||
17 | */ |
||
18 | class RadarGraph extends Graph |
||
19 | { |
||
20 | public $grid; |
||
21 | public $axis; |
||
22 | private $posx; |
||
23 | private $posy; |
||
24 | private $len; |
||
25 | private $axis_title; |
||
26 | |||
27 | public function __construct($width = 300, $height = 200, $cachedName = '', $timeout = 0, $inline = 1) |
||
28 | { |
||
29 | parent::__construct($width, $height, $cachedName, $timeout, $inline); |
||
30 | $this->posx = $width / 2; |
||
31 | $this->posy = $height / 2; |
||
32 | $this->len = min($width, $height) * 0.35; |
||
33 | $this->SetColor([255, 255, 255]); |
||
34 | $this->SetTickDensity(TICKD_NORMAL); |
||
35 | $this->SetScale('lin'); |
||
36 | $this->SetGridDepth(DEPTH_FRONT); |
||
37 | } |
||
38 | |||
39 | public function HideTickMarks($aFlag = true) |
||
40 | { |
||
41 | $this->axis->scale->ticks->SupressTickMarks($aFlag); |
||
42 | } |
||
43 | |||
44 | public function ShowMinorTickmarks($aFlag = true) |
||
45 | { |
||
46 | $this->yscale->ticks->SupressMinorTickMarks(!$aFlag); |
||
47 | } |
||
48 | |||
49 | public function SetScale($axtype, $ymin = 1, $ymax = 1, $dummy1 = null, $dumy2 = null) |
||
50 | { |
||
51 | if ($axtype != 'lin' && $axtype != 'log') { |
||
52 | Util\JpGraphError::RaiseL(18003, $axtype); |
||
53 | //("Illegal scale for radarplot ($axtype). Must be \"lin\" or \"log\""); |
||
54 | } |
||
55 | if ($axtype == 'lin') { |
||
56 | $this->yscale = new LinearScale($ymin, $ymax); |
||
57 | $this->yscale->ticks = new RadarLinearTicks(); |
||
58 | $this->yscale->ticks->SupressMinorTickMarks(); |
||
59 | } elseif ($axtype == 'log') { |
||
60 | $this->yscale = new LogScale($ymin, $ymax); |
||
61 | $this->yscale->ticks = new RadarLogTicks(); |
||
62 | } |
||
63 | |||
64 | $this->axis = new RadarAxis($this->img, $this->yscale); |
||
65 | $this->grid = new RadarGrid(); |
||
66 | } |
||
67 | |||
68 | public function SetSize($aSize) |
||
69 | { |
||
70 | if ($aSize < 0.1 || $aSize > 1) { |
||
71 | Util\JpGraphError::RaiseL(18004, $aSize); |
||
72 | //("Radar Plot size must be between 0.1 and 1. (Your value=$s)"); |
||
73 | } |
||
74 | $this->len = min($this->img->width, $this->img->height) * $aSize / 2; |
||
75 | } |
||
76 | |||
77 | public function SetPlotSize($aSize) |
||
78 | { |
||
79 | $this->SetSize($aSize); |
||
80 | } |
||
81 | |||
82 | public function SetTickDensity($densy = TICKD_NORMAL, $dummy1 = null) |
||
83 | { |
||
84 | $this->ytick_factor = 25; |
||
85 | switch ($densy) { |
||
86 | case TICKD_DENSE: |
||
87 | $this->ytick_factor = 12; |
||
88 | |||
89 | break; |
||
90 | case TICKD_NORMAL: |
||
91 | $this->ytick_factor = 25; |
||
92 | |||
93 | break; |
||
94 | case TICKD_SPARSE: |
||
95 | $this->ytick_factor = 40; |
||
96 | |||
97 | break; |
||
98 | case TICKD_VERYSPARSE: |
||
99 | $this->ytick_factor = 70; |
||
100 | |||
101 | break; |
||
102 | default: |
||
103 | Util\JpGraphError::RaiseL(18005, $densy); |
||
104 | //("RadarPlot Unsupported Tick density: $densy"); |
||
105 | } |
||
106 | } |
||
107 | |||
108 | public function SetPos($px, $py = 0.5) |
||
109 | { |
||
110 | $this->SetCenter($px, $py); |
||
111 | } |
||
112 | |||
113 | public function SetCenter($px, $py = 0.5) |
||
114 | { |
||
115 | if ($px >= 0 && $px <= 1) { |
||
116 | $this->posx = $this->img->width * $px; |
||
117 | } else { |
||
118 | $this->posx = $px; |
||
119 | } |
||
120 | if ($py >= 0 && $py <= 1) { |
||
121 | $this->posy = $this->img->height * $py; |
||
122 | } else { |
||
123 | $this->posy = $py; |
||
124 | } |
||
125 | } |
||
126 | |||
127 | public function SetColor($aColor) |
||
128 | { |
||
129 | $this->SetMarginColor($aColor); |
||
130 | } |
||
131 | |||
132 | public function SetTitles($aTitleArray) |
||
133 | { |
||
134 | $this->axis_title = $aTitleArray; |
||
135 | } |
||
136 | |||
137 | public function Add($aPlot) |
||
138 | { |
||
139 | if ($aPlot == null) { |
||
140 | Util\JpGraphError::RaiseL(25010); //("Graph::Add() You tried to add a null plot to the graph."); |
||
141 | } |
||
142 | if (is_array($aPlot) && safe_count($aPlot) > 0) { |
||
143 | $cl = $aPlot[0]; |
||
144 | } else { |
||
145 | $cl = $aPlot; |
||
146 | } |
||
147 | |||
148 | if ($cl instanceof Text\Text) { |
||
149 | $this->AddText($aPlot); |
||
150 | } elseif (($cl instanceof Plot\IconPlot)) { |
||
151 | $this->AddIcon($aPlot); |
||
152 | } else { |
||
153 | $this->plots[] = $aPlot; |
||
154 | } |
||
155 | } |
||
156 | |||
157 | public function GetPlotsYMinMax($aPlots) |
||
158 | { |
||
159 | $min = $aPlots[0]->Min(); |
||
160 | $max = $aPlots[0]->Max(); |
||
161 | foreach ($this->plots as $p) { |
||
162 | $max = max($max, $p->Max()); |
||
163 | $min = min($min, $p->Min()); |
||
164 | } |
||
165 | if ($min < 0) { |
||
166 | Util\JpGraphError::RaiseL(18006, $min); |
||
167 | //("Minimum data $min (Radar plots should only be used when all data points > 0)"); |
||
168 | } |
||
169 | |||
170 | return [$min, $max]; |
||
171 | } |
||
172 | |||
173 | public function StrokeIcons() |
||
174 | { |
||
175 | if ($this->iIcons != null) { |
||
176 | $n = safe_count($this->iIcons); |
||
177 | for ($i = 0; $i < $n; ++$i) { |
||
178 | $this->iIcons[$i]->Stroke($this->img); |
||
179 | } |
||
180 | } |
||
181 | } |
||
182 | |||
183 | public function StrokeTexts() |
||
184 | { |
||
185 | if ($this->texts != null) { |
||
186 | $n = safe_count($this->texts); |
||
187 | for ($i = 0; $i < $n; ++$i) { |
||
188 | $this->texts[$i]->Stroke($this->img); |
||
189 | } |
||
190 | } |
||
191 | } |
||
192 | |||
193 | // Stroke the Radar graph |
||
194 | public function Stroke($aStrokeFileName = '') |
||
195 | { |
||
196 | // If the filename is the predefined value = '_csim_special_' |
||
197 | // we assume that the call to stroke only needs to do enough |
||
198 | // to correctly generate the CSIM maps. |
||
199 | // We use this variable to skip things we don't strictly need |
||
200 | // to do to generate the image map to improve performance |
||
201 | // a best we can. Therefor you will see a lot of tests !$_csim in the |
||
202 | // code below. |
||
203 | $_csim = ($aStrokeFileName === _CSIM_SPECIALFILE); |
||
204 | |||
205 | // We need to know if we have stroked the plot in the |
||
206 | // GetCSIMareas. Otherwise the CSIM hasn't been generated |
||
207 | // and in the case of GetCSIM called before stroke to generate |
||
208 | // CSIM without storing an image to disk GetCSIM must call Stroke. |
||
209 | $this->iHasStroked = true; |
||
210 | |||
211 | $n = safe_count($this->plots); |
||
212 | // Set Y-scale |
||
213 | |||
214 | if (!$this->yscale->IsSpecified() && safe_count($this->plots) > 0) { |
||
215 | list($min, $max) = $this->GetPlotsYMinMax($this->plots); |
||
216 | $this->yscale->AutoScale($this->img, 0, $max, $this->len / $this->ytick_factor); |
||
217 | } elseif ($this->yscale->IsSpecified() && |
||
218 | ($this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified())) { |
||
219 | // The tick calculation will use the user suplied min/max values to determine |
||
220 | // the ticks. If auto_ticks is false the exact user specifed min and max |
||
221 | // values will be used for the scale. |
||
222 | // If auto_ticks is true then the scale might be slightly adjusted |
||
223 | // so that the min and max values falls on an even major step. |
||
224 | $min = $this->yscale->scale[0]; |
||
225 | $max = $this->yscale->scale[1]; |
||
226 | $this->yscale->AutoScale( |
||
227 | $this->img, |
||
228 | $min, |
||
229 | $max, |
||
230 | $this->len / $this->ytick_factor, |
||
231 | $this->yscale->auto_ticks |
||
232 | ); |
||
233 | } |
||
234 | |||
235 | // Set start position end length of scale (in absolute pixels) |
||
236 | $this->yscale->SetConstants($this->posx, $this->len); |
||
237 | |||
238 | // We need as many axis as there are data points |
||
239 | $nbrpnts = $this->plots[0]->GetCount(); |
||
240 | |||
241 | // If we have no titles just number the axis 1,2,3,... |
||
242 | if ($this->axis_title == null) { |
||
243 | for ($i = 0; $i < $nbrpnts; ++$i) { |
||
244 | $this->axis_title[$i] = $i + 1; |
||
245 | } |
||
246 | } elseif (safe_count($this->axis_title) < $nbrpnts) { |
||
247 | Util\JpGraphError::RaiseL(18007); |
||
248 | // ("Number of titles does not match number of points in plot."); |
||
249 | } |
||
250 | for ($i = 0; $i < $n; ++$i) { |
||
251 | if ($nbrpnts != $this->plots[$i]->GetCount()) { |
||
252 | Util\JpGraphError::RaiseL(18008); |
||
253 | //("Each radar plot must have the same number of data points."); |
||
254 | } |
||
255 | } |
||
256 | |||
257 | if (!$_csim) { |
||
258 | if ($this->background_image != '') { |
||
259 | $this->StrokeFrameBackground(); |
||
260 | } else { |
||
261 | $this->StrokeFrame(); |
||
262 | $this->StrokeBackgroundGrad(); |
||
263 | } |
||
264 | } |
||
265 | $astep = 2 * M_PI / $nbrpnts; |
||
266 | |||
267 | if (!$_csim) { |
||
268 | if ($this->iIconDepth == DEPTH_BACK) { |
||
269 | $this->StrokeIcons(); |
||
270 | } |
||
271 | |||
272 | // Prepare legends |
||
273 | for ($i = 0; $i < $n; ++$i) { |
||
274 | $this->plots[$i]->Legend($this); |
||
275 | } |
||
276 | $this->legend->Stroke($this->img); |
||
277 | $this->footer->Stroke($this->img); |
||
278 | } |
||
279 | |||
280 | if (!$_csim) { |
||
281 | if ($this->grid_depth == DEPTH_BACK) { |
||
282 | // Draw axis and grid |
||
283 | for ($i = 0, $a = M_PI / 2; $i < $nbrpnts; ++$i, $a += $astep) { |
||
284 | $this->axis->Stroke($this->posy, $a, $grid[$i], $this->axis_title[$i], $i == 0); |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||
285 | } |
||
286 | $this->grid->Stroke($this->img, $grid); |
||
287 | } |
||
288 | if ($this->iIconDepth == DEPTH_BACK) { |
||
289 | $this->StrokeIcons(); |
||
290 | } |
||
291 | } |
||
292 | |||
293 | // Plot points |
||
294 | $a = M_PI / 2; |
||
295 | for ($i = 0; $i < $n; ++$i) { |
||
296 | $this->plots[$i]->Stroke($this->img, $this->posy, $this->yscale, $a); |
||
297 | } |
||
298 | |||
299 | if (!$_csim) { |
||
300 | if ($this->grid_depth != DEPTH_BACK) { |
||
301 | // Draw axis and grid |
||
302 | for ($i = 0, $a = M_PI / 2; $i < $nbrpnts; ++$i, $a += $astep) { |
||
303 | $this->axis->Stroke($this->posy, $a, $grid[$i], $this->axis_title[$i], $i == 0); |
||
304 | } |
||
305 | $this->grid->Stroke($this->img, $grid); |
||
306 | } |
||
307 | |||
308 | $this->StrokeTitles(); |
||
309 | $this->StrokeTexts(); |
||
310 | if ($this->iIconDepth == DEPTH_FRONT) { |
||
311 | $this->StrokeIcons(); |
||
312 | } |
||
313 | } |
||
314 | |||
315 | // Should we do any final image transformation |
||
316 | if ($this->iImgTrans && !$_csim) { |
||
317 | $tform = new Image\ImgTrans($this->img->img); |
||
318 | $this->img->img = $tform->Skew3D( |
||
319 | $this->iImgTransHorizon, |
||
320 | $this->iImgTransSkewDist, |
||
321 | $this->iImgTransDirection, |
||
322 | $this->iImgTransHighQ, |
||
323 | $this->iImgTransMinSize, |
||
324 | $this->iImgTransFillColor, |
||
325 | $this->iImgTransBorder |
||
326 | ); |
||
327 | } |
||
328 | |||
329 | if (!$_csim) { |
||
330 | // If the filename is given as the special "__handle" |
||
331 | // then the image handler is returned and the image is NOT |
||
332 | // streamed back |
||
333 | if ($aStrokeFileName == _IMG_HANDLER) { |
||
334 | return $this->img->img; |
||
335 | } |
||
336 | // Finally stream the generated picture |
||
337 | $this->cache->PutAndStream($this->img, $this->cache_name, $this->inline, $aStrokeFileName); |
||
338 | } |
||
339 | } |
||
340 | } // @class |
||
341 |