One last version of our histogram interface will allow for display
of the histogram contents as points with error bars instead of vertical
columns.
We use the class HistogramStatR1
(see Refactoring), which includes the
following methods that deal with the error values for each bin:
- double [] getBinErrors()-
access to the bin error values for all bins
- double getBinError(int
bin) - access to the error value for a particular bin
- void makeBinErrors()
- create error values for each bin using error=sqrt(#
entries)
- boolean packErrors(double
[] errors) - set the error values from an array.
The class HistErrPanel
, a subclass of PlotPanel,
provides for drawing either error bars or the usual columns bars
to represent the histogram contents. (It also includes the option
of drawing a function over the top of the histogram distribution
but we will wait to discuss that in Chapter
8: Physics : Histogram Errors & Fits.
In the example below we use these two classes to create a similar
applet as that in the Median section.
HistErrorApplet.java
- uses the HistErrPanel to display contents of an
instance of HistogramStatR1. It follows the same basic
form as the Timers demo where
instances of java.util.Timer and java.util.TimerTask update
the display of the histogram during the filling of the
histogram. Includes a "Go" button to initiate the filling
of the histogram. To simulate data taking, a combination
of a Gaussian and random background values are generated.
The number of values come from an entry in a text field.
+ New
class:
HistErrPanel.java
- Display the histogram data with the option of points
and error bars in addition to the usual bin column type
display. It is a subclass of PlotPanel.
+ Previous classes:
Chapter
8:Tech: HistogramStatR1.java,
Chapter
8:Tech:
MakeData.java, Updateable.java
Chapter
7:Tech: HistogramStat.java,
Chapter
6:Tech: Histogram.java,
HistPanel.java
Chapter
6:Tech: DrawFunction.java
Chapter
6:Tech: PlotPanel.java,
PlotFormat.java
|
import
java.awt.*;
import javax.swing.*;
/**
* Display the histogram data. Subclass of PlotPanel
that
* provides the option of drawing error bars.
*/
public class HistErrPanel extends PlotPanel
{
// Histogram access
protected Histogram fHistogram;
// Flags values to indicate type of bin display
public final static int DRAW_BARS
= 0;
public final static int DRAW_PTS =
1;
public final static int DRAW_PTS_ERRS = 2;
int fDrawType = DRAW_BARS;
// Color settings for the bin display
Color fSymColor =
Color.BLUE;
Color fErrorBarColor = Color.BLUE;
protected Color fBarLineColor = Color.DARK_GRAY;
protected Color fBarFillColor = Color.PINK;
int gap = 2; // 2 pixels between bars
// Include the option to draw a function on the
panel
DrawFunction [] fDrawFunctions = null;
boolean fDrawFunctionFlag = false;
// 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 the numbers for the axes scale.
double [] fXScaleValue;
double [] fYScaleValue;
// Number of values to put on each axis.
int fNumYScaleValues = 2;
int fNumXScaleValues = 5;
// Symbol types for plotting points
public final static int RECT
= 0;
public final static int RECTFILL = 1;
public final static int OVAL
= 2;
public final static int OVALFILL = 3;
// Set default symbol to filled oval.
int fSymbolType = OVALFILL;
/** Create the panel with the histogram.
**/
public HistErrPanel (Histogram histogram) {
fHistogram = histogram;
getScaling ();
} // ctor
/**
* Create the panel with the histogram
and pass array
* of functions to draw over the histogram
plot.
**/
public HistErrPanel (Histogram histogram,
DrawFunction
[] fDrawFunctions) {
fHistogram = histogram;
getScaling ();
// Option of drawing on top of the
histogram.
if ( fDrawFunctions != null){
this.fDrawFunctions
= fDrawFunctions;
fDrawFunctionFlag
= true;
}
} // ctor
/** Switch to a new histogram. **/
public void setHistogram (Histogram histogram)
{
fHistogram = histogram;
getScaling ();
repaint ();
} // setHistogram
/**
* Get the values for putting scaling
numbers 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;
}
/** Optional symbol color settings. **/
public void setSymColors (Color color) {
if ( color != null) fSymColor=color;
}
/**
* 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 maxDataValue = fHistogram.getMax
();
// Ignore if no data in the histogram.
// Assumes no negative contents.
if (maxDataValue == 0) return;
// Make vertical room for error bars
if (fDrawType == DRAW_PTS_ERRS) {
// Use estimate
of the error bar for max value
maxDataValue
+= Math.sqrt (maxDataValue);
}
// Get the histogram bin array.
int [] bins = fHistogram.getBins ();
// Remember foreground color for later
Color oldColor = g.getColor ();
// Choose to draw bins as either points
with or without error bars
// or vertical column bars.
switch (fDrawType) {
case DRAW_PTS:
case DRAW_PTS_ERRS:
drawPoints
(g,bins,maxDataValue);
break;
case DRAW_BARS:
default:
drawBars (g,bins,maxDataValue);
break;
}
// Draw the numbers along the axes
drawAxesNumbers (g, fXScaleValue,
fYScaleValue);
// Draw the overlay functions
if (fDrawFunctionFlag) {
for (int i=0;
i < fDrawFunctions.length; i++){
fDrawFunctions[i].draw
(
g,
fFrameX,
fFrameY,
fFrameWidth,
fFrameHeight,
fXScaleValue,fYScaleValue);
}
}
g.setColor (oldColor); //reset original
color
} // paintContents
/**
* Draw vertical bars to represent
the histogram
* bin contents.
* @param g graphics context
* @param bins histogram array
* @param maxDataValue the large bin
value. Used to set
* the scale of the histogram.
**/
void drawBars (Graphics g, int [] bins, int max_data_value)
{
// Find the left margin width.
int side_space = (int)(fFrameWidth*fSideMargin);
// Find the width of the frame within
which to draw
// the bars.
// Leave room for gaps between frame
and the sides of
// the first and last bars.
int draw_width= fFrameWidth - 2 *
side_space -
(bins.length-1)
* gap;
// Leave a gap between top of tallest
bar and the frame.
int draw_height= (int)(fFrameHeight*
(1.0 - fTopMargin));
// Calculate step size between adjacent
bars.
// To avoid build up of roundoff 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 += gap;
// Scale the bars to the maximum bin
value.
float scale_factor= (float)draw_height/max_data_value;
// Starting horizontal point on left
hand side.
int start_x = fFrameX + side_space;
// Starting vertical point on bottom
left of frame
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[fYScaleValue.length-1]
=
(double)(fFrameHeight/scale_factor);
} // drawBars
/**
* Draw points instead of
column bars for the histogram contents.
* Option to include error
bars.
**/
void drawPoints (Graphics g, int [] bins, int
max_data_value) {
// If error bars on the points requested,
then
// obtain the error array.
double [] err_y = null;
if (fDrawType == DRAW_PTS_ERRS) {
// Get the histogram error
array. If the histogram is
// not the HistogramStatR1
subclass, an exception will occur
// here.
if (fHistogram instanceof
HistogramStatR1){
err_y
= ( (HistogramStatR1)fHistogram).getBinErrors ();
if
(err_y == null) return;
} else
return;
}
// Find the left margin width.
int side_space = (int)
(fFrameWidth*fSideMargin);
// For data symbols get size relative
to the frame.
int sym_dim = (int) (fFrameWidth
*.01);
// Leave a gap between top of tallest
bar and the frame.
int draw_height= (int)
(fFrameHeight* (1.0 - fTopMargin));
// Scale the data points to the maximum
bin value.
float scale_factor= (float)draw_height/max_data_value;
// Calculate step size between adjacent
points.
// To avoid build up of roundoff errors,
the point
// positions will be calculated from
a FP value.
float step_width= (float)fFrameWidth/
(float)bins.length;
// Calculate starting point horizontally.
Put points
// in middle of bin.
int start_x = Math.round (step_width/2)
+ fFrameX;
// Starting vertical point on bottom
left of frame
int start_y = fFrameY + fFrameHeight;
// Use half the width of a bin as
the x error bar.
int x_error_bar = fFrameWidth/(2 *
bins.length);
for (int i=0; i < bins.length; i++)
{
int x = (int)
(i * step_width)+start_x;
int y = start_y - (int)
(bins[i] * scale_factor);
// Draw data point symbols
g.setColor (fSymColor);
switch (fSymbolType)
{
case RECT
:
g.drawRect
(x-sym_dim, y-sym_dim, 2*sym_dim, 2*sym_dim);
break;
case RECTFILL
:
g.fillRect
(x-sym_dim, y-sym_dim, 2*sym_dim+1, 2*sym_dim+1);
break;
case OVAL
:
g.drawOval
(x-sym_dim, y-sym_dim, 2*sym_dim, 2*sym_dim);
break;
case OVALFILL
:
default :
g.fillOval
(x-sym_dim, y-sym_dim, 2*sym_dim+1, 2*sym_dim+1);
break;
}
g.setColor (fErrorBarColor);
if (fDrawType == DRAW_PTS_ERRS)
{
//
Draw x error bar as bin width.
g.drawLine
(x-x_error_bar,y,x+x_error_bar,y);
if
(err_y != null) {
int
y_bar = (int)(err_y[i] * scale_factor/2.0);
//
Draw y error bars but keep within the
//
frame.
int
dy_up = y - y_bar;
if
(dy_up < fFrameY) dy_up = fFrameY;
int
dy_down = y + y_bar;
if
(dy_down > start_y) dy_down = start_y;
g.drawLine
(x, dy_up, x, dy_down);
}
}
}
// Find the scale value of the full
frame height.
fYScaleValue[fYScaleValue.length-1]
=
(double)(fFrameHeight/scale_factor);
} // drawPoints
// Methods overriding those in PlotPanel
String getTitle ()
{ return fHistogram.getTitle ();}
String getXLabel ()
{ return fHistogram.getXLabel ();}
/**
* Set type of symbol to
use for a point in the plot.
* @param type set to one
of the class constants.
**/
void setSymbolType (int type) {
if (type < 0 || type > 3) return;
fSymbolType = type;
}
/** Set flag for drawing the error bars or not.
**/
void setDrawType (int drawType) {
fDrawType = drawType;
}
/**
* Pass object to array
of functions to draw over
* the histogram distributions.
**/
void setDrawFunction ( DrawFunction [] functions)
{
fDrawFunctions = functions;
}
/** Pass flag to draw a function overlay. **/
void setDrawFunctionEnabled (boolean flag) {
fDrawFunctionFlag = flag;
// Don't enable if no function available.
// Could throw an exception or a
warning dialog here.
//if ( drawFunction = null) fDrawFunctionFlag
= false;
} // setDrawFunctionEnabled
} // class HistErrPanel |
import
javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* This program will run as an applet inside
* an application frame.
*
* The applet uses the HistErrPanel
to display contents of
* an instance of HistogramStatR1.
*
* The java.util.Timer and java.util.TimerTask
are used
* to update the display of the histogram during
the filling
* of the histogram.
*
* Includes "Go" button to initiate the filling
of the histogram.
* To simulate data taking, a combination of
a Gaussian and random
* background values are generated.
*
* The number of values taken from
* entry in a JTextField. "Clear" button
clears the histogram.
* In standalone mode, the Exit button closes
the program.
*
**/
public class HistErrorApplet extends JApplet
implements ActionListener, Updateable
{
// Use the HistPanel JPanel subclass here
HistErrPanel fOutputPanel;
HistogramStatR1 histogram;
boolean fMakingHist =
false;
boolean fUpdateDisplay = false;
MakeData fMakeData;
int fNumDataPoints=100;
// Use the java.util Timer and TimerTask combo
// for timing events.
java.util.Timer fTimer;
// A text field for input strings
JTextField fTextField;
// Flag for whether the applet is in a browser
// or running via the main () below.
boolean fInBrowser = true;
int fDrawType = HistErrPanel.DRAW_PTS_ERRS;
// Buttons
JButton fGoButton;
JButton fStatsButton;
JButton fClearButton;
JButton fExitButton;
/**
* Create a User Interface with a
histogram and a Go button
* to initiate processing and a Clear
button to clear the .
* histogram. In application mode,
the Exit button stops the
* program. Add a stats button to
open a frame window to show
* statistical measures.
**/
public void init () {
Container content_pane = getContentPane
();
JPanel panel = new JPanel (new BorderLayout
());
// Use a textfield for an input
parameter.
fTextField =
new JTextField (Integer.toString
(fNumDataPoints), 10);
// If return hit after entering
text, the
// actionPerformed will be invoked.
fTextField.addActionListener (this);
fGoButton = new JButton ("Go");
fGoButton.addActionListener (this);
fStatsButton = new JButton ("Stats");
fStatsButton.addActionListener (this);
fClearButton = new JButton ("Clear");
fClearButton.addActionListener (this);
fExitButton = new JButton ("Exit");
fExitButton.addActionListener (this);
JPanel control_panel = new JPanel
();
control_panel.add (fTextField);
control_panel.add (fGoButton);
control_panel.add (fStatsButton);
control_panel.add (fClearButton);
control_panel.add (fExitButton);
// Create a histogram and start
filling it.
makeHist ();
fGoButton.setText ("Stop");
fClearButton.setEnabled (false);
fStatsButton.setEnabled (false);
if (fInBrowser) fExitButton.setEnabled
(false);
// JPanel subclass here.
fOutputPanel = new HistErrPanel
(histogram);
fOutputPanel.setDrawType (fDrawType);
panel.add (fOutputPanel,"Center");
panel.add (control_panel,"South");
// Add text area with scrolling
to the contentPane.
content_pane.add (panel);
} // init
public void actionPerformed (ActionEvent e)
{
Object source = e.getSource ();
if (source == fGoButton || source
== fTextField) {
String strNumDataPoints
= fTextField.getText ();
try {
fNumDataPoints
= Integer.parseInt (strNumDataPoints);
}
catch (NumberFormatException
ex) {
//
Could open an error dialog here but just
//
display a message on the browser status line.
showStatus
("Bad input value");
return;
}
if (!fMakingHist)
{
makeHist ();
fGoButton.setText ("Stop");
fStatsButton.setEnabled (false);
fClearButton.setEnabled (false);
} else {
// Stop button has been pushed
fGoButton.setText ("Go");
fStatsButton.setEnabled (true);
fClearButton.setEnabled (true);
fMakingHist = false;
}
} else if (source == fStatsButton)
{
displayStats
();
} else if (source == fClearButton)
{
// Clear the histogram
histogram.clear ();
repaint ();
} else if (!fInBrowser)
System.exit
(0);
} // actionPerformed
/** Create a frame to display the distribution
statistics. **/
void displayStats () {
JFrame frame =
new JFrame
("Histogram Distributions Statistics");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
JTextArea area = new JTextArea ();
double [] stats = histogram.getStats
();
if (stats != null) {
area.append ("Number
entries = " + histogram.getTotal ()+"\n");
String stat = PlotFormat.getFormatted
(
stats[HistogramStatR1.I_MEDIAN],
1000.0,0.001,3);
area.append ("Median
value = "+ stat +" "+"\n");
stat = PlotFormat.getFormatted
(
stats[HistogramStatR1.I_MEAN],
1000.0,0.001,3);
area.append ("Mean value
= "+ stat +" ");
stat = PlotFormat.getFormatted
(
stats[HistogramStatR1.I_MEAN_ERROR],
1000.0,0.001,3);
area.append (" +/- "+stat+"\n");
stat = PlotFormat.getFormatted
(
stats[HistogramStatR1.I_STD_DEV],
1000.0,0.001,3);
area.append ("Std. Dev.
= "+stat+"\n");
stat = PlotFormat.getFormatted
(
stats[HistogramStatR1.I_SKEWNESS],
1000.0,0.001,3);
area.append ("Skewness
= "+stat+"\n");
stat = PlotFormat.getFormatted
(
stats[HistogramStatR1.I_KURTOSIS],
1000.0,0.001,3);
area.append ("Kurtosis
= "+stat+"\n");
} else {
area.setText ("No statistical
information available");
}
frame.getContentPane ().add (area);
frame.setSize (200,200);
frame.setVisible (true);;
} // displayStats
/**
* Create the histogram,
create the MakeData instance and
* spin it off in a thread.
Set up a java.util.Timer to
* run the PaintHistTask
periodically.
**/
void makeHist () {
if (fMakingHist) return; // only
fill one hist at a time.
fMakingHist
= true;
// Create an instance of the histogram
class.
// Make it wide enough enough to
include the data.
if (histogram == null)
histogram
= new HistogramStatR1 ("Gaussian + Random Background",
"Data",
20,-10.0,10.0);
// Create the runnable object to
fill the histogram
// Center signal at 3.0 and create
background between
// -10 and 10. The fraction of the
data due to the
// Gaussian will be 0.60. The maximum
delay between
// data poins will be 500msecs.
fMakeData =
new MakeData (this,
histogram, fNumDataPoints,
3.0, 0.60, -10.0, 10.0, 500);
Thread data_thread = new Thread
(fMakeData);
// Before starting the filling,
create the timer task
// that will cause the histogram
display to update
// during the filling.
// Create a timer. TimerTask created
in MakeHist ()
fTimer = new java.util.Timer ();
// Start the timer after 100ms and
then repeat calls
// to run in PaintHistTask object
every 250ms.
fTimer.schedule (new PaintHistTask
(), 100, 250);
// Now start the data filling.
data_thread.start ();
} // makeHist
/**
* Use the inner class technique to define the
* TimerTask subclass for signalling that the
display
* should be updated.
*/
class PaintHistTask extends java.util.TimerTask
{
public void run () {
if (fDrawType == HistErrPanel.DRAW_PTS_ERRS)
histogram.makeBinErrors ();
fUpdateDisplay = true;
}
} // class PaintHistTask
/**
* Repaint the histogram
display. If called by
* the MakeData object,
it will turn off the
* update display flag
and return the fMakingHist flag
* to indicate if updating
should continue.
**/
public boolean update (Object obj) {
// Don't update the display until
the timer
// turns on the fUpdateDisplay flag.
if ( fUpdateDisplay) {
// Possible this method
called before fOutputPanel
// created in the init
(). So check if it exists
// before attempting
to repaint.
if (fOutputPanel !=
null) {
// Now
rescale and draw.
fOutputPanel.getScaling
();
fOutputPanel.repaint
();
}
fUpdateDisplay = false;
}
return fMakingHist;
} // update
/** Called when the histogram filling is finished.
**/
public void done () {
fMakingHist = false;
// Stop the histogram display updates.
fTimer.cancel ();
fOutputPanel.getScaling ();
fOutputPanel.repaint ();
// Reset the buttons.
fGoButton.setText ("Go");
fGoButton.setEnabled (true);
fStatsButton.setEnabled (true);
fClearButton.setEnabled (true);
} // done
public static void main (String[] args) {
//
int frame_width=450;
int frame_height=300;
// Create an instance of the applet
and display it on a frame.
HistErrorApplet applet = new HistErrorApplet
();
applet.fInBrowser = false;
applet.init ();
// Following anonymous class used
to close window & exit program
JFrame f = new JFrame ("Demo");
f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
// Add applet to the frame
f.getContentPane ().add (applet);
f.setSize (new Dimension (frame_width,frame_height));
f.setVisible (true);
} // main
} // class HistErrorApplet
|
References
& Web Resources
Last update: Nov. 8, 2004
|