/*
* Created on Jun 10, 2005
* @author Rafael Santos (rafael.santos@lac.inpe.br)
*
* Part of the Java Advanced Imaging Stuff site
* (http://www.lac.inpe.br/~rafael.santos/Java/JAI)
*
* STATUS: Complete, but could be improved, for example, with:
* - Plotting more than one band of the histogram.
* - Considering the minimum number of pixels in a bin.
* - Customization as a JavaBean.
*
* Redistribution and usage conditions must be done under the
* Creative Commons license:
* English: http://creativecommons.org/licenses/by-nc-sa/2.0/br/deed.en
* Portuguese: http://creativecommons.org/licenses/by-nc-sa/2.0/br/deed.pt
* More information on design and applications are on the projects' page
* (http://www.lac.inpe.br/~rafael.santos/Java/JAI).
*/
package org.geotools.renderedimage.viewer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.media.jai.Histogram;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.HistogramDescriptor;
import javax.swing.JComponent;
/**
* This class displays a histogram (instance of Histogram) as a component.
* Only the first histogram band ins considered for plotting.
* The component has a tooltip which displays the bin index and bin count for the
* bin under the mouse cursor.
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/render/src/main/java/org/geotools/renderedimage/viewer/DisplayHistogram.java $
*/
public class DisplayHistogram extends JComponent implements MouseMotionListener
{
/**
*
*/
private static final long serialVersionUID = -8640931037312978101L;
// The histogram and its title.
private Histogram histogram;
private String title;
// Some data and hints for the histogram plot.
private int[] counts;
private double maxCount;
private int indexMultiplier = 1;
private int skipIndexes = 8;
// The components' dimensions.
private int width,height=250;
// Some constants for this component.
private int verticalTicks = 10;
private Insets border = new Insets(40,70,40,30);
private int binWidth = 3;
private Color backgroundColor = Color.BLACK;
private Color barColor = new Color(255,255,200);
private Color marksColor = new Color(100,180,255);
private Font fontSmall = new Font("monospaced",0,10);
private Font fontLarge = new Font("default",Font.ITALIC,20);
/**
* The constructor for this class, which will set its fields' values and get some information
* about the histogram.
* @param histogram the histogram to be plotted.
* @param title the title of the plot.
*/
public DisplayHistogram(Histogram histogram,String title)
{
this(title);
setHistogram(histogram);
}
private void setHistogram(Histogram histogram) {
this.histogram = histogram;
// Calculate the components dimensions.
width = histogram.getNumBins(0)*binWidth;
// Get the histogram data.
counts = histogram.getBins(0);
// Get the max and min counts.
maxCount = Integer.MIN_VALUE;
for(int c=0;c<counts.length;c++) maxCount = Math.max(maxCount,counts[c]);
repaint();
}
public DisplayHistogram(String title) {
this.title=title;
addMouseMotionListener(this);
}
/**
* Override the default bin width (for plotting)
*/
public void setBinWidth(int newWidth)
{
binWidth = newWidth;
width = histogram.getNumBins(0)*binWidth;
}
/**
* Override the default height for the plot.
* @param h the new height.
*/
public void setHeight(int h)
{
height = h;
}
/**
* Override the index multiplying factor (for bins with width != 1)
*/
public void setIndexMultiplier(int i)
{
indexMultiplier = i;
}
/**
* Override the index skipping factor (determines how many labels will be
* printed on the index axis).
*/
public void setSkipIndexes(int i)
{
skipIndexes = i;
}
/**
* Set the maximum value (used to scale the histogram y-axis). The default value
* is defined in the constructor and can be overriden with this method.
*/
public void setMaxCount(int m)
{
maxCount = m;
}
/**
* This method informs the maximum size of this component, which will be the same as the preferred size.
*/
@Override
public Dimension getMaximumSize()
{
return getPreferredSize();
}
/**
* This method informs the minimum size of this component, which will be the same as the preferred size.
*/
@Override
public Dimension getMinimumSize()
{
return getPreferredSize();
}
/**
* This method informs the preferred size of this component, which will be constant.
*/
@Override
public Dimension getPreferredSize()
{
return new Dimension(width+border.left+border.right,height+border.top+border.bottom);
}
/**
* This method will paint the component.
*/
@Override
protected void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
// Draw the background.
g2d.setColor(backgroundColor);
g2d.fillRect(0,0,getSize().width,getSize().height);
// Draw some marks.
g2d.setColor(marksColor);
g2d.drawRect(border.left,border.top,width,height);
// Draw the histogram bars.
g2d.setColor(barColor);
for(int bin=0;bin<histogram.getNumBins(0);bin++)
{
int x = border.left+bin*binWidth;
double barStarts = border.top+height*(maxCount-counts[bin])/(1.*maxCount);
double barEnds = Math.ceil(height*counts[bin]/(1.*maxCount));
g2d.drawRect(x,(int)barStarts,binWidth,(int)barEnds);
}
// Draw the values on the horizontal axis. We will plot only 1/8th of them.
g2d.setColor(marksColor);
g2d.setFont(fontSmall);
FontMetrics metrics = g2d.getFontMetrics();
int halfFontHeight = metrics.getHeight()/2;
for(int bin=0;bin<=histogram.getNumBins(0);bin++)
{
if (bin % skipIndexes == 0)
{
String label = ""+(indexMultiplier*bin);
int textHeight = metrics.stringWidth(label); // remember it will be rotated!
int x = border.left+bin*binWidth+binWidth/2;
g2d.translate(border.left+bin*binWidth+halfFontHeight,border.top+height+textHeight+2);
g2d.rotate(-Math.PI/2);
g2d.drawString(label,0,0);
g2d.rotate(Math.PI/2);
g2d.translate(-(border.left+bin*binWidth+halfFontHeight),-(border.top+height+textHeight+2));
}
}
// Draw the values on the vertical axis. Let's draw only some of them.
double step = (int)(maxCount/verticalTicks);
for(int l=0;l<=verticalTicks;l++) // last will be done separately
{
String label;
if (l == verticalTicks) label = ""+maxCount;
else label = ""+(l*step);
int textWidth = metrics.stringWidth(label);
g2d.drawString(label,border.left-2-textWidth,border.top+height-l*(height/verticalTicks));
}
// Draw the title.
g2d.setFont(fontLarge);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
metrics = g2d.getFontMetrics();
int textWidth = metrics.stringWidth(title);
g2d.drawString(title,(border.left+width+border.right-textWidth)/2,28);
}
/**
* This method does not do anything, it is here to keep the MouseMotionListener interface happy.
*/
public void mouseDragged(MouseEvent e)
{
}
/**
* This method will be called when the mouse is moved over the component. It will
* set the tooltip text on the component to show the histogram data.
*/
public void mouseMoved(MouseEvent e)
{
int x = e.getX(); int y = e.getY();
// Don't show anything out of the plot region.
if ((x > border.left) && (x < border.left+width) && (y > border.top) && (y < border.top+height))
{
// Convert the X to an index on the histogram.
x = (x-border.left)/binWidth;
y = counts[x];
setToolTipText((indexMultiplier*x)+": "+y);
}
else
{
setToolTipText(null);
}
}
public void setImage(PlanarImage wrapRenderedImage) {
setHistogram(
(Histogram) HistogramDescriptor.create(
wrapRenderedImage,
null,
new Integer(1),
new Integer(1),
new int[]{65536},
new double[]{0},
new double[]{65535},
null).getProperty("histogram"));
}
} // end class