4.1. Creating a Horizontal Bar Chart

Problem

You want to create a simple data-driven horizontal bar chart on a web page without using an applet or graphics library.

Solution

Use nested HTML tables with the width percentages calculated dynamically:

<table border="0">
  <logic:iterate id="row" name="foo" property="bar">
    <tr>
      <td align="right" width="20%">
        <bean:write name"row" property="label"/>
      </td>
      <td align="left" width="80%">
        <table width='<bean:write name="row" 
                              property="percentage"/>%' 
             bgcolor="blue">
          <tr>
            <td align="right">
              <font color="white">
                <bean:write name="row" 
                        property="percentage"/>%
              </font>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </logic:iterate>
</table>

Discussion

Displaying tables of raw numeric data may satisfy the functional requirements of your application, but outputting this information in a graph can make a tremendous difference to your end users. However, as soon as you start talking about graphics, the groans begin. Should you buy a reporting engine? What about a graphics-rendering framework? Do you need both? In many situations, if your application requirements can be met fairly by bar graphs, a combination of some clever HTML and Struts can do the work for you.

Consider a web application that displays weather forecast information. The application needs to display a bar chart that shows the chance of precipitation for the upcoming week. You'll create the WeeklyWeather class that holds the weather forecast as shown Example 4-1.

Example 4-1. JavaBean containing weather-related data

package com.oreilly.strutsckbk.ch04;

import java.util.ArrayList;
import java.util.List;

public class WeeklyWeather {
    
    public WeeklyWeather( ) {
        weekForecast = new ArrayList( );
        weekForecast.add(new DailyForecast("Sunday", 70));
        weekForecast.add(new DailyForecast("Monday", 40));
        weekForecast.add(new DailyForecast("Tuesday", 20));
        weekForecast.add(new DailyForecast("Wednesday", 5));
        weekForecast.add(new DailyForecast("Thursday", 50));
        weekForecast.add(new DailyForecast("Friday", 40));
        weekForecast.add(new DailyForecast("Saturday", 90));
    }
    
    public List getWeekForecast( ) {
        return weekForecast;        
    }
    
    private List weekForecast;
}

The WeeklyWeather class uses the DailyForecast class, shown in Example 4-2, to encapsulate the pairing of the day and the chance of precipitation,

Example 4-2. Value object for daily forecast data

package com.oreilly.strutsckbk.ch04;

public class DailyForecast {
    
    public DailyForecast(String day, int chanceOfPrecip) {
        this.day = day;
        this.chancePrecip = chanceOfPrecip;
    }

    public int getChancePrecip( ) {
        return chancePrecip;
    }

    public void setChancePrecip(int chancePrecip) {
        this.chancePrecip = chancePrecip;
    }
    public String getDay( ) {
        return day;
    }
    public void setDay(String day) {
        this.day = day;
    }

    private String day;
    private int chancePrecip;

}

Now that the Model has been created for the application, the JSP (horizontal_chart.jsp) to render the chart can be written as shown in Example 4-3.

Example 4-3. Precipitation graph JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

<html>
<head>
  <title>Struts Cookbook - Chapter 04</title>
</head>
<body bgcolor="white">
<h2>Struts Cookbook Chapter 4 Examples</h2>
<div align="center">
  <hr />
  <h3>Color Bar Chart (horizontal)</h3>
  <jsp:useBean id="weeklyWeather" 
            class="com.oreilly.strutsckbk.ch04.WeeklyWeather"/>
  <table border="0" width="60%">
  <logic:iterate id="dayEntry" name="weeklyWeather" property="weekForecast">
    <tr>
      <td align="right" width="20%">
        <bean:write name="dayEntry" property="day"/></td>
      <td align="left" width="80%">
        <table width='<bean:write name="dayEntry" 
                              property="chancePrecip"/>%' 
             bgcolor="#003366">
          <tr>
            <td align="right">
              <font color="white">
                <bean:write name="dayEntry" 
                        property="chancePrecip"/>%
            </font>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </logic:iterate>
  </table>
</div>
</body>
</html>

Figure 4-1 shows the resultant web page.

Simple HTML horizontal bar graph

Figure 4-1. Simple HTML horizontal bar graph

The horizontal chart is generated by iterating over the weekForecast property of the WeeklyWeather bean. A row of the table is generated for each element of the weekForecast property, with each row consisting of two columns. The first (left-most) column holds the name of the day of the week, and the second column displays the bars of the graph. The JSP page enables the graphing magic by specifying a nested HTML table within the cell. The nested table uses the chancePrecip property of the DailyForecast to set the width of the table as a percentage:

<table width='<bean:write name="dayEntry" 
                      property="chancePrecip"/>%' 
     bgcolor="#003366">

This percentage indicates the fractional amount of space that the table will occupy within the containing table cell. This nested table is then filled to this percentage with the background color specified by the bgcolor attribute. The actual content of this inner table is the percentage value text. By displaying the numeric percentage along with the graphic, you can pack a lot of information in a small space. If you didn't want to display the raw percentage data here, you could have set the cell contents to a nonblank space:

<td>&nbsp;</td>

In the Solution and in the Weekly Forecast example, the data to be graphed were in the form of a percentage value. The value was used without modification as the width percentage for the table. In many cases, the data is not a percentage but a raw scalar value. The main purpose for the chart may be to show a relative comparison of the data. You can compute the percentage value for the table width using simple arithmetic.

Continuing with the weather theme, suppose that you wanted to display the expected number of inches of rainfall per day for next week (starting with Sunday). Here is the raw forecast data from the weather service:

Day of week

Rainfall (inches)

Sunday

1.5

Monday

2.0

Tuesday

1.0

Wednesday

0.2

Thursday

0.8

Friday

1.0

Saturday

3.0

To turn these values into percentages for comparison purposes, you would use the following formula:

Percentage = (rainfall / max(rainfall)) * 100

With this formula, the day with the maximum rainfall amount will yield a percentage of 100 percent. Since the percentage calculation is used for presentation purposes, performing the calculation on the JSP page is acceptable. The JSP expression language (EL) available with JSTL supports arithmetic. EL does not, however, provide an easy way to calculate the maximum value. You will code this calculation to the WeeklyForecast Java class. You'll want to add the rainfallAmount float property to the DailyForecast object. Then, you'll want to add a method to the WeeklyForecast that calculates and returns the maximum rainfall.

Now you can use this data to create the bar graph. Example 4-4 shows the JSP page (horizontal_chart_jstl.jsp) to create the graph. In addition to its calculation abilities, the JSTL fmt:formatNumber tag formats the text representing the numeric value. This page shows how to use an image instead of background color to fill the graph.

Example 4-4. Expected rainfall JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>

<html>
<head>
  <title>Struts Cookbook - Chapter 04</title>
</head>
<body bgcolor="white">
<h2>Struts Cookbook Chapter 4 Examples</h2>
<div align="center">
  <h3>Expected Rainfall</h3>
  <table border="0" width="60%">
  <c:forEach var="dayEntry" items="${weeklyWeather.weekForecast}">
    <tr>
      <td align="right" width="20%">
        <bean:write name="dayEntry" property="day"/>
      </td>
      <td align="left" width="80%">
        <table background="images/raincloud.gif" 
           width="<c:out value='${(dayEntry.rainfall div weeklyWeather.
           maxRainfall) * 100}'/>%">
          <tr>
            <td align="right">
              <span style="{color:black;background-color:white}">
                <fmt:formatNumber value="${dayEntry.rainfall}" 
                pattern="##.0"/>"
              </span>
            </td>
           </tr>
        </table>
      </td>
    </tr>
  </c:forEach>
  </table>
</div>
</body>
</html>

The key to making the chart work is the calculation of the width percentage using JSTL:

width="<c:out value='${(dayEntry.rainfall div weeklyWeather.
maxRainfall) * 100}'/>%

The rendered bar chart is shown in Figure 4-2; it's obviously going to be a wet week.

Expected rainfall graph

Figure 4-2. Expected rainfall graph

See Also

The graphics shown in this recipe are created through a clever use of HTML. More complex graphics can be created by dynamically generating actual graphic images using graphic support provided by Java. Jason Hunter shows some of these techniques in Java Servlet Programming (O'Reilly).

If you are unfamiliar with JSTL you'll want to check out Recipe 3.1. Recipe 4.2 demonstrates how to render a more traditional vertical bar chart.

Get Jakarta Struts Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.