使用PHP和GD进行图形处理
因为越来越多的web 站点开始结合数据库在他们的部分设计中,我们能够使用在数据库中采集的数据来 显示统计结果。今天有很多的web 站点都有意使用某些民意测验或投票应用。在表格中显示数据是一件事,
但是 创建一个数据的图形则就是另外一回事了。当然,我们可以使用表格将可用的条状图形接在一起显示,
但是对于线形图或饼状图又如何做呢?PHP 具有一系列的图形函数,这些函数允许我们创建图形,让我们看
一看这些函数对于使用和创建一些图形有什么好处。
然而要紧记,在PHP中的图形函数要求你的系统安装了GD图形库。你可以在www.boutell.com/gd/找到,
还可以得到一些关于在你的系统上如何安装的一些指导。
注意:我写这篇文章时是在Red Hat Linux 6.2机器上,然后是在Windows 2000机器上结束它的 。在Red
Hat我运行的是PHP和MySQL的最新版本,但是在Windows上我不得不回到MySQL 3.21.29和PHP 3.0.11版本上,
因为它有我要处理的所有的东西 -- SQL和PHP的运行都不需要作修改。在这里让我们不要将整个事情放在交
叉平台上,好吗?
为了保持数据简单,并且允许我们集中注意力在创建图形上,我使用了一个小的假定的数据集 -- 一个
国际公司今年前六个月的销售指标。我根据的是一个办事处位于伦敦和另一个在亚特兰大。
Month 1 Month 2 Month 3 Month 4 Month 5 Month 6
伦敦 325 345 400 390 370 320
亚特兰大 300 280 270 300 350 410
前六个月的销售指标
然后我将数据输入到一个MySQL数据库中。我在下面包括了一个数据的卸出:
# MySQL dump 7.1
#
# Host: localhost Database: graphing
#--------------------------------------------------------
# Server version 3.22.32
#
# Table structure for table 'sales'
#
CREATE TABLE sales (
g_id int(11) DEFAULT '0' NOT NULL auto_increment,
g_month tinyint(4) DEFAULT '0' NOT NULL,
g_team tinytext NOT NULL,
g_num int(11) DEFAULT '0' NOT NULL,
PRIMARY KEY (g_id)
);
#
# Dumping data for table 'sales'
#
INSERT INTO sales VALUES (1,1,'London',325);
INSERT INTO sales VALUES (2,1,'Atlanta',300);
INSERT INTO sales VALUES (3,2,'London',345);
INSERT INTO sales VALUES (4,2,'Atlanta',280);
INSERT INTO sales VALUES (5,3,'London',400);
INSERT INTO sales VALUES (6,3,'Atlanta',270);
INSERT INTO sales VALUES (7,4,'London',390);
INSERT INTO sales VALUES (8,4,'Atlanta',300);
INSERT INTO sales VALUES (9,5,'London',370);
INSERT INTO sales VALUES (10,5,'Atlanta',350);
INSERT INTO sales VALUES (11,6,'London',320);
INSERT INTO sales VALUES (12,6,'Atlanta',410);
我的数据库叫做graphing,包含数据的表叫做sales。我将月份当做一个整数存在 g_month字段中。
在我们开始绘图之前,先学一些基础知识,看一下PHP是如何创建一个图像。我们需要做的第一件事就
是告诉浏览器它正在得到一个图像和所得到图像的类型:
<?php
Header( "Content-type: image/gif");
?>
即然浏览器知道它正在获取一个GIF 图像了,我们可以开始创建图形了。首先我们需要创建一个将用来
绘图的空白的画布。ImageCreate函数可以做到这一点。ImageCreate将返回一个图像的标识符,并且需要告
诉函数用象素计算的画布有多大,x(宽度)与y(高度)。
<?php
$image = imagecreate(200,200);
?>
现在我们得到了一个200像素乘200像素的可用来绘图的空白画布。下一步是创建将要用在图像中的一些
颜色。为了做到这一点我们需要使用ImageColorAllocate函数,还有颜色的RGB值。ImageColorAllocate 将
返回一个我们刚创建的颜色标识。我们将在画布上绘图时使用颜色标识。ImageColorAllocate的工作方式是
我们需要为所处理的每一个图像分配一个颜色 -- 所以如果我们创建3个GIF并且在每一个上面使用红色,我
们应该分配红色3次(一次为一个GIF)。我将分配一个名为$maroon的颜色,给定红色值为100,绿色为0 和
兰色为0。同时我将同样创建白色。
<?php
$maroon = ImageColorAllocate($image,100,0,0);
$white = ImageColorAllocate($image,255,255,255);
?>
即然我们已经得到了颜色,就可以画点什么了。第一件事情要叫的就是将画布白色涂成白色。函数
ImageFilledRectangle将在画布上画一个矩形,并且用指定的颜色进行填充。
<?php
ImageFilledRectangle($image,0,0,200,200,$white);
?>
告诉ImageFilledRectangle的第一件事就是(同样用于所有的图像函数)哪一个图像是我们正在处理的 ,
所以传给它$image标识。然后它需要知道x和y坐标来开始画矩形(0,0 -- 左上角)和结束矩形的坐标( 200,
200 -- 画布的右下角)。最后告诉它的事情就是在矩形内填充的颜色,这个例子为$maroon。现在我们可以
开始在白色背景上绘图了。
<?php
ImageRectangle($image,10,10,190,190,$maroon);
ImageFilledRectangle($image,50,50,150,150,$maroon);
?>
ImageRectangle工作完全与ImageFilledRectangle同样的方式,除了它不使用颜色填充矩形以外。一旦
我们画完了,就可以输出图形了 --
<?php
ImageGIF($image);
?>
然后清除我们存在内存中的图像。
<?php
ImageDestroy($image);
?>
这就是我们得到的:
然而,它还不是一个要求的图形。
(未完待续)
使用PHP和GD进行图形处理
线形图形 为了开始,我们可以创建一个空的200x200像素的画布,并填充为灰色。在这上面可以绘制我们的线段。
我们可以为每个办公室分配一个颜色,红色代表伦敦,兰色代表亚特兰大。
现在可以开始取出数据并把它画出来了。需要牢记的一件重要的事就是在画线的时候,ImageLine 要求
我们告诉它开始处的x和y坐标还有线的结束坐标。现在如果从一个数据库中抓出数据,将一次只能得到一组
坐标。所以可以或者将数据存储在数组中,或使用一个临时变量来保存前一组坐标。我选择了使用数组。
除了将要绘制的实际坐标之外,还需要知道可达到的最大值(图形的顶部)还有信息列的数目(沿着x轴要
画的点的个数)。所以在与数据库连接之后,从数据库中选出了MAX(g_num) 值并将其保存起来为后面使用。
然后选出所以g_team等于'London'的所有记录,并得到返回的记录行数,所以我们将知道有多少要处理的数
据列数。从前面的表格中,你可以看到我们将处理6个月或列数据,并且最大值为410。
<?php
$columns = 6;
$max = 410;
?>
x值总是最容易处理的 -- 它的范围从0到图像的宽度,此处为200。我们将在0处理开始画线并希望在200
结束,所以在以$x=0开始之后,将需要增加$x值直到$columns-1次(此处为5)以达到200 -- 这样我们要画
6次,最后一次将在200处。记住这点以后我们可以创建一个名为$xincrement的变量,并赋给它200除以
($columns-1)的值。然后将给出$x一个开始值0 并且在每一次循环的底部,当从结果集中抓取记录行时,我
们可以给$x增加$xincement。
<?php
$xincrement = bcdiv(200,$columns-1,0);
?>
bcdiv用第二个参数除以第一个参数,并且返回根据第三个参数所指定的小数位数的值。为了使用BCMath
函数,你需要在Unix下将其编译到PHP中,但是Windows版本已经将其编译进去了。README.BCMATH在PHP源代
码的根目录中,它将解释在哪和如何做。
y值将与$max数值成比例,所以我们所能做的就是用$max来除g_num得到比例的小数值,然后乘以200 --
图像的高度。
<?php
$y = bcmul(bcdiv($salesRow[0],$max,2),200,2);
?>
$salesRow[0]是当前行的g_num值,除上$max,并且结果定为小数部分为2位。然后使用bcmul将结果与200
相乘。
然后可以做的就是在整个数组中循环,使用当前元素作为线的开始,当前元素+1作为线的结束。因为最
后一个元素将永远不会是一行的开始,我们必需将循环减1,所以将当小于或等于$columns-1 时循环改为小
于$columns-1时循环。
让我们看一下迄今为止的代码:
<?php
// 发送头信息和创建初始的空白画布
Header( "Content-type: image/gif");
$image = imagecreate(200,200);
// 分配一些颜色
$red = ImageColorAllocate($image,255,0,0);
$blue = ImageColorAllocate($image,0,0,255);
$white = ImageColorAllocate($image,255,255,255);
$grey = ImageColorAllocate($image,200,200,200);
// 创建一个初始的灰色矩形用于绘图
ImageFilledRectangle($image,0,0,200,200,$grey);
// 连接mysql服务器并选择数据库
$connect = mysql_connect("","root","");
mysql_select_db("graphing",$connect);
// 找到结果集中的最大数值
$sql = "SELECT MAX(g_num) FROM sales";
$maxResult = mysql_query($sql,$connect);
$max = mysql_result($maxResult,0,0);
// 得到伦敦的结果集
$sql = "SELECT g_num FROM sales WHERE g_team='London' ORDER BY g_month";
$salesResult = mysql_query($sql,$connect);
// 找到返回多少列,就是'columns'的数值
$columns = mysql_num_rows($salesResult);
// $x每次增加多少量?
$xincrement = bcdiv(200,$columns-1,0);
$x=0;
// $i 将记录行的数值
$i=0;
// 在我们所有的数据行中循环
while($salesRow=mysql_fetch_array($salesResult)) {
// 象上面所讨论地计算y的坐标值
$y = bcmul(bcdiv($salesRow[0],$max,2),200,2);
// 在$points数组中增加值
$points[$i][0] = $x;
$points[$i][1] = $y;
// 增加$x的值用$xincrement
$x+=$xincrement;
// 增加 $i
$i++;
}
// 现在我们在$points数组中循环,此时$i小于$columns-1
for($i=0;$i<$columns-1;$i++) {
// 我们传递 $points[$I][0] 作为第一个x坐标,$points[$I][1] 为第一个y坐标
// $points[$I+1][0], $points[$I+1][1] 将是下一个x,y坐标集
ImageLine($image,$points[$i][0],$points[$i][1],$points[$i+1][0],
$points[$i+1][1],$red);
}
// 输出GIF给浏览器并且释放内存
ImageGIF($image);
ImageDestroy($image);
?>
这样将给出我们如下的结果:
<img src="http://www.phpstar.com/pic/2000090702.gif">
它既不正确也很气人。问题在于我们是按传统的x和y坐标方法处理的,这种方法是从左下角向外发散的。
Image函数的坐标系统是从左上角发散的,所以这时我们的x位置是正确的,我们的y位置弄反了。
我们需要做的就是修改确定y位置的代码以便按相反的方式工作 -- 改变行
<?php
ImageLine($image,$points[$i][0],$points[$i][1],$points[$i+1][0],$points[$i+1][1],$red);
?>
变成读取:
<?php
ImageLine($image,$points[$i][0],200-$points[$i][1],$points[$i+1][0],200-$points[$i+1][1],$red);
?>
我们将得到图形:
<img src="http://www.phpstar.com/pic/2000090703.gif">
现在我们需要做的是加入一条线,它是将亚特兰大的数据进行了图形化。
<?php
$sql = "SELECT g_num FROM sales WHERE g_team='Atlanta' ORDER BY g_month";
$salesResult = mysql_query($sql,$connect);
$columns = mysql_num_rows($salesResult);
$xincrement = bcdiv(200,$columns-1,0);
$x=0;
$i=0;
while($salesRow=mysql_fetch_array($salesResult)) {
$y = bcmul(bcdiv($salesRow[0],$max,2),200,2);
$points[$i][0] = $x;
$points[$i][1] = $y;
$x+=$xincrement;
$i++;
}
for($i=0;$i<$columns-1;$i++) {
ImageLine($image,$points[$i][0],200-$points[$i][1],$points[$i+1][0],200-$points[$i+1][1],$blue);
}
?>
<img src="http://www.phpstar.com/pic/2000090704.gif">
我们用在这个例子中的数据是静态的,在以后的部分我们将看一下如何处理动态数据,并且看一下在图
表中增加一些轴线和标签。
译者注:
本文中的例子,我已经在php 3.0.16下,使用旧版的gd库测试成功。对于高版本的php(4.0以上版本)由于不再支持gif文件,所以我将Imagegif改成了ImagePNG函数执行后,成功。而且对于Header输出的MIME类型可以仍然使用image/gif格式说明。如果大家有什么建议欢迎来信。对于php 4.0.2版本与其使用的gd库均可从http://www.mm4.de/处下载。不过这个网站上则是全的文件包而不是独立的动态库。还有一点要说明的是这里我所说的版本全部是windows下的。至于linux则可以直接在编译时设定即可。
责任编辑:semirock