Generalmente la forma m�s com�n de comparaci�n de datos son las tablas y los gr�ficos. La comparaci�n de datos en un gr�fico permite, sin duda, una interpretaci�n m�s sencilla. Es dif�cil imaginarse una mejor manera de visualizaci�n de gran cantidad de datos y diferentes tipos de dependencias (por ejemplo, la modelaci�n de los cursos de divisas) que diagramas generados din�micamente. En nuestro caso, nos ocuparemos de la creaci�n de un diagrama de barras.
En el Listado 5 se muestra una clase que permite crear diagramas a partir de los datos colocados en una tabla. Luego mostraremos c�mo se puede extender para que los diagramas tengan una presentaci�n m�s interesante.
Iniciaremos la creaci�n de la clase Chart defini�ndole sus atributos:
- image � identificador de la imagen,
- width, height � tama�os del gr�fico resultante,
- margin_x, margin_y � m�rgenes,
- _x_min, _x_max, _y_min, _y_max � limitaci�n del espacio para el gr�fico,
- _range_w, _range_h � ancho y altura del espacio para el gr�fico.
<?php
class Chart {
var $image; //identicador de la imagen
var $title = 'Chart'; //t�tulo del diagrama
var $width = 800; //ancho de la imagen resultante
var $height = 600; //altura de la imagen resultante
var $margin_x = 40; //margen horizontal
var $margin_y = 40; //margen vertical
var $bar_w = 0.8; //relaci�n del ancho de la columna a
//la distancia entre ellas
var $bar_color = Array(100, 160, 200); //color de las columnas
var $text_color = Array(0, 0, 0); //color del texto
var $grid_color = Array(240, 240, 200); //color de la cuadr�cula
var $_x_min; //valor m�nimo de x del espacio para el diagrama
var $_x_max; //valor m�ximo de x del espacio para el diagrama
var $_y_min; //valor m�nimo de y del espacio para el diagrama
var $_y_max; //valor m�ximo de y del espacio para el diagrama
var $_range_w; //ancho del espacio para el diagrama
var $_range_h; //altura del espacio para el diagrama
/**
* M�todo draw � genera la imagen
* como argumento recibe la tabla de valores
*/
function draw($series) {
//generamos la imagen cuya altura y ancho son definidos
$this->image = imagecreate($this->width, $this->height);
//asignamos el color de fondo
$this->translateColor();
//definimos el espacio para la imagen
$this->calcRange($series);
//dibujamos la cuadr�cula
$this->drawGrid();
//colocamos los valores consecutivos en el diagrama
foreach($series as $x=>$y) {
$this->setValue($x, $y);
}
//colocamos el t�tulo del diagrama
imagestring($this->image, 5, $this->margin_x/2, $this->margin_y/2,
$this->title, $this->translateColor($this->text_color));
header("Content-type: image/png");
imagepng($this->image);
imagedestroy($this->image);
exit;
}
/**
* M�todo drawGrid � dibuja la cuadr�cula sobre la imagen
* su par�metro puede ser la distancia entre las l�neas consecutivas
*/
function drawGrid($size = 20) {
$y = $size*round($this->_y_min/$size) - $size;
while($y < ($this->_y_max + $size)) {
//calculamos los valores consecutivos de y
$y0 = $this->translateCoordY($y);
//dibujamos las l�neas horizontales
imageline($this->image, 0, $y0, $this->width, $y0,
$this->translateColor($this->grid_color));
$y += $size;
}
}
/**
* M�todo setValue � dibuja un rect�ngulo con base en los par�metros recibidos
* los par�metros son los valores x y y
*/
function setValue($x, $y) {
//definimos todas las coordenadas del rect�ngulo de ancho de bar_w
$p = Array(
($x-$this->bar_w/2), $y,
($x+$this->bar_w/2), $y,
($x+$this->bar_w/2), 0,
($x-$this->bar_w/2), 0
);
//Las convertimos a coordenadas de la imagen
$r = $this->translatePoly($p);
//dibujamos el rect�ngulo
imagefilledpolygon($this->image, $r, sizeof($r)/2,
$this->translateColor($this->bar_color));
}
/**
* M�todo calcRange � define el espacio para la gr�fica
* su par�metro es la tabla de valores
*/
function calcRange($series) {
//comenzamos en el inicio de las coordenadas polares
$this->_x_min = 0; $this->_y_min = 0;
$this->_x_max = 1; $this->_y_max = 1;
foreach($series as $x=>$y) {
//cambiamos el rango dependiendo del valor
if($x >= $this->_x_max) $this->_x_max = $x;
if($x < $this->_x_min) $this->_x_min = $x;
if($y >= $this->_y_max) $this->_y_max = $y;
if($y < $this->_y_min) $this->_y_min = $y;
}
//definimos el ancho y la altura del diagrama
$this->_range_w = $this->_x_max-$this->_x_min;
$this->_range_h = $this->_y_max-$this->_y_min;
}
/**
* M�todo translatePoly � proyecta los puntos del espacio del diagrama
* al area de la imagen su par�metro es la tabla con los puntos x, y
* consecutivos
*/
function translatePoly($p) {
$r = Array();
for($i=0; $i<sizeof($p); $i+=2) {
//convertimos los puntos consecutivamente
$r[] = $this->translateCoordX($p[$i]);
$r[] = $this->translateCoordY($p[$i+1]);
}
return $r;
}
/**
* M�todos translateCoordX y translateCoordY � transforman las coordenadas
* del diagrama su par�metro es el valor x y y correspondientemente
* el m�todo devuelve el valor de la coordenada para el �rea de la imagen
*/
function translateCoordX($x) {
return round($this->margin_x+($this->width-2*$this->margin_x)*
($x-$this->_x_min)/$this->_range_w);
}
function translateCoordY($y) {
return round($this->margin_y-($this->height-2*$this->margin_y)*
($y-$this->_y_max)/$this->_range_h);
}
/**
* M�todo translateColor � permite obtener el identificador del color
* su par�metro es la tabla de colores RGB
* el m�todo devuelve el identificador del color
*/
function translateColor($rgb = Array(255, 255, 255)) {
return imagecolorallocate($this->image, $rgb[0], $rgb[1], $rgb[2]);
}
}
?>
El m�todo principal es el draw(), cuya invocaci�n permitir� generar el gr�fico. El par�metro de la invocaci�n es la tabla que contiene la serie de datos para presentar. Para colocar adecuadamente los valores en el diagrama crearemos el m�todo que determina su rango calcRange() y los m�todos - translateCoordX() y translateCoordY() - que permitir�n proyectar los puntos del espacio del diagrama al espacio definido de la imagen resultante. Estas acciones garantizar�n la presentaci�n de la serie de datos en el lugar adecuado, sin necesidad de adaptarlos de antemano.
El m�todo setValue() servir� para colocar un valor determinado en el diagrama dibujando una barra de altura determinada. Para seleccionar el color utilizaremos el m�todo translateColor(), que ser� responsable de su asignaci�n.
En el Listado 6 (chart.php) se mostr� un ejemplo del uso de la clase Chart, y su efecto fue visualizado en la Figura 3. En el ejemplo se cre� el objeto Chart, luego se rellen� la tabla de datos con valores aleatorios en el rango (-200, 200). El diagrama fue dibujado llamando al m�todo draw().
<?php
require('Chart.class.php');
//cantidad de valores
$count = 32;
//objeto Chart
$chart = new Chart();
//creamos una tabla con
//valores aleatorios
$array = Array();
for($i=0; $i<$count; $i++) {
$array[] = rand(-200, 200);
}
//colocamos los valores y
//generamos la imagen
$chart->draw($array);
?>

Tambi�n podemos utilizar la clase Chart de una manera mucho m�s efectiva y dibujar un diagrama similar pero mucho m�s complejo � v�ase el Listado 7. El efecto del funcionamiento del script chart2.php se puede apreciar en la Figura 4.

<?php
require('Chart.class.php');
class CChart extends Chart {
var $shadow_color = Array(0, 0, 0); // color de la sombra
var $shadow_size = 2; //desplazamiento de la sombra
//M�todo setValue � dibuja un rect�ngulo con base en los valores proporcionados
//los par�metros son los valores x y y
function setValue($x, $y) {
//definimos todas las coordenadas del rect�ngulo
$x0 = $this->translateCoordX($x-$this->bar_w/2);
$y0 = $this->translateCoordY($y);
$x1 = $this->translateCoordX($x+$this->bar_w/2);
$y1 = $this->translateCoordY(0);
//desplazamos las coordenadas para obtener el efecto de sombra
$r = Array(
$x0+$this->shadow_size, $y0+$this->shadow_size,
$x0+$this->shadow_size, $y1,
$x1+$this->shadow_size, $y1,
$x1+$this->shadow_size, $y0+$this->shadow_size
);
//dibujamos la sombra
imagefilledpolygon($this->image, $r, sizeof($r)/2,
$this->translateColor($this->shadow_color));
//definimos el color que depende de la posici�n en el eje y cuya claridad depende del valor
if($y < 0) {
$bar_color_id = $this->translateColor(Array(170, 255, 0),
1-$y/$this->_y_min);
} else
{
$bar_color_id = $this->translateColor(Array(0, 135, 200),
1-$y/$this->_y_max);
}
$r = Array($x0, $y0, $x0, $y1, $x1, $y1, $x1, $y0);
//dibujamos el rect�ngulo s�lido
imagefilledpolygon($this->image, $r, sizeof($r)/2, $bar_color_id);
//dibujamos el marco sobre el rect�ngulo
imagepolygon($this->image, $r, sizeof($r)/2,
$this->translateColor($this->shadow_color));
//describimos el valor tomando en consideraci�n su posici�n
if($y < 0) {
imagestring($this->image, 0, $x0, $y0+4, $y,
$this->translateColor($this->text_color));
} else
{
imagestring($this->image, 0, $x0, $y0-10, $y,
$this->translateColor($this->text_color));
}
}
//M�todo translateColor � permite obtener el identificador correspondiente del color aclarado,
//el par�metro es la tabla de los componente RGB del color y el nivel de claridad del color
//el m�todo devuelve el identicador del color
function translateColor($rgb = Array(255, 255, 255), $p=0) {
if($p>255) {
$p = 255;
} else
if($p<0) {
$p = 0;
}
//enumeramos los valores de los componentes del color
$r = round($rgb[0]+(255-$rgb[0])*$p);
$g = round($rgb[1]+(255-$rgb[1])*$p);
$b = round($rgb[2]+(255-$rgb[2])*$p);
return imagecolorallocate($this->image, $r, $g, $b);
}
}
//n�mero de valores
$count = 32;
//objeto Chart
$chart = new CChart();
//creamos la tabla de los valores aleatorios consecutivos
$array = Array();
for($i=0; $i<$count; $i++) {
$array[] = rand(-200, 200);
}
//colocamos los valores y generamos la imagen
$chart->draw($array);
?>
En el archivo chart2.php aumentamos la clase Chart sobrecargando los m�todos setValue() y translateColor(). En este caso, para unos mejores efectos de visualizaci�n, nos serviremos de sombras y de diferentes niveles de claridad de los colores. Las barras las dibujaremos con marcos para que sean m�s distinguibles.
�M�todos de la clase Chart
- M�todo draw()
- Como ya se mencion� con anterioridad, el par�metro de invocaci�n de este m�todo es una serie de datos: tabla de valores. Para empezar, creamos la imagen de ancho width y altura height. Seleccionamos un color, �ste ser� el color del fondo de la imagen, translateColor(), nota: ejecutando este m�todo sin par�metros recibir�amos el color blanco. Luego asignamos el espacio para el diagrama: m�todo calcRange(). La cuadr�cula ser� dibujada llamando al m�todo drawGrid(). Las barras que representan los valores son dibujadas invocando el m�todo setValue() dentro de un bucle, sus par�metros son la llave y el valor de los elementos de la tabla de datos. Luego se coloca el t�tulo del diagrama con un color dado. Para terminar se env�a el encabezado y se genera la imagen para visualizar. Antes de terminar el script, se libera el espacio de memoria ocupado por la imagen.
- M�todo calcRange()
- Este m�todo permite seleccionar el rango de datos. Dado que el espacio del diagrama no puede estar vac�o, iniciamos definiendo un campo de tama�o 1 por 1. En el bucle analizamos los valores consecutivos y de acuerdo a ellos ampliamos ese espacio. Despu�s de obtener los valores m�nimos y m�ximos, definimos el ancho y la altura de ese espacio.
- M�todo translateCoordX() y translateCoordY()
- Estos m�todos son usados para definir las coordenadas del pixel en la imagen para el elemento dado en la tabla de datos, es decir, para la llave y el valor dado recibimos respectivamente las coordenadas x y y del punto. De esta manera podemos proyectar los puntos del espacio del diagrama a un espacio limitado de la imagen. Adem�s tomamos en cuenta los m�rgenes asignados a la imagen.
- M�todo translatePoly()
- Es un m�todo auxiliar que permite realizar la proyecci�n de muchos puntos consecutivos al espacio de la imagen. Los valores x y y del primer punto son el primer y el segundo elemento de la tabla, el tercer y el cuarto elemento son las coordenadas del siguiente punto, etc... El m�todo fue introducido para dibujar pol�gonos (imagepolygon() e imagefilledpolygon()), donde se utilizaba un acuerdo id�ntico de almacenamiento de las coordenadas de los puntos.
- M�todo translateColor()
- Su par�metro al ser invocado es una tabla de tres elementos, es decir, los elementos del color: R (rojo), G (verde), B (azul). Devuelve el identificador correspondiente del color.
- M�todo drawGrid()
- El par�metro opcional de invocaci�n de este m�todo, es el valor que indica cada cu�ntas l�neas hay que colocar una l�nea horizontal. Las l�neas son colocadas en un bucle de tal manera que la primera se encuentre debajo del valor m�nimo de la barra y la �ltima por encima de la barra de mayor valor. Desde luego, entre ellas debe haber una l�nea que corresponde al eje horizontal del diagrama. Las l�neas abarcan todo el ancho de la imagen.
- M�todo setValue()
- Es el m�todo responsable de dibujar barras que corresponden a los valores consecutivos del diagrama. La barra es un rect�ngulo cuyo ancho esta definido por el atributo bar_w. La posici�n x y el nivel y dependen de la llave y del valor del elemento de la tabla que contiene una serie de datos. Las coordenadas definidas se proyectan al espacio de la imagen, luego se coloca el pol�gono, en nuestro caso, un rect�ngulo.
�Extensi�n de la clase Chart � CChart
La clase CChart es la extensi�n de la clase Chart que consiste en la variaci�n en la manera de colocar las barras, a�adiendo el cambio del color dependiendo del valor que se le asigna. Aqu� se sobrecarg� los m�todos setValue() y translateColor(). Tambi�n se le a�adi� dos nuevos atributos: shadow_color y shadow_size.
- M�todo translateColor()
- El m�todo fue modificado en relaci�n a su primera versi�n. La diferencia consiste en haber introducido el peso, quedando como segundo par�metro en la invocaci�n del m�todo. El peso define el nivel de claridad del color, donde el valor 1 nos dar� el color blanco y el valor 0 no influir� en el color.
- M�todo setValue()
- Para empezar, definimos las coordenadas de los puntos que corresponden a los v�rtices de la barra. Modific�ndolos de manera adecuada dibujamos el efecto de sombras. Luego, definimos el valor correspondiente del rect�ngulo tomando en cuenta el valor que le corresponde a la barra. Dependiendo de si el valor es positivo o negativo, seleccionamos uno de los dos colores, lo aclaramos: menos para valores mayores y m�s para valores menores. Adem�s dibujamos un marco que resaltar� los l�mites de las barras. Para terminar, colocamos, ya sea por encima o por debajo de la barra, el valor en forma num�rica.