1<?php 2 3namespace CpChart\Chart; 4 5use CpChart\Data; 6use CpChart\Image; 7 8/** 9 * Radar - class to draw radar charts 10 * 11 * Version : 2.1.4 12 * Made by : Jean-Damien POGOLOTTI 13 * Last Update : 19/01/2014 14 * 15 * This file can be distributed under the license you can find at : 16 * 17 * http://www.pchart.net/license 18 * 19 * You can find the whole class documentation on the pChart web site. 20 */ 21class Radar 22{ 23 /** 24 * @var Image 25 */ 26 public $pChartObject; 27 28 /** 29 * Draw a radar chart 30 * @param Image $Object 31 * @param Data $Values 32 * @param array $Format 33 */ 34 public function drawRadar(Image $Object, Data $Values, array $Format = []) 35 { 36 $this->pChartObject = $Object; 37 38 $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; 39 $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; 40 $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; 41 $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; 42 $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; 43 $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : 0; 44 $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true; 45 $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; 46 $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true; 47 $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true; 48 $AxisFontName = isset($Format["AxisFontName"]) ? $Format["AxisFontName"] : $this->pChartObject->FontName; 49 $AxisFontSize = isset($Format["AxisFontSize"]) ? $Format["AxisFontSize"] : $this->pChartObject->FontSize; 50 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false; 51 $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true; 52 $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName 53 ; 54 $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize 55 ; 56 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; 57 $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; 58 $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; 59 $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; 60 $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; 61 $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; 62 $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; 63 $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; 64 $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; 65 $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; 66 $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; 67 $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; 68 $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; 69 $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; 70 $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; 71 $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null; 72 $Layout = isset($Format["Layout"]) ? $Format["Layout"] : RADAR_LAYOUT_STAR; 73 $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; 74 $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; 75 $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true; 76 $SkipLabels = isset($Format["SkipLabels"]) ? $Format["SkipLabels"] : 1; 77 $LabelMiddle = isset($Format["LabelMiddle"]) ? $Format["LabelMiddle"] : false; 78 $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true; 79 $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; 80 $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; 81 $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; 82 $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; 83 $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; 84 $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; 85 $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true; 86 $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; 87 $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; 88 $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true; 89 $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : true; 90 $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false; 91 $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : 40; 92 $FontSize = $Object->FontSize; 93 $X1 = $Object->GraphAreaX1; 94 $Y1 = $Object->GraphAreaY1; 95 $X2 = $Object->GraphAreaX2; 96 $Y2 = $Object->GraphAreaY2; 97 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; 98 99 /* Cancel default tick length if ticks not enabled */ 100 if ($DrawTicks == false) { 101 $TicksLength = 0; 102 } 103 104 /* Data Processing */ 105 $Data = $Values->getData(); 106 $Palette = $Values->getPalette(); 107 108 /* Catch the number of required axis */ 109 $LabelSerie = $Data["Abscissa"]; 110 if ($LabelSerie) { 111 $Points = count($Data["Series"][$LabelSerie]["Data"]); 112 } else { 113 $Points = 0; 114 foreach ($Data["Series"] as $SerieName => $DataArray) { 115 if (count($DataArray["Data"]) > $Points) { 116 $Points = count($DataArray["Data"]); 117 } 118 } 119 } 120 121 /* Draw the axis */ 122 $CenterX = ($X2 - $X1) / 2 + $X1; 123 $CenterY = ($Y2 - $Y1) / 2 + $Y1; 124 125 $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2); 126 if ($WriteLabels) { 127 $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; 128 } 129 /* Determine the scale if set to automatic */ 130 if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) { 131 if ($FixedMax != VOID) { 132 $Max = $FixedMax; 133 } else { 134 $Max = 0; 135 foreach ($Data["Series"] as $SerieName => $DataArray) { 136 if ($SerieName != $LabelSerie) { 137 if (max($DataArray["Data"]) > $Max) { 138 $Max = max($DataArray["Data"]); 139 } 140 } 141 } 142 } 143 $MaxSegments = $EdgeHeight / 20; 144 $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]); 145 146 $Segments = $Scale["Rows"]; 147 $SegmentHeight = $Scale["RowHeight"]; 148 } 149 150 if ($LabelMiddle && $SkipLabels == 1) { 151 $Axisoffset = (360 / $Points) / 2; 152 } elseif ($LabelMiddle && $SkipLabels != 1) { 153 $Axisoffset = (360 / ($Points / $SkipLabels)) / 2; 154 } elseif (!$LabelMiddle) { 155 $Axisoffset = 0; 156 } 157 158 /* Background processing */ 159 if ($DrawBackground) { 160 $RestoreShadow = $Object->Shadow; 161 $Object->Shadow = false; 162 163 if ($BackgroundGradient == null) { 164 if ($Layout == RADAR_LAYOUT_STAR) { 165 $Color = [ 166 "R" => $BackgroundR, 167 "G" => $BackgroundG, 168 "B" => $BackgroundB, 169 "Alpha" => $BackgroundAlpha 170 ]; 171 $PointArray = []; 172 for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) { 173 $PointArray[] = cos(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterX; 174 $PointArray[] = sin(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterY; 175 } 176 $Object->drawPolygon($PointArray, $Color); 177 } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { 178 $Color = [ 179 "R" => $BackgroundR, 180 "G" => $BackgroundG, 181 "B" => $BackgroundB, 182 "Alpha" => $BackgroundAlpha 183 ]; 184 $Object->drawFilledCircle( 185 $CenterX, 186 $CenterY, 187 $EdgeHeight, 188 $Color 189 ); 190 } 191 } else { 192 $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; 193 $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; 194 $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; 195 $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) 196 / $Segments 197 ; 198 199 if ($Layout == RADAR_LAYOUT_STAR) { 200 for ($j = $Segments; $j >= 1; $j--) { 201 $Color = [ 202 "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, 203 "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, 204 "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, 205 "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j 206 ]; 207 $PointArray = []; 208 209 for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) { 210 $PointArray[] = cos(deg2rad($i + $AxisRotation)) 211 * ($EdgeHeight / $Segments) * $j + $CenterX 212 ; 213 $PointArray[] = sin(deg2rad($i + $AxisRotation)) 214 * ($EdgeHeight / $Segments) * $j + $CenterY 215 ; 216 } 217 $Object->drawPolygon($PointArray, $Color); 218 } 219 } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { 220 for ($j = $Segments; $j >= 1; $j--) { 221 $Color = [ 222 "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, 223 "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, 224 "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, 225 "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j 226 ]; 227 $Object->drawFilledCircle( 228 $CenterX, 229 $CenterY, 230 ($EdgeHeight / $Segments) * $j, 231 $Color 232 ); 233 } 234 } 235 } 236 $Object->Shadow = $RestoreShadow; 237 } 238 239 /* Axis to axis lines */ 240 $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]; 241 $ColorDotted = [ 242 "R" => $AxisR, 243 "G" => $AxisG, 244 "B" => $AxisB, 245 "Alpha" => $AxisAlpha * .8, 246 "Ticks" => 2 247 ]; 248 if ($Layout == RADAR_LAYOUT_STAR) { 249 for ($j = 1; $j <= $Segments; $j++) { 250 for ($i = 0; $i < 360; $i = $i + (360 / $Points)) { 251 $EdgeX1 = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 252 $EdgeY1 = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 253 $EdgeX2 = cos(deg2rad($i + $AxisRotation + (360 / $Points))) 254 * ($EdgeHeight / $Segments) * $j + $CenterX 255 ; 256 $EdgeY2 = sin(deg2rad($i + $AxisRotation + (360 / $Points))) 257 * ($EdgeHeight / $Segments) * $j + $CenterY 258 ; 259 260 $Object->drawLine($EdgeX1, $EdgeY1, $EdgeX2, $EdgeY2, $Color); 261 } 262 } 263 } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { 264 for ($j = 1; $j <= $Segments; $j++) { 265 $Radius = ($EdgeHeight / $Segments) * $j; 266 $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color); 267 } 268 } 269 270 if ($DrawAxisValues) { 271 if ($LabelsBackground) { 272 $Options = [ 273 "DrawBox" => true, 274 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 275 "BoxR" => $LabelsBGR, 276 "BoxG" => $LabelsBGG, 277 "BoxB" => $LabelsBGB, 278 "BoxAlpha" => $LabelsBGAlpha 279 ]; 280 } else { 281 $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]; 282 } 283 if ($AxisBoxRounded) { 284 $Options["BoxRounded"] = true; 285 } 286 287 $Options["FontName"] = $AxisFontName; 288 $Options["FontSize"] = $AxisFontSize; 289 290 $Angle = 360 / ($Points * 2); 291 for ($j = 1; $j <= $Segments; $j++) { 292 $Label = $j * $SegmentHeight; 293 294 if ($Layout == RADAR_LAYOUT_CIRCLE) { 295 $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 296 $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 297 } elseif ($Layout == RADAR_LAYOUT_STAR) { 298 $EdgeX1 = cos(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 299 $EdgeY1 = sin(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 300 $EdgeX2 = cos(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments) 301 * $j + $CenterX 302 ; 303 $EdgeY2 = sin(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments) 304 * $j + $CenterY 305 ; 306 307 $EdgeX1 = ($EdgeX2 - $EdgeX1) / 2 + $EdgeX1; 308 $EdgeY1 = ($EdgeY2 - $EdgeY1) / 2 + $EdgeY1; 309 } 310 311 $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options); 312 } 313 } 314 315 /* Axis lines */ 316 $ID = 0; 317 for ($i = 0; $i < 360; $i = $i + (360 / $Points)) { 318 $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX; 319 $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY; 320 321 if ($ID % $SkipLabels == 0) { 322 $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color); 323 } else { 324 $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $ColorDotted); 325 } 326 327 if ($WriteLabels) { 328 $LabelX = cos(deg2rad($i + $AxisRotation + $Axisoffset)) 329 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX 330 ; 331 $LabelY = sin(deg2rad($i + $AxisRotation + $Axisoffset)) 332 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY 333 ; 334 335 if ($LabelSerie) { 336 $Label = isset($Data["Series"][$LabelSerie]["Data"][$ID]) 337 ? $Data["Series"][$LabelSerie]["Data"][$ID] : "" 338 ; 339 } else { 340 $Label = $ID; 341 } 342 343 if ($ID % $SkipLabels == 0) { 344 if ($LabelPos == RADAR_LABELS_ROTATED) { 345 $Object->drawText( 346 $LabelX, 347 $LabelY, 348 $Label, 349 [ 350 "Angle" => (360 - ($i + $AxisRotation + $Axisoffset)) - 90, 351 "Align" => TEXT_ALIGN_BOTTOMMIDDLE 352 ] 353 ); 354 } else { 355 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 356 $Object->drawText( 357 $LabelX, 358 $LabelY, 359 $Label, 360 ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] 361 ); 362 } 363 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 364 $Object->drawText( 365 $LabelX, 366 $LabelY, 367 $Label, 368 ["Align" => TEXT_ALIGN_BOTTOMLEFT] 369 ); 370 } 371 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 372 $Object->drawText( 373 $LabelX, 374 $LabelY, 375 $Label, 376 ["Align" => TEXT_ALIGN_MIDDLELEFT] 377 ); 378 } 379 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 380 $Object->drawText( 381 $LabelX, 382 $LabelY, 383 $Label, 384 ["Align" => TEXT_ALIGN_TOPLEFT] 385 ); 386 } 387 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 388 $Object->drawText( 389 $LabelX, 390 $LabelY, 391 $Label, 392 ["Align" => TEXT_ALIGN_BOTTOMRIGHT] 393 ); 394 } 395 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 396 $Object->drawText( 397 $LabelX, 398 $LabelY, 399 $Label, 400 ["Align" => TEXT_ALIGN_MIDDLERIGHT] 401 ); 402 } 403 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 404 $Object->drawText( 405 $LabelX, 406 $LabelY, 407 $Label, 408 ["Align" => TEXT_ALIGN_TOPRIGHT] 409 ); 410 } 411 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 412 $Object->drawText( 413 $LabelX, 414 $LabelY, 415 $Label, 416 ["Align" => TEXT_ALIGN_TOPMIDDLE] 417 ); 418 } 419 } 420 } 421 } 422 $ID++; 423 } 424 425 /* Compute the plots position */ 426 $ID = 0; 427 $Plot = []; 428 foreach ($Data["Series"] as $SerieName => $DataS) { 429 if ($SerieName != $LabelSerie) { 430 $Color = [ 431 "R" => $Palette[$ID]["R"], 432 "G" => $Palette[$ID]["G"], 433 "B" => $Palette[$ID]["B"], 434 "Alpha" => $Palette[$ID]["Alpha"], 435 "Surrounding" => $PointSurrounding 436 ]; 437 foreach ($DataS["Data"] as $Key => $Value) { 438 $Angle = (360 / $Points) * $Key; 439 $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value; 440 441 $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX; 442 $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY; 443 444 $Plot[$ID][] = [$X, $Y, $Value]; 445 446 if ($RecordImageMap) { 447 $this->pChartObject->addToImageMap( 448 "CIRCLE", 449 sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)), 450 $this->pChartObject->toHTMLColor( 451 $Palette[$ID]["R"], 452 $Palette[$ID]["G"], 453 $Palette[$ID]["B"] 454 ), 455 $DataS["Description"], 456 $Data["Series"][$LabelSerie]["Data"][$Key] . " = " . $Value 457 ); 458 } 459 } 460 $ID++; 461 } 462 } 463 464 /* Draw all that stuff! */ 465 foreach ($Plot as $ID => $Points) { 466 $Color = [ 467 "R" => $Palette[$ID]["R"], 468 "G" => $Palette[$ID]["G"], 469 "B" => $Palette[$ID]["B"], 470 "Alpha" => $Palette[$ID]["Alpha"], 471 "Surrounding" => $PointSurrounding 472 ]; 473 474 /* Draw the polygons */ 475 if ($DrawPoly) { 476 if ($PolyAlpha != null) { 477 $Color = [ 478 "R" => $Palette[$ID]["R"], 479 "G" => $Palette[$ID]["G"], 480 "B" => $Palette[$ID]["B"], 481 "Alpha" => $PolyAlpha, 482 "Surrounding" => $PointSurrounding 483 ]; 484 } 485 $PointsArray = []; 486 for ($i = 0; $i < count($Points); $i++) { 487 $PointsArray[] = $Points[$i][0]; 488 $PointsArray[] = $Points[$i][1]; 489 } 490 $Object->drawPolygon($PointsArray, $Color); 491 } 492 493 $Color = [ 494 "R" => $Palette[$ID]["R"], 495 "G" => $Palette[$ID]["G"], 496 "B" => $Palette[$ID]["B"], 497 "Alpha" => $Palette[$ID]["Alpha"], 498 "Surrounding" => $PointSurrounding 499 ]; 500 501 /* Bubble and labels settings */ 502 $TextSettings = [ 503 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 504 "FontName" => $ValueFontName, 505 "FontSize" => $ValueFontSize, 506 "R" => $Palette[$ID]["R"], 507 "G" => $Palette[$ID]["G"], 508 "B" => $Palette[$ID]["B"] 509 ]; 510 $InnerColor = [ 511 "R" => $InnerBubbleR, 512 "G" => $InnerBubbleG, 513 "B" => $InnerBubbleB, 514 "Alpha" => $InnerBubbleAlpha 515 ]; 516 if ($OuterBubbleR != VOID) { 517 $OuterColor = [ 518 "R" => $OuterBubbleR, 519 "G" => $OuterBubbleG, 520 "B" => $OuterBubbleB, 521 "Alpha" => $OuterBubbleAlpha 522 ]; 523 } else { 524 $OuterColor = [ 525 "R" => $Palette[$ID]["R"] + 20, 526 "G" => $Palette[$ID]["G"] + 20, 527 "B" => $Palette[$ID]["B"] + 20, 528 "Alpha" => $Palette[$ID]["Alpha"] 529 ]; 530 } 531 /* Loop to the starting points if asked */ 532 if ($LineLoopStart && $DrawLines) { 533 $Object->drawLine( 534 $Points[count($Points) - 1][0], 535 $Points[count($Points) - 1][1], 536 $Points[0][0], 537 $Points[0][1], 538 $Color 539 ); 540 } 541 542 /* Draw the lines & points */ 543 for ($i = 0; $i < count($Points); $i++) { 544 if ($DrawLines && $i < count($Points) - 1) { 545 $Object->drawLine( 546 $Points[$i][0], 547 $Points[$i][1], 548 $Points[$i + 1][0], 549 $Points[$i + 1][1], 550 $Color 551 ); 552 } 553 if ($DrawPoints) { 554 $Object->drawFilledCircle( 555 $Points[$i][0], 556 $Points[$i][1], 557 $PointRadius, 558 $Color 559 ); 560 } 561 if ($WriteValuesInBubble && $WriteValues) { 562 $TxtPos = $this->pChartObject->getTextBox( 563 $Points[$i][0], 564 $Points[$i][1], 565 $ValueFontName, 566 $ValueFontSize, 567 0, 568 $Points[$i][2] 569 ); 570 $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2); 571 572 $this->pChartObject->drawFilledCircle( 573 $Points[$i][0], 574 $Points[$i][1], 575 $Radius + $OuterBubbleRadius, 576 $OuterColor 577 ); 578 $this->pChartObject->drawFilledCircle( 579 $Points[$i][0], 580 $Points[$i][1], 581 $Radius, 582 $InnerColor 583 ); 584 } 585 586 if ($WriteValues) { 587 $this->pChartObject->drawText( 588 $Points[$i][0] - 1, 589 $Points[$i][1] - 1, 590 $Points[$i][2], 591 $TextSettings 592 ); 593 } 594 } 595 } 596 } 597 598 /** 599 * Draw a radar chart 600 * @param Image $Object 601 * @param Data $Values 602 * @param array $Format 603 */ 604 public function drawPolar(Image $Object, Data $Values, array $Format = []) 605 { 606 $this->pChartObject = $Object; 607 608 $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; 609 $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; 610 $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; 611 $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; 612 $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; 613 $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : -90; 614 $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true; 615 $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; 616 $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true; 617 $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true; 618 $AxisFontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; 619 $AxisFontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; 620 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false; 621 $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true; 622 $ValueFontName = isset($Format["ValueFontName"]) 623 ? $Format["ValueFontName"] : $this->pChartObject->FontName 624 ; 625 $ValueFontSize = isset($Format["ValueFontSize"]) 626 ? $Format["ValueFontSize"] : $this->pChartObject->FontSize 627 ; 628 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; 629 $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; 630 $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; 631 $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; 632 $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; 633 $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; 634 $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; 635 $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; 636 $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; 637 $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; 638 $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; 639 $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; 640 $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; 641 $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; 642 $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; 643 $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null; 644 $AxisSteps = isset($Format["AxisSteps"]) ? $Format["AxisSteps"] : 20; 645 $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; 646 $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; 647 $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true; 648 $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true; 649 $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; 650 $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; 651 $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; 652 $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; 653 $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; 654 $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; 655 $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true; 656 $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; 657 $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; 658 $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true; 659 $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : false; 660 $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false; 661 $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : null; 662 $FontSize = $Object->FontSize; 663 $X1 = $Object->GraphAreaX1; 664 $Y1 = $Object->GraphAreaY1; 665 $X2 = $Object->GraphAreaX2; 666 $Y2 = $Object->GraphAreaY2; 667 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; 668 669 if ($AxisBoxRounded) { 670 $DrawAxisValues = true; 671 } 672 673 /* Cancel default tick length if ticks not enabled */ 674 if ($DrawTicks == false) { 675 $TicksLength = 0; 676 } 677 678 /* Data Processing */ 679 $Data = $Values->getData(); 680 $Palette = $Values->getPalette(); 681 682 /* Catch the number of required axis */ 683 $LabelSerie = $Data["Abscissa"]; 684 if ($LabelSerie) { 685 $Points = count($Data["Series"][$LabelSerie]["Data"]); 686 } else { 687 $Points = 0; 688 foreach ($Data["Series"] as $SerieName => $DataArray) { 689 if (count($DataArray["Data"]) > $Points) { 690 $Points = count($DataArray["Data"]); 691 } 692 } 693 } 694 695 /* Draw the axis */ 696 $CenterX = ($X2 - $X1) / 2 + $X1; 697 $CenterY = ($Y2 - $Y1) / 2 + $Y1; 698 699 $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2); 700 if ($WriteLabels) { 701 $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; 702 } 703 704 /* Determine the scale if set to automatic */ 705 if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) { 706 if ($FixedMax != VOID) { 707 $Max = $FixedMax; 708 } else { 709 $Max = 0; 710 foreach ($Data["Series"] as $SerieName => $DataArray) { 711 if ($SerieName != $LabelSerie) { 712 if (max($DataArray["Data"]) > $Max) { 713 $Max = max($DataArray["Data"]); 714 } 715 } 716 } 717 } 718 $MaxSegments = $EdgeHeight / 20; 719 $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]); 720 721 $Segments = $Scale["Rows"]; 722 $SegmentHeight = $Scale["RowHeight"]; 723 } 724 725 726 /* Background processing */ 727 if ($DrawBackground) { 728 $RestoreShadow = $Object->Shadow; 729 $Object->Shadow = false; 730 731 if ($BackgroundGradient == null) { 732 $Color = [ 733 "R" => $BackgroundR, 734 "G" => $BackgroundG, 735 "B" => $BackgroundB, 736 "Alpha" => $BackgroundAlpha 737 ]; 738 $Object->drawFilledCircle( 739 $CenterX, 740 $CenterY, 741 $EdgeHeight, 742 $Color 743 ); 744 } else { 745 $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; 746 $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; 747 $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; 748 $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) 749 / $Segments 750 ; 751 752 for ($j = $Segments; $j >= 1; $j--) { 753 $Color = [ 754 "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, 755 "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, 756 "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, 757 "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j 758 ]; 759 $Object->drawFilledCircle( 760 $CenterX, 761 $CenterY, 762 ($EdgeHeight / $Segments) * $j, 763 $Color 764 ); 765 } 766 } 767 $Object->Shadow = $RestoreShadow; 768 } 769 770 /* Axis to axis lines */ 771 $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]; 772 for ($j = 1; $j <= $Segments; $j++) { 773 $Radius = ($EdgeHeight / $Segments) * $j; 774 $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color); 775 } 776 777 if ($DrawAxisValues) { 778 if ($LabelsBackground) { 779 $Options = [ 780 "DrawBox" => true, 781 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 782 "BoxR" => $LabelsBGR, 783 "BoxG" => $LabelsBGG, 784 "BoxB" => $LabelsBGB, 785 "BoxAlpha" => $LabelsBGAlpha 786 ]; 787 } else { 788 $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]; 789 } 790 791 if ($AxisBoxRounded) { 792 $Options["BoxRounded"] = true; 793 } 794 795 $Options["FontName"] = $AxisFontName; 796 $Options["FontSize"] = $AxisFontSize; 797 798 $Angle = 360 / ($Points * 2); 799 for ($j = 1; $j <= $Segments; $j++) { 800 $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 801 $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 802 $Label = $j * $SegmentHeight; 803 804 $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options); 805 } 806 } 807 808 /* Axis lines */ 809 $ID = 0; 810 for ($i = 0; $i <= 359; $i = $i + $AxisSteps) { 811 $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX; 812 $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY; 813 814 $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color); 815 816 if ($WriteLabels) { 817 $LabelX = cos(deg2rad($i + $AxisRotation)) 818 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX 819 ; 820 $LabelY = sin(deg2rad($i + $AxisRotation)) 821 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY 822 ; 823 $Label = $i . "°"; 824 825 if ($LabelPos == RADAR_LABELS_ROTATED) { 826 $Object->drawText( 827 $LabelX, 828 $LabelY, 829 $Label, 830 ["Angle" => (360 - $i), "Align" => TEXT_ALIGN_BOTTOMMIDDLE] 831 ); 832 } else { 833 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 834 $Object->drawText( 835 $LabelX, 836 $LabelY, 837 $Label, 838 ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] 839 ); 840 } 841 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 842 $Object->drawText( 843 $LabelX, 844 $LabelY, 845 $Label, 846 ["Align" => TEXT_ALIGN_BOTTOMLEFT] 847 ); 848 } 849 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 850 $Object->drawText( 851 $LabelX, 852 $LabelY, 853 $Label, 854 ["Align" => TEXT_ALIGN_MIDDLELEFT] 855 ); 856 } 857 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 858 $Object->drawText( 859 $LabelX, 860 $LabelY, 861 $Label, 862 ["Align" => TEXT_ALIGN_TOPLEFT] 863 ); 864 } 865 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 866 $Object->drawText( 867 $LabelX, 868 $LabelY, 869 $Label, 870 ["Align" => TEXT_ALIGN_BOTTOMRIGHT] 871 ); 872 } 873 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 874 $Object->drawText( 875 $LabelX, 876 $LabelY, 877 $Label, 878 ["Align" => TEXT_ALIGN_MIDDLERIGHT] 879 ); 880 } 881 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 882 $Object->drawText( 883 $LabelX, 884 $LabelY, 885 $Label, 886 ["Align" => TEXT_ALIGN_TOPRIGHT] 887 ); 888 } 889 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 890 $Object->drawText( 891 $LabelX, 892 $LabelY, 893 $Label, 894 ["Align" => TEXT_ALIGN_TOPMIDDLE] 895 ); 896 } 897 } 898 } 899 $ID++; 900 } 901 902 /* Compute the plots position */ 903 $ID = 0; 904 $Plot = []; 905 foreach ($Data["Series"] as $SerieName => $DataSet) { 906 if ($SerieName != $LabelSerie) { 907 $Color = [ 908 "R" => $Palette[$ID]["R"], 909 "G" => $Palette[$ID]["G"], 910 "B" => $Palette[$ID]["B"], 911 "Alpha" => $Palette[$ID]["Alpha"], 912 "Surrounding" => $PointSurrounding 913 ]; 914 foreach ($DataSet["Data"] as $Key => $Value) { 915 $Angle = $Data["Series"][$LabelSerie]["Data"][$Key]; 916 $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value; 917 918 $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX; 919 $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY; 920 921 if ($RecordImageMap) { 922 $this->pChartObject->addToImageMap( 923 "CIRCLE", 924 sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)), 925 $this->pChartObject->toHTMLColor( 926 $Palette[$ID]["R"], 927 $Palette[$ID]["G"], 928 $Palette[$ID]["B"] 929 ), 930 $DataSet["Description"], 931 $Data["Series"][$LabelSerie]["Data"][$Key] . "° = " . $Value 932 ); 933 } 934 935 $Plot[$ID][] = [$X, $Y, $Value]; 936 } 937 $ID++; 938 } 939 } 940 941 /* Draw all that stuff! */ 942 foreach ($Plot as $ID => $Points) { 943 $Color = [ 944 "R" => $Palette[$ID]["R"], 945 "G" => $Palette[$ID]["G"], 946 "B" => $Palette[$ID]["B"], 947 "Alpha" => $Palette[$ID]["Alpha"], 948 "Surrounding" => $PointSurrounding 949 ]; 950 951 /* Draw the polygons */ 952 if ($DrawPoly) { 953 if ($PolyAlpha != null) { 954 $Color = [ 955 "R" => $Palette[$ID]["R"], 956 "G" => $Palette[$ID]["G"], 957 "B" => $Palette[$ID]["B"], 958 "Alpha" => $PolyAlpha, 959 "Surrounding" => $PointSurrounding 960 ]; 961 } 962 $PointsArray = []; 963 for ($i = 0; $i < count($Points); $i++) { 964 $PointsArray[] = $Points[$i][0]; 965 $PointsArray[] = $Points[$i][1]; 966 } 967 968 $Object->drawPolygon($PointsArray, $Color); 969 } 970 971 $Color = [ 972 "R" => $Palette[$ID]["R"], 973 "G" => $Palette[$ID]["G"], 974 "B" => $Palette[$ID]["B"], 975 "Alpha" => $Palette[$ID]["Alpha"], 976 "Surrounding" => $PointSurrounding 977 ]; 978 979 /* Bubble and labels settings */ 980 $TextSettings = [ 981 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 982 "FontName" => $ValueFontName, 983 "FontSize" => $ValueFontSize, 984 "R" => $Palette[$ID]["R"], 985 "G" => $Palette[$ID]["G"], 986 "B" => $Palette[$ID]["B"] 987 ]; 988 $InnerColor = [ 989 "R" => $InnerBubbleR, 990 "G" => $InnerBubbleG, 991 "B" => $InnerBubbleB, 992 "Alpha" => $InnerBubbleAlpha 993 ]; 994 if ($OuterBubbleR != VOID) { 995 $OuterColor = [ 996 "R" => $OuterBubbleR, 997 "G" => $OuterBubbleG, 998 "B" => $OuterBubbleB, 999 "Alpha" => $OuterBubbleAlpha 1000 ]; 1001 } else { 1002 $OuterColor = [ 1003 "R" => $Palette[$ID]["R"] + 20, 1004 "G" => $Palette[$ID]["G"] + 20, 1005 "B" => $Palette[$ID]["B"] + 20, 1006 "Alpha" => $Palette[$ID]["Alpha"] 1007 ]; 1008 } 1009 /* Loop to the starting points if asked */ 1010 if ($LineLoopStart && $DrawLines) { 1011 $Object->drawLine( 1012 $Points[count($Points) - 1][0], 1013 $Points[count($Points) - 1][1], 1014 $Points[0][0], 1015 $Points[0][1], 1016 $Color 1017 ); 1018 } 1019 /* Draw the lines & points */ 1020 for ($i = 0; $i < count($Points); $i++) { 1021 if ($DrawLines && $i < count($Points) - 1) { 1022 $Object->drawLine( 1023 $Points[$i][0], 1024 $Points[$i][1], 1025 $Points[$i + 1][0], 1026 $Points[$i + 1][1], 1027 $Color 1028 ); 1029 } 1030 if ($DrawPoints) { 1031 $Object->drawFilledCircle( 1032 $Points[$i][0], 1033 $Points[$i][1], 1034 $PointRadius, 1035 $Color 1036 ); 1037 } 1038 if ($WriteValuesInBubble && $WriteValues) { 1039 $TxtPos = $this->pChartObject->getTextBox( 1040 $Points[$i][0], 1041 $Points[$i][1], 1042 $ValueFontName, 1043 $ValueFontSize, 1044 0, 1045 $Points[$i][2] 1046 ); 1047 $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2); 1048 1049 $this->pChartObject->drawFilledCircle( 1050 $Points[$i][0], 1051 $Points[$i][1], 1052 $Radius + $OuterBubbleRadius, 1053 $OuterColor 1054 ); 1055 $this->pChartObject->drawFilledCircle( 1056 $Points[$i][0], 1057 $Points[$i][1], 1058 $Radius, 1059 $InnerColor 1060 ); 1061 } 1062 1063 if ($WriteValues) { 1064 $this->pChartObject->drawText( 1065 $Points[$i][0] - 1, 1066 $Points[$i][1] - 1, 1067 $Points[$i][2], 1068 $TextSettings 1069 ); 1070 } 1071 } 1072 } 1073 } 1074} 1075