在上一个应用程序中,我们已经使用了已有图像和文本。我们还没有了解绘图的例子,现在就介绍它。
在这个例子中,我们将在本网站中进行一次民意测验,让用户为虚构的选举进行投票。投票结果保存到一个MySQL数据库中,并用图像函数绘制出条形图,以表示投票结果。
这些函数的另一个主要应用是绘制图形。还可以用图形来表示任何需要表示的数据——交易量、网站点击数和任何我们所喜欢的。
对于这个例子,我们花了几分钟建立了一个名为poll的MySQL数据库。该数据库包含一个名为poll_results的表,该表的candidate列保存了候选人的名字,num_votes列保存了候选人收到的投票数。我们还为该数据库创建了一个名为poll的用户,其密码为poll。这个表的创建过程简单明了,可以通过运行如程序清单22-3所示的SQL脚本完成。可以使用如下命令,以root用户的身份进行登录:
mysql-u root-p<pollsetup.sql
当然,也可以以具有适当权限的用户身份进行登录。
程序清单22-3 pollsetup.sql——创建Poll数据库
create database poll;
use poll;
create table poll_results(
candidate varchar(30),
num_votes int
);
insert into poll_results values
(/'John Smith/',0),
(/'Mary Jones/',0),
(/'Fred Bloggs/',0)
;
grant all privileges
on poll.*
to [email protected]
identified by/'poll/';
该数据库包含了三个候选人。我们通过vote.html页面提供一个投票界面。该页面代码如程序清单22-4所示。
程序清单22-4 vote.html——用户可以在此投票
<html>
<head>
<title>Polling</title>
<head>
<body>
<h1>Pop Poll</h1>
<p>Who will you vote for in the election?</p>
<form method=/"post/"action=/"show_poll.php/">
<input type=/"radio/"name=/"vote/">John Smith<br/>
<input type=/"radio/"name=/"vote/">Mary Jones<br/>
<input type=/"radio/"name=/"vote/">Fred Bloggs<br/><br/>
<input type=/"submit/">
</form>
</body>
</html>
该页面的输出结果如图22-7所示。
图 22-7 用户可以在此投票,点击提交按钮将显示当前投票结果通常的想法是,当用户点击提交按钮时,将他们的投票添加到数据库中,然后再将数据库中所有的投票取出,绘制当前结果的条形图。
在用户投票之后的条形图输出如图22-8所示。
图 22-8 投票结果通过将一系列线条、矩形和文本绘制到背景图来实现创建该图像的脚本非常长。我们将它分为4部分,并分别加以讨论。这些脚本大多数都是我们非常熟悉的;我们已经看过许多类似的MySQL示例。我们已经了解了如何用单一的颜色绘制一个背景画布,以及如何在背景图上打印文本标签。
与前面所介绍的脚本相比较,该脚本新的部分是绘制线条和矩形。我们将重点讨论这些部分。第1部分(脚本1个部分的第1部分)如程序清单22-5-1所示。
第1部分,如程序清单22-5-1所示,将连接到MySQL数据库,根据用户的投票更新投票数,获得新票数。在获得新票数信息后,就可开始通过计算绘制图表。第2部分代码如程序清单22-5-2所示。
程序清单22-5-1 show_poll.php——脚本的第1部分将更新投票数据库并检索新的投票结果
<?php
/*******************************************
Database query to get poll info
*******************************************/
//get vote from form
$vote=$_REQUEST[/'vote/'];
//log in to database
if(!$db_conn=new mysqli(/'localhost/',/'poll/',/'poll/',/'poll/'))
{
echo/'Could not connect to db<br/>/';
exit;
}
if(!empty($vote))//if they filled the form out,add their vote
{
$vote=addslashes($vote);
$query=/"update poll_results
set num_votes=num_votes+1
where candidate=/'$vote/'/";
if(!([email protected]$db_conn->query($query)))
{
echo/'Could not connect to db<br/>/';
exit;
}
}
//get current results of poll,regardless of whether they voted
$query=/'select*from poll_results/';
if(!([email protected]$db_conn->query($query)))
{
echo/'Could not connect to db<br/>/';
exit;
}
$num_candidates=$result->num_rows;
//calculate total number of votes so far
$total_votes=0;
while($row=$result->fetch_object)
{
$total_votes+=$row->num_votes;
}
$result->data_seek(0);//reset result pointer
程序清单22-5-2 show_poll.php——脚本的第2部分设置绘画所需要的所有变量
/*******************************************
Initial calculations for graph
*******************************************/
//set up constants
putenv(/'GDFONTPATH=C:WINDOWSFonts/');
$width=500;//width of image in pixels-this will fit in 640x480
$left_margin=50;//space to leave on left of graph
$right_margin=50;//ditto right
$bar_height=40;
$bar_spacing=$bar_height/2;
$font=/'arial/';
$title_size=16;//point
$main_size=12;//point
$small_size=12;//point
$text_indent=10;//position for text labels from edge of image
//set up initial point to draw from
$x=$left_margin+60;//place to draw baseline of the graph
$y=50;//ditto
$bar_unit=($width-($x+$right_margin))/100;//one/"point/"on the graph
//calculate height of graph-bars plus gaps plus some margin
$height=$num_candidates*($bar_height+$bar_spacing)+50;
脚本的第2部分创建了一些用来实际绘制图形的变量。
计算出这些类型的变量值是冗长乏味的,但是想想完成之后的图像是什么模样,会使这个过程变得轻松一点。在这里,我们使用的值是通过在纸上拟定设计效果,然后再估计所要求的比例而得到的。
宽度变量$width是背景总宽度。我们也设置了左边缘和右边缘的宽度(分别使用$left_margin和$right_margin变量),每条长方形的厚度和它们之间的间距($bar_height和$bar_spacing),以及字体、字体大小和标签的位置($font、$title_size、$main_size、$small_size和$text_indent)。
在给定这些基本的值后,就可以进行一些基本的计算了。我们希望绘制一条基线,所有的横条都从该线伸展开。通过在左边缘加上文本标签的宽度可以得到基线的x坐标位置,然后根据边框线估计基线的y坐标位置。如果灵活性非常重要,还可以根据最长的名称获得确切的宽度。
我们还需要计算两个重要的值:第一,在图像上表示一个单元的宽度:
$bar_unit=($width-($x+$right_margin))/100;//one/"point/"on the graph
这是长方形条的最大宽度——从基线到右边空白处——分为100份,因为我们的图形要显示百分值。
第二个值是背景总高度:
$height=$num_candidates*($bar_height+$bar_spacing)+50;
这基本上是每一条长方形的高度乘以其数据,再加上标题所需的高度。脚本的第3部分如程序清单22-5-3所示。
程序清单22-5-3 show_poll.php——脚本的第3部分建立图形并准备添加数据
/*******************************************
Set up base image
*******************************************/
//create a blank canvas
$im=imagecreatetruecolor($width,$height);
//Allocate colors
$white=imagecolorallocate($im,255,255,255);
$blue=imagecolorallocate($im,0,64,128);
$black=imagecolorallocate($im,0,0,0);
$pink=imagecolorallocate($im,255,78,243);
$text_color=$black;
$percent_color=$black;
$bg_color=$white;
$line_color=$black;
$bar_color=$blue;
$number_color=$pink;
//Create/"canvas/"to draw on
imagefilledrectangle($im,0,0,$width,$height,$bg_color);
//Draw outline around canvas
imagerectangle($im,0,0,$width-1,$height-1,$line_color);
//Add title
$title=/'Poll Results/';
$title_dimensions=imagettfbbox($title_size,0,$font,$title);
$title_length=$title_dimensions[2]-$title_dimensions[0];
$title_height=abs($title_dimensions[7]-$title_dimensions[1]);
$title_above_line=abs($title_dimensions[7]);
$title_x=($width-$title_length)/2;//center it in x
$title_y=($y-$title_height)/2+$title_above_line;//center in y gap
imagettftext($im,$title_size,0,$title_x,$title_y,
$text_color,$font,$title);
//Draw a base line from a little above first bar location
//to a little below last
imageline($im,$x,$y-5,$x,$height-15,$line_color);
在第3部分,我们将建立基本图像,分配颜色,然后开始绘制图形。
首先,我们将填充图形的背景颜色,使用如下所示函数:
imagefilledrectangle($im,0,0,$width,$height,$bg_color);
顾名思义,ImageFilledRectangle函数将绘制一个中间填充了颜色的矩形。该函数的第一个参数与其他图形函数一样都是图像标识符。然后我们将此矩形的起点和结束点的x与y坐标传递给该函数。其起始点和结束点分别对应矩形的左上角和右下角。在这个例子中,我们还在整个背景中填充了背景颜色,该颜色是最后一个参数:白色。
然后调用:
imagerectangle($im,0,0,$width-1,$height-1,$line_color);
如上语句将绘制出围绕画布边沿的黑色轮廓线。该函数绘制了一个轮廓矩形而不是实心矩形。而其参数与前面的函数是一样的。请注意,我们绘制的矩形坐标是一直绘制到$width-1和$height-1——从(0,0)一直到画布的宽度和高度。如果我们绘制的是$width和$height,矩形将超出画布区域。
在这里,我们使用了前一个示例所介绍的逻辑和函数,将标题写到图像中,并将其置于图像中央。
最后,我们为每个长方形图绘制了基线:
imageline($im,$x,$y-5,$x,$height-15,$line_color);
ImageLine函数可以在我们所指定的图像($im)上画一条线,我们指定了该直线的起点坐标($x,$y-5)和终点坐标($x,$height-15),并通过变量$line_color指定颜色。
在这个例子中,我们在第一条长方形图的上面一点绘制了基线,其终点是背景的底端一点。
现在,我们准备将数据填充到该图形上。脚本的第4部分如程序清单22-5-4所示。
程序清单22-5-4 show_poll.php——脚本的第4部分将实际的数据绘制到该图像上,并结束整个程序
/*******************************************
Draw data into graph
*******************************************/
//Get each line of db data and draw corresponding bars
while($row=$result->fetch_object)
{
if($total_votes>0)
$percent=intval(($row->num_votes/$total_votes)*100);
else
$percent=0;
//display percent for this value
$percent_dimensions=imagettfbbox($main_size,0,$font,$percent./'%/');
$percent_length=$percent_dimensions[2]-$percent_dimensions[0];
imagettftext($im,$main_size,0,$width-$percent_length-$text_indent,
$y+($bar_height/2),$percent_color,$font,$percent./'%/');
//length of bar for this value
$bar_length=$x+($percent*$bar_unit);
//draw bar for this value
imagefilledrectangle($im,$x,$y-2,$bar_length,$y+$bar_height,$bar_color);
//draw title for this value
imagettftext($im,$main_size,0,$text_indent,$y+($bar_height/2),
$text_color,$font,/"$row->candidate/");
//draw outline showing 100%
imagerectangle($im,$bar_length+1,$y-2,
($x+(100*$bar_unit)),$y+$bar_height,$line_color);
//display numbers
imagettftext($im,$small_size,0,$x+(100*$bar_unit)-50,$y+($bar_height/2),
$number_color,$font,$row->num_votes./'//'.$total_votes);
//move down to next bar
$y=$y+($bar_height+$bar_spacing);
}
/*******************************************
Display image
*******************************************/
Header(/'Content-type:image/png/');
imagepng($im);
/*******************************************
Clean up
*******************************************/
imagedestroy($im);
?>
第4部分脚本将在数据库中逐个检索候选人,计算各自投票百分比,并为每位候选人绘制条形图并写上标签。
这里,我们又调用了ImageTTFText函数来为图像添加标签。我们调用ImageFilled-Rectangle函数绘制一个实心条形图:
imagefilledrectangle($im,$x,$y-2,$bar_length,$y+$bar_height,$bar_color);
并使用ImageRectangle函数绘制了标志100%的轮廓:
imagerectangle($im,$bar_length+1,$y-2,
($x+(100*$bar_unit)),$y+$bar_height,$line_color);
在完成所有这些条形图之后,调用ImagePNG函数输出了图像,并调用ImageDestroy函数清理资源。
这是一段比较长的代码,但是它合乎需要,或者适合通过一个界面自动产生投票结果。该脚本一个重要的缺陷是它缺乏反欺骗机制。用户很快就可以发现他们可以重复投票而使投票结果变得毫无意义。
我们可以使用类似的方法绘制直线图形,甚至如果擅长数学的话,还可以用这样的方法来绘制杂乱的流程图。