Generar gráficos utilizando la librería GD

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.
Listado 5: Archivo Chart.class.php
<?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().

Listado 6: Archivo chart.php
<?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);
?>
Figura 3: Efecto del funcionamiento del script chart.php

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.

Figura 4: Efecto del funcionamiento del script chart2.php
Listado 7: Archivo chart2.php
<?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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP