By Scott Mitchell
A powerful and common use for data-driven web pages is to create an on-line report. While text-only reports are nice and all, any report can be easily snazzed up by adding an impressive looking chart. One way to add charts is to use an ActiveX control, like MSChart. The advantage to using such an approach is that MSChart is very powerful. With it you can easily have a multitude of charts drawn: 3d graphs, line graphs, pie charts, etc. The major drawback is that the user must first download the ActiveX control (roughly 900k, if I remember correctly). They must also "trust" that your ActiveX control won't hurt their computer. Often times when I encounter a site that requires I use a Java applet or download an ActiveX control, I keep right on surfing by.
(If you're interested in learning how to use MSChart, there's a great article on the site: MSChart Example Code.)
In this article I will present a technique to display bar charts using just good ol' fashioned HTML. This means that there are no 900k downloads, no security issues, no hassles for your end user. It also guarentees that it will display properly for any browser which supports tables.
I will first present a static bar chart. That is, the data points to be plotted will be hard coded in. Later in the article I will show you how you can retrieve information from a database to dynamically populate the bar chart. So, let's delve into some code! (Complete source code in non-chopped up format is also available: Static Example $ Dynamic Example.)
First, we will do the common two lines to start out any ASP page:
<%@ LANGUAGE="VBSCRIPT" %>
<% Option Explicit %>
Of course using Option Explicit is always a good idea. If you're unfamiliar with what Option Explicit does, there is a good FAQ about it.
Now, we want to set some constants which will effect how the chart looks.
These constants are the height of the graph in pixels, the width of the graph
in pixels, and the name of the image that will be the bars of the graph.
In this example, I created an image that was 20 pixels wide and 5 pixels high,
and named it
bluebar.gif. I suggest you make a similar sized bar, coloring it
whatever color you want your bar chart.
'How many pixels high/wide we want our bar graph
Const graphHeight = 300
Const graphWidth = 450
'Where is our bar image?
Const barImage = "/images/bluebar.gif"
Now we are ready to write our subroutine that will display the bar chart.
The subroutine is called
BarChart and takes four parameters:
- data - a one dimensional array of all the data points (starts at 0)
- labels - a one dimentional array of all the labels for the bars (starts at 0,
should be the same size as the data array)
- title - a string; the title you want on the bar graoh
- axislabel - a string; the title you want for your labels
Basically we are going to print out a TABLE which has n+1 columns, where n equals the cardnality of the array (in English: there are as many columns as there are entries in the array, plus one). In each column, except for the first, we are going to do an
<IMG SRC...>with a HEIGHT proportional to the value of the current datapoint in the array. I know this may sound a bit confusing, but perhaps looking at some code will help:
sub BarChart(data, labels, title, axislabel)
Response.Write("<TABLE CELLSPACING=0 CELLPADDING=1 BORDER=0")
Response.Write(" WIDTH=" & graphWidth & ">" & chr(13))
Response.Write("<TR><TH COLSPAN=" & UBound(data) - LBound(data) + 2)
Response.Write("<FONT SIZE=+2>" & title & "</FONT></TH></TR>"
Response.Write("<TR><TD VALIGN=TOP ALIGN=RIGHT>" & chr(13))
'Find the highest value
Dim hi, low
hi = data(LBound(data))
for i = LBound(data) to UBound(data)
if data(i) > hi then hi = data(i)
'Print out the highest value at the top of the chart
Response.Write(hi & "</TD>")
widthpercent = CInt((1 / (UBound(data) - LBound(data) + 1)) * 100)
For i = LBound(data) to UBound(data)
Response.Write(" <TD VALIGN=BOTTOM ROWSPAN=2 WIDTH="
Response.Write(widthpercent & "% >" & chr(13))
Response.Write(" <IMG SRC=""" & barImage & """ WIDTH=100% ")
Response.Write("HEIGHT=" & CInt(data(i)/hi * graphHeight) & ">")
Response.Write(" </TD>" & chr(13))
Response.Write("<TR><TD VALIGN=BOTTOM ALIGN=RIGH>>0</TD></TR>")
Response.Write("<TR><TD ALIGN=RIGHT VALIGN=BOTTOM>")
Response.Write(axislabel & "</TD>" & chr(13))
for i = LBound(labels) to UBound(labels)
Response.Write("<TD VALIGN=BOTTOM ALIGN=CENTER>" & labels(i))
Response.Write("</TD>" & chr(13))
Response.Write("</TR>" & chr(13))
OK, this may seem a bit confusing. If you're confused or totally lost, I suggest that you download the source and try it out yourself. You might want to put the above sub and constants in a text file so that you can just include the file whenever you want to use the bar chart function. (If you're unfamiliar with includes, read this FAQ.)
Before we can call the subroutine, we need to create and initialize both data and label arrays. Here are some random values in both arrays:
Dim dataArray(10) dataArray(0) = 8 dataArray(1) = 10 dataArray(2) = 8 dataArray(3) = 14 dataArray(4) = 6 dataArray(5) = 13 dataArray(6) = 7 dataArray(7) = 11 dataArray(8) = 8 dataArray(9) = 9 Dim labelArray(10) labelArray(0) = "3/2" labelArray(1) = "3/3" labelArray(2) = "3/4" labelArray(3) = "3/5" labelArray(4) = "3/6" labelArray(5) = "3/7" labelArray(6) = "3/8" labelArray(7) = "3/9" labelArray(8) = "3/10" labelArray(9) = "3/11" %>
Soon I will show you how to create and fill these two arrays from a database. For now, though, we have hard coded the size of both arrays and hard coded the values in the arrays. Note that the arrays are 0 based. The BarChart subroutine is written to expect C-style arrays (0..arraySize-1 as opposed to 1..arraySize)
Now, to display the chart, inside the
HTML tags, we just need to
call the BarChart function passing the data and label arrays, as well as the
bar chart title and the axis label.
Here is an example:
<HTML> <BODY> <% BarChar dataArray, labelArray,"Telephone Sales","Date" %> </BODY> </HTML>
I hope this makes sense so far! If you're interested in learning how to create bar charts dynamically from database queries, continue on to the next section!