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.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;
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.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 boolean showBaseAlignment = false;
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(String bam,
String reference,
int nbasesInView)
{
super();
setBackground(Color.white);
this.bam = bam;
this.nbasesInView = nbasesInView;
if(reference != null)
{
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()
{
String msg = (mouseOverSAMRecord != null ?
mouseOverSAMRecord.getReadName() + "\n" +
mouseOverSAMRecord.getAlignmentStart() + ".." +
mouseOverSAMRecord.getAlignmentEnd() + "\nisize=" +
mouseOverSAMRecord.getInferredInsertSize() + "\nrname=" +
mouseOverSAMRecord.getReferenceName() : null);
if(msg != null && mouseOverInsertion != null)
msg = msg + "\nInsertion at:" +mouseOverInsertion;
return msg;
String samtoolCmd = "";
if(System.getProperty("samtoolDir") != null)
samtoolCmd = System.getProperty("samtoolDir");
String cmd[] = { samtoolCmd+File.separator+"samtools",
264
265
266
267
268
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
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)
{
(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());
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());
}
}
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();
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
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);
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
// 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()))
{
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);
}
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 = (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);
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
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
/**
* 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();
1087
1088
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
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);
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
/**
* 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())
1150
1151
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
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);
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
/**
* 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());
}
}
}
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
/**
* 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());
private void createMenus(JComponent menu)
{
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());
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
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;
}
repaint();
}
});
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();
}
repaint();
}
});
viewMenu.add(checkBoxStrandStackView);
menu.add(viewMenu);
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
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");
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
final String hgts[] =
{"500", "800", "1000", "1500", "2500", "5000"};
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);
}
});
}
}
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(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
markInsertions.setEnabled(true);
markInsertions.setEnabled(false);
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;
}
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
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;
}
/**
*
* @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();
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
/**
* 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;
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())
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
if(gotoMateMenuItem != null)
popup.remove(gotoMateMenuItem);
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);
}
popup.show(e.getComponent(),
e.getX(), e.getY());
class PairedRead
{
SAMRecord sam1;
SAMRecord sam2;
}
if(args.length == 0)
{
FileSelectionPanel fileSelection = new FileSelectionPanel();
fileSelection.showPanel();
bam = fileSelection.getBamFile();
reference = fileSelection.getReferenceFile();
if(reference == null || reference.equals(""))
reference = null;
}
else
bam = args[0];
int nbasesInView = 1000;
for(int i=0;i<args.length; i++)
{
if(args[i].equals("-a"))
bam = args[++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());