Home : Course Map : Chapter 6 : Java : Tech :
Histogram Plot
JavaTech
Course Map
Chapter 6

Introduction
AWT
Swing
Containers
  Demo 1
UI Components
  Demo 2
UI Layout
  Demo 3   Demo 4
Text Display
  Demo 5
Drawing
  Demo 6   Demo 7
Draw Polygons
  Demo 8   Demo 9
Colors
Text Draw 
  Demo 10
Images
  Demo 11
Exercises

    Supplements
AWT
  Demo 1
Drawing
  Demo 2
Text Drawing
  Demo 3
UI Components
  Demo 4

Java2D
Shapes & Areas
  Demo 1   Demo 2
Stroke & Paint
  Demo 3
Transforms
  Demo 4
Gradients&Textures
  Demo 5   Demo 6
Text
  Demo 7   Demo 8
     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

In this section we look at adding graphics to histograms. See Chapter 3: Tech : Histograms and Chapter 4 : Tech : More Histograms for earlier discussions of histogramming.

To simplify our histogram class hierarchy, we start with a new base Histogram class (code also shown below) that combines the attributes and methods of the earlier BasicHist and BetterHist classes, which we created to illustrate class and inheritance concepts. We added several new methods to our histogram to get or set various values describing the histogram. We will extend Histogram with various subclasses later. (See Chapter 8: Tech. Also, the Chapter 8: Tech : Refactoring section talks about reorganization of classes as we did here.)

For our display of histograms we create the HistPanel class. This class extends PlotPanel and overrides its PaintContents() method to draw the histogram graph. (See the Starting to Plot section for an explanation of how to use PlotPanel.) The class takes a Histogram object in the constructor. A new histogram can replace the current one with the setHistgram(Histogram hist) method.

We will use this histogramming panel in several demonstration projects in the course.


DrawHistApplet.java - create an instance of the Histogram class and fill it with values chosen from a Gaussian random number generator. Then create an instance of HistPanel and add it to the content pane.

+ New classes:

HistPanel.java - PlotPanel subclass that displays instances of Histogram. It draws retangles to represent the bin values as columns. The PlotPanel methods provide the scale information, axes labeling. title, etc.

Histogram.java - our new histogram base class combines the previous BasicHist and BetterHist classes. It includes the basics of histograms such as bin array, over/under flow variables, etc. In subsequent chapters we will create subclasses of this class with additional capabilities.

+ Previous classes:
Chapter 6: Tech: PlotPanel.java, PlotFormat.java

Java Techniques & Other Programming Notes

  • The Graphics class methods use integer values for the drawing positions and dimensions.When coordinates depend on floating point calculations there will be  round off errors. These errors can build up in a series of drawing commands that sum rounded off values. In the paintBars() method in HistPanel we reduce the effect by using floating point values to do the position computations before casting to the integer values.

  • Java2D methods offer the option of using floating-point values to reduce the round off problem.

  • The HistPanel class properties include a number of variables for the display settings such as the colors of the frame and so forth. Additional getter/setter methods could be added to allow for access and modifications to these variables.

 

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

/**
  *  Create an instance of Histogram, add a Gaussian generated
  *  distribution of data to it and display it.
 **/
public class DrawHistApplet extends JApplet
{
  Histogram fHistogram = null;

  public void init ()  {

    Container content_pane = getContentPane ();

    // Create a histogram with Gaussian distribution.
    makeHist ();

    // Create an instance of a JPanel sub-class
    HistPanel hist_panel = new HistPanel (fHistogram);

    // And add one or more panels to the JApplet panel.
    content_pane.add (hist_panel);

  } // init

  /**
    *  Create the histogram and a set of data drawn
    *  from a Gaussian random number generator.
   **/
  void makeHist () {
    // Create an instance of the Random class for
    // producing our random values.
    java.util.Random r = new java.util.Random ();

    // Them method nextGaussian in the class Random produces
    // a value centered at 0.0 and a standard deviation
    // of 1.0.

    // Create an instance of our basic gHistogram class.
    // Make it wide enough enough to include most of the
    // gaussian values.
    fHistogram = new Histogram ("Gaussian Distribution", "random values",
                             10,-2.0,2.0);

    // Fill histogram with Gaussian distribution
    for (int i=0; i<100000; i++) {
         double val = r.nextGaussian ();
         fHistogram.add (val);
    }
  } // makeHist

} // class DrawHistApplet

import java.awt.*;
import javax.swing.*;

/**  Display histogram data on a PlotPanel subclass. **/
public class HistPanel extends PlotPanel {

  // Histogram reference
  protected Histogram fHistogram;

  // Histogram bar parameters.
  protected Color fBarLineColor = Color.DARK_GRAY;
  protected Color fBarFillColor = Color.PINK;
  int fGap = 2;  // 2 pixels between bars

  // Fractional margin between highest bar and top frame
  double fTopMargin  = 0.05;

  // Fractional margnin between side bars and frame
  double fSideMargin = 0.01;

  // Arrays to hold numbers for axes scale values
  double [] fXScaleValue;
  double [] fYScaleValue;

  // Number of values on each axis
  int fNumYScaleValues = 2;
  int fNumXScaleValues = 5;

  /** Create the panel with the histogram. **/
  public HistPanel (Histogram histogram) {
    fHistogram = histogram;
    getScaling ();
  }

  /** Switch to a new histogram. **/
  public void setHistogram (Histogram histogram){
    fHistogram = histogram;
    getScaling ();
    repaint ();
  }

  /**
    * Get the values for the scale values on
    * the plot axes.
   **/
  void getScaling () {

    fYScaleValue = new double[fNumYScaleValues];
    // Use lowest value of 0;
    fYScaleValue[0] = 0.0;

    fXScaleValue = new double[fNumXScaleValues];
    // First get the low and high values;
    fXScaleValue[0] = fHistogram.getLo ();
    fXScaleValue[fNumXScaleValues-1] = fHistogram.getHi ();

    // Then calculate the difference between the values
    //  (assumes linear scale)
    double range = fXScaleValue[fNumXScaleValues-1] -
                        fXScaleValue[0];
    double dx = range/(fNumXScaleValues-1);

    // Now set the intermediate scale values.
    for (int i=1; i < (fNumXScaleValues-1); i++) {
        fXScaleValue[i] = i*dx + fXScaleValue[0];
    }

  } // getScaling

  /** Optional bar color settings. **/
  public void setBarColors (Color line, Color fill) {
    if ( line != null) fBarLineColor = line;
    if ( fill != null) fBarFillColor = fill;
  }


  /**
    * Overrides the abstract method in PlotPanel superclass.
    * Draw the vertical bars that represent the bin contents.
    * Draw also the numbers for the scales on the axes.
   **/
  void paintContents (Graphics g) {

    // Get the histogram max value and bin data
    int max_data_value = fHistogram.getMax ();
    int [] bins = fHistogram.getBins ();

    if (max_data_value == 0) return;

    Color old_color=g.getColor ();  // remember color for later

    // Dimensions of the drawing area for the bars
    int side_space =  (int) (fFrameWidth*fSideMargin);
    int draw_width= fFrameWidth - 2 * side_space- (bins.length-1)*fGap;

    int draw_height=  (int)(fFrameHeight* (1.0 - fTopMargin));

    // To avoid build up of round off errors, the bar
    // positions will be calculated from a FP value.
    float step_width= (float)draw_width/(float)bins.length;
    int bar_width = Math.round (step_width);
    step_width += fGap;

    // Scale the bars to the maximum bin value.
    float scale_factor= (float)draw_height/max_data_value;

    int start_x = fFrameX + side_space;
    int start_y = fFrameY + fFrameHeight;

    for (int i=0; i < bins.length; i++) {
        int bar_height =  (int)(bins[i] * scale_factor);

        int bar_x =  (int)(i * step_width) + start_x;

        // Bar drawn from top left corner
        int bar_y= start_y-bar_height;

        g.setColor (fBarLineColor);
        g.drawRect (bar_x,bar_y, bar_width ,bar_height);

        g.setColor (fBarFillColor);
        g.fillRect (bar_x+1, bar_y+1, bar_width-2, bar_height-1);
    }

    // Find the scale value of the full frame height.
    fYScaleValue[1] =  (double) (fFrameHeight/scale_factor);

    // Draw the numbers along the axes.
    drawAxesNumbers (g, fXScaleValue, fYScaleValue);

    g.setColor (old_color); //reset original color

  } // paintContents

  // Methods overriding those in PlotPanel
  String getTitle () {
    return fHistogram.getTitle ();
  }

  String getXLabel () {
    return fHistogram.getXLabel ();
  }

} // class HistPanel

/** This class provides the bare essentials for a histogram.**/
public class Histogram
{
  protected String fTitle = "Histogram";
  protected String fXLabel = "Data";

  protected int [] fBins;
  protected int fNumBins;
  protected int fUnderflows;
  protected int fOverflows;

  protected double fLo;
  protected double fHi;
  protected double fRange;

  /** The constructor will create an array of a given
    * number of bins. The range of the histogram given
    * by the upper and lower limit values.
   **/
  public Histogram (int numBins, double lo, double hi) {
    // Check for bad range values.
    // Could throw an exception but will just
    // use default values;
    if (hi < lo) {
        lo = 0.0;
        hi = 1.0;
    }
    if (numBins <= 0) numBins = 1;
    fNumBins = numBins;
    fBins = new int[fNumBins];
    fLo = lo;
    fHi = hi;
    fRange = fHi - fLo;
  } // ctor

  // This constructor includes the title and horizontal
  // axis label.
  public Histogram (String title, String xLabel,
                   int fNumBins, double lo, double hi) {
    this (fNumBins, lo, hi);// Invoke overloaded constructor
    fTitle= title;
    fXLabel = xLabel;
  } // ctor

//--- Histogram description --------------------------------
  /** Get to title string. **/
  public String getTitle ()
  { return fTitle; }

  /** Set the title. **/
  public void setTitle (String title)
  { fTitle = title; }

  /** Get to the horizontal axis label. **/
  public String getXLabel ()
  { return fXLabel; }

  /** Set the horizontal axis label. **/
  public void setXLabel (String xLabel)
  { fXLabel = xLabel; }

//--- Bin info access --------------------------------------
  /** Get the low end of the range. **/
  public double getLo ()
  { return fLo; }

  /** Get the high end of the range.**/
  public double getHi ()
  { return fHi; }

  /** Get the number of entries in the largest bin. **/
  public int getMax () {
    int max = 0;
    for (int i=0; i < fNumBins;i++)
         if (max < fBins[i]) max = fBins[i];
    return max;
  }

  /**
    * This method returns a reference to the fBins.
    * Note that this means the values of the histogram
    * could be altered by the caller object.
   **/
  public int [] getBins () {
    return fBins;
  }

  /** Get the number of entries in the smallest bin.**/
  public int getMin () {
    int min = getMax ();
    for (int i=0; i < fNumBins; i++)
         if (min > fBins[i]) min = fBins[i];
    return min;
  }

  /** Get the total number of entries not counting
    * overflows and underflows.
   **/
  public int getTotal () {
    int total = 0;
    for (int i=0; i < fNumBins; i++)
         total += fBins[i];
    return total;
  }

  /**
    * Add an entry to a bin.
    * @param x double value added if it is in the range:
    *   lo <= x < hi
   **/
  public void add (double x) {
    if (x >= fHi) fOverflows++;
    else if (x < fLo) fUnderflows++;
    else {
      double val = x - fLo;

      // Casting to int will round off to lower
      // integer value.
      int bin =  (int) (fNumBins *  (val/fRange) );

      // Increment the corresponding bin.
      fBins[bin]++;
    }
  }

  /** Clear the histogram bins and the over and under flows.**/
  public void clear () {
    for (int i=0; i < fNumBins; i++) {
      fBins[i] = 0;
      fOverflows = 0;
      fUnderflows= 0;
    }
  }

  /**
    * Provide access to the value in the bin element
    * specified by bin_num.

    * Return the underflows if bin value negative,
    * Return the overflows if bin value more than
    * the number of bins.
   **/
  public int getValue (int bin_num) {
    if (bin_num < 0)
        return fUnderflows;
    else if (bin_num >= fNumBins)
        return fOverflows;
    else
        return fBins[bin_num];
  }

  /**
    * Get the average and standard deviation of the
    * distribution of entries.
    * @return double array
   **/
  public double [] getStats () {
    int total = 0;

    double wt_total = 0;
    double wt_total2 = 0;
    double [] stat = new double[2];
    double bin_width = fRange/fNumBins;

    for (int i=0; i < fNumBins;i++) {
      total += fBins[i];

      double bin_mid =  (i - 0.5) * bin_width + fLo;
      wt_total  += fBins[i] * bin_mid;
      wt_total2 += fBins[i] * bin_mid * bin_mid;
    }

    if (total > 0) {
      stat[0] = wt_total / total;
      double av2 = wt_total2 / total;
      stat[1] = Math.sqrt (av2 - stat[0] * stat[0]);
    } else {
      stat[0] = 0.0;
      stat[1] = -1.0;
    }

    return stat;
  }// getStats()

 /**
   * Create the histogram from a user derived array along with the
   * under and overflow values.
   * The low and high range values that the histogram
   * corresponds to must be in passed as well.

   *
   * @param userBins array of int values.
   * @param under number of underflows.
   * @param over number of overflows.
   * @param lo value of the lower range limit.
   * @param hi value of the upper range limit.
  **/
  public void pack (int [] user_bins,
                   int under, int over,
                   double lo, double hi) {
    fNumBins = user_bins.length;
    fBins = new int[fNumBins];
    for (int i = 0; i < fNumBins; i++) {
      fBins[i] = user_bins[i];
    }

    fLo = lo;
    fHi = hi;
    fRange = fHi-fLo;
    fUnderflows = under;
    fOverflows = over;
  }// pack()

}// class Histogram

 

References & Web Resources


Latest update: Oct. 30, 2004

            Tech
Java Tech Graphics
Starting to Plot
  Demo 1
Drawing Panel
  Demo 2
Histogram Display

  Demo 3
Exercises

           Physics
Display Text Data
  Demo 1
Plot Data
  Demo 2
Find Max/Min
  Demo 3
Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.