icons
11 years ago
markers
10 years ago
GoogleChart.php
11 years ago
GoogleChartApi.php
11 years ago
GoogleChartAxis.php
11 years ago
GoogleChartData.php
11 years ago
GoogleChartIcon.php
11 years ago
GoogleChartMarker.php
11 years ago
LICENSE
11 years ago
GoogleChart.php
1081 lines
| 1 | <?php |
| 2 | |
| 3 | /** @file |
| 4 | * This file is part of Google Chart PHP library. |
| 5 | * |
| 6 | * Copyright (c) 2010 Rémi Lanvin <remi@cloudconnected.fr> |
| 7 | * |
| 8 | * Licensed under the MIT license. |
| 9 | * |
| 10 | * For the full copyright and license information, please view the LICENSE file. |
| 11 | */ |
| 12 | |
| 13 | require_once 'GoogleChartApi.php'; |
| 14 | require_once 'GoogleChartData.php'; |
| 15 | require_once 'GoogleChartAxis.php'; |
| 16 | require_once 'GoogleChartMarker.php'; |
| 17 | |
| 18 | /** |
| 19 | * A chart. |
| 20 | * |
| 21 | * This class represent a chart. It provides a bunch of setters to customize it. |
| 22 | * When creating a new chart, you need to specify 3 things: |
| 23 | * - type of the chart (see http://code.google.com/apis/chart/docs/gallery/chart_gall.html) |
| 24 | * - width |
| 25 | * - height |
| 26 | * |
| 27 | * Then you need to add data to that chart using GoogleChartData class. |
| 28 | * |
| 29 | * Depending on the type of chart, you can also add one or more axis using GoogleChartAxis class. |
| 30 | * |
| 31 | * @par Line chart example |
| 32 | * |
| 33 | * @include line_chart.php |
| 34 | * |
| 35 | * @par Work around for unimplemented features |
| 36 | * |
| 37 | * You can override any parameter by setting its value in the class. |
| 38 | * For example, to following code will override the background: |
| 39 | * |
| 40 | * \code |
| 41 | * $chart = new GoogleChart('lc', 500, 200); |
| 42 | * $chart->chf = 'b,s,cccccc'; |
| 43 | * var_dump($chart->getQuery()); |
| 44 | * \endcode |
| 45 | * |
| 46 | * You can use this method for working with features that are currently |
| 47 | * not implemented in the library (or buggy). |
| 48 | */ |
| 49 | class GoogleChart extends GoogleChartApi |
| 50 | { |
| 51 | const AUTOSCALE_OFF = false; |
| 52 | const AUTOSCALE_VALUES = true; |
| 53 | |
| 54 | const BACKGROUND = 'bg'; |
| 55 | const CHART_AREA = 'c'; |
| 56 | |
| 57 | const TEXT = 't'; |
| 58 | const SIMPLE_ENCODING = 's'; |
| 59 | const EXTENDED_ENCODING = 'e'; |
| 60 | |
| 61 | /** |
| 62 | * Store the type of the chart as string. |
| 63 | */ |
| 64 | protected $type = ''; |
| 65 | |
| 66 | /** |
| 67 | * Width |
| 68 | */ |
| 69 | protected $width = ''; |
| 70 | |
| 71 | /** |
| 72 | * Height |
| 73 | */ |
| 74 | protected $height = ''; |
| 75 | |
| 76 | /** |
| 77 | * List of all data series (GoogleChartData) |
| 78 | */ |
| 79 | protected $data = array(); |
| 80 | |
| 81 | /** |
| 82 | * Data format (text, simple encoding or extended encoding) |
| 83 | */ |
| 84 | protected $data_format = self::TEXT; |
| 85 | |
| 86 | /** |
| 87 | * Data format have different separator character |
| 88 | */ |
| 89 | protected $data_separator = array( |
| 90 | self::TEXT => '|', |
| 91 | self::SIMPLE_ENCODING => ',', |
| 92 | self::EXTENDED_ENCODING => ',' |
| 93 | ); |
| 94 | |
| 95 | /** |
| 96 | * List of all axes (GoogleChartAxis) |
| 97 | */ |
| 98 | protected $axes = array(); |
| 99 | |
| 100 | /** |
| 101 | * List of all markers (GoogleChartMarker) @c chm parameter |
| 102 | */ |
| 103 | protected $markers = array(); |
| 104 | |
| 105 | /** |
| 106 | * List of dynamic markers (GooglechartIcon). @c chem parameter |
| 107 | */ |
| 108 | protected $dynamic_markers = array(); |
| 109 | |
| 110 | protected $grid_lines = null; |
| 111 | |
| 112 | protected $chts = false; |
| 113 | protected $title = null; |
| 114 | protected $title_color = '000000'; |
| 115 | protected $title_size = '12'; |
| 116 | |
| 117 | protected $autoscale = true; |
| 118 | protected $scale = null; |
| 119 | |
| 120 | protected $legend_position = null; |
| 121 | protected $legend_label_order = null; |
| 122 | protected $legend_skip_empty = true; |
| 123 | |
| 124 | protected $fills = null; |
| 125 | |
| 126 | protected $_compute_data_label = false; |
| 127 | |
| 128 | //~ protected $chma = false; |
| 129 | protected $margin = null; |
| 130 | protected $legend_size = null; |
| 131 | |
| 132 | /** |
| 133 | * Create a new chart. |
| 134 | * |
| 135 | * @param $type (string) |
| 136 | * Google chart type. |
| 137 | * @param $width (int) |
| 138 | * @param $height (int) |
| 139 | * |
| 140 | * @see http://code.google.com/apis/chart/docs/gallery/chart_gall.html |
| 141 | */ |
| 142 | public function __construct($type, $width, $height) |
| 143 | { |
| 144 | $this->type = $type; |
| 145 | $this->width = $width; |
| 146 | $this->height = $height; |
| 147 | |
| 148 | //~ $this->setAutoscale(self::AUTOSCALE_Y_AXIS); |
| 149 | //~ $this->setQueryMethod(self::POST); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Set the data format used by the chart. |
| 154 | * Default is GoogleChart::TEXT (basic text format). |
| 155 | * @since 0.5 |
| 156 | */ |
| 157 | public function setDataFormat($format) |
| 158 | { |
| 159 | if ( $format !== self::TEXT && $format !== self::SIMPLE_ENCODING && $format !== self::EXTENDED_ENCODING ) { |
| 160 | throw new InvalidArgumentException('Invalid data format'); |
| 161 | } |
| 162 | |
| 163 | $this->data_format = $format; |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Add a data serie to the chart. |
| 168 | * |
| 169 | * @param $data (GoogleChartData) |
| 170 | * @see GoogleChartData |
| 171 | */ |
| 172 | public function addData(GoogleChartData $data) |
| 173 | { |
| 174 | if ( $data->hasIndex() ) |
| 175 | throw new LogicException('Invalid data serie. This data serie has already been added.'); |
| 176 | |
| 177 | $index = array_push($this->data, $data); |
| 178 | $data->setIndex($index - 1); |
| 179 | return $this; |
| 180 | } |
| 181 | |
| 182 | /** |
| 183 | * Add a visible axis to the chart. |
| 184 | * |
| 185 | * @param $axis (GoogleChartAxis) |
| 186 | * @see GoogleChartAxis |
| 187 | */ |
| 188 | public function addAxis(GoogleChartAxis $axis) |
| 189 | { |
| 190 | $this->axes[] = $axis; |
| 191 | |
| 192 | return $this; |
| 193 | } |
| 194 | |
| 195 | /** |
| 196 | * Add a marker to the chart. |
| 197 | * |
| 198 | * @param $marker (GoogleChartMarker) |
| 199 | * @see GoogleChartShapeMarker, GoogleChartTextMarker, GoogleChartLineMarker |
| 200 | * @return $this |
| 201 | */ |
| 202 | public function addMarker(GoogleChartMarker $marker) |
| 203 | { |
| 204 | $this->markers[] = $marker; |
| 205 | |
| 206 | return $this; |
| 207 | } |
| 208 | |
| 209 | /** |
| 210 | * Add a dynamic icon marker to the chart. |
| 211 | * |
| 212 | * Dynamic icon marker are different than regular marker. Technically, they |
| 213 | * are defined using @c chem parameter instead of @c chm for regular marker. |
| 214 | * |
| 215 | * @param $marker (GoogleChartIcon) |
| 216 | * @return $this |
| 217 | */ |
| 218 | public function addDynamicMarker(GoogleChartIcon $marker) |
| 219 | { |
| 220 | $this->dynamic_markers[] = $marker; |
| 221 | |
| 222 | return $this; |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * @name Scaling |
| 227 | */ |
| 228 | //@{ |
| 229 | /** |
| 230 | * Set autoscaling mode. |
| 231 | * |
| 232 | * Autoscaling is a feature provided by this library. Because Google Chart |
| 233 | * default scale is 0:100, most of the time your data will not appears the |
| 234 | * way you want. So you need to set a scale for the chart. |
| 235 | * |
| 236 | * @see http://code.google.com/p/googlechartphplib/wiki/Autoscaling |
| 237 | * |
| 238 | * @see http://code.google.com/apis/chart/docs/data_formats.html#data_scaling |
| 239 | * |
| 240 | * @param $autoscale (bool) |
| 241 | * @return $this |
| 242 | */ |
| 243 | public function setAutoscale($autoscale) |
| 244 | { |
| 245 | if ( $autoscale !== true && $autoscale !== false ) { |
| 246 | throw new InvalidArgumentException('Invalid autoscale mode.'); |
| 247 | } |
| 248 | |
| 249 | $this->autoscale = $autoscale; |
| 250 | return $this; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Set a global scale for the chart. |
| 255 | * Turns off autoscaling. |
| 256 | * @since 0.5 |
| 257 | * @param $min (int) |
| 258 | * @param $max (int) |
| 259 | * @return $this |
| 260 | */ |
| 261 | public function setScale($min, $max) |
| 262 | { |
| 263 | $this->setAutoscale(false); |
| 264 | |
| 265 | $this->scale = array( |
| 266 | 'min' => $min, |
| 267 | 'max' => $max |
| 268 | ); |
| 269 | return $this; |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * Return the scale. |
| 274 | * Note that after the chart has been computed, this function will returns |
| 275 | * the actual scale computed by the chart. |
| 276 | * |
| 277 | * @return array |
| 278 | */ |
| 279 | public function getScale() |
| 280 | { |
| 281 | return $this->scale; |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Compute the @c chds parameter. |
| 286 | * @internal |
| 287 | */ |
| 288 | public function computeChds() |
| 289 | { |
| 290 | if ( $this->scale === null ) { |
| 291 | throw new LogicException('Cannot compute scale that has not been set'); |
| 292 | } |
| 293 | return $this->scale['min'].','.$this->scale['max']; |
| 294 | } |
| 295 | //@} |
| 296 | |
| 297 | /** |
| 298 | * @name Chart title and style (chtt, chts) |
| 299 | */ |
| 300 | //@{ |
| 301 | /** |
| 302 | * Set the chart title (@c chtt). |
| 303 | * |
| 304 | * @param $title (string) |
| 305 | * |
| 306 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_chart_title |
| 307 | * @return $this |
| 308 | */ |
| 309 | public function setTitle($title) |
| 310 | { |
| 311 | $this->title = $title; |
| 312 | return $this; |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Returns chart title setted by setTitle(). |
| 317 | * @return string |
| 318 | */ |
| 319 | public function getTitle() |
| 320 | { |
| 321 | return $this->title; |
| 322 | } |
| 323 | |
| 324 | /** @internal |
| 325 | * Compute @c chtt parameter (chart title). |
| 326 | * |
| 327 | * @return string or null if parameter is not needed |
| 328 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_chart_title |
| 329 | */ |
| 330 | public function computeChtt() |
| 331 | { |
| 332 | if ( $this->title === null ) |
| 333 | return null; |
| 334 | |
| 335 | return str_replace(array("\r","\n"), array('','|'), $this->title); |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Set the color of the title (@c chts). |
| 340 | * |
| 341 | * @param $color (string in ) The title color, in RRGGBB hexadecimal format. Default color is black. |
| 342 | * |
| 343 | * @since 0.4 |
| 344 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_chart_title |
| 345 | */ |
| 346 | public function setTitleColor($color) |
| 347 | { |
| 348 | $this->chts = true; |
| 349 | $this->title_color = $color; |
| 350 | return $this; |
| 351 | } |
| 352 | |
| 353 | /** |
| 354 | * Returns the title color. |
| 355 | * |
| 356 | * If no title color has been set using setTitleColor(), it will returns |
| 357 | * the default title color. |
| 358 | * |
| 359 | * @since 0.4 |
| 360 | * @return string in RRGGBB format |
| 361 | */ |
| 362 | public function getTitleColor() |
| 363 | { |
| 364 | return $this->title_color; |
| 365 | } |
| 366 | |
| 367 | /** |
| 368 | * Set the font size of the title (@c chts). |
| 369 | * |
| 370 | * @param $size (int) Font size of the title, in points. |
| 371 | * |
| 372 | * @since 0.4 |
| 373 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_chart_title |
| 374 | */ |
| 375 | public function setTitleSize($size) |
| 376 | { |
| 377 | $this->chts = true; |
| 378 | $this->title_size = $size; |
| 379 | return $this; |
| 380 | } |
| 381 | |
| 382 | /** |
| 383 | * Returns the title size. |
| 384 | * |
| 385 | * If no title size has been set using setTitleSize(), it will returns the |
| 386 | * default title color. |
| 387 | * |
| 388 | * @since 0.4 |
| 389 | * @return string |
| 390 | */ |
| 391 | public function getTitleSize() |
| 392 | { |
| 393 | return $this->title_size; |
| 394 | } |
| 395 | |
| 396 | /** @internal |
| 397 | * Compute @c chts parameter. |
| 398 | * |
| 399 | * @since 0.4 |
| 400 | * @return string or null if the parameter is not needed |
| 401 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_chart_title |
| 402 | */ |
| 403 | public function computeChts() |
| 404 | { |
| 405 | return $this->title_color.','.$this->title_size; |
| 406 | } |
| 407 | |
| 408 | /** |
| 409 | * @internal |
| 410 | * Return true if chts parameter is needed |
| 411 | * Little trick here, if no title is set, then chts is not needed, even |
| 412 | * if specified |
| 413 | * @return bool |
| 414 | */ |
| 415 | public function hasChts() |
| 416 | { |
| 417 | return $this->chts; |
| 418 | } |
| 419 | |
| 420 | //@} |
| 421 | |
| 422 | /** |
| 423 | * @name Chart Legend Text and Style (@c chdl, @c chdlp, @c chma) |
| 424 | */ |
| 425 | //@{ |
| 426 | |
| 427 | /** |
| 428 | * Set position of the legend box (@c chdlp). |
| 429 | * |
| 430 | * The parameter is not checked so you can pass whatever you want. This way, |
| 431 | * if the Google Chart API evolves, this library will still works. However, |
| 432 | * be warned that you chart may not be displayed as expected if you pass wrong |
| 433 | * parameter. |
| 434 | * |
| 435 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_legend |
| 436 | * |
| 437 | * @param $position (string) |
| 438 | * One of the following: 'b', 'bv', 't', 'tv', 'r', 'l' |
| 439 | * (read Google Chart Documentation for details). |
| 440 | * @return $this |
| 441 | */ |
| 442 | public function setLegendPosition($position) |
| 443 | { |
| 444 | $this->legend_position = $position; |
| 445 | return $this; |
| 446 | } |
| 447 | |
| 448 | /** |
| 449 | * Set labels order inside the legend box (chdlp). |
| 450 | * |
| 451 | * The parameter is not checked so you can pass whatever you want. This way, |
| 452 | * if the Google Chart API evolves, this library will still works. However, |
| 453 | * be warned that you chart may not be displayed as expected if you pass wrong |
| 454 | * parameter. |
| 455 | * |
| 456 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_legend |
| 457 | * |
| 458 | * @param $label_order (string) |
| 459 | * One of the following: 'l', 'r', 'a', or a list of numbers |
| 460 | * separated by commas (read Google Chart Documentation for details). |
| 461 | * @return $this |
| 462 | */ |
| 463 | public function setLegendLabelOrder($label_order) |
| 464 | { |
| 465 | $this->legend_label_order = $label_order; |
| 466 | return $this; |
| 467 | } |
| 468 | |
| 469 | /** |
| 470 | * Set if empty legends entries shoud be skipped in the legend or not. |
| 471 | * |
| 472 | * @param $skip_empty (bool) |
| 473 | */ |
| 474 | public function setLegendSkipEmpty($skip_empty) |
| 475 | { |
| 476 | $this->legend_skip_empty = (bool) $skip_empty; |
| 477 | return $this; |
| 478 | } |
| 479 | |
| 480 | /** |
| 481 | * Size of the legend box (@c chma). |
| 482 | * |
| 483 | * @since 0.5 |
| 484 | * @param $width (int) |
| 485 | * @param $height (int) |
| 486 | */ |
| 487 | public function setLegendSize($width, $height) |
| 488 | { |
| 489 | $this->legend_size = array( |
| 490 | 'width' => $width, |
| 491 | 'heigh' => $height |
| 492 | ); |
| 493 | return $this; |
| 494 | } |
| 495 | |
| 496 | /** |
| 497 | * @internal |
| 498 | * @since 0.4 |
| 499 | */ |
| 500 | public function computeChdlp() |
| 501 | { |
| 502 | $str = ''; |
| 503 | if ( $this->legend_position !== null ) { |
| 504 | $str .= $this->legend_position; |
| 505 | } |
| 506 | if ( $this->legend_skip_empty === true ) { |
| 507 | $str .= 's'; |
| 508 | } |
| 509 | if ( $this->legend_label_order !== null ) { |
| 510 | $str .= '|'.$this->legend_label_order; |
| 511 | } |
| 512 | return $str; |
| 513 | } |
| 514 | |
| 515 | /** |
| 516 | * @internal |
| 517 | * @since 0.4 |
| 518 | */ |
| 519 | public function hasChdlp() |
| 520 | { |
| 521 | return $this->legend_skip_empty === true || $this->legend_position !== null || $this->legend_label_order !== null; |
| 522 | } |
| 523 | //@} |
| 524 | |
| 525 | /** |
| 526 | * Specify solid or dotted grid lines on the chart. (@c chg) |
| 527 | * |
| 528 | * @param $x_axis_step_size |
| 529 | * @param $y_axis_step_size |
| 530 | * @param $dash_length |
| 531 | * @param $space_length |
| 532 | * @param $x_offset |
| 533 | * @param $y_offset |
| 534 | * |
| 535 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_grid_lines |
| 536 | */ |
| 537 | public function setGridLines($x_axis_step_size, $y_axis_step_size, $dash_length = false, |
| 538 | $space_length = false, $x_offset = false, $y_offset = false) |
| 539 | { |
| 540 | $this->grid_lines = $x_axis_step_size.','.$y_axis_step_size; |
| 541 | if ( $dash_length !== false ) { |
| 542 | $this->grid_lines .= ','.$dash_length; |
| 543 | if ( $space_length !== false ) { |
| 544 | $this->grid_lines .= ','.$space_length; |
| 545 | if ( $x_offset !== false ) { |
| 546 | $this->grid_lines .= ','.$x_offset; |
| 547 | if ( $y_offset !== false ) { |
| 548 | $this->grid_lines .= ','.$y_offset; |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | } |
| 553 | return $this; |
| 554 | } |
| 555 | |
| 556 | /** |
| 557 | * @name Gradient, Solid and Stripped Fills (chf) |
| 558 | */ |
| 559 | //@{ |
| 560 | /** |
| 561 | * Set a solid background (fill) for an area (@c chf). |
| 562 | * |
| 563 | * @param $color (string) RGB color |
| 564 | * @param $area One of the following: |
| 565 | * - GoogleChart::BACKGROUND for the whole background |
| 566 | * - GoogleChart::CHART_AREA for only the chart area background |
| 567 | * @return $this |
| 568 | */ |
| 569 | public function setFill($color, $area = self::BACKGROUND) |
| 570 | { |
| 571 | if ( $area != self::BACKGROUND && $area != self::CHART_AREA ) { |
| 572 | throw new InvalidArgumentException('Invalid fill area.'); |
| 573 | } |
| 574 | |
| 575 | $this->fills[$area] = $area.',s,'.$color; |
| 576 | return $this; |
| 577 | } |
| 578 | |
| 579 | /** |
| 580 | * Set the opacity for solid background (fill). |
| 581 | * |
| 582 | * @param $opacity (int) Between 0 (transparent) and 100 (opaque) |
| 583 | * @return $this |
| 584 | */ |
| 585 | public function setOpacity($opacity) |
| 586 | { |
| 587 | if ( $opacity < 0 || $opacity > 100 ) { |
| 588 | throw new InvalidArgumentException('Invalid opacity (must be between 0 and 100).'); |
| 589 | } |
| 590 | |
| 591 | // 100% = 255 |
| 592 | $opacity = str_pad(dechex(round($opacity * 255 / 100)), 8, 0, STR_PAD_LEFT); |
| 593 | |
| 594 | // Opacity doesn't work with other backgrounds |
| 595 | $this->fills[self::BACKGROUND] = 'a,s,'.$opacity; |
| 596 | |
| 597 | return $this; |
| 598 | } |
| 599 | |
| 600 | /** |
| 601 | * Gradient fill. |
| 602 | * |
| 603 | * @param $angle (int) |
| 604 | * A number specifying the angle of the gradient |
| 605 | * from 0 (horizontal) to 90 (vertical). |
| 606 | * @param $colors (array) |
| 607 | * An array of color of the fill. Each color can be a |
| 608 | * string in RRGGBB hexadecimal format, or an array of two values: RRGGBB |
| 609 | * color, and color centerpoint. |
| 610 | * |
| 611 | * @see http://code.google.com/apis/chart/docs/chart_params.html#gcharts_gradient_fills |
| 612 | */ |
| 613 | public function setGradientFill($angle, array $colors, $area = self::BACKGROUND) |
| 614 | { |
| 615 | if ( $angle < 0 || $angle > 90 ) { |
| 616 | throw new InvalidArgumentException('Invalid angle (must be between 0 and 90).'); |
| 617 | } |
| 618 | |
| 619 | if ( ! isset($colors[1]) ) { |
| 620 | throw new InvalidArgumentException('You must specify at least 2 colors to create a gradient fill.'); |
| 621 | } |
| 622 | |
| 623 | if ( $area != self::BACKGROUND && $area != self::CHART_AREA ) { |
| 624 | throw new InvalidArgumentException('Invalid area.'); |
| 625 | } |
| 626 | |
| 627 | $tmp = array(); |
| 628 | $i = 0; |
| 629 | $n = sizeof($colors); |
| 630 | for ( $i = 0; $i < $n; $i++ ) { |
| 631 | $centerpoint = null; |
| 632 | $color = null; |
| 633 | |
| 634 | if ( is_array($colors[$i]) ) { |
| 635 | $c = $colors[$i]; |
| 636 | if ( ! isset($c[0]) ) { |
| 637 | throw new InvalidArgumentException('Each color must be an array of the color code in RRGGBB and the color centerpoint.'); |
| 638 | } |
| 639 | $color = $c[0]; |
| 640 | if ( isset($c[1]) ) { |
| 641 | $centerpoint = $c[1]; |
| 642 | } |
| 643 | } |
| 644 | else { |
| 645 | $color = $colors[$i]; |
| 646 | } |
| 647 | // No color centerpoint, try to calculate a good one: |
| 648 | if ( ! $centerpoint ) { |
| 649 | $centerpoint = $i / ($n-1); |
| 650 | } |
| 651 | $tmp[] = $color.','.round($centerpoint,2); |
| 652 | } |
| 653 | |
| 654 | $this->fills[$area] = $area.',lg,'.$angle.','.implode(',',$tmp); |
| 655 | } |
| 656 | |
| 657 | /** |
| 658 | * Striped fill. |
| 659 | * @todo |
| 660 | */ |
| 661 | public function setStripedFill($angle, array $colors, $area = self::BACKGROUND) |
| 662 | { |
| 663 | |
| 664 | } |
| 665 | //@} |
| 666 | |
| 667 | /** |
| 668 | * @name Chart Margins |
| 669 | */ |
| 670 | //@{ |
| 671 | |
| 672 | /** |
| 673 | * Set margin around the charts (@c chma). |
| 674 | * |
| 675 | * This function works like the CSS property "margin" : |
| 676 | * - If you specify only one parameter, then this value is used for all. |
| 677 | * - If you specify 2 parameters, then first is "top/bottom" and second is "left/right" |
| 678 | * - If you specify 4 parameters, then they are: top, right, bottom, left (tips: it's clockwise). |
| 679 | * |
| 680 | * @since 0.5 |
| 681 | * |
| 682 | * @param $top (float) |
| 683 | * @param $right (float) |
| 684 | * @param $bottom (float) |
| 685 | * @param $left (float) |
| 686 | * @return $this |
| 687 | */ |
| 688 | public function setMargin($top, $right = null, $bottom = null, $left = null) |
| 689 | { |
| 690 | // If only one value, then all have the same values |
| 691 | if ( $left === null && $right === null && $bottom === null ) { |
| 692 | $this->margin = array( |
| 693 | 'left' => (float) $top, |
| 694 | 'right' => (float) $top, |
| 695 | 'top' => (float) $top, |
| 696 | 'bottom' => (float) $top |
| 697 | ); |
| 698 | } |
| 699 | elseif ( $left === null && $bottom === null ) { |
| 700 | $this->margin = array( |
| 701 | 'left' => (float) $right, |
| 702 | 'right' => (float) $right, |
| 703 | 'top' => (float) $top, |
| 704 | 'bottom' => (float) $top |
| 705 | ); |
| 706 | } |
| 707 | else { |
| 708 | $this->margin = array( |
| 709 | 'left' => (float) $left, |
| 710 | 'right' => (float) $right, |
| 711 | 'top' => (float) $top, |
| 712 | 'bottom' => (float) $bottom |
| 713 | ); |
| 714 | } |
| 715 | return $this; |
| 716 | } |
| 717 | |
| 718 | /** |
| 719 | * @internal |
| 720 | */ |
| 721 | public function computeChma() |
| 722 | { |
| 723 | $str = ''; |
| 724 | if ( $this->margin ) { |
| 725 | $str = implode(',',$this->margin); |
| 726 | } |
| 727 | if ( $this->legend_size ) { |
| 728 | $str .= '|'.implode(',',$this->legend_size); |
| 729 | } |
| 730 | return $str; |
| 731 | } |
| 732 | |
| 733 | /** |
| 734 | * @internal |
| 735 | */ |
| 736 | public function hasChma() |
| 737 | { |
| 738 | return $this->margin !== null || $this->legend_size !== null; |
| 739 | } |
| 740 | |
| 741 | //@} |
| 742 | |
| 743 | /** |
| 744 | * @name URL creation |
| 745 | */ |
| 746 | //@{ |
| 747 | |
| 748 | /** |
| 749 | * Compute the whole query as an array. |
| 750 | * @internal |
| 751 | * Shouldn't be overrided, but who knows? |
| 752 | */ |
| 753 | protected function computeQuery() |
| 754 | { |
| 755 | $q = array( |
| 756 | 'cht' => $this->type, |
| 757 | 'chs' => $this->width.'x'.$this->height |
| 758 | ); |
| 759 | |
| 760 | $this->compute($q); |
| 761 | |
| 762 | $q = array_merge($q, $this->parameters); |
| 763 | |
| 764 | return $q; |
| 765 | } |
| 766 | |
| 767 | /** |
| 768 | * Compute the whole query as an array. |
| 769 | * @internal |
| 770 | * To be overrided by child classes. |
| 771 | */ |
| 772 | protected function compute(array & $q) |
| 773 | { |
| 774 | if ( $this->grid_lines ) { |
| 775 | $q['chg'] = $this->grid_lines; |
| 776 | } |
| 777 | if ( $this->fills ) { |
| 778 | $q['chf'] = implode('|',$this->fills); |
| 779 | } |
| 780 | |
| 781 | if ( $this->hasChma() ) { |
| 782 | $q['chma'] = $this->computeChma(); |
| 783 | } |
| 784 | $this->computeTitle($q); |
| 785 | |
| 786 | $this->computeScale($q); |
| 787 | $this->computeData($q); |
| 788 | $this->computeMarkers($q); |
| 789 | $this->computeAxes($q); |
| 790 | } |
| 791 | |
| 792 | /** |
| 793 | * Compute title related parameters (chtt and chts) |
| 794 | * @internal |
| 795 | */ |
| 796 | protected function computeTitle(array & $q) |
| 797 | { |
| 798 | if ( $this->title ) { |
| 799 | $q['chtt'] = $this->computeChtt(); |
| 800 | |
| 801 | if ( $this->hasChts() ) { |
| 802 | $q['chts'] = $this->computeChts(); |
| 803 | } |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | /** |
| 808 | * @internal |
| 809 | * @since 0.5 |
| 810 | */ |
| 811 | protected function computeScale(array & $q) |
| 812 | { |
| 813 | if ( ! $this->autoscale ) |
| 814 | return $this; |
| 815 | |
| 816 | $value_min = 0; |
| 817 | $value_max = 0; |
| 818 | |
| 819 | foreach ( $this->data as $i => $d ) { |
| 820 | $values = $d->getValues(); |
| 821 | if ( $values === null || empty($values) ) |
| 822 | continue; |
| 823 | |
| 824 | $max = max($values); |
| 825 | $min = min($values); |
| 826 | if ( $max > $value_max ) { |
| 827 | $value_max = $max; |
| 828 | } |
| 829 | if ( $min < $value_min ) { |
| 830 | $value_min = $min; |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | if ( $value_min > 0 ) |
| 835 | $value_min = 0; |
| 836 | |
| 837 | $this->scale = array('min' => $value_min, 'max' => $value_max); |
| 838 | return $this; |
| 839 | } |
| 840 | |
| 841 | /** |
| 842 | * Compute data series. |
| 843 | * |
| 844 | * @note This function is too long. I think it needs a redesign, but for the |
| 845 | * moment I have no idea how to make it shorter. |
| 846 | * |
| 847 | * @internal |
| 848 | */ |
| 849 | protected function computeData(array & $q) |
| 850 | { |
| 851 | $data = array(); |
| 852 | |
| 853 | $colors = array(); |
| 854 | $colors_needed = false; |
| 855 | |
| 856 | $styles = array(); |
| 857 | $styles_needed = false; |
| 858 | |
| 859 | $fills = array(); |
| 860 | |
| 861 | $scales = array(); |
| 862 | $scale_needed = false; |
| 863 | |
| 864 | $legends = array(); |
| 865 | $legends_needed = false; |
| 866 | |
| 867 | if ( $this->_compute_data_label ) { |
| 868 | $labels = array(); |
| 869 | } |
| 870 | |
| 871 | foreach ( $this->data as $i => $d ) { |
| 872 | // Data serie values and scale |
| 873 | if ( $d->hasValues() ) { |
| 874 | $data[] = $d->computeChd($this->data_format, $this->scale); |
| 875 | // Compute per-data scale only if autoscale if off |
| 876 | if ( ! $this->autoscale && ! $this->scale ) { |
| 877 | $scales[] = $d->computeChds(); |
| 878 | if ( $d->hasCustomScale() ) { |
| 879 | $scale_needed = true; |
| 880 | } |
| 881 | } |
| 882 | } |
| 883 | |
| 884 | // Data serie color (chco) |
| 885 | $colors[] = $d->computeChco(); |
| 886 | if ( $colors_needed == false && $d->hasChco() ) { |
| 887 | $colors_needed = true; |
| 888 | } |
| 889 | |
| 890 | // Data serie style (chls) |
| 891 | $styles[] = $d->computeChls(); |
| 892 | if ( $styles_needed == false && $d->hasChls() ) { |
| 893 | $styles_needed = true; |
| 894 | } |
| 895 | |
| 896 | $tmp = $d->computeChm($i); |
| 897 | if ( $tmp ) { |
| 898 | $fills[] = $tmp; |
| 899 | } |
| 900 | |
| 901 | $legends[] = $d->getLegend(); |
| 902 | if ( $legends_needed == false && $d->hasCustomLegend() ) { |
| 903 | $legends_needed = true; |
| 904 | } |
| 905 | |
| 906 | if ( $this->_compute_data_label ) { |
| 907 | $labels[] = $d->computeChl(); |
| 908 | } |
| 909 | } |
| 910 | if ( ! isset($data[0]) ) |
| 911 | return; |
| 912 | |
| 913 | $q['chd'] = $this->data_format.':'.implode($this->data_separator[$this->data_format],$data); |
| 914 | |
| 915 | if ( $colors_needed ) { |
| 916 | $q['chco'] = implode(',',$colors); |
| 917 | } |
| 918 | if ( $styles_needed ) { |
| 919 | $q['chls'] = implode('|',$styles); |
| 920 | } |
| 921 | |
| 922 | if ( $this->_compute_data_label ) { |
| 923 | $tmp = rtrim(implode('|',$labels),'|'); |
| 924 | if ( $tmp ) { |
| 925 | $q['chl'] = $tmp; |
| 926 | } |
| 927 | } |
| 928 | |
| 929 | if ( $this->scale ) { |
| 930 | $q['chds'] = $this->computeChds(); |
| 931 | } |
| 932 | elseif ( $scale_needed && isset($scales[0]) ) { |
| 933 | $q['chds'] = implode(',', $scales); |
| 934 | } |
| 935 | |
| 936 | // Legends |
| 937 | if ( $legends_needed ) { |
| 938 | $q['chdl'] = implode('|',$legends); |
| 939 | if ( $this->hasChdlp() ) { |
| 940 | $q['chdlp'] = $this->computeChdlp(); |
| 941 | } |
| 942 | } |
| 943 | |
| 944 | if ( isset($fills[0]) ) |
| 945 | $q['chm'] = implode('|',$fills); |
| 946 | |
| 947 | return $this; |
| 948 | } |
| 949 | |
| 950 | |
| 951 | /** |
| 952 | * Compute the markers. |
| 953 | * @internal |
| 954 | * This function loops through the lists of the markers. |
| 955 | */ |
| 956 | protected function computeMarkers(array & $q) |
| 957 | { |
| 958 | $markers = array(); |
| 959 | $dynamic_markers = array(); |
| 960 | $additional_data = array(); |
| 961 | |
| 962 | $nb_data_series = sizeof($this->data); |
| 963 | $current_index = $nb_data_series; |
| 964 | |
| 965 | $array = $this->markers + $this->dynamic_markers; |
| 966 | foreach ( $array as $m ) { |
| 967 | $data = $m->getData(); |
| 968 | |
| 969 | $index = null; |
| 970 | if ( $data ) { |
| 971 | // Get the data serie index |
| 972 | $index = $data->getIndex(); |
| 973 | if ( $index === null ) { |
| 974 | $additional_data[] = $data->computeChd($this->data_format); |
| 975 | $index = $current_index; |
| 976 | $current_index += 1; |
| 977 | } |
| 978 | } |
| 979 | |
| 980 | // Now $index contains the correct data serie index |
| 981 | $tmp = $m->compute($index, $this->type); |
| 982 | if ( $tmp === null ) |
| 983 | continue; // Ignore empty markers |
| 984 | |
| 985 | if ( $m instanceof GoogleChartMarker ) { |
| 986 | $markers[] = $tmp; |
| 987 | } |
| 988 | else { |
| 989 | $dynamic_markers[] = $tmp; |
| 990 | } |
| 991 | } |
| 992 | |
| 993 | if ( isset($markers[0]) ) { |
| 994 | $q['chm'] = (isset($q['chm']) ? $q['chm'].'|' : '').implode('|',$markers); |
| 995 | } |
| 996 | |
| 997 | if ( isset($dynamic_markers[0]) ) { |
| 998 | $q['chem'] = implode('|',$dynamic_markers); |
| 999 | } |
| 1000 | |
| 1001 | // Append every additional_data to 'chd' |
| 1002 | if ( isset($additional_data[0]) ) { |
| 1003 | $q['chd'] = $this->data_format.$nb_data_series.substr($q['chd'],1).$this->data_separator[$this->data_format].implode($this->data_separator[$this->data_format],$additional_data); |
| 1004 | } |
| 1005 | } |
| 1006 | |
| 1007 | /** |
| 1008 | * Compute axes. |
| 1009 | * @internal |
| 1010 | */ |
| 1011 | protected function computeAxes(array & $q) |
| 1012 | { |
| 1013 | $axes = array(); |
| 1014 | $labels = array(); |
| 1015 | $ranges = array(); |
| 1016 | $tick_marks = array(); |
| 1017 | $styles = array(); |
| 1018 | $label_positions = array(); |
| 1019 | foreach ( $this->axes as $i => $a ) { |
| 1020 | $axes[] = $a->getName(); |
| 1021 | if ( $a->hasCustomLabels() ) { |
| 1022 | $labels[] = sprintf($a->getLabels(), $i); |
| 1023 | } |
| 1024 | |
| 1025 | $tmp = $a->getRange(); |
| 1026 | if ( $tmp !== null ) { |
| 1027 | $ranges[] = sprintf($tmp, $i); |
| 1028 | } |
| 1029 | |
| 1030 | $tmp = $a->getTickMarks(); |
| 1031 | if ( $tmp !== null ) { |
| 1032 | $tick_marks[] = sprintf($tmp, $i); |
| 1033 | } |
| 1034 | |
| 1035 | $tmp = $a->computeChxs($i, $this->type); |
| 1036 | if ( $tmp !== null ) { |
| 1037 | $styles[] = $tmp; |
| 1038 | } |
| 1039 | |
| 1040 | if ( $a->hasChxp() ) { |
| 1041 | $label_positions[] = $a->computeChxp($i); |
| 1042 | } |
| 1043 | } |
| 1044 | if ( isset($axes[0]) ) { |
| 1045 | $q['chxt'] = implode(',',$axes); |
| 1046 | if ( isset($labels[0]) ) { |
| 1047 | $q['chxl'] = implode('|',$labels); |
| 1048 | } |
| 1049 | if ( isset($ranges[0]) ) { |
| 1050 | $q['chxr'] = implode('|', $ranges); |
| 1051 | } |
| 1052 | if ( isset($tick_marks[0]) ) { |
| 1053 | $q['chxtc'] = implode('|', $tick_marks); |
| 1054 | } |
| 1055 | if ( isset($styles[0]) ) { |
| 1056 | $q['chxs'] = implode('|', $styles); |
| 1057 | } |
| 1058 | if ( isset($label_positions[0]) ) { |
| 1059 | $q['chxp'] = implode('|',$label_positions); |
| 1060 | } |
| 1061 | } |
| 1062 | |
| 1063 | return $this; |
| 1064 | } |
| 1065 | |
| 1066 | //@} |
| 1067 | |
| 1068 | |
| 1069 | } |
| 1070 | |
| 1071 | /** @example line_chart.php |
| 1072 | * A basic example of how to work with line chart. |
| 1073 | */ |
| 1074 | /** @example line_chart_sin_cos.php |
| 1075 | * Another line chart example, with multiple data series. |
| 1076 | */ |
| 1077 | /** |
| 1078 | * @example line_chart_full.php |
| 1079 | * Another line chart example with plenty of options enabled. |
| 1080 | */ |
| 1081 |