Bubble.php
1 year ago
Indicator.php
1 year ago
Pie.php
1 year ago
Radar.php
1 year ago
Scatter.php
1 year ago
Split.php
1 year ago
Spring.php
1 year ago
Stock.php
1 year ago
Surface.php
1 year ago
Bubble.php
325 lines
| 1 | <?php |
| 2 | |
| 3 | namespace CpChart\Chart; |
| 4 | |
| 5 | use CpChart\Data; |
| 6 | use CpChart\Image; |
| 7 | /** |
| 8 | * Bubble - class to draw bubble charts |
| 9 | * |
| 10 | * Version : 2.1.4 |
| 11 | * Made by : Jean-Damien POGOLOTTI |
| 12 | * Last Update : 19/01/2014 |
| 13 | * |
| 14 | * This file can be distributed under the license you can find at : |
| 15 | * |
| 16 | * http://www.pchart.net/license |
| 17 | * |
| 18 | * You can find the whole class documentation on the pChart web site. |
| 19 | */ |
| 20 | class Bubble |
| 21 | { |
| 22 | /** |
| 23 | * @var Image |
| 24 | */ |
| 25 | public $pChartObject; |
| 26 | /** |
| 27 | * @var Data |
| 28 | */ |
| 29 | public $pDataObject; |
| 30 | /** |
| 31 | * @param Image $pChartObject |
| 32 | * @param Data $pDataObject |
| 33 | */ |
| 34 | public function __construct(Image $pChartObject, Data $pDataObject) |
| 35 | { |
| 36 | $this->pChartObject = $pChartObject; |
| 37 | $this->pDataObject = $pDataObject; |
| 38 | } |
| 39 | /** |
| 40 | * Prepare the scale |
| 41 | * |
| 42 | * @param mixed $DataSeries |
| 43 | * @param mixed $WeightSeries |
| 44 | */ |
| 45 | public function bubbleScale($DataSeries, $WeightSeries) |
| 46 | { |
| 47 | if (!is_array($DataSeries)) { |
| 48 | $DataSeries = [$DataSeries]; |
| 49 | } |
| 50 | if (!is_array($WeightSeries)) { |
| 51 | $WeightSeries = [$WeightSeries]; |
| 52 | } |
| 53 | /* Parse each data series to find the new min & max boundaries to scale */ |
| 54 | $NewPositiveSerie = []; |
| 55 | $NewNegativeSerie = []; |
| 56 | $MaxValues = 0; |
| 57 | $LastPositive = 0; |
| 58 | $LastNegative = 0; |
| 59 | foreach ($DataSeries as $Key => $SerieName) { |
| 60 | $SerieWeightName = $WeightSeries[$Key]; |
| 61 | $this->pDataObject->setSerieDrawable($SerieWeightName, \false); |
| 62 | $serieData = $this->pDataObject->Data["Series"][$SerieName]["Data"]; |
| 63 | if (count($serieData) > $MaxValues) { |
| 64 | $MaxValues = count($serieData); |
| 65 | } |
| 66 | foreach ($serieData as $Key => $Value) { |
| 67 | if ($Value >= 0) { |
| 68 | $BubbleBounds = $Value + $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; |
| 69 | if (!isset($NewPositiveSerie[$Key])) { |
| 70 | $NewPositiveSerie[$Key] = $BubbleBounds; |
| 71 | } elseif ($NewPositiveSerie[$Key] < $BubbleBounds) { |
| 72 | $NewPositiveSerie[$Key] = $BubbleBounds; |
| 73 | } |
| 74 | $LastPositive = $BubbleBounds; |
| 75 | } else { |
| 76 | $BubbleBounds = $Value - $this->pDataObject->Data["Series"][$SerieWeightName]["Data"][$Key]; |
| 77 | if (!isset($NewNegativeSerie[$Key])) { |
| 78 | $NewNegativeSerie[$Key] = $BubbleBounds; |
| 79 | } elseif ($NewNegativeSerie[$Key] > $BubbleBounds) { |
| 80 | $NewNegativeSerie[$Key] = $BubbleBounds; |
| 81 | } |
| 82 | $LastNegative = $BubbleBounds; |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | /* Check for missing values and all the fake positive serie */ |
| 87 | if (count($NewPositiveSerie)) { |
| 88 | for ($i = 0; $i < $MaxValues; $i++) { |
| 89 | if (!isset($NewPositiveSerie[$i])) { |
| 90 | $NewPositiveSerie[$i] = $LastPositive; |
| 91 | } |
| 92 | } |
| 93 | $this->pDataObject->addPoints($NewPositiveSerie, "BubbleFakePositiveSerie"); |
| 94 | } |
| 95 | /* Check for missing values and all the fake negative serie */ |
| 96 | if (count($NewNegativeSerie)) { |
| 97 | for ($i = 0; $i < $MaxValues; $i++) { |
| 98 | if (!isset($NewNegativeSerie[$i])) { |
| 99 | $NewNegativeSerie[$i] = $LastNegative; |
| 100 | } |
| 101 | } |
| 102 | $this->pDataObject->addPoints($NewNegativeSerie, "BubbleFakeNegativeSerie"); |
| 103 | } |
| 104 | } |
| 105 | public function resetSeriesColors() |
| 106 | { |
| 107 | $Data = $this->pDataObject->getData(); |
| 108 | $Palette = $this->pDataObject->getPalette(); |
| 109 | $ID = 0; |
| 110 | foreach ($Data["Series"] as $SerieName => $SeriesParameters) { |
| 111 | if ($SeriesParameters["isDrawable"]) { |
| 112 | $this->pDataObject->Data["Series"][$SerieName]["Color"]["R"] = $Palette[$ID]["R"]; |
| 113 | $this->pDataObject->Data["Series"][$SerieName]["Color"]["G"] = $Palette[$ID]["G"]; |
| 114 | $this->pDataObject->Data["Series"][$SerieName]["Color"]["B"] = $Palette[$ID]["B"]; |
| 115 | $this->pDataObject->Data["Series"][$SerieName]["Color"]["Alpha"] = $Palette[$ID]["Alpha"]; |
| 116 | $ID++; |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | /* Prepare the scale */ |
| 121 | public function drawBubbleChart($DataSeries, $WeightSeries, $Format = "") |
| 122 | { |
| 123 | $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : VOID; |
| 124 | $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : \true; |
| 125 | $BorderWidth = isset($Format["BorderWidth"]) ? $Format["BorderWidth"] : 1; |
| 126 | $Shape = isset($Format["Shape"]) ? $Format["Shape"] : BUBBLE_SHAPE_ROUND; |
| 127 | $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null; |
| 128 | $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0; |
| 129 | $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0; |
| 130 | $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0; |
| 131 | $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30; |
| 132 | $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false; |
| 133 | if (!is_array($DataSeries)) { |
| 134 | $DataSeries = [$DataSeries]; |
| 135 | } |
| 136 | if (!is_array($WeightSeries)) { |
| 137 | $WeightSeries = [$WeightSeries]; |
| 138 | } |
| 139 | $Data = $this->pDataObject->getData(); |
| 140 | $Palette = $this->pDataObject->getPalette(); |
| 141 | if (isset($Data["Series"]["BubbleFakePositiveSerie"])) { |
| 142 | $this->pDataObject->setSerieDrawable("BubbleFakePositiveSerie", \false); |
| 143 | } |
| 144 | if (isset($Data["Series"]["BubbleFakeNegativeSerie"])) { |
| 145 | $this->pDataObject->setSerieDrawable("BubbleFakeNegativeSerie", \false); |
| 146 | } |
| 147 | $this->resetSeriesColors(); |
| 148 | list($XMargin, $XDivs) = $this->pChartObject->scaleGetXSettings(); |
| 149 | foreach ($DataSeries as $Key => $SerieName) { |
| 150 | $AxisID = $Data["Series"][$SerieName]["Axis"]; |
| 151 | $Mode = $Data["Axis"][$AxisID]["Display"]; |
| 152 | $Format = $Data["Axis"][$AxisID]["Format"]; |
| 153 | $Unit = $Data["Axis"][$AxisID]["Unit"]; |
| 154 | if (isset($Data["Series"][$SerieName]["Description"])) { |
| 155 | $SerieDescription = $Data["Series"][$SerieName]["Description"]; |
| 156 | } else { |
| 157 | $SerieDescription = $SerieName; |
| 158 | } |
| 159 | $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; |
| 160 | $X = $this->pChartObject->GraphAreaX1 + $XMargin; |
| 161 | $Y = $this->pChartObject->GraphAreaY1 + $XMargin; |
| 162 | $Color = ["R" => $Palette[$Key]["R"], "G" => $Palette[$Key]["G"], "B" => $Palette[$Key]["B"], "Alpha" => $Palette[$Key]["Alpha"]]; |
| 163 | if ($ForceAlpha != VOID) { |
| 164 | $Color["Alpha"] = $ForceAlpha; |
| 165 | } |
| 166 | if ($DrawBorder) { |
| 167 | if ($BorderWidth != 1) { |
| 168 | if ($Surrounding != null) { |
| 169 | $BorderR = $Palette[$Key]["R"] + $Surrounding; |
| 170 | $BorderG = $Palette[$Key]["G"] + $Surrounding; |
| 171 | $BorderB = $Palette[$Key]["B"] + $Surrounding; |
| 172 | } |
| 173 | if ($ForceAlpha != VOID) { |
| 174 | $BorderAlpha = $ForceAlpha / 2; |
| 175 | } |
| 176 | $BorderColor = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]; |
| 177 | } else { |
| 178 | $Color["BorderAlpha"] = $BorderAlpha; |
| 179 | if ($Surrounding != null) { |
| 180 | $Color["BorderR"] = $Palette[$Key]["R"] + $Surrounding; |
| 181 | $Color["BorderG"] = $Palette[$Key]["G"] + $Surrounding; |
| 182 | $Color["BorderB"] = $Palette[$Key]["B"] + $Surrounding; |
| 183 | } else { |
| 184 | $Color["BorderR"] = $BorderR; |
| 185 | $Color["BorderG"] = $BorderG; |
| 186 | $Color["BorderB"] = $BorderB; |
| 187 | } |
| 188 | if ($ForceAlpha != VOID) { |
| 189 | $Color["BorderAlpha"] = $ForceAlpha / 2; |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | foreach ($Data["Series"][$SerieName]["Data"] as $iKey => $Point) { |
| 194 | $Weight = $Point + $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]; |
| 195 | $PosArray = $this->pChartObject->scaleComputeY($Point, ["AxisID" => $AxisID]); |
| 196 | $WeightArray = $this->pChartObject->scaleComputeY($Weight, ["AxisID" => $AxisID]); |
| 197 | if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { |
| 198 | if ($XDivs == 0) { |
| 199 | $XStep = 0; |
| 200 | } else { |
| 201 | $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; |
| 202 | } |
| 203 | $Y = floor($PosArray); |
| 204 | $CircleRadius = floor(abs($PosArray - $WeightArray) / 2); |
| 205 | if ($Shape == BUBBLE_SHAPE_SQUARE) { |
| 206 | if ($RecordImageMap) { |
| 207 | $this->pChartObject->addToImageMap("RECT", floor($X - $CircleRadius) . "," . floor($Y - $CircleRadius) . "," . floor($X + $CircleRadius) . "," . floor($Y + $CircleRadius), $this->pChartObject->toHTMLColor($Palette[$Key]["R"], $Palette[$Key]["G"], $Palette[$Key]["B"]), $SerieDescription, $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); |
| 208 | } |
| 209 | if ($BorderWidth != 1) { |
| 210 | $this->pChartObject->drawFilledRectangle($X - $CircleRadius - $BorderWidth, $Y - $CircleRadius - $BorderWidth, $X + $CircleRadius + $BorderWidth, $Y + $CircleRadius + $BorderWidth, $BorderColor); |
| 211 | $this->pChartObject->drawFilledRectangle($X - $CircleRadius, $Y - $CircleRadius, $X + $CircleRadius, $Y + $CircleRadius, $Color); |
| 212 | } else { |
| 213 | $this->pChartObject->drawFilledRectangle($X - $CircleRadius, $Y - $CircleRadius, $X + $CircleRadius, $Y + $CircleRadius, $Color); |
| 214 | } |
| 215 | } elseif ($Shape == BUBBLE_SHAPE_ROUND) { |
| 216 | if ($RecordImageMap) { |
| 217 | $this->pChartObject->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . floor($CircleRadius), $this->pChartObject->toHTMLColor($Palette[$Key]["R"], $Palette[$Key]["G"], $Palette[$Key]["B"]), $SerieDescription, $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); |
| 218 | } |
| 219 | if ($BorderWidth != 1) { |
| 220 | $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius + $BorderWidth, $BorderColor); |
| 221 | $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); |
| 222 | } else { |
| 223 | $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); |
| 224 | } |
| 225 | } |
| 226 | $X = $X + $XStep; |
| 227 | } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) { |
| 228 | if ($XDivs == 0) { |
| 229 | $XStep = 0; |
| 230 | } else { |
| 231 | $XStep = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $XMargin * 2) / $XDivs; |
| 232 | } |
| 233 | $X = floor($PosArray); |
| 234 | $CircleRadius = floor(abs($PosArray - $WeightArray) / 2); |
| 235 | if ($Shape == BUBBLE_SHAPE_SQUARE) { |
| 236 | if ($RecordImageMap) { |
| 237 | $this->pChartObject->addToImageMap("RECT", floor($X - $CircleRadius) . "," . floor($Y - $CircleRadius) . "," . floor($X + $CircleRadius) . "," . floor($Y + $CircleRadius), $this->pChartObject->toHTMLColor($Palette[$Key]["R"], $Palette[$Key]["G"], $Palette[$Key]["B"]), $SerieDescription, $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); |
| 238 | } |
| 239 | if ($BorderWidth != 1) { |
| 240 | $this->pChartObject->drawFilledRectangle($X - $CircleRadius - $BorderWidth, $Y - $CircleRadius - $BorderWidth, $X + $CircleRadius + $BorderWidth, $Y + $CircleRadius + $BorderWidth, $BorderColor); |
| 241 | $this->pChartObject->drawFilledRectangle($X - $CircleRadius, $Y - $CircleRadius, $X + $CircleRadius, $Y + $CircleRadius, $Color); |
| 242 | } else { |
| 243 | $this->pChartObject->drawFilledRectangle($X - $CircleRadius, $Y - $CircleRadius, $X + $CircleRadius, $Y + $CircleRadius, $Color); |
| 244 | } |
| 245 | } elseif ($Shape == BUBBLE_SHAPE_ROUND) { |
| 246 | if ($RecordImageMap) { |
| 247 | $this->pChartObject->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . floor($CircleRadius), $this->pChartObject->toHTMLColor($Palette[$Key]["R"], $Palette[$Key]["G"], $Palette[$Key]["B"]), $SerieDescription, $Data["Series"][$WeightSeries[$Key]]["Data"][$iKey]); |
| 248 | } |
| 249 | if ($BorderWidth != 1) { |
| 250 | $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius + $BorderWidth, $BorderColor); |
| 251 | $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); |
| 252 | } else { |
| 253 | $this->pChartObject->drawFilledCircle($X, $Y, $CircleRadius, $Color); |
| 254 | } |
| 255 | } |
| 256 | $Y = $Y + $XStep; |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | public function writeBubbleLabel($SerieName, $SerieWeightName, $Points, $Format = "") |
| 262 | { |
| 263 | $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; |
| 264 | if (!is_array($Points)) { |
| 265 | $Point = $Points; |
| 266 | $Points = []; |
| 267 | $Points[] = $Point; |
| 268 | } |
| 269 | $Data = $this->pDataObject->getData(); |
| 270 | if (!isset($Data["Series"][$SerieName]) || !isset($Data["Series"][$SerieWeightName])) { |
| 271 | return 0; |
| 272 | } |
| 273 | list($XMargin, $XDivs) = $this->pChartObject->scaleGetXSettings(); |
| 274 | $AxisID = $Data["Series"][$SerieName]["Axis"]; |
| 275 | $AxisMode = $Data["Axis"][$AxisID]["Display"]; |
| 276 | $AxisFormat = $Data["Axis"][$AxisID]["Format"]; |
| 277 | $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; |
| 278 | $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; |
| 279 | $X = $InitialX = $this->pChartObject->GraphAreaX1 + $XMargin; |
| 280 | $Y = $InitialY = $this->pChartObject->GraphAreaY1 + $XMargin; |
| 281 | $Color = ["R" => $Data["Series"][$SerieName]["Color"]["R"], "G" => $Data["Series"][$SerieName]["Color"]["G"], "B" => $Data["Series"][$SerieName]["Color"]["B"], "Alpha" => $Data["Series"][$SerieName]["Color"]["Alpha"]]; |
| 282 | foreach ($Points as $Key => $Point) { |
| 283 | $Value = $Data["Series"][$SerieName]["Data"][$Point]; |
| 284 | $PosArray = $this->pChartObject->scaleComputeY($Value, ["AxisID" => $AxisID]); |
| 285 | if (isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Point])) { |
| 286 | $Abscissa = $Data["Series"][$Data["Abscissa"]]["Data"][$Point] . " : "; |
| 287 | } else { |
| 288 | $Abscissa = ""; |
| 289 | } |
| 290 | $Value = $this->pChartObject->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit); |
| 291 | $Weight = $Data["Series"][$SerieWeightName]["Data"][$Point]; |
| 292 | $Caption = $Abscissa . $Value . " / " . $Weight; |
| 293 | if (isset($Data["Series"][$SerieName]["Description"])) { |
| 294 | $Description = $Data["Series"][$SerieName]["Description"]; |
| 295 | } else { |
| 296 | $Description = "No description"; |
| 297 | } |
| 298 | $Series = [["Format" => $Color, "Caption" => $Caption]]; |
| 299 | if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { |
| 300 | if ($XDivs == 0) { |
| 301 | $XStep = 0; |
| 302 | } else { |
| 303 | $XStep = ($this->pChartObject->GraphAreaX2 - $this->pChartObject->GraphAreaX1 - $XMargin * 2) / $XDivs; |
| 304 | } |
| 305 | $X = floor($InitialX + $Point * $XStep); |
| 306 | $Y = floor($PosArray); |
| 307 | } else { |
| 308 | if ($XDivs == 0) { |
| 309 | $YStep = 0; |
| 310 | } else { |
| 311 | $YStep = ($this->pChartObject->GraphAreaY2 - $this->pChartObject->GraphAreaY1 - $XMargin * 2) / $XDivs; |
| 312 | } |
| 313 | $X = floor($PosArray); |
| 314 | $Y = floor($InitialY + $Point * $YStep); |
| 315 | } |
| 316 | if ($DrawPoint == LABEL_POINT_CIRCLE) { |
| 317 | $this->pChartObject->drawFilledCircle($X, $Y, 3, ["R" => 255, "G" => 255, "B" => 255, "BorderR" => 0, "BorderG" => 0, "BorderB" => 0]); |
| 318 | } elseif ($DrawPoint == LABEL_POINT_BOX) { |
| 319 | $this->pChartObject->drawFilledRectangle($X - 2, $Y - 2, $X + 2, $Y + 2, ["R" => 255, "G" => 255, "B" => 255, "BorderR" => 0, "BorderG" => 0, "BorderB" => 0]); |
| 320 | } |
| 321 | $this->pChartObject->drawLabelBox($X, $Y - 3, $Description, $Series, $Format); |
| 322 | } |
| 323 | } |
| 324 | } |
| 325 |