/** * Copyright Copyright 2010-12 Simon Andrews * * This file is part of BamQC. * * BamQC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * BamQC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with BamQC; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Changelog: * - Piero Dalle Pezze: Class creation. */ package uk.ac.babraham.BamQC.Graphs; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.math.BigDecimal; import java.math.RoundingMode; import javax.swing.JFrame; import javax.swing.JPanel; import uk.ac.babraham.BamQC.Utilities.AxisScale; import uk.ac.babraham.BamQC.Utilities.FormatNumber; /** * * @author Piero Dalle Pezze * */ public class StackedHorizontalBarGraph extends JPanel { private static final long serialVersionUID = -5947375412672203276L; protected String [] labels; protected double [][] values; protected String xLabel; protected String title; protected double maxX = 0.0d; protected double minX = 0.0d; protected double xInterval; protected int height = -1; protected int width = -1; public StackedHorizontalBarGraph (String [] labels, double [][] values, String xLabel, String title, double minX, double maxX) { this.labels = labels; this.values = values; this.xLabel = xLabel; this.title = title; this.minX = minX; this.maxX = maxX; this.xInterval = new AxisScale (minX, maxX).getInterval(); } public StackedHorizontalBarGraph (String [] labels, double [][] values, String xLabel, String title) { this.labels = labels; this.values = values; this.xLabel = xLabel; this.title = title; double tempSum; for(int i=0; i<values.length; i++) { tempSum = 0.0d; for(int j=0; j<values[i].length; j++) { tempSum = tempSum + values[i][j]; } if(tempSum > maxX) maxX = tempSum; else if(tempSum < minX) minX = tempSum; } // log.debug("maxX is "+maxX); this.xInterval = new AxisScale (minX, maxX).getInterval(); } @Override public Dimension getPreferredSize () { return new Dimension(800,600); } @Override public Dimension getMinimumSize () { return new Dimension(100,200); } @Override public int getHeight () { if (height <0) { return super.getHeight(); } return height; } @Override public int getWidth () { if (width <0) { return super.getWidth(); } return width; } @Override protected void paintComponent(Graphics g) { g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); if (g instanceof Graphics2D) { ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } // First we need to find the widest label int widestLabel = 0; for (int l=0;l<labels.length;l++) { int width = g.getFontMetrics().stringWidth(labels[l]); if (width > widestLabel) widestLabel = width; } // Add 3px either side for a bit of space; widestLabel += 6; // Calculate how much space each y line is going to get. Each sequence // gets a line as does the axis and there is a blank line at top and bottom. int yLineHeight = getHeight()/(labels.length+3); // Draw a title int titleWidth = g.getFontMetrics().stringWidth(title); g.drawString(title, (getWidth()/2) - (titleWidth/2), (yLineHeight/2)+(g.getFontMetrics().getAscent()/2)); // Draw the axes // Original methods from HorizontalBarGraph to try X and Y axes. // y axis g.drawLine(widestLabel, yLineHeight, widestLabel, getHeight()-(yLineHeight*2)); // x axis g.drawLine(widestLabel, getHeight()-(yLineHeight*2), getWidth()-20, getHeight()-(yLineHeight*2)); // Draw the xLabel under the xAxis g.drawString(xLabel, (getWidth()/2) - (g.getFontMetrics().stringWidth(xLabel)/2), getHeight()-5); // draw the scale for the x axis double currentValue = minX; int currentPosition = 0; int lastXLabelEnd = 0; while (currentValue < maxX) { int xPos = getX((float)currentValue, widestLabel); String label = "" + new BigDecimal(currentValue).setScale( FormatNumber.getFirstSignificantNonNullDecimalPosition(xInterval), RoundingMode.HALF_UP).doubleValue(); label = label.replaceAll(".0$", ""); // Don't leave trailing .0s where we don't need them. int labelWidth = g.getFontMetrics().stringWidth(label); currentPosition = xPos-(labelWidth/2); if(currentPosition > lastXLabelEnd) { g.drawLine(xPos, getHeight()-(yLineHeight*2), xPos, getHeight()-(yLineHeight*2)+3); g.drawString(label, currentPosition, getHeight()-(yLineHeight*2)+(g.getFontMetrics().getHeight()+3)); lastXLabelEnd = currentPosition + labelWidth + 5; } currentValue += xInterval; } // Now draw the data // set yOffset int xOffset=0, yOffset=yLineHeight-6; for(int i=0; i<values.length; i++) { String label = labels[i]; int labelWidth = g.getFontMetrics().stringWidth(label); g.drawString(label, widestLabel-(3+labelWidth), yLineHeight*(i+2)-(yLineHeight/2)+(g.getFontMetrics().getAscent()/2)); //set yPos (the y coordinate are fine) int xPos=0, yPos=(yLineHeight*(i+1))+3; int cumulativeXOffset = 0; for(int j=0; j<values[i].length; j++) { int xValue = getX(Math.min(values[i][j], maxX), widestLabel); // set xPos and xOffset if(cumulativeXOffset==0) xPos=widestLabel; else xPos=cumulativeXOffset; xOffset=xValue-widestLabel; g.setColor(new Color(200,0,0)); g.fillRect(xPos, yPos, xOffset, yOffset); g.setColor(Color.BLACK); g.drawRect(xPos, yPos, xOffset, yOffset); cumulativeXOffset = xPos+xOffset; } } } private int getX(double value, int longestLabel) { int lengthToUse = getWidth()-(longestLabel+20); double proportion = value/maxX; return longestLabel+(int)(lengthToUse*proportion); } public static void main(String[] argv) { JFrame f = new JFrame(); f.setSize(600, 300); double[][] values = new double[3][4]; String[] names = new String[3]; values[0][0] = 2; values[0][1] = 1; values[0][2] = 10; values[0][3] = 1; names[0] = "Item 1"; values[1][0] = 4; values[1][1] = 1; values[1][2] = 8; values[1][3] = 2; names[1] = "Very long Item 2"; values[2][0] = 1; values[2][1] = 7; values[2][2] = 2; values[2][3] = 6; names[2] = "Item 3"; f.getContentPane().add(new StackedHorizontalBarGraph(names, values, "count", "Stacked Horizontal Bar Graph Test")); WindowListener wndCloser = new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }; f.addWindowListener(wndCloser); f.setVisible(true); } }