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.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.FocusEvent;
import java.awt.event.FocusListener;
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.JTextArea;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
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;
import uk.ac.sanger.artemis.components.SwingWorker;
import uk.ac.sanger.artemis.components.Utilities;
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 Vector<String> seqNames = new Vector<String>();
private String bam;
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 Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR);
private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
private boolean showBaseAlignment = false;
private JCheckBoxMenuItem checkBoxStackView = new JCheckBoxMenuItem("Stack View");
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 Point lastMousePoint = null;
private SAMRecord mouseOverSAMRecord = null;
private SAMRecord highlightSAMRecord = null;
// 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;
public static org.apache.log4j.Logger logger4j =
org.apache.log4j.Logger.getLogger(BamView.class);
public BamView(String bam,
String reference,
int nbasesInView)
{
super();
setBackground(Color.white);
this.bam = bam;
this.nbasesInView = nbasesInView;
if(reference != null)
{
{
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);
}
//Options.getOptions().getFontUIResource();
//final javax.swing.plaf.FontUIResource font_ui_resource =
// new javax.swing.plaf.FontUIResource(getFont());
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.getHeight();
MultiLineToolTipUI.initialize();
setToolTipText("");
}
public String getToolTipText()
{
return ( mouseOverSAMRecord != null ?
mouseOverSAMRecord.getReadName() + "\n" +
mouseOverSAMRecord.getAlignmentStart() + ".." +
mouseOverSAMRecord.getAlignmentEnd() + "\nisize=" +
mouseOverSAMRecord.getInferredInsertSize() + "\nrname=" +
mouseOverSAMRecord.getReferenceName(): null);
String samtoolCmd = "";
if(System.getProperty("samtoolDir") != null)
samtoolCmd = System.getProperty("samtoolDir");
String cmd[] = { samtoolCmd+File.separator+"samtools",
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
if(samtools.getProcessStderr() != null)
System.out.println(samtools.getProcessStderr());
String header = samtools.getProcessStdout();
StringReader samReader = new StringReader(header);
BufferedReader buff = new BufferedReader(samReader);
String line;
try
{
while((line = buff.readLine()) != null)
{
if(line.indexOf("LN:") > -1)
{
String parts[] = line.split("\t");
String name = "";
int seqLength = 0;
for(int i=0; i<parts.length; i++)
{
if(parts[i].startsWith("LN:"))
seqLength = Integer.parseInt( parts[i].substring(3) );
else if(parts[i].startsWith("SN:"))
name = parts[i].substring(3);
}
seqLengths.put(name, seqLength);
seqNames.add(name);
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
String samtoolCmd = "";
if(System.getProperty("samtoolDir") != null)
samtoolCmd = System.getProperty("samtoolDir");
for(int i=0; i<cmd.length;i++)
System.out.print(cmd[i]+" ");
System.out.println();
else
readsInView.clear();
RunSamTools samtools = new RunSamTools(cmd, null, null, readsInView);
if(samtools.getProcessStderr() != null)
System.out.println(samtools.getProcessStderr());
}*/
private void readHeaderPicard()
{
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();
/**
* Read a SAM or BAM file.
*/
private void readFromBamPicard(int start, int end)
{
// Open the input file. Automatically detects whether input is SAM or BAM
// and delegates to a reader implementation for the appropriate format.
File bamFile = new File(bam);
File indexFile = new File(bam+".bai");
final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
inputSam.setValidationStringency(ValidationStringency.SILENT);
if(readsInView == null)
readsInView = new Vector<SAMRecord>();
else
readsInView.clear();
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);
}
lastLen = len;
}
}
else
{
String refName = (String) combo.getSelectedItem();
iterateOverBam(inputSam, refName, start, end);
}
inputSam.close();
//System.out.println("readFromBamPicard "+start+".."+end);
}
/**
* 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)
CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end);
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
SAMRecord samRecord = it.next();
readsInView.add(samRecord);
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)
{
popFrame.show("Data Read Incomplete",
"Over 97 % of the maximum memory\nlimit ("+
(memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb).\n"+
"Zoom in or consider increasing the\nmemory for this application.",
mainPanel.getLocationOnScreen().y,
20000);
}
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();
synchronized (this)
try
{
float heapFractionUsedBefore = (float) ((float) memory.getHeapMemoryUsage().getUsed() /
(float) memory.getHeapMemoryUsage().getMax());
readFromBamPicard(start, end);
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());
}
setCursor(cdone);
}
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)
{
popFrame.show("Stack View",
"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.",
20000);
}
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);
Rectangle r = jspView.getViewport().getViewRect();
int nreads = readsInView.size();
for(int i=0; i<nreads; i++)
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++)
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;
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
String readSeq = samRecord.getReadString();
int offset = getSequenceOffset(samRecord.getReferenceName());
List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks();
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;
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);
// highlight
if(highlightSAMRecord != null &&
highlightSAMRecord.getReadName().equals(samRecord.getReadName()))
{
int 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);
}
int 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;
}
}
/**
* 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 = (getHeight() - scaleHeight) - samRecord.getReadString().length();
if(ypos > r.getMaxY() || ypos < r.getMinY())
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
int ypos = (getHeight() - 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);
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);
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
/**
* 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();
1033
1034
1035
1036
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
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);
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
/**
* 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())
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
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
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())
{
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;
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);
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
/**
* 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());
}
}
}
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
/**
* 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);
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());
public void focusGained(FocusEvent fe){}
public void focusLost(FocusEvent fe){}
private void createMenus(JComponent view)
JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads");
checkBoxSingle.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
repaint();
isSingle = !isSingle;
}
});
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;
repaint();
}
});
Loading
Loading full blame...