Developer

Create a reusable graph background in JSP

Displaying graphs on Java Server Pages is a fairly simple task, but you can make the process cleaner by creating reusable backgrounds. Learn this technique and see how to encode graph data.


A useful technique for producing neat, concise bar graphs in Java Server Pages (JSP) is to create a reusable background. To achieve this reusability, you need to allow the graph to be resized, and you should manage the bars so that they won’t ever extend past the bounds of the graph area. You also need to encode the graph data into a useful image format. We'll walk through the process using this sample code.

What do you need?
To get started with the examples in this article, you will need JDK 1.2, or higher (http://java.sun.com). You will also need a Web server that supports JSP. I tested the examples on Tomcat (http://www.apache.org), and I performed the encoding using the com.sun.image.codec.jpeg  classes that are distributed with the Sun Java 2 SDK.

Reusable background
Since you want your graphs to have reusable backgrounds, you should create a Java class that handles the layout, including the header area and the outer border. See Figure A for an example.

Figure A


As you can see, I applied drop shadows to both the header and the graph area. The header has a white, one-pixel-wide border, and the graph area has a thin black border. The borders add some definition to the drop shadows.

The borders are easy to make. Use the Graphics2D object’s fill() method to fill the blue header rectangle, and then use the draw() method to create the border with a different color.

Creating the drop shadow effect is also easy. First, draw the drop shadow using the fill() method. Now, draw the header over the drop shadow with an offset of seven pixels. The offset creates the illusion of 3D distance and thus produces the shadow effect.

Sample graph
Let’s imagine that our company sells farm produce and that we want a bar graph that displays its sales. In a real-life scenario, we should be getting the data from a database or an XML file, but for simplicity’s sake, we'll assume that our data is stored in two arrays as follows:
String datanames[] = {"Apples", "Oranges", "Peaches", "Lemons", "Grapefruit"};
int datavalues[] = {11, 62, 33, 102, 50};


The first array stores the names of the various items our company sells. The second array has the corresponding sales figures per type of produce.

Getting the bar graph ready for display
The graph is going to be an image in JPEG format, so we'll need to set the correct MIME, or content type. The browser uses the MIME type to decide how to display the response. The following code sets the MIME type:
response.setContentType("image/jpeg");

Next, we need an object that represents the image. The Java 2D API supplies the BufferedImage class, which provides a way to store and manage pixel data in memory. We want the graph to have color, so we use the TYPE_INT_RGB image type. WIDTH and HEIGHT integers specify the dimensions of the image in pixels:
BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

Now that we have a BufferedImage, we can set up the Graphics2D context by calling the createGraphics() method:
Graphics2D biContext = bi.createGraphics();

Width, height, and maximum
The programmer who creates the graph sets the WIDTH depending on the importance of the graph and the overall layout of the page. The graph elements resize themselves correctly to accommodate changes in the graph’s width.

The widths of the header and border areas, as well as the longest bar in the graph, are calculated using WIDTH. This ensures that none of the graphic elements exceeds the width of the graph and attempts to paint off the right-hand side of the image.

The number of records in the dataset determines the HEIGHT of the graph. As more elements are added to the datavalues[] and datanames[] arrays, the graph grows in height to accommodate the values that need to be displayed.

The maximum value is calculated and used for the width of the longest bar. Thereafter, all other bars’ widths are calculated relative to the maximum:
int barWidth = (innerWIDTH * currentValue) / maximum;

The algorithm above uses the maximum data value and the graph’s innerWIDTH (the graph area) to ensure that if WIDTH is changed, the bars will grow or shrink to suit the new WIDTH.

Displaying the graph background
To display the graph, we need to create the background image and then add the graph data. First, create a graphBG object and then call its draw() method:
graphBG gr = new graphBG();
gr.draw(biContext, WIDTH, HEIGHT, "Farm Produce", "Overall Average: " + average);


The draw() method’s parameters include the graphics context, biContext, and WIDTH and HEIGHT, which are used by the graphBG class to determine the widths and heights of the header and graph areas. Finally, the average data value is calculated and appended to the text that is displayed in the header.

Looping through the bars
The y position of each bar is calculated using y_pos = i * displayHeight + headerOffset, where displayHeight equals the combined height of the text above a bar and the height of the bar. headerOffset represents the vertical distance from the top of the graph, taking into account the height of the header area and its drop shadow.

I created the bars and their borders using a similar technique to the one I used for the border around the header. I contracted the width and height of the bar borders by one pixel to make each bar appear to have a red border and to create the cutout effect facilitated by the bars’ white inner borders drawn on the graph’s white background.

Encoding
We've set up the graph in memory and are now ready to encode the graph and display it to the user. We won't be able to use the default JSP output stream for the JPEG, so we'll need to get a stream from the response object using response.getOutputStream(). With this output stream, we can create a JPEGImageEncoder and call encode(), passing the BufferedImage we created previously:
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(output);
encoder.encode(bi);


The generated image is relatively small, weighing in at only 13.7 kilobytes. Figure B shows the final output.

Figure B


The output from index.jsp is a JPEG image in every sense. You can save it on your desktop or grab it with the Print Screen button. If you need to display more than one graph on the same page or place a graph inline with other content, you can use an HTML img tag (<img src = ”index.jsp”>) and then, for example, use a table to place the graph wherever it is needed.

Possibly one of the oldest Internet tricks uses a dynamically generated image to do something other than just display an image. Imagine that you needed to keep track of the number of people who see the image (as is the case with hit counters and ad servers). You could include impression counting, database or file access, or just about anything else in index.jsp and process it behind the scenes without the need to reroute the user through a buffer page.

Conclusion
In this article, we examined a technique for producing neat, visually pleasing bar graphs. We resized the graph’s width and height intelligently, encoded a JPEG image, and discussed putting the finished graph into the correct place by modifying HTML.

 

Editor's Picks

Free Newsletters, In your Inbox