mirror of
https://github.com/php/web-php.git
synced 2026-04-28 01:13:10 +02:00
6b2d7bb52f
instead of a GIF to be more politically correct and it also uses slightly transparent bars.
386 lines
9.2 KiB
C++
386 lines
9.2 KiB
C++
<?php /* -*- C++ -*- */
|
|
/*
|
|
** $Id$
|
|
** $Source$
|
|
**
|
|
** PHP Class for creating bar charts using the GD library functions.
|
|
**
|
|
** Authors: Bjørn Borud, <borud@guardian.no>
|
|
*/
|
|
|
|
class barchart {
|
|
/* minimum allowed maximum */
|
|
var $min_allowed_max;
|
|
|
|
/* Image data */
|
|
var $im;
|
|
var $width, $height;
|
|
var $white, $black, $gray;
|
|
var $data;
|
|
var $colors;
|
|
var $legends;
|
|
|
|
/* margins for drawing area */
|
|
var $left = 60;
|
|
var $right = 200;
|
|
var $top = 70;
|
|
var $bottom = 70;
|
|
var $y_tick_padding = 10;
|
|
|
|
/* drawing parameters */
|
|
var $bar_width;
|
|
var $bar_padding = 5; /* space between bars */
|
|
var $y_scale;
|
|
|
|
/* legends, labels and titles */
|
|
var $y_label = "";
|
|
var $title = "";
|
|
|
|
/* drawing area (the graph without borders etc.) */
|
|
var $d_width, $d_height;
|
|
|
|
var $y_ticks;
|
|
var $y_data_max;
|
|
var $y_bar_max;
|
|
|
|
var $num_bars, $bar_width, $num_parts;
|
|
|
|
/* default fonts */
|
|
var $legend_font = 2;
|
|
|
|
|
|
/* Compute the values for Y axis ticks */
|
|
function get_y_ticks ($y_max) {
|
|
$order_y = floor(log10($y_max));
|
|
$magnitude = pow(10, $order_y);
|
|
$fdig = ($y_max / $magnitude) * 10;
|
|
$top = ceil($fdig) * $magnitude * 0.1;
|
|
|
|
/* Limit number of divisions to max 10*/
|
|
if ($fdig < 50) {
|
|
$divs = 0.5;
|
|
} else {
|
|
$divs = 1.0;
|
|
}
|
|
|
|
/* compute number of horizontal Y lines */
|
|
|
|
$tlines = $top / ($divs * $magnitude);
|
|
for ($i = 0; $i < $tlines; $i++) {
|
|
$retval[] = ($i + 1) * $divs * $magnitude;
|
|
}
|
|
|
|
return($retval);
|
|
}
|
|
|
|
|
|
/* find the max sum of a bar */
|
|
function get_max_value ($d) {
|
|
$bar_count = count($d);
|
|
$part_count = count($d[0]);
|
|
$max_value = 0;
|
|
|
|
for ($i = 0; $i < $bar_count; $i++) {
|
|
$sum = 0;
|
|
for ($j = 0; $j < $part_count; $j++) {
|
|
$sum += $d[$i][$j];
|
|
}
|
|
|
|
if ($sum > $max_value) {
|
|
$max_value = $sum;
|
|
}
|
|
}
|
|
/* If max is too small, set it to something sane. */
|
|
if ($max_value <= $this->min_allowed_max) {
|
|
$max_value = $this->min_allowed_max;
|
|
}
|
|
|
|
return($max_value);
|
|
}
|
|
|
|
|
|
function init($x, $y, $d, $c, $l) {
|
|
$this->min_allowed_max = 5;
|
|
$this->im = ImageCreateTrueColor($x, $y);
|
|
$this->width = $x;
|
|
$this->height = $y;
|
|
|
|
$this->data = $d;
|
|
$this->legends = $l;
|
|
|
|
$this->num_bars = count($this->data);
|
|
$this->num_parts = count($this->data[0]);
|
|
|
|
/* drawing area */
|
|
$this->d_width = $this->width - $this->right - $this->left;
|
|
$this->d_height = $this->height - $this->top - $this->bottom;
|
|
|
|
$this->y_data_max = $this->get_max_value($this->data);
|
|
$this->y_ticks = $this->get_y_ticks($this->y_data_max);
|
|
$this->y_bar_max = $this->y_ticks[count($this->y_ticks) - 1];
|
|
|
|
$this->bar_width = ($this->d_width / $this->num_bars);
|
|
|
|
|
|
/* if the bar width is real small then round to 1 */
|
|
if ($this->bar_width < 1) {
|
|
$this->bar_width = 1;
|
|
$this->bar_padding = 0;
|
|
}
|
|
|
|
$this->y_scale = $this->d_height / $this->y_bar_max;
|
|
|
|
|
|
$this->white = ImageColorAllocate($this->im, 255, 255, 255);
|
|
$this->black = ImageColorAllocate($this->im, 0, 0, 0);
|
|
$this->red = ImageColorAllocate($this->im, 190, 0, 0);
|
|
$this->green = ImageColorAllocate($this->im, 0, 190, 0);
|
|
$this->blue = ImageColorAllocate($this->im, 0, 0, 190);
|
|
$this->gray = ImageColorAllocate($this->im, 204, 204, 204);
|
|
|
|
$num_colors = count($c);
|
|
|
|
ImageAlphaBlending($this->im, false);
|
|
ImageFilledRectangle($this->im, 0, 0, $x, $y, $this->white);
|
|
ImageAlphaBlending($this->im, true);
|
|
|
|
for ($i = 0; $i < $num_colors; $i++) {
|
|
if(count($c[$i])>3) {
|
|
$this->colors[] = ImageColorResolveAlpha($this->im,
|
|
$c[$i][0],
|
|
$c[$i][1],
|
|
$c[$i][2],
|
|
$c[$i][3]);
|
|
|
|
} else {
|
|
$this->colors[] = ImageColorAllocate($this->im,
|
|
$c[$i][0],
|
|
$c[$i][1],
|
|
$c[$i][2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** remap a pair of coordinates. sort coordinates if we're
|
|
** going to use them to draw a rectangle by setting the last
|
|
** argument equal to 1
|
|
*/
|
|
function remap_quad($x1, $y1, $x2, $y2, $rect) {
|
|
$tx1 = $x1 + $this->left;
|
|
$tx2 = $x2 + $this->left;
|
|
|
|
$ty1 = $this->height - $this->bottom - $y1;
|
|
$ty2 = $this->height - $this->bottom - $y2;
|
|
|
|
if ($rect == 1) {
|
|
if ($tx1 > $tx2) { $tmp = $tx1; $tx1 = $tx2; $tx2 = $tmp;}
|
|
if ($ty1 > $ty2) { $tmp = $ty1; $ty1 = $ty2; $ty2 = $tmp;}
|
|
}
|
|
|
|
return(array($tx1, $ty1, $tx2, $ty2));
|
|
}
|
|
|
|
|
|
function draw_point($x, $y) {
|
|
ImageLine($this->im, $x-4, $y-4, $x+4, $y+4, $this->black);
|
|
ImageLine($this->im, $x-4, $y+4, $x+4, $y-4, $this->black);
|
|
}
|
|
|
|
|
|
function draw_line($x1, $y1, $x2, $y2, $color) {
|
|
$c = $this->remap_quad($x1, $y1, $x2, $y2, 0);
|
|
ImageLine($this->im, $c[0], $c[1], $c[2], $c[3], $color);
|
|
}
|
|
|
|
|
|
function draw_filled_rect($x1, $y1, $x2, $y2, $color) {
|
|
$c = $this->remap_quad($x1, $y1, $x2, $y2, 1);
|
|
|
|
ImageFilledRectangle($this->im, $c[0], $c[1],
|
|
$c[2], $c[3], $color);
|
|
}
|
|
|
|
|
|
function draw_string ($size, $x, $y, $s, $color) {
|
|
$c = $this->remap_quad($x, $y, 0, 0, 0);
|
|
ImageString($this->im, $size, $c[0], $c[1], $s, $color);
|
|
}
|
|
|
|
|
|
function draw_string_up($size, $x, $y, $s, $color) {
|
|
$c = $this->remap_quad($x, $y, 0, 0, 0);
|
|
ImageStringUp($this->im, $size, $c[0], $c[1], $s, $color);
|
|
}
|
|
|
|
|
|
function string_width ($s, $size) {
|
|
return(strlen($s) * ImageFontWidth($size));
|
|
}
|
|
|
|
|
|
function draw_title($title, $fontsize) {
|
|
|
|
$x = ($this->width - $this->string_width($title, $fontsize)) / 2;
|
|
$y = ($this->top - $this->yx[$fontsize]) / 3;
|
|
|
|
ImageString($this->im, $fontsize, $x, $y, $title, $this->black);
|
|
}
|
|
|
|
|
|
function draw_x_labels ($labels, $fontsize) {
|
|
|
|
/* how many lines of labels */
|
|
$n_lines = count($labels);
|
|
|
|
$y_offset = 10;
|
|
|
|
/* y position of the first row of labels */
|
|
$y = $this->height - $this->bottom + $y_offset;
|
|
|
|
for ($i = 0; $i < $n_lines; $i++) {
|
|
|
|
/* number of "columns" */
|
|
$n_strings = count($labels[$i]);
|
|
$dx = $this->d_width / (double)$n_strings;
|
|
$half_padded_dx = ($dx - $this->bar_padding) / 2.0;
|
|
|
|
for ($j = 0; $j < $n_strings; $j++) {
|
|
|
|
$label_width = $this->string_width($labels[$i][$j], $fontsize);
|
|
|
|
$x = $this->left + ($j * $dx) + $this->bar_padding +
|
|
$half_padded_dx - ($label_width / 2);
|
|
|
|
ImageString($this->im,
|
|
$fontsize,
|
|
$x,
|
|
$y,
|
|
$labels[$i][$j],
|
|
$this->black);
|
|
}
|
|
$y += ImageFontHeight($fontsize) + 2;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
function draw_axis ($axis_color, $font_color, $fontsize) {
|
|
|
|
$this->draw_line(0,-1,$this->d_width + 10 ,-1, $axis_color);
|
|
$this->draw_line(0,0,0,$this->d_height + 10, $axis_color);
|
|
|
|
$num_y_lines = count($this->y_ticks);
|
|
|
|
/* draw the zero */
|
|
$this->draw_string($fontsize,
|
|
- (ImageFontWidth($fontsize)
|
|
+ $this->y_tick_padding),
|
|
(ImageFontHeight($fontsize) * 0.5),
|
|
0,
|
|
$font_color);
|
|
|
|
/* draw the horizontal lines and their values */
|
|
for ($i = 0; $i < $num_y_lines; $i++) {
|
|
$y = $this->y_scale * $this->y_ticks[$i];
|
|
$this->draw_line(0, $y, $this->d_width, $y, $axis_color);
|
|
|
|
$y_tick = (int) $this->y_ticks[$i];
|
|
$x_pos = - ($this->string_width($y_tick, $fontsize)
|
|
+ $this->y_tick_padding);
|
|
|
|
$this->draw_string($fontsize,
|
|
$x_pos,
|
|
$y + (ImageFontHeight($fontsize) * 0.5),
|
|
$y_tick,
|
|
$font_color);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
function draw_legends ($fontsize) {
|
|
|
|
$num_legends = count($this->legends);
|
|
|
|
$y1 = 0;
|
|
for ($i = $num_legends -1; $i >= 0; $i--) {
|
|
|
|
$x1 = $this->d_width + 20;
|
|
$y1 += ImageFontHeight($fontsize) * 2;
|
|
$x2 = $this->d_width + 20 + ImageFontHeight($fontsize);
|
|
$y2 = $y1 + ImageFontHeight($fontsize);
|
|
|
|
|
|
$this->draw_filled_rect($x1, $y1, $x2, $y2, $this->colors[$i]);
|
|
|
|
$this->draw_string($fontsize,
|
|
$x2 + 10,
|
|
$y2,
|
|
$this->legends[$i],
|
|
$this->black);
|
|
|
|
}
|
|
|
|
/* Draw Y axis unit */
|
|
$yaxisunit_font = 2;
|
|
$ycenterpos = ($this->d_height / 2) - ($this->string_width($yaxisunit_font, $this->y_label) / 2);
|
|
$this->draw_string_up($yaxisunit_font, -60, $ycenterpos, $this->y_label, $this->black);
|
|
|
|
/* Draw graph title */
|
|
$title_font = 5;
|
|
$xcenterpos = ($this->d_width / 2) - ($this->string_width($title_font, $this->title) / 2);
|
|
$this->draw_string($title_font, $xcenterpos, $this->d_height + 40, $this->title, $this->black);
|
|
}
|
|
|
|
|
|
function draw_bars() {
|
|
for ($i = 0; $i < $this->num_bars; $i++) {
|
|
|
|
$x1 = $i * $this->bar_width + $this->bar_padding;
|
|
$x2 = $x1 + $this->bar_width - $this->bar_padding;
|
|
|
|
$y1 = 0;
|
|
|
|
for ($j = $this->num_parts-1; $j >= 0; $j--) {
|
|
|
|
$y2 = $y1 + $this->data[$i][$j];
|
|
$this->draw_filled_rect($x1,
|
|
$y1 * $this->y_scale,
|
|
$x2,
|
|
$y2 * $this->y_scale,
|
|
$this->colors[$j] );
|
|
|
|
$y1 += $this->data[$i][$j];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
function display() {
|
|
|
|
$this->draw_axis($this->black, $this->red, 2);
|
|
$this->draw_legends($this->legend_font);
|
|
$this->draw_bars();
|
|
|
|
ImagePNG($this->im);
|
|
}
|
|
|
|
|
|
function destroy() {
|
|
if ($this->im) {
|
|
ImageDestroy($this->im);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Local Variables:
|
|
* tab-width: 3
|
|
* End:
|
|
*/
|
|
?>
|