/**
* Copyright Copyright 2010-14 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:
* - Simon Andrews: Class creation.
*/
package uk.ac.babraham.BamQC.Graphs;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
/**
*
* @author Simon Andrews
*
*/
public class QualityBoxPlot extends JPanel {
private static final long serialVersionUID = -6133984815195517118L;
private double [] means;
private double [] medians;
private double [] lowest;
private double [] highest;
private double [] lowerQuartile;
private double [] upperQuartile;
private String [] xLabels;
private String graphTitle;
private double minY;
private double maxY;
private double yInterval;
private static final Color GOOD = new Color(195,230,195);
private static final Color BAD = new Color(230,220,195);
private static final Color UGLY = new Color(230,195,195);
private static final Color GOOD_DARK = new Color(175,230,175);
private static final Color BAD_DARK = new Color(230,215,175);
private static final Color UGLY_DARK = new Color(230,175,175);
private int height = -1;
private int width = -1;
public QualityBoxPlot (double [] means, double [] medians, double [] lowest, double [] highest, double [] lowerQuartile, double [] upperQuartile, double minY, double maxY, double yInterval, String [] xLabels, String graphTitle) {
this.means = means;
this.medians = medians;
this.lowest = lowest;
this.highest = highest;
this.lowerQuartile = lowerQuartile;
this.upperQuartile = upperQuartile;
this.minY = minY;
this.maxY = maxY;
this.yInterval = yInterval;
this.xLabels = xLabels;
this.graphTitle = graphTitle;
this.yInterval = yInterval;
}
@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);
int lastY = 0;
double yStart;
if (minY % yInterval == 0) {
yStart = minY;
}
else {
yStart = yInterval * (((int)minY/yInterval)+1);
}
int xOffset = 0;
for (double i=yStart;i<=maxY;i+=yInterval) {
String label = ""+i;
label = label.replaceAll(".0$", "");
int width = g.getFontMetrics().stringWidth(label);
if (width > xOffset) {
xOffset = width;
}
g.drawString(label, 2, getY(i)+(g.getFontMetrics().getAscent()/2));
}
// Give the x axis a bit of breathing space
xOffset += 5;
g.setColor(Color.BLACK);
// Draw the graph title
int titleWidth = g.getFontMetrics().stringWidth(graphTitle);
g.drawString(graphTitle, (xOffset + ((getWidth()-(xOffset+10))/2)) - (titleWidth/2), 30);
// Work out the width of the x axis bins
int baseWidth = (getWidth()-(xOffset+10))/means.length;
if (baseWidth<1) baseWidth = 1;
// First draw faint boxes over alternating bases so you can see which is which
int lastXLabelEnd = 0;
for (int i=0;i<means.length;i++) { // Now draw some background colours which show good / bad quality
if (i%2 != 0) {
g.setColor(UGLY);
}
else {
g.setColor(UGLY_DARK);
}
g.fillRect(xOffset+(baseWidth*i), getY(20), baseWidth, getY(yStart)-getY(20));
if (i%2 != 0) {
g.setColor(BAD);
}
else {
g.setColor(BAD_DARK);
}
g.fillRect(xOffset+(baseWidth*i), getY(28), baseWidth, getY(20)-getY(28));
if (i%2 != 0) {
g.setColor(GOOD);
}
else {
g.setColor(GOOD_DARK);
}
g.fillRect(xOffset+(baseWidth*i), getY(maxY), baseWidth, getY(28)-getY(maxY));
g.setColor(Color.BLACK);
int baseNumberWidth = g.getFontMetrics().stringWidth(xLabels[i]);
int labelStart = ((baseWidth/2)+xOffset+(baseWidth*i))-(baseNumberWidth/2);
if (labelStart > lastXLabelEnd) {
g.drawString(xLabels[i], labelStart, getHeight()-25);
lastXLabelEnd = labelStart+g.getFontMetrics().stringWidth(xLabels[i])+5;
}
}
// Now draw the axes
g.drawLine(xOffset, getHeight()-40, getWidth()-10,getHeight()-40);
g.drawLine(xOffset, getHeight()-40, xOffset, 40);
g.drawString("Position in read (bp)", (getWidth()/2) - (g.getFontMetrics().stringWidth("Position in read (bp)")/2), getHeight()-5);
// Now draw the boxplots
for (int i=0;i<medians.length;i++) {
int boxBottomY = getY(lowerQuartile[i]);
int boxTopY = getY(upperQuartile[i]);
int lowerWhiskerY = getY(lowest[i]);
int upperWhiskerY = getY(highest[i]);
int medianY = getY(medians[i]);
// System.out.println("For base "+i+" values are BoxBottom="+lowerQuartile[i]+" boxTop="+upperQuartile[i]+" whiskerBottom="+lowest[i]+" whiskerTop="+highest[i]+" median="+medians[i]);
// System.out.println("For base "+i+" Yvalues are BoxBottom="+boxBottomY+" boxTop="+boxTopY+" whiskerBottom="+lowerWhiskerY+" whiskerTop="+upperWhiskerY+" median="+medianY);
// Draw the main box
g.setColor(new Color(240,240,0));
g.fillRect(xOffset+(baseWidth*i)+2, boxTopY, baseWidth-4, boxBottomY-boxTopY);
g.setColor(Color.BLACK);
g.drawRect(xOffset+(baseWidth*i)+2, boxTopY, baseWidth-4, boxBottomY-boxTopY);
// Draw the upper whisker
g.drawLine(xOffset+(baseWidth*i)+(baseWidth/2), upperWhiskerY, xOffset+(baseWidth*i)+(baseWidth/2), boxTopY);
g.drawLine(xOffset+(baseWidth*i)+2, upperWhiskerY, xOffset+(baseWidth*(i+1))-2, upperWhiskerY);
// Draw the lower whisker
g.drawLine(xOffset+(baseWidth*i)+(baseWidth/2), lowerWhiskerY, xOffset+(baseWidth*i)+(baseWidth/2), boxBottomY);
g.drawLine(xOffset+(baseWidth*i)+2, lowerWhiskerY, xOffset+(baseWidth*(i+1))-2, lowerWhiskerY);
// Draw the median line
g.setColor(new Color(200,0,0));
g.drawLine(xOffset+(baseWidth*i)+2, medianY, (xOffset+(baseWidth*(i+1)))-2,medianY);
}
// Now overlay the means
g.setColor(new Color(0,0,200));
lastY = getY(means[0]);
for (int i=1;i<means.length;i++) {
int thisY = getY(means[i]);
g.drawLine((baseWidth/2)+xOffset+(baseWidth*(i-1)), lastY, (baseWidth/2)+xOffset+(baseWidth*i), thisY);
lastY = thisY;
}
}
public int getY(double y) {
return (getHeight()-40) - (int)(((getHeight()-80)/(maxY-minY))*(y-minY));
}
}