Como generar imágenes dinámicamente con PHP
Objetivos:
*Aprender el uso de algunas funciones básicas para la creación dinámica de imágenes
*Aprender a utilizar la lógica de programación de un grafico de barras dinámico
Una de los paquetes de funciones que este grandioso lenguaje de programación web trae es el de generar imágenes dinámicamente, para que podrÃa servir esto? imagÃnense un caso en donde quieran mostrar un gráfico de barras con los datos de una estadÃstica, (sea estadÃstica de visitas, parte de un cuadro de mando integral o lo que quieran imaginarse)
En este simple artÃculo explicare brevemente algunas de las tantas funciones que trae PHP en un ejemplo práctico.
Antes que nada imaginemos una necesidad puntual para permitir que PHP nos ayude, en este caso vamos a imaginar que necesitamos hacer un gráfico de barras con los datos de unas estadÃsticas de visitas.
Estas son las unciones que vamos a utilizar y en cierta forma también de que manera:
imagecreate(int ancho, int alto)
Esta función crea la base, el “lienzo donde haremos nuestras pinceladas…
ImageColorAllocate($lienzo, R, G, B)
Con esta función, definiremos los colores que queremos.
imagefilledrectangle($lienzo, int x1, int y1, int x2, int y2, $color)
Esta función nos creara cuadrados/rectángulos rellenos. Doy por sentado que saben que el par x1, y1 equivale al punto superior izquierdo y el otro par al punto inferior derecho.
imageline($lienzo,int x1, int y1, int x2, int y2, $color);
Con esta trazaremos lÃneas rectas, Se es sabido que para trazar una recta se hacen falta dos puntos, en estos casos los dos puntos extremos.
ImageString($lienzo, fuente[1,2,3,4,5],int x,int y, string, $color);
Con esta funcion escribiremos los detalles en el grafico.
imgpng($lienzo, "nombredearchivo.png")
Con esta función guardaremos la imagen creada.
Ahora bien, arremanguémonos…
De la teorÃa a la práctica
Antes que nada debemos traer los datos, voy a suponer que ya los han traÃdo de algún lado y se lo pasamos a nuestra función que crea las imágenes en un par de arrays indexados que explicare a continuación.
Declaramos la función dibujar Imagen llamada con los siguientes parámetros (llamadas por ejemplo de una base de datos);
* arrayValores es un array de 6 elementos (uno por cada dÃa a mostrar) que contiene la cantidad de visitas en cada posición del array.
* $arrayDias contiene los dÃas en el mismo orden que las visitas, (pudo haberlos juntado en un solo array pero me resulto mas didáctico asÃ)
* $prom es un numero con el promedio ya calculado, todos me imagino sabrán sacar el promedio de los contenidos de un array por eso ya lo traigo de afuera
function dibujarImagen($ArrayValores, $arrayDÃas, $prom);
Vamos a invertir los arrays para que se vea bien en las barras (elúltimo dÃa al final)
$arrayValores = array_reverse($arrayValores);
$arrayDias = array_reverse($arrayDias);
El próximo paso es crear el ambiente de trabajo (lienzo) donde vamos a dibujar.
Con la función que les comente anteriormente, creamos una imagen de 300px x 200px, y el identificador del área de trabajo la guardamos en $lienzo
$lienzo = imagecreate(300, 200);
Bárbaro, ya tenemos un lindo espacio de trabajo, vamos a renegar con los colores, ya verán que es fácil.
Trabajaremos con 5 colores, vamos a definirlos (recordemos cuando explique la función que requerÃa valores R, G, B:
Color rojo para representar la lÃnea del promedio
$promedio = ImageColorAllocate($lienzo, 255, 0, 0);
Colores para intercalar el fondo
$background = ImageColorAllocate($lienzo, 236, 245, 252);
$background2 = ImageColorAllocate($lienzo, 228, 241, 254);
Colores de cada barra
$barra = ImageColorAllocate($lienzo, 100, 149, 237);
Color del texto
$letras = ImageColorAllocate($lienzo, 0, 0, 0);
Vamos ahora a pintar el fondo de dos colores, queda bastante bien,
Es fácil hacerlo con un ciclo for:
for ($i=0; $i < 60; $i=$i+1)
{
if ($i%2==0)
{
imagefilledrectangle($lienzo, 0, $i*10, 300, $i*10+30, $background);
}
else
{
imagefilledrectangle($lienzo, 0, $i*10, 300, $i*10+30, $background2);
}
}
(Voy a omitir la explicación de porque los números, pero básicamente cada 10 px se intercala el color, les dejo a uds ver la lógica ya que no es muy complicada)
La imagen (si la creáramos) quedarÃa asÃ:

Ahora el siguiente paso es dibujar los ejes cartesiano x e y.
El primero se refiere al eje Y va desde el pÃxel 30, 30 hasta el pÃxel 30, 170:
imageline($lienzo, 30,30, 30, 170, $letras);
Este, el eje X, va desde el pÃxel 10,160 hasta el pÃxel 270,160
imageline($lienzo, 10,160, 270, 160, $letras);
Vamos a dibujar las referencias, o sea a aclararle al lector que la lÃnea roja es del promedio y la azul de arriba es la del valor máximo:
imageline($lienzo, 31,190, 50, 190, $promedio);
ImageString($lienzo, 3, 60, 183, "= Promedio", $letras);
imageline($lienzo, 170,190, 190, 190, $barra);
ImageString($lienzo, 3, 200, 183, "= Maximo ", $letras);
Creo que ya se van dando cuenta de que acabamos de hacer, que dibujamos una pequeña referencia al pie de la imagen.
Bien, vamos a imprimir el valor 0 en la base del eje Y
ImageString($lienzo, 3, 15, 145, "0", $letras);
Ahora el siguiente paso es tener que buscar el valor máximo de los valores que le pasamos a la función, lo buscamos recorriendo el array. Hay varias formas de recorrerlo, yo elegà esta:
$mayor = 0;
for($i=0; $i < count ($arrayValores); $i++)
{
if ($mayor < $arrayValores[$i])
{
$mayor = $arrayValores[$i];
}
}
Vamos ahora a dibujar la lÃnea tope del valor más alto asi:
ImageString($lienzo, 3, 15, 33, $mayor, $letras);
imageline($lienzo, 31,40, 270,40, $barra);
Pero y los avances? Mirá, asà es como quedarÃa ahora:

Bien, ya nos va quedando la cosa, no?
Ahora vamos a complicarnos un poco mas, porque para dibujar las barras, hay que generar una cierta escala y hay que tener en cuenta que los puntos superiors de las barras son numeros mas bajos que la base de la barra. Asi que para determinar esta escala y los puntos Y superiores izquierdos vamos a tener que programar otra función:
Function parametros($valor,$mayor)
$valor es el numero de visitantes de ese dÃa, y $mayor es el mayor valor del array.
Ahora la acción de esta función, es ver cual será el valor Y del punto superior izquierdo según la escala.
Seria algo asi:
if ($valor == $mayor)
{
if ($mayor==0)
{
$y = 159;
}
else
{
$y = 40;
}
}
else
{
$porcentaje = $valor * 100 / $mayor;
$y = $porcentaje * (159 - 40) / 100;
$y = 159 - $y;
}
Tenemos que si el valor que estamos pasando es el mayor de todos, ya sabemos cual va a ser ese punto, ya que el grafico llegara al tope del gráfico.
En cambio, si no es el mayor valor, hay que hacer unos pequeños cálculos para saber en que escala estará, de eso se encarga el else.
Una vez que ya tenemos ese valor y sea el mayor o no, la función lo devuelve y cerramos la función
return $y;
}
Bien ahora continuemos dibujando las barras, total ya tenemos la función que nos devuelve a escala el valor en px más alto de cada barra.
También tenemos el piso (base) que no se modifica con lo que ya tenemos los puntos superior izquierdo e inferior izquierdo que hacen falta para cada rectángulo.
Cada barra constara de tres lÃneas:
En la primera llamamos a la función que creamos para que nos devuelva el y del punto superior izquierdo y lo guardamos en $y. Los parámetros son el valor del primer array, y el valor mayor que calculamos entes en el ciclo for.
$y = parametros($arrayValores[0],$mayor);
Después dibujamos el rectángulo propiamente dicho
imagefilledrectangle($lienzo, 40, $y, 60, 159, $barra);
a su vez lo completamos con la informar la cantidad de esa barra.
ImageString($lienzo, 2, 45, 159, $arrayDias[0], $letras);
Esto se repetirá con las 6 barras restantes (pueden meterlas en un for si quieren, por razones didácticas las explico de esta forma.)
La segunda barra seria (hace 4 dÃas):
$y = parametros($arrayValores[1],$mayor);
imagefilledrectangle($lienzo, 70, $y, 90, 159, $barra);
ImageString($lienzo, 2, 75, 159, $arrayDias[1], $letras);
La tercera (hace 3 dÃas):
$y = parametros($arrayValores[2],$mayor);
imagefilledrectangle($lienzo, 100, $y, 120, 159, $barra);
ImageString($lienzo, 2, 105, 159, $arrayDias[2], $letras);
La cuarta (antes de ayer):
$y = parametros($arrayValores[3],$mayor);
imagefilledrectangle($lienzo, 130, $y, 150, 159, $barra);
ImageString($lienzo, 2, 135, 159, $arrayDias[3], $letras);
La quinta (ayer):
$y = parametros($arrayValores[4],$mayor);
imagefilledrectangle($lienzo, 160, $y, 180, 159, $barra);
ImageString($lienzo, 2, 165, 159, $arrayDias[4], $letras);
Y el dÃa de hoy (actual):
$y = parametros($arrayValores[5],$mayor);
imagefilledrectangle($lienzo, 190, $y, 210, 159, $barra);
ImageString($lienzo, 2, 195, 159, "hoy", $letras);
Genial, ya tenemos todas las barras dibujadas, vamos a agregarle un detallecito mas, porque no nos servirÃan de mucho barras inútiles mostrando los nueceros que podrÃamos haber impreso sin tantas vueltas.
Por ejemplo vamos a cruzarle una lÃnea promedio en rojo a todas las barras para comparar fácilmente cada barra con la lÃnea promedio.
Para esto es necesario saber donde ubicarla, y de nuevo recurrimos a la función que declaramos anteriormente, asà nos devolverá la posición en el eje y de la lÃnea.
Veamos:
Llamamos a la función con el valor promedio traÃdo ya de afuera y con el valor mayor que habÃamos calculado en uno de los ciclos detallados anteriormente
$y = parametros($prom,$mayor);
Una vez que tenemos el y, podemos dibujar la lÃnea.
imageline($lienzo, 31,$y, 270,$y, $promedio);
Y le escribimos el valor promedio para tener una idea de esa posición
ImageString($lienzo, 1, 245 , $y-10, $prom, $letras);
Eso es todo tenemos nuestro lienzo pintarrajeado, ahora esperemos que se seque, o sea creemos el archivo de este modo y cerramos la función:
imagepng($lienzo ,"tmpstats.png");
}
Vean el ejemplo de una de las estadisticas de este weblog:

Pueden usar las funciones imagejpeg() imagegif() dependiendo el archivo que quieran como salida. En este caso es un PNG.
Resumiendo:
PHP te hace todo muy fácil con las funciones de imágenes, es cuestión de llamarlas pasándoles el parámetro correcto, son de sintaxis intuitiva y fácil de recordar. Para profundizar un poquito mas sobre el tema recomiendo que entren a la pagina www.php.net y busquen la documentación en su idioma.
Asi quedarian las dos funciónes que acabamos de programar.
Lucas Zallio
eleZeta