Newer
Older
/* JamView
*
* created: 2009
*
* This file is part of Artemis
*
* Copyright(C) 2009 Genome Research Limited
*
* This program 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 2
* of the License, or(at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import javax.swing.ButtonGroup;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
tjc
committed
import org.apache.log4j.Level;
import net.sf.samtools.AlignmentBlock;
import net.sf.samtools.SAMFileHeader;
import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.SAMSequenceRecord;
import net.sf.samtools.SAMFileReader.ValidationStringency;
import net.sf.samtools.util.CloseableIterator;
import uk.ac.sanger.artemis.Entry;
import uk.ac.sanger.artemis.EntryGroup;
import uk.ac.sanger.artemis.Options;
import uk.ac.sanger.artemis.Selection;
import uk.ac.sanger.artemis.SelectionChangeEvent;
import uk.ac.sanger.artemis.SelectionChangeListener;
import uk.ac.sanger.artemis.components.DisplayAdjustmentEvent;
import uk.ac.sanger.artemis.components.DisplayAdjustmentListener;
import uk.ac.sanger.artemis.components.FeatureDisplay;
tjc
committed
import uk.ac.sanger.artemis.components.FileViewer;
import uk.ac.sanger.artemis.components.SwingWorker;
import uk.ac.sanger.artemis.editor.MultiLineToolTipUI;
import uk.ac.sanger.artemis.sequence.MarkerRange;
import uk.ac.sanger.artemis.sequence.NoSequenceException;
import uk.ac.sanger.artemis.util.Document;
import uk.ac.sanger.artemis.util.DocumentFactory;
import uk.ac.sanger.artemis.util.OutOfRangeException;
public class BamView extends JPanel
implements DisplayAdjustmentListener, SelectionChangeListener
private Hashtable<String, Integer> seqLengths = new Hashtable<String, Integer>();
private Hashtable<String, Integer> offsetLengths;
private List<String> bamList;
private List<Integer> hideBamList = new Vector<Integer>();
private SAMRecordFlagPredicate samRecordFlagPredicate;
private boolean isSingle = false;
private boolean isSNPs = false;
private boolean isStackView = false;
private boolean isPairedStackView = false;
private FeatureDisplay feature_display;
private Selection selection;
private JPanel mainPanel;
private Ruler ruler;
private int startBase = -1;
private int endBase = -1;
private boolean showBaseAlignment = false;
private JMenu bamFilesMenu = new JMenu("BAM files");
private JCheckBoxMenuItem logMenuItem = new JCheckBoxMenuItem("Use Log Scale", logScale);
private JCheckBoxMenuItem checkBoxStackView = new JCheckBoxMenuItem("Stack View");
private JCheckBoxMenuItem baseQualityColour = new JCheckBoxMenuItem("Colour by Base Quality");;
private JCheckBoxMenuItem markInsertions = new JCheckBoxMenuItem("Mark Insertions");
tjc
committed
private AlphaComposite translucent =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f);
tjc
committed
private Color lightGrey = new Color(200, 200, 200);
private Color darkGreen = new Color(0, 150, 0);
private Color darkOrange = new Color(255,140,0);
private Color deepPink = new Color(139,10,80);
private Point lastMousePoint = null;
private SAMRecord mouseOverSAMRecord = null;
private SAMRecord highlightSAMRecord = null;
private String mouseOverInsertion;
// record of where a mouse drag starts
private int dragStart = -1;
private int maxHeight = 800;
tjc
committed
private int BASE_HEIGHT;
private PopupMessageFrame popFrame = new PopupMessageFrame();
private PopupMessageFrame waitingFrame = new PopupMessageFrame("waiting...");
public static org.apache.log4j.Logger logger4j =
org.apache.log4j.Logger.getLogger(BamView.class);
public BamView(List<String> bamList,
this.bamList = bamList;
this.nbasesInView = nbasesInView;
if(reference != null)
{
try
{
getEntry(reference,entryGroup);
}
catch (NoSequenceException e)
{
e.printStackTrace();
}
}
try
{
readHeaderPicard();
}
catch(java.lang.UnsupportedClassVersionError err)
{
JOptionPane.showMessageDialog(null,
"This requires Java 1.6 or higher.",
"Check Java Version", JOptionPane.WARNING_MESSAGE);
}
final javax.swing.plaf.FontUIResource font_ui_resource =
Options.getOptions().getFontUIResource();
while(keys.hasMoreElements())
{
Object key = keys.nextElement();
Object value = UIManager.get(key);
if(value instanceof javax.swing.plaf.FontUIResource)
UIManager.put(key, font_ui_resource);
}
setFont(Options.getOptions().getFont());
ALIGNMENT_PIX_PER_BASE = fm.charWidth('M');
BASE_HEIGHT = fm.getMaxAscent();
MultiLineToolTipUI.initialize();
setToolTipText("");
}
public String getToolTipText()
{
tjc
committed
if(mouseOverSAMRecord == null)
return null;
String msg =
mouseOverSAMRecord.getReadName() + "\n" +
mouseOverSAMRecord.getAlignmentStart() + ".." +
mouseOverSAMRecord.getAlignmentEnd() + "\nisize=" +
mouseOverSAMRecord.getInferredInsertSize() + "\nmapq=" +
mouseOverSAMRecord.getMappingQuality()+"\nrname="+
mouseOverSAMRecord.getReferenceName();
tjc
committed
if( mouseOverSAMRecord.getReadPairedFlag() &&
mouseOverSAMRecord.getProperPairFlag() &&
!mouseOverSAMRecord.getMateUnmappedFlag())
tjc
committed
{
msg = msg +
tjc
committed
(mouseOverSAMRecord.getReadNegativeStrandFlag() ? "-" : "+")+" / "+
(mouseOverSAMRecord.getMateNegativeStrandFlag() ? "-" : "+");
}
else
msg = msg +
tjc
committed
(mouseOverSAMRecord.getReadNegativeStrandFlag() ? "-" : "+");
if(msg != null && mouseOverInsertion != null)
msg = msg + "\nInsertion at:" +mouseOverInsertion;
return msg;
String bam = bamList.get(0);
File bamFile = new File(bam);
File indexFile = new File(bam+".bai");
final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
SAMFileHeader header = inputSam.getFileHeader();
List<SAMSequenceRecord> readGroups = header.getSequenceDictionary().getSequences();
for(int i=0; i<readGroups.size(); i++)
{
seqLengths.put(readGroups.get(i).getSequenceName(),
readGroups.get(i).getSequenceLength());
seqNames.add(readGroups.get(i).getSequenceName());
}
inputSam.close();
private void readFromBamPicard(int start, int end, int bamIndex)
{
// Open the input file. Automatically detects whether input is SAM or BAM
// and delegates to a reader implementation for the appropriate format.
String bam = bamList.get(bamIndex);
//System.out.println("Reading from ... "+bam);
File bamFile = new File(bam);
File indexFile = new File(bam+".bai");
final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
inputSam.setValidationStringency(ValidationStringency.SILENT);
if(concatSequences)
{
int len = 0;
int lastLen = 1;
for(int i=0; i<seqNames.size(); i++)
{
int thisLength = seqLengths.get(seqNames.get(i));
len += thisLength;
if( (lastLen >= start && lastLen < end) ||
(len >= start && len < end) ||
(start >= lastLen && start < len) ||
(end >= lastLen && end < len) )
{
int offset = getSequenceOffset(seqNames.get(i));
int thisStart = start - offset;
if(thisStart < 1)
thisStart = 1;
int thisEnd = end - offset;
if(thisEnd > thisLength)
thisEnd = thisLength;
//System.out.println("READ "+seqNames.get(i)+" "+thisStart+".."+thisEnd);
iterateOverBam(inputSam, seqNames.get(i), thisStart, thisEnd, bamIndex);
}
lastLen = len;
}
}
else
{
String refName = (String) combo.getSelectedItem();
iterateOverBam(inputSam, refName, start, end, bamIndex);
}
inputSam.close();
//System.out.println("readFromBamPicard "+start+".."+end);
//System.out.println("Reads in view ... "+readsInView.size());
}
/**
* Iterate over BAM file and load into the <code>List</code> of
* <code>SAMRecord</code>.
* @param inputSam
* @param refName
* @param start
* @param end
*/
private void iterateOverBam(final SAMFileReader inputSam,
String refName, int start, int end,
int bamIndex)
boolean multipleBAM = false;
if(bamList.size() > 1)
multipleBAM = true;
CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end);
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
if( samRecordFlagPredicate == null ||
!samRecordFlagPredicate.testPredicate(samRecord))
{
if(samRecordMapQPredicate == null ||
samRecordMapQPredicate.testPredicate(samRecord))
{
if(multipleBAM)
samRecord.setAttribute("FL", bamIndex);
}
cnt = 0;
float heapFraction =
(float)((float)memory.getHeapMemoryUsage().getUsed()/
(float)memory.getHeapMemoryUsage().getMax());
logger4j.debug("Heap memory usage (used/max): "+heapFraction);
if(heapFraction > 0.97)
{
(memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb).\n"+
"Zoom in or consider increasing the\nmemory for this application.",
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
it.close();
}
private int getSequenceLength()
{
if(concatSequences)
{
int len = 0;
for(int i=0; i<seqNames.size(); i++)
len += seqLengths.get(seqNames.get(i));
return len;
}
else
return seqLengths.get((String) combo.getSelectedItem());
}
/**
* For BAM files with multiple references sequences, calculate
* the offset from the start of the concatenated sequence for
* a given reference.
* @param refName
* @return
*/
protected int getSequenceOffset(String refName)
{
if(!concatSequences)
return 0;
if(offsetLengths == null)
{
offsetLengths = new Hashtable<String, Integer>(combo.getItemCount());
int offset = 0;
for(int i=0; i<combo.getItemCount(); i++)
{
String thisSeqName = (String) combo.getItemAt(i);
offsetLengths.put(thisSeqName, offset);
offset += seqLengths.get(combo.getItemAt(i));
}
return offsetLengths.get(refName);
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if(startBase > 0)
start = startBase;
else
if(endBase > 0)
end = endBase;
else
end = start + nbasesInView - 1;
if(end > seqLength)
end = seqLength;
boolean changeToStackView = false;
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
if(!waitingFrame.isVisible())
waitingFrame.showWaiting("loading...", mainPanel);
synchronized (this)
try
{
float heapFractionUsedBefore = (float) ((float) memory.getHeapMemoryUsage().getUsed() /
(float) memory.getHeapMemoryUsage().getMax());
if(readsInView == null)
readsInView = new Vector<SAMRecord>();
else
readsInView.clear();
for(int i=0; i<bamList.size(); i++)
{
if(!hideBamList.contains(i))
readFromBamPicard(start, end, i);
}
float heapFractionUsedAfter = (float) ((float) memory.getHeapMemoryUsage().getUsed() /
(float) memory.getHeapMemoryUsage().getMax());
// System.out.println("Heap Max : "+memory.getHeapMemoryUsage().getMax());
// System.out.println("Heap Used : "+memory.getHeapMemoryUsage().getUsed());
// System.out.println("Heap memory used "+heapFractionUsedAfter);
if ((heapFractionUsedAfter - heapFractionUsedBefore) > 0.06
&& !isStackView && heapFractionUsedAfter > 0.8)
{
checkBoxStackView.setSelected(true);
isStackView = true;
changeToStackView = true;
}
if ((!isStackView && !isStrandStackView)
|| pixPerBase * 1.08f >= ALIGNMENT_PIX_PER_BASE)
{
Collections.sort(readsInView, new SAMRecordComparator());
}
}
catch (OutOfMemoryError ome)
JOptionPane.showMessageDialog(this, "Out of Memory");
readsInView.clear();
return;
//System.out.println(start+".."+end+" " +
// "sequence length = "+getSequenceLength()+
// " pixPerBase="+pixPerBase);
if(showBaseAlignment)
drawStackView(g2, seqLength, pixPerBase, start, end);
else if(isPairedStackView)
drawPairedStackView(g2, seqLength, pixPerBase, start, end);
else if(isStrandStackView)
drawStrandStackView(g2, seqLength, pixPerBase, start, end);
else
drawLineView(g2, seqLength, pixPerBase, start, end);
if(isCoverage)
{
coveragePanel.setStartAndEnd(start, end);
coveragePanel.setPixPerBase(pixPerBase);
coveragePanel.repaint();
}
if(changeToStackView)
{
"Note :: Changed to the stack view to save memory.\n"+
"Currently this is using "+
(memory.getHeapMemoryUsage().getUsed()/1000000.f)+" Mb "+
"and the maximum\nmemory limit is "+
(memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.",
return (float)mainPanel.getWidth() / (float)nbasesInView;
{
if(feature_display == null)
return seqLength+nbasesInView/3;
/**
* Draw the zoomed-in base view.
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawBaseAlignment(Graphics2D g2,
int seqLength,
float pixPerBase,
final int start,
tjc
committed
end = start + ( mainPanel.getWidth() * ALIGNMENT_PIX_PER_BASE );
if(bases != null)
{
// draw the reference sequence
ypos+=11;
if(seqEnd > bases.getLength())
seqEnd = bases.getLength();
bases.getSubSequence(new Range(refSeqStart, seqEnd), Bases.FORWARD).toUpperCase();
tjc
committed
g2.setColor(lightGrey);
g2.fillRect(0, ypos-11, mainPanel.getWidth(), 11);
drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end);
}
catch (OutOfRangeException e)
{
e.printStackTrace();
}
}
drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end);
g2.setStroke(new BasicStroke (2.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
Rectangle r = jspView.getViewport().getViewRect();
int nreads = readsInView.size();
ypos += 11;
SAMRecord thisRead = readsInView.get(i);
if (ypos < r.getMaxY() || ypos > r.getMinY())
drawSequence(g2, thisRead, ypos, refSeq, refSeqStart);
drawn[i] = true;
int thisEnd = thisRead.getAlignmentEnd();
if (thisEnd == 0)
thisEnd = thisRead.getAlignmentStart() + thisRead.getReadLength();
for (int j = i + 1; j < nreads; j++)
SAMRecord nextRead = readsInView.get(j);
int nextStart = nextRead.getAlignmentStart();
if (nextStart > thisEnd + 1)
{
if (ypos < r.getMaxY() || ypos > r.getMinY())
drawSequence(g2, nextRead, ypos, refSeq, refSeqStart);
drawn[j] = true;
thisEnd = nextRead.getAlignmentEnd();
if (thisEnd == 0)
thisEnd = nextStart + nextRead.getReadLength();
}
else if (ypos > r.getMaxY() || ypos < r.getMinY())
break;
catch (ArrayIndexOutOfBoundsException ae)
{
System.err.println(readsInView.size()+" "+nreads);
ae.printStackTrace();
}
Dimension d = getPreferredSize();
d.setSize(getPreferredSize().getWidth(), ypos);
setPreferredSize(d);
/**
* Draw the query sequence
* @param g2
* @param read
* @param pixPerBase
* @param ypos
*/
private void drawSequence(Graphics2D g2, SAMRecord samRecord,
int ypos, String refSeq, int refSeqStart)
if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped
int len = 0;
int refPos = 0;
String readSeq = samRecord.getReadString();
int offset = getSequenceOffset(samRecord.getReferenceName());
byte[] phredQuality = null;
if(baseQualityColour.isSelected())
phredQuality = samRecord.getBaseQualities();
Hashtable<Integer, String> insertions = null;
List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks();
for(int i=0; i<blocks.size(); i++)
AlignmentBlock block = blocks.get(i);
int blockStart = block.getReadStart();
for(int j=0; j<block.getLength(); j++)
int readPos = blockStart-1+j;
xpos = block.getReferenceStart() - 1 + j + offset;
refPos = xpos - refSeqStart + 1;
setColourByBaseQuality(g2, phredQuality[readPos]);
if(isSNPs && refSeq != null && refPos > 0 && refPos < refSeq.length())
if(readSeq.charAt(readPos) != refSeq.charAt(refPos))
g2.setColor(Color.red);
else
g2.setColor(col);
}
g2.drawString(readSeq.substring(readPos, readPos+1),
refPos*ALIGNMENT_PIX_PER_BASE, ypos);
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
// look for insertions
if(markInsertions.isSelected() && i < blocks.size()-1)
{
int blockEnd = blockStart+block.getLength();
int nextBlockStart = blocks.get(i+1).getReadStart();
int insertSize = nextBlockStart - blockEnd;
if(insertSize > 0)
{
if(insertions == null)
insertions = new Hashtable<Integer, String>();
g2.setColor(deepPink);
int xscreen = refPos*ALIGNMENT_PIX_PER_BASE;
insertions.put(xscreen,
(samRecord.getAlignmentStart()+len-2)+" "+
readSeq.substring(blockEnd-1, nextBlockStart-1));
g2.drawLine(xscreen, ypos, xscreen, ypos-BASE_HEIGHT);
// mark on reference sequence as well
if(bases != null)
g2.drawLine(xscreen, 11, xscreen, 11-BASE_HEIGHT);
g2.setColor(col);
}
}
// highlight
if(highlightSAMRecord != null &&
highlightSAMRecord.getReadName().equals(samRecord.getReadName()) &&
blocks.size() > 0)
refPos = blocks.get(0).getReferenceStart()+offset-refSeqStart;
int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
int width = len*ALIGNMENT_PIX_PER_BASE;
g2.setColor(Color.red);
g2.drawRect(xstart, ypos-BASE_HEIGHT, width, BASE_HEIGHT);
}
if(lastMousePoint != null && blocks.size() > 0)
refPos = blocks.get(0).getReferenceStart()+offset-refSeqStart;
int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
int xend = (refPos+len)*ALIGNMENT_PIX_PER_BASE;
if(lastMousePoint.getY() > ypos-11 && lastMousePoint.getY() < ypos)
if(lastMousePoint.getX() > xstart &&
lastMousePoint.getX() < xend)
{
mouseOverSAMRecord = samRecord;
if(insertions != null)
mouseOverInsertion = insertions.get((int)lastMousePoint.getX());
/**
* Colour bases on their mapping quality.
* @param g2
* @param baseQuality
*/
private void setColourByBaseQuality(Graphics2D g2, byte baseQuality)
{
if (baseQuality < 10)
g2.setColor(Color.blue);
else if (baseQuality < 20)
g2.setColor(darkGreen);
else if (baseQuality < 30)
g2.setColor(darkOrange);
else
g2.setColor(Color.black);
}
/**
* Draw zoomed-out view.
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawLineView(Graphics2D g2, int seqLength, float pixPerBase, int start, int end)
{
drawSelectionRange(g2, pixPerBase,start, end);
if(showScale)
drawScale(g2, start, end, pixPerBase, getHeight());
new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
SAMRecord samRecord = readsInView.get(i);
SAMRecord samNextRecord = null;
if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing
int ypos = getYPos(scaleHeight, samRecord.getReadString().length()); // (getHeight() - scaleHeight) - samRecord.getReadString().length();
if(ypos > r.getMaxY() || ypos < r.getMinY())
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
int ypos = getYPos(scaleHeight, Math.abs(samRecord.getInferredInsertSize()));
if( (ypos > r.getMaxY() || ypos < r.getMinY()) && ypos > 0 )
if(samRecord.getReadName().equals(samNextRecord.getReadName()))
if(samRecord.getAlignmentEnd() < samNextRecord.getAlignmentStart() &&
(samNextRecord.getAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
drawTranslucentLine(g2,
(int)((samRecord.getAlignmentEnd()-getBaseAtStartOfView())*pixPerBase),
(int)((samNextRecord.getAlignmentStart()-getBaseAtStartOfView())*pixPerBase), ypos);
if( samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
samNextRecord.getReadNegativeStrandFlag() )
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
drawRead(g2, samNextRecord, pixPerBase, ypos, baseAtStartOfView);
drawLoneRead(g2, samRecord, ypos, pixPerBase, baseAtStartOfView);
drawLoneRead(g2, samRecord, ypos, pixPerBase, baseAtStartOfView);
private int getYPos(int scaleHeight, int size)
{
int ypos;
if(!logScale)
ypos = (getHeight() - scaleHeight) - size;
else
{
int logInfSize = (int)( Math.log(size) * 100);
ypos = (getHeight() - scaleHeight) - logInfSize;
}
return ypos;
}
tjc
committed
/**
* Draw the reads as lines in vertical stacks. The reads are colour
* coded as follows:
*
* blue - reads are unique and are paired with a mapped mate
* black - reads are unique and are not paired or have an unmapped mate
* green - reads are duplicates
tjc
committed
*
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawStackView(Graphics2D g2,
int seqLength,
float pixPerBase,
int start,
int end)
{
drawSelectionRange(g2, pixPerBase,start, end);
if(isShowScale())
BasicStroke stroke = new BasicStroke(
1.3f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int ypos = (getHeight() - scaleHeight);
tjc
committed
int maxEnd = 0;
int lstStart = 0;
int lstEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
for(int i=0; i<readsInView.size(); i++)
{
SAMRecord samRecord = readsInView.get(i);
int offset = getSequenceOffset(samRecord.getReferenceName());
tjc
committed
int recordStart = samRecord.getAlignmentStart()+offset;
int recordEnd = samRecord.getAlignmentEnd()+offset;
tjc
committed
if(lstStart != recordStart || lstEnd != recordEnd)
{
if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped
g2.setColor(Color.black);
else
g2.setColor(Color.blue);
if(maxEnd < recordStart)
{
ypos = (getHeight() - scaleHeight)-2;
maxEnd = recordEnd+2;
}
else
ypos = ypos-2;
tjc
committed
g2.setColor(darkGreen);
tjc
committed
lstStart = recordStart;
lstEnd = recordEnd;
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
/**
* Draw the reads as lines in vertical stacks. The reads are colour
* coded as follows:
*
* blue - reads are unique and are paired with a mapped mate
* black - reads are unique and are not paired or have an unmapped mate
* green - reads are duplicates
*
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawStrandStackView(Graphics2D g2,
int seqLength,
float pixPerBase,
int start,
int end)
{
drawSelectionRange(g2, pixPerBase,start, end);
BasicStroke stroke = new BasicStroke(
1.3f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
int scaleHeight = 15;
drawScale(g2, start, end, pixPerBase, ((getHeight()+scaleHeight)/2));
int ymid = (getHeight()/ 2);
// positive strand
drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -2, pixPerBase, stroke);
// negative strand
drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), 2, pixPerBase, stroke);
}
private void drawStrand(Graphics2D g2,
boolean isStrandNegative,
int scaleHeight,
int ymid,
int ystep,
float pixPerBase,
Stroke stroke)
{
int ypos = (getHeight() - scaleHeight);
int maxEnd = 0;
int lstStart = 0;
int lstEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
for(int i=0; i<readsInView.size(); i++)
{
SAMRecord samRecord = readsInView.get(i);
if( samRecord.getReadNegativeStrandFlag() == isStrandNegative )
{
int offset = getSequenceOffset(samRecord.getReferenceName());
int recordStart = samRecord.getAlignmentStart()+offset;
int recordEnd = samRecord.getAlignmentEnd()+offset;
if(lstStart != recordStart || lstEnd != recordEnd)
{
if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped
g2.setColor(Color.black);
else
g2.setColor(Color.blue);
if(maxEnd < recordStart)
{
ypos = ymid + ystep;
maxEnd = recordEnd+2;
}
else
ypos = ypos + ystep;
}
else
g2.setColor(darkGreen);
lstStart = recordStart;
lstEnd = recordEnd;
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
/**
* Draw the reads as lines in vertical stacks. The reads are colour
* coded as follows:
*
* blue - reads are unique and are paired with a mapped mate
* black - reads are unique and are not paired or have an unmapped mate
* green - reads are duplicates
*
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawPairedStackView(Graphics2D g2,
int seqLength,
float pixPerBase,
int start,
int end)
{
drawSelectionRange(g2, pixPerBase,start, end);
if(isShowScale())
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
Vector<PairedRead> pairedReads = new Vector<PairedRead>();
for(int i=0; i<readsInView.size(); i++)
{
SAMRecord samRecord = readsInView.get(i);
if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped
continue;
SAMRecord samNextRecord = null;
if(i < readsInView.size()-1)
{
samNextRecord = readsInView.get(++i);
PairedRead pr = new PairedRead();
if(samRecord.getReadName().equals(samNextRecord.getReadName()))
{
if(samRecord.getAlignmentStart() < samNextRecord.getAlignmentStart())
{
pr.sam1 = samRecord;
pr.sam2 = samNextRecord;
}
else
{
pr.sam2 = samRecord;
pr.sam1 = samNextRecord;
}
}
else
{
--i;
pr.sam1 = samRecord;
pr.sam2 = null;
}
pairedReads.add(pr);
}
}
Collections.sort(pairedReads, new PairedReadComparator());
Stroke originalStroke = new BasicStroke (1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
Stroke stroke =
new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int ypos = getHeight() - scaleHeight - 3;
int lastEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
for(int i=0; i<pairedReads.size(); i++)
{
PairedRead pr = pairedReads.get(i);
if(pr.sam1.getAlignmentStart() > lastEnd)
{
ypos = getHeight() - scaleHeight - 3;
if(pr.sam2 != null)
{
lastEnd = pr.sam2.getAlignmentEnd();
}
else
lastEnd = pr.sam1.getAlignmentEnd();
}
else
ypos = ypos - 3;
if(ypos > r.getMaxY() || ypos < r.getMinY())
g2.setStroke(originalStroke);
g2.setColor(Color.LIGHT_GRAY);
if(pr.sam2 != null)
{
drawTranslucentJointedLine(g2,
(int)((pr.sam1.getAlignmentEnd()-getBaseAtStartOfView())*pixPerBase),
(int)((pr.sam2.getAlignmentStart()-getBaseAtStartOfView())*pixPerBase), ypos);
}
else
{
if(!pr.sam1.getMateUnmappedFlag() &&
pr.sam1.getMateReferenceName().equals(pr.sam1.getReferenceName()))
{
int prStart;
if(pr.sam1.getAlignmentStart() > pr.sam1.getMateAlignmentStart())
prStart = pr.sam1.getAlignmentEnd();
else
prStart = pr.sam1.getAlignmentStart();
drawTranslucentJointedLine(g2,
(int)( (prStart-getBaseAtStartOfView())*pixPerBase),
(int)( (pr.sam1.getMateAlignmentStart()-getBaseAtStartOfView())*pixPerBase), ypos);
}
}
if( pr.sam1.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
( pr.sam2 != null && pr.sam2.getReadNegativeStrandFlag() ) )
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
drawRead(g2, pr.sam1, pixPerBase, ypos, baseAtStartOfView);
if(pr.sam2 != null)
drawRead(g2, pr.sam2, pixPerBase, ypos, baseAtStartOfView);
}
}
/**
* Draw a read that apparently has a read mate that is not in view.
* @param g2
* @param thisRead
* @param ypos
* @param pixPerBase
* @param originalStroke
* @param stroke
*/
private void drawLoneRead(Graphics2D g2, SAMRecord samRecord, int ypos,
float pixPerBase, int baseAtStartOfView)
boolean offTheTop = false;
int offset = getSequenceOffset(samRecord.getReferenceName());
int thisStart = samRecord.getAlignmentStart()+offset;
int thisEnd = thisStart + samRecord.getReadString().length() -1;
if(ypos <= 0)
{
offTheTop = true;
ypos = samRecord.getReadString().length();
}
if(Math.abs(samRecord.getMateAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
{
g2.setColor(Color.LIGHT_GRAY);
if(samRecord.getAlignmentEnd() < samRecord.getMateAlignmentStart())
{
(int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
drawTranslucentLine(g2,
(int)((thisEnd-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
int nextStart =
(int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
drawTranslucentLine(g2,
(int)((thisStart-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
if(offTheTop)
g2.setColor(darkOrange);
else if(samRecord.getReadNegativeStrandFlag()) // strand of the query (1 for reverse)
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
showSNPsOnReads(g2, samRecord, pixPerBase, ypos, offset);
private void drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos)
g2.drawLine( 0, ypos-14,
(int)((end - getBaseAtStartOfView())*pixPerBase), ypos-14);
private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division, int ypos)
int markStart = (Math.round(start/division)*division);
if(markStart < 1)
markStart = 1;
g2.drawString(Integer.toString(m), x, ypos-1);
g2.drawLine((int)x, ypos-14,(int)x, ypos-11);
/**
* Draw a y-scale for inferred size (isize) of reads.
* @param g2
* @param xScaleHeight
*/
private void drawYScale(Graphics2D g2, int xScaleHeight)
int maxY = getPreferredSize().height-xScaleHeight;
if(logScale)
{
int start = 10;
int count = 0;
int ypos = getYPos(xScaleHeight, start);
while(ypos > 0 && count < 15 && start > 1)
{
g2.drawLine(0, ypos, 2, ypos);
g2.drawString(Integer.toString(start), 3, ypos);
start = start*5;
ypos = getYPos(xScaleHeight, start);
count++;
}
return;
}
for(int i=100; i<maxY; i+=100)
{
int ypos = getHeight()-i-xScaleHeight;
g2.drawLine(0, ypos, 2, ypos);
g2.drawString(Integer.toString(i), 3, ypos);
/**
* Draw a given read.
* @param g2
* @param thisRead
* @param pixPerBase
* @param ypos
float pixPerBase,
int ypos,
int baseAtStartOfView)
int offset = getSequenceOffset(thisRead.getReferenceName());
int thisStart = thisRead.getAlignmentStart()+offset-baseAtStartOfView;
int thisEnd = thisRead.getAlignmentEnd()+offset-baseAtStartOfView;
if(highlightSAMRecord != null &&
highlightSAMRecord.getReadName().equals(thisRead.getReadName()))
{
Stroke originalStroke = g2.getStroke();
Stroke stroke =
new BasicStroke (3.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
Color c = g2.getColor();
g2.setColor(Color.black);
g2.drawLine((int)( thisStart * pixPerBase), ypos,
(int)( thisEnd * pixPerBase), ypos);
g2.setColor(c);
}
g2.drawLine((int)( thisStart * pixPerBase), ypos,
(int)( thisEnd * pixPerBase), ypos);
// test if the mouse is over this read
if(lastMousePoint != null)
{
if(lastMousePoint.getY()+2 > ypos && lastMousePoint.getY()-2 < ypos)
if(lastMousePoint.getX() > thisStart * pixPerBase &&
lastMousePoint.getX() < thisEnd * pixPerBase)
{
mouseOverSAMRecord = thisRead;
}
}
showSNPsOnReads(g2, thisRead, pixPerBase, ypos, offset);
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
/**
* Highlight a selected range
* @param g2
* @param pixPerBase
* @param start
* @param end
*/
private void drawSelectionRange(Graphics2D g2, float pixPerBase, int start, int end)
{
if(getSelection() != null)
{
Range selectedRange = getSelection().getSelectionRange();
if(selectedRange != null)
{
int rangeStart = selectedRange.getStart();
int rangeEnd = selectedRange.getEnd();
if(end < rangeStart || start > rangeEnd)
return;
int x = (int) (pixPerBase*(rangeStart-getBaseAtStartOfView()));
int width = (int) (pixPerBase*(rangeEnd-rangeStart+1));
g2.setColor(Color.pink);
g2.fillRect(x, 0, width, getHeight());
}
}
}
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
/**
* Draw a translucent line
* @param g2
* @param start
* @param end
* @param ypos
*/
private void drawTranslucentLine(Graphics2D g2, int start, int end, int ypos)
{
Composite origComposite = g2.getComposite();
g2.setComposite(translucent);
g2.drawLine(start, ypos, end, ypos);
g2.setComposite(origComposite);
}
/**
* Draw a translucent line
* @param g2
* @param start
* @param end
* @param ypos
*/
private void drawTranslucentJointedLine(Graphics2D g2, int start, int end, int ypos)
{
Composite origComposite = g2.getComposite();
g2.setComposite(translucent);
int mid = (int) ((end-start)/2.f)+start;
//g2.drawLine(start, ypos, end, ypos);
g2.drawLine(start, ypos, mid, ypos-5);
g2.drawLine(mid, ypos-5, end, ypos);
g2.setComposite(origComposite);
}
/**
* Display the SNPs for the given read.
* @param g2
* @param thisRead
* @param pixPerBase
* @param ypos
*/
private void showSNPsOnReads(Graphics2D g2, SAMRecord thisRead,
{
int thisStart = thisRead.getAlignmentStart();
int thisEnd = thisRead.getAlignmentEnd();
// use alignment blocks of the contiguous alignment of
// subsets of read bases to a reference sequence
List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
new Range(thisStart+offset, thisEnd+offset), Bases.FORWARD);
offset = offset - getBaseAtStartOfView();
for(int i=0; i<blocks.size(); i++)
AlignmentBlock block = blocks.get(i);
for(int j=0; j<block.getLength(); j++)
int readPos = block.getReadStart()-1+j;
int refPos = block.getReferenceStart()+j;
if (Character.toUpperCase(refSeq[refPos-thisStart]) != readSeq[readPos])
{
g2.drawLine((int) ((refPos+offset) * pixPerBase), ypos + 2,
(int) ((refPos+offset) * pixPerBase), ypos - 2);
g2.setColor(col);
}
catch (OutOfRangeException e)
{
e.printStackTrace();
/**
* Add the alignment view to the supplied <code>JPanel</code> in
* a <code>JScrollPane</code>.
* @param mainPanel panel to add the alignment to
* @param autohide automatically hide the top panel containing the buttons
*/
public void addJamToPanel(final JPanel mainPanel,
final boolean autohide,
final FeatureDisplay feature_display)
if(feature_display != null)
{
this.feature_display = feature_display;
this.selection = feature_display.getSelection();
topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
}
else
{
topPanel = new JMenuBar();
frame.setJMenuBar((JMenuBar)topPanel);
JMenu fileMenu = new JMenu("File");
topPanel.add(fileMenu);
JMenuItem readBam = new JMenuItem("Open new BamView ...");
fileMenu.add(readBam);
readBam.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String[] s = new String[0];
BamView.main(s);
}
});
JMenuItem close = new JMenuItem("Close");
fileMenu.add(close);
close.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
BamView.this.setVisible(false);
Component comp = BamView.this;
while( !(comp instanceof JFrame) )
comp = comp.getParent();
((JFrame)comp).dispose();
}
});
JMenuItem exit = new JMenuItem("Exit");
fileMenu.add(exit);
exit.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
int status = JOptionPane.showConfirmDialog(BamView.this,
JOptionPane.OK_CANCEL_OPTION);
if(status != JOptionPane.OK_OPTION)
return;
System.exit(0);
}
});
if(seqNames.size() > 1)
{
int len = 0;
for(int i=0; i<seqNames.size(); i++)
len += seqLengths.get(seqNames.get(i));
if(feature_display != null &&
len == feature_display.getSequenceLength())
concatSequences = true;
else if(bases != null &&
len == bases.getLength() )
concatSequences = true;
}
final JCheckBox buttonAutoHide = new JCheckBox("Hide", autohide);
buttonAutoHide.setToolTipText("Auto-Hide");
final MouseMotionListener mouseMotionListener = new MouseMotionListener()
}
public void mouseMoved(MouseEvent e)
{
int thisHgt = HEIGHT;
if (thisHgt < 5)
thisHgt = 15;
int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY());
if (y < thisHgt)
}
mainPanel.repaint();
mainPanel.revalidate();
}
};
addMouseMotionListener(mouseMotionListener);
combo = new JComboBox(seqNames);
combo.setEditable(false);
combo.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
laststart = -1;
lastend = -1;
setZoomLevel(BamView.this.nbasesInView);
goTo.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
try
{
int basePosition = Integer.parseInt(baseText.getText());
}
catch (NumberFormatException nfe)
{
JOptionPane.showMessageDialog(BamView.this,
"Expecting a base number!", "Number Format",
JOptionPane.WARNING_MESSAGE);
}
JButton zoomIn = new JButton("-");
Insets ins = new Insets(1,1,1,1);
zoomIn.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
JButton zoomOut = new JButton("+");
zoomOut.setMargin(ins);
zoomOut.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
if (showBaseAlignment)
return;
setZoomLevel((int) (BamView.this.nbasesInView * .9));
jspView = new JScrollPane(this,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
setDisplay(1, nbasesInView, null);
mainPanel.setLayout(new BorderLayout());
if(topPanel instanceof JPanel)
mainPanel.add(topPanel, BorderLayout.NORTH);
mainPanel.add(jspView, BorderLayout.CENTER);
JPanel bottomPanel = new JPanel(new BorderLayout());
coveragePanel = new CoveragePanel(this);
bottomPanel.add(coveragePanel, BorderLayout.CENTER);
if(feature_display == null)
{
scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 1, nbasesInView, 1,
getMaxBasesInPanel(getSequenceLength()));
scrollBar.setUnitIncrement(nbasesInView/20);
scrollBar.addAdjustmentListener(new AdjustmentListener()
{
public void adjustmentValueChanged(AdjustmentEvent e)
{
repaint();
}
});
bottomPanel.add(scrollBar, BorderLayout.SOUTH);
}
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
coveragePanel.setPreferredSize(new Dimension(900, 100));
coveragePanel.setVisible(false);
jspView.getVerticalScrollBar().setValue(
jspView.getVerticalScrollBar().getMaximum());
jspView.getVerticalScrollBar().setUnitIncrement(maxUnitIncrement);
public void keyPressed(final KeyEvent event)
setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
setZoomLevel((int) (BamView.this.nbasesInView * .9));
addMouseListener(new PopupListener());
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
private void addToViewMenu(final int thisBamIndex)
{
File f = new File(bamList.get(thisBamIndex));
final JCheckBoxMenuItem cbBam = new JCheckBoxMenuItem(
f.getName(), true);
bamFilesMenu.add(cbBam);
cbBam.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(cbBam.isSelected())
hideBamList.remove(new Integer(thisBamIndex));
else
hideBamList.add(new Integer(thisBamIndex));
laststart = -1;
lastend = -1;
repaint();
}
});
}
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
{
final JMenuItem addBam = new JMenuItem("Add Bam ...");
menu.add(addBam);
addBam.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
FileSelectionDialog bamFileSelection = new FileSelectionDialog(null, false);
List<String> bamFiles = bamFileSelection.getBamFiles();
int count = bamList.size();
bamList.addAll(bamFileSelection.getBamFiles());
for(int i=0; i<bamFiles.size(); i++)
addToViewMenu(i+count);
laststart = -1;
lastend = -1;
repaint();
}
});
bamFilesMenu.setFont(addBam.getFont());
menu.add(bamFilesMenu);
for(int i=0; i<bamList.size(); i++)
addToViewMenu(i);
menu.add(new JSeparator());
final JCheckBoxMenuItem checkBoxPairedStackView = new JCheckBoxMenuItem("Paired Stack View");
final JCheckBoxMenuItem checkBoxStrandStackView = new JCheckBoxMenuItem("Strand Stack View");
final JCheckBoxMenuItem checkIsizeStackView = new JCheckBoxMenuItem("Inferred Size View", true);
checkBoxStackView.setFont(checkIsizeStackView.getFont());
baseQualityColour.setFont(checkIsizeStackView.getFont());
markInsertions.setFont(checkIsizeStackView.getFont());
group.add(checkBoxStackView);
group.add(checkBoxPairedStackView);
group.add(checkBoxStrandStackView);
group.add(checkIsizeStackView);
checkIsizeStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
lastend = -1;
if(checkIsizeStackView.isSelected())
{
isStackView = false;
isPairedStackView = false;
isStrandStackView = false;
checkBoxStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
lastend = -1;
isStackView = !isStackView;
if(isStackView)
{
isPairedStackView = !isStackView;
checkBoxPairedStackView.setSelected(!isStackView);
checkBoxPairedStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
lastend = -1;
isPairedStackView = !isPairedStackView;
if(isPairedStackView)
{
isStackView = !isPairedStackView;
checkBoxStackView.setSelected(!isPairedStackView);
}
repaint();
}
});
checkBoxStrandStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
lastend = -1;
isStrandStackView = !isStrandStackView;
if(isStrandStackView)
{
isStackView = !isStrandStackView;
isPairedStackView = !isStrandStackView;
checkBoxStackView.setSelected(!isStrandStackView);
checkBoxPairedStackView.setSelected(!isStrandStackView);
setViewportMidPoint();
viewMenu.add(checkBoxStrandStackView);
menu.add(viewMenu);
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
JMenu showMenu = new JMenu("Show");
JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads");
checkBoxSingle.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaint();
isSingle = !isSingle;
}
});
showMenu.add(checkBoxSingle);
final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNPs");
checkBoxSNPs.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (isSNPs && bases == null)
{
JOptionPane.showMessageDialog(null,
"No reference sequence supplied to identify SNPs.", "SNPs",
JOptionPane.INFORMATION_MESSAGE);
}
isSNPs = !isSNPs;
if(isSNPs)
baseQualityColour.setSelected(false);
repaint();
}
});
showMenu.add(checkBoxSNPs);
baseQualityColour.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(baseQualityColour.isSelected())
{
checkBoxSNPs.setSelected(false);
isSNPs = false;
}
repaint();
}
});
showMenu.add(baseQualityColour);
markInsertions.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaint();
}
});
showMenu.add(markInsertions);
showMenu.add(new JSeparator());
JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage");
checkBoxCoverage.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
isCoverage = !isCoverage;
coveragePanel.setVisible(isCoverage);
repaint();
}
});
showMenu.add(checkBoxCoverage);
menu.add(showMenu);
final JCheckBoxMenuItem checkBoxSync =
new JCheckBoxMenuItem("Asynchronous", asynchronous);
checkBoxSync.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
asynchronous = checkBoxSync.isSelected();
}
});
JMenu maxHeightMenu = new JMenu("Plot Height");
final String hgts[] =
{"500", "800", "1000", "1500", "2500", "5000", "50000"};
ButtonGroup bgroup = new ButtonGroup();
for(int i=0; i<hgts.length; i++)
{
final String hgt = hgts[i];
final JCheckBoxMenuItem maxHeightMenuItem = new JCheckBoxMenuItem(hgt);
bgroup.add(maxHeightMenuItem);
maxHeightMenuItem.setSelected(hgts[i].equals(Integer.toString(maxHeight)));
maxHeightMenu.add(maxHeightMenuItem);
maxHeightMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(maxHeightMenuItem.isSelected())
maxHeight = Integer.parseInt(hgt);
int start = getBaseAtStartOfView();
setDisplay(start, nbasesInView+start, null);
}
});
}
menu.add(new JSeparator());
logMenuItem.setFont(checkIsizeStackView.getFont());
menu.add(logMenuItem);
logMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
logScale = logMenuItem.isSelected();
repaint();
}
});
JMenuItem filter = new JMenuItem("Filter by Flag...");
menu.add(filter);
filter.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
new SAMRecordFilter(BamView.this);
}
});
}
public void setVisible(boolean visible)
{
super.setVisible(visible);
mainPanel.setVisible(visible);
private void setViewportMidPoint()
{
Point p = jspView.getViewport().getLocation();
p.y = (getHeight() - jspView.getViewport().getViewRect().height)/2;
jspView.getViewport().setViewPosition(p);
}
if(feature_display != null)
return feature_display.getForwardBaseAtLeftEdge();
else
return scrollBar.getValue();
}
/**
* Set the panel size based on the number of bases visible
* and repaint.
* @param nbasesInView
*/
if(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
this.nbasesInView = (int)(mainPanel.getWidth()/pixPerBase);
if(ruler == null)
ruler = new Ruler();
jspView.setColumnHeaderView(ruler);
showBaseAlignment = true;
markInsertions.setEnabled(true);
}
else if(jspView != null)
{
jspView.setColumnHeaderView(null);
if(!isStrandStackView)
jspView.getVerticalScrollBar().setValue(
jspView.getVerticalScrollBar().getMaximum());
markInsertions.setEnabled(false);
{
scrollBar.setValues(startValue, nbasesInView, 1,
getMaxBasesInPanel(getSequenceLength()));
scrollBar.setUnitIncrement(nbasesInView/20);
scrollBar.setBlockIncrement(nbasesInView);
}
* Set the start and end base positions to display.
* @param start
* @param end
* @param event
public void setDisplay(int start,
int end,
DisplayAdjustmentEvent event)
this.startBase = start;
this.endBase = end;
else
{
if(feature_display == null)
pixPerBase = 1000.f/(float)(end-start+1);
else
pixPerBase = feature_display.getWidth()/(float)(end-start+1);
}
if(event == null)
{
this.startBase = -1;
this.endBase = -1;
}
/**
* Return an Artemis entry from a file
* @param entryFileName
* @param entryGroup
* @return
* @throws NoSequenceException
*/
private Entry getEntry(final String entryFileName, final EntryGroup entryGroup)
throws NoSequenceException
{
final Document entry_document = DocumentFactory.makeDocument(entryFileName);
final EntryInformation artemis_entry_information =
Options.getArtemisEntryInformation();
//System.out.println(entryFileName);
final uk.ac.sanger.artemis.io.Entry new_embl_entry =
EntryFileDialog.getEntryFromFile(null, entry_document,
artemis_entry_information,
false);
if(new_embl_entry == null) // the read failed
return null;
Entry entry = null;
try
{
if(entryGroup.getSequenceEntry() != null)
bases = entryGroup.getSequenceEntry().getBases();
else
entry = new Entry(bases,new_embl_entry);
entryGroup.add(entry);
}
catch(OutOfRangeException e)
{
new MessageDialog(null, "read failed: one of the features in " +
entryFileName + " has an out of range " +
"location: " + e.getMessage());
}
return entry;
}
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
private boolean isShowScale()
{
return showScale;
}
public void setShowScale(boolean showScale)
{
this.showScale = showScale;
}
public JScrollPane getJspView()
{
return jspView;
}
public void setBases(Bases bases)
{
this.bases = bases;
}
/**
* Remove JScrollPane border
*/
public void removeBorder()
{
Border empty = new EmptyBorder(0,0,0,0);
jspView.setBorder(empty);
}
/**
* Handle a mouse drag event on the drawing canvas.
**/
if(event.getButton() == MouseEvent.BUTTON3 || bases == null)
tjc
committed
highlightSAMRecord = null;
if(event.getClickCount() > 1)
{
getSelection().clear();
repaint();
return;
}
highlightRange(event,
MouseEvent.BUTTON1_DOWN_MASK & MouseEvent.BUTTON2_DOWN_MASK);
}
/**
*
* @param event
* @param onmask
*/
private void highlightRange(MouseEvent event, int onmask)
{
int start = (int) ( ( (event.getPoint().getX())/pixPerBase) + getBaseAtStartOfView() );
if(start < 1)
start = 1;
if(start > seqLength)
start = seqLength;
if (dragStart < 0 && (event.getModifiersEx() & onmask) == onmask)
dragStart = start;
else if((event.getModifiersEx() & onmask) != onmask)
dragStart = -1;
if(dragStart < 0)
drag_range = new MarkerRange (bases.getForwardStrand(), start, start);
else
drag_range = new MarkerRange (bases.getForwardStrand(), dragStart, start);
getSelection().setMarkerRange(drag_range);
repaint();
}
}
private Selection getSelection()
{
return selection;
}
protected int getBasesInView()
{
return nbasesInView;
}
public void displayAdjustmentValueChanged(final DisplayAdjustmentEvent event)
{
if(!asynchronous)
{
// if not asynchronous
displayAdjustmentWork(event);
return;
}
public Object construct()
{
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if(event.getStart() != ((FeatureDisplay)event.getSource()).getForwardBaseAtLeftEdge())
{
waitingFrame.showWaiting("waiting...", mainPanel);
waitingFrame.setVisible(false);
return null;
}
};
worker.start();
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
/**
* Carry out the display agjustment event action.
* @param event
*/
private void displayAdjustmentWork(final DisplayAdjustmentEvent event)
{
if(event.getType() == DisplayAdjustmentEvent.SCALE_ADJUST_EVENT)
{
laststart = -1;
lastend = -1;
BamView.this.startBase = event.getStart();
BamView.this.endBase = event.getEnd();
int width = feature_display.getMaxVisibleBases();
setZoomLevel(width);
repaint();
}
else
{
setDisplay(event.getStart(),
event.getStart()+feature_display.getMaxVisibleBases(), event);
repaint();
}
}
public void selectionChanged(SelectionChangeEvent event)
{
repaint();
setPreferredSize(new Dimension(mainPanel.getWidth(), 15));
setBackground(Color.white);
setFont(getFont().deriveFont(11.f));
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
drawBaseScale(g2, start, end, 12);
}
private void drawBaseScale(Graphics2D g2, int start, int end, int ypos)
{
int startMark = (((int)(start/10))*10)+1;
if(end > getSequenceLength())
end = getSequenceLength();
g2.drawString(Integer.toString(i), xpos, ypos);
xpos+=(ALIGNMENT_PIX_PER_BASE/2);
g2.drawLine(xpos, ypos+1, xpos, ypos+5);
}
}
/**
* Popup menu listener
*/
class PopupListener extends MouseAdapter
{
JMenuItem gotoMateMenuItem;
tjc
committed
JMenuItem showDetails;
public void mouseClicked(MouseEvent e)
{
if(e.isPopupTrigger() ||
e.getButton() == MouseEvent.BUTTON3)
return;
BamView.this.requestFocus();
if(e.getClickCount() > 1)
getSelection().clear();
else if(e.getButton() == MouseEvent.BUTTON1)
highlightSAMRecord = mouseOverSAMRecord;
else
highlightRange(e, MouseEvent.BUTTON2_DOWN_MASK);
repaint();
}
public void mousePressed(MouseEvent e)
{
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e)
{
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e)
{
if(e.isPopupTrigger())
if(gotoMateMenuItem != null)
popup.remove(gotoMateMenuItem);
tjc
committed
if(showDetails != null)
popup.remove(showDetails);
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
if( mouseOverSAMRecord != null &&
mouseOverSAMRecord.getReadPairedFlag() &&
!mouseOverSAMRecord.getMateUnmappedFlag() )
{
final SAMRecord thisSAMRecord = mouseOverSAMRecord;
gotoMateMenuItem = new JMenuItem("Go to mate of : "+
thisSAMRecord.getReadName());
gotoMateMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String name = thisSAMRecord.getMateReferenceName();
if(name.equals("="))
name = thisSAMRecord.getReferenceName();
int offset = getSequenceOffset(name);
if(feature_display != null)
feature_display.makeBaseVisible(
thisSAMRecord.getMateAlignmentStart()+offset);
else
scrollBar.setValue(
thisSAMRecord.getMateAlignmentStart()+offset-
(nbasesInView/2));
highlightSAMRecord = thisSAMRecord;
}
});
popup.add(gotoMateMenuItem);
tjc
committed
}
if( mouseOverSAMRecord != null)
{
final SAMRecord thisSAMRecord = mouseOverSAMRecord;
showDetails = new JMenuItem("Show details of : "+
thisSAMRecord.getReadName());
showDetails.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
FileViewer viewDetail = new FileViewer(thisSAMRecord.getReadName(), true, false);
appendToDetailView(thisSAMRecord, viewDetail);
}
});
popup.add(showDetails);
}
popup.show(e.getComponent(),
e.getX(), e.getY());
tjc
committed
private void appendToDetailView(SAMRecord thisSAMRecord, FileViewer viewDetail)
{
viewDetail.appendString("Read Name "+thisSAMRecord.getReadName()+"\n", Level.INFO);
viewDetail.appendString("Coordinates "+thisSAMRecord.getAlignmentStart()+".."+
thisSAMRecord.getAlignmentEnd()+"\n", Level.DEBUG);
viewDetail.appendString("Length "+thisSAMRecord.getReadLength()+"\n", Level.DEBUG);
viewDetail.appendString("Reference Name "+thisSAMRecord.getReferenceName()+"\n", Level.DEBUG);
viewDetail.appendString("Inferred Size "+thisSAMRecord.getInferredInsertSize()+"\n", Level.DEBUG);
viewDetail.appendString("Mapping Quality "+thisSAMRecord.getMappingQuality()+"\n", Level.DEBUG);
tjc
committed
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
if(thisSAMRecord.getProperPairFlag() && !thisSAMRecord.getMateUnmappedFlag())
{
viewDetail.appendString("Mate Reference Name "+thisSAMRecord.getMateReferenceName()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Start Coordinate "+thisSAMRecord.getMateAlignmentStart()+"\n", Level.DEBUG);
viewDetail.appendString("Strand (read/mate) "+
(thisSAMRecord.getReadNegativeStrandFlag() ? "-" : "+")+" / "+
(thisSAMRecord.getMateNegativeStrandFlag() ? "-" : "+"), Level.DEBUG);
}
else
{
viewDetail.appendString("Strand (read) "+
(thisSAMRecord.getReadNegativeStrandFlag() ? "-" : "+"), Level.DEBUG);
}
viewDetail.appendString("\n\nCigar String "+thisSAMRecord.getCigarString(), Level.DEBUG);
viewDetail.appendString("\n\nFlags:", Level.INFO);
viewDetail.appendString("\nDuplicate Read "+
(thisSAMRecord.getDuplicateReadFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nFirst of Pair "+
(thisSAMRecord.getFirstOfPairFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nMate Unmapped "+
(thisSAMRecord.getMateUnmappedFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nProper Pair "+
(thisSAMRecord.getProperPairFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nRead Fails Vendor\nQuality Check "+
(thisSAMRecord.getReadFailsVendorQualityCheckFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nRead Unmapped "+
(thisSAMRecord.getReadUnmappedFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nSecond Of Pair "+
(thisSAMRecord.getSecondOfPairFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\n\nRead Bases:\n", Level.INFO);
viewDetail.appendString(new String(thisSAMRecord.getReadBases()), Level.DEBUG);
tjc
committed
}
protected SAMRecordFlagPredicate getSamRecordFlagPredicate()
{
return samRecordFlagPredicate;
}
protected void setSamRecordFlagPredicate(
SAMRecordFlagPredicate samRecordFlagPredicate)
{
laststart = -1;
lastend = -1;
this.samRecordFlagPredicate = samRecordFlagPredicate;
}
tjc
committed
protected SAMRecordMapQPredicate getSamRecordMapQPredicate()
{
return samRecordMapQPredicate;
}
protected void setSamRecordMapQPredicate(
SAMRecordMapQPredicate samRecordMapQPredicate)
{
laststart = -1;
lastend = -1;
this.samRecordMapQPredicate = samRecordMapQPredicate;
}
class PairedRead
{
SAMRecord sam1;
SAMRecord sam2;
}
List<String> bam = new Vector<String>();;
if(args.length == 0)
{
FileSelectionDialog fileSelection = new FileSelectionDialog(null, true);
bam = fileSelection.getBamFiles();
reference = fileSelection.getReferenceFile();
if(reference == null || reference.equals(""))
reference = null;
}
else if(!args[0].startsWith("-"))
{
for(int i=0; i< args.length; i++)
bam.add(args[i]);
}
int nbasesInView = 1000;
{
while(i < args.length-1 && !args[++i].startsWith("-"))
bam.add(args[i]);
--i;
}
else if(args[i].equals("-r"))
reference = args[++i];
else if(args[i].equals("-v"))
nbasesInView = Integer.parseInt(args[++i]);
else if(args[i].equals("-s"))
System.setProperty("samtoolDir", args[++i]);
else if(args[i].startsWith("-h"))
{
System.out.println("-h\t show help");
System.out.println("-a\t BAM/SAM file to display");
System.out.println("-r\t reference file (optional)");
System.out.println("-v\t number of bases to display in the view (optional)");
/*System.out.println("-s\t samtool directory");*/
final BamView view = new BamView(bam, reference, nbasesInView);
JFrame frame = new JFrame("BamView");
// translucent
//frame.getRootPane().putClientProperty("Window.alpha", new Float(0.9f));
frame.addWindowFocusListener(new WindowFocusListener()
{
public void windowGainedFocus(WindowEvent e)
{
view.requestFocus();
}
public void windowLostFocus(WindowEvent e){}
});
view.addJamToPanel((JPanel)frame.getContentPane(), frame, false, null);
view.jspView.getVerticalScrollBar().setValue(
view.jspView.getVerticalScrollBar().getMaximum());