Newer
Older
*
* 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.io.FileOutputStream;
import java.io.IOException;
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 javax.swing.text.JTextComponent;
tjc
committed
import org.apache.log4j.Level;
import net.sf.picard.sam.BuildBamIndex;
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.FeatureVector;
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.EntryEdit;
import uk.ac.sanger.artemis.components.FeatureDisplay;
tjc
committed
import uk.ac.sanger.artemis.components.FileViewer;
import uk.ac.sanger.artemis.components.NonModalDialog;
import uk.ac.sanger.artemis.components.SwingWorker;
import uk.ac.sanger.artemis.components.genebuilder.AutoCompleteComboDocument;
import uk.ac.sanger.artemis.components.variant.FeatureContigPredicate;
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.FTPSeekableStream;
public class BamView extends JPanel
implements DisplayAdjustmentListener, SelectionChangeListener
private Hashtable<String, SAMFileReader> samFileReaderHash = new Hashtable<String, SAMFileReader>();
private Hashtable<String, Integer> seqLengths = new Hashtable<String, Integer>();
private Hashtable<String, Integer> offsetLengths;
tjc
committed
protected List<String> bamList;
private List<Integer> hideBamList = new Vector<Integer>();
private boolean isSingle = false;
private boolean isSNPs = false;
private FeatureDisplay feature_display;
private Selection selection;
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 cbStackView = new JCheckBoxMenuItem("Stack", true);
private JCheckBoxMenuItem cbPairedStackView = new JCheckBoxMenuItem("Paired Stack");
private JCheckBoxMenuItem cbStrandStackView = new JCheckBoxMenuItem("Strand Stack");
private JCheckBoxMenuItem cbIsizeStackView = new JCheckBoxMenuItem("Inferred Size", false);
private JCheckBoxMenuItem cbCoverageView = new JCheckBoxMenuItem("Coverage", false);
private JCheckBoxMenuItem cbLastSelected;
private ButtonGroup buttonGroup = new ButtonGroup();
private JCheckBoxMenuItem colourByCoverageColour = new JCheckBoxMenuItem("Coverage Plot Colours");
private JCheckBoxMenuItem baseQualityColour = new JCheckBoxMenuItem("Base Quality");
private JCheckBoxMenuItem markInsertions = new JCheckBoxMenuItem("Mark Insertions", true);
tjc
committed
private AlphaComposite translucent =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f);
private CoveragePanel coverageView = new CoveragePanel();
private static Color LIGHT_GREY = new Color(200, 200, 200);
private static Color DARK_GREEN = new Color(0, 150, 0);
private static Color DARK_ORANGE = new Color(255,140,0);
private static Color DEEP_PINK = 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 static int MAX_BASES = 26000;
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,
String reference,
int nbasesInView,
final EntryEdit entry_edit,
final FeatureDisplay feature_display,
final Bases bases,
final JPanel containerPanel,
final JFrame frame)
{
this(bamList, reference, nbasesInView, feature_display, bases, containerPanel, frame);
this.entry_edit = entry_edit;
}
public BamView(List<String> bamList,
int nbasesInView,
final FeatureDisplay feature_display,
final Bases bases,
this.bamList = bamList;
this.feature_display = feature_display;
this.bases = bases;
containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS));
containerPanel.add(mainPanel);
// filter out unmapped reads by default
setSamRecordFlagPredicate(
new SAMRecordFlagPredicate(SAMRecordFlagPredicate.READ_UNMAPPED_FLAG));
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);
}
catch (IOException e)
{
e.printStackTrace();
}
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("");
buttonGroup.add(cbStackView);
buttonGroup.add(cbPairedStackView);
buttonGroup.add(cbStrandStackView);
buttonGroup.add(cbIsizeStackView);
buttonGroup.add(cbCoverageView);
jspView = new JScrollPane(this,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
jspView.setViewportBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.DARK_GRAY));
Border empty = new EmptyBorder(0,0,0,0);
jspView.setBorder(empty);
jspView.getVerticalScrollBar().setUnitIncrement(8);
}
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;
/**
* Get the BAM index file from the list
* @param bam
* @return
* @throws IOException
*/
private File getBamIndexFile(String bam) throws IOException
{
File bamIndexFile = null;
if (bam.startsWith("http") || bam.startsWith("ftp"))
{
final URL urlBamIndexFile = new URL(bam + ".bai");
InputStream is = urlBamIndexFile.openStream();
// Create temp file.
bamIndexFile = File.createTempFile(urlBamIndexFile.getFile().replaceAll(
"[\\/\\s]", "_"), ".bai");
bamIndexFile.deleteOnExit();
FileOutputStream out = new FileOutputStream(bamIndexFile);
int c;
while ((c = is.read()) != -1)
out.write(c);
out.flush();
out.close();
is.close();
logger4j.debug("create... " + bamIndexFile.getAbsolutePath());
/**
* Get the SAM file reader.
* @param bam
* @return
* @throws IOException
*/
private SAMFileReader getSAMFileReader(final String bam) throws IOException
{
if(samFileReaderHash.containsKey(bam))
return samFileReaderHash.get(bam);
if(!bamIndexFile.exists())
{
System.out.println("Index file not found so using picard to index the BAM.");
System.out.println("This requires BAM to be sorted by coordinate and for the\n"+
"sort order to be in the header line, HD.");
System.out.println("If this does not work run:\nsamtools index "+bam);
// Use Picard to index the file
// requires reads to be sorted by coordinate
new BuildBamIndex().instanceMain(
new String[]{ "I="+bam, "O="+bamIndexFile.getAbsolutePath() });
}
if(bam.startsWith("ftp"))
{
FTPSeekableStream fss = new FTPSeekableStream(new URL(bam));
samFileReader = new SAMFileReader(fss, bamIndexFile, false);
}
else if(!bam.startsWith("http"))
samFileReader = new SAMFileReader(bamFile, bamIndexFile);
}
else
{
final URL urlBamFile = new URL(bam);
samFileReader = new SAMFileReader(urlBamFile, bamIndexFile, false);
samFileReader.setValidationStringency(ValidationStringency.SILENT);
samFileReaderHash.put(bam, samFileReader);
return samFileReader;
String bam = bamList.get(0);
final SAMFileReader inputSam = getSAMFileReader(bam);
//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());
}
private void readFromBamPicard(int start, int end, int bamIndex, float pixPerBase)
{
// 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);
final SAMFileReader inputSam = getSAMFileReader(bam);
//final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
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, pixPerBase, bam);
}
lastLen = len;
}
}
else
{
String refName = (String) combo.getSelectedItem();
iterateOverBam(inputSam, refName, start, end, bamIndex, pixPerBase, bam);
//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, float pixPerBase,
String bam)
tjc
committed
boolean multipleBAM = false;
if(bamList.size() > 1)
multipleBAM = true;
CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end);
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
int seqOffset = getSequenceOffset(refName);
int offset = seqOffset- getBaseAtStartOfView();
if( samRecordFlagPredicate == null ||
!samRecordFlagPredicate.testPredicate(samRecord))
{
if(samRecordMapQPredicate == null ||
samRecordMapQPredicate.testPredicate(samRecord))
tjc
committed
if(multipleBAM)
samRecord.setAttribute("FL", bamIndex);
if(isCoverageView(pixPerBase))
coverageView.addRecord(samRecord, offset, bam);
if(isCoverage)
coveragePanel.addRecord(samRecord, offset, bam);
snpPanel.addRecord(samRecord, seqOffset);
if(!isCoverageView(pixPerBase))
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(readsInView.size() > checkMemAfter*2 && !waitingFrame.isVisible())
waitingFrame.showWaiting("loading...", mainPanel);
if(heapFraction > 0.90)
"Using > 90 % of the maximum memory limit:"+
(memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.\n"+
"Not all reads in this range have been read in. Zoom in or\n"+
"consider increasing the memory 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));
}*/
FeatureVector features = feature_display.getEntryGroup().getAllFeatures();
offsetLengths = new Hashtable<String, Integer>(seqNames.size());
for(int i=0; i<seqNames.size(); i++)
{
FeatureContigPredicate predicate = new FeatureContigPredicate(seqNames.get(i).trim());
for(int j=0; j<features.size(); j++)
{
if(predicate.testPredicate(features.elementAt(j)))
{
offsetLengths.put(seqNames.get(i), features.elementAt(j).getFirstBase()-1);
break;
}
}
if(offsetLengths.size() != seqNames.size())
System.err.println("Found: "+offsetLengths.size() +" of "+ seqNames.size());
JOptionPane.showMessageDialog(this,
"There is a problem matching the reference sequences\n"+
"to the names in the BAM file. This may mean the labels\n"+
"on the reference features do not match those in the in\n"+
"the BAM file.",
"Problem Found", JOptionPane.WARNING_MESSAGE);
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();
coverageView.init(this, pixPerBase, start, end);
if(isCoverage)
coveragePanel.init(this, pixPerBase, start, end);
if(isSNPplot)
snpPanel.init(this, pixPerBase, start, end);
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, pixPerBase);
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)
changeToStackView = true;
}
if((!isStackView() && !isStrandStackView()) || isBaseAlignmentView(pixPerBase))
{
Collections.sort(readsInView, new SAMRecordComparator());
}
else if( (isStackView() || isStrandStackView()) &&
bamList.size() > 1)
{
// merge multiple BAM files
Collections.sort(readsInView, new SAMRecordPositionComparator(BamView.this));
}
catch (OutOfMemoryError ome)
JOptionPane.showMessageDialog(this, "Out of Memory");
readsInView.clear();
return;
catch(IOException me)
{
me.printStackTrace();
}
catch(net.sf.samtools.util.RuntimeIOException re)
{
JOptionPane.showMessageDialog(this, re.getMessage());
}
drawCoverage(g2,start, end, pixPerBase);
drawStackView(g2, seqLength, pixPerBase, start, end);
drawPairedStackView(g2, seqLength, pixPerBase, start, end);
else
drawLineView(g2, seqLength, pixPerBase, start, end);
if(isCoverage)
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();
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);
// 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>();
tjc
committed
(refPos+refSeqStart+1)+" "+
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);
}
}
1030
1031
1032
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
// highlight
if(highlightSAMRecord != null &&
highlightSAMRecord.getReadName().equals(samRecord.getReadName()))
{
refPos = block.getReferenceStart() + offset - refSeqStart;
int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
int width = block.getLength()*ALIGNMENT_PIX_PER_BASE;
Color col1 = g2.getColor();
g2.setColor(Color.red);
g2.drawRect(xstart, ypos-BASE_HEIGHT, width, BASE_HEIGHT);
if(i < blocks.size()-1)
{
int nextStart =
(blocks.get(i+1).getReferenceStart() + offset - refSeqStart)*ALIGNMENT_PIX_PER_BASE;
g2.drawLine(xstart+width, ypos-(BASE_HEIGHT/2), nextStart, ypos-(BASE_HEIGHT/2));
}
g2.setColor(col1);
}
else if(i < blocks.size()-1)
{
refPos = block.getReferenceStart() + offset - refSeqStart;
int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
int width = block.getLength()*ALIGNMENT_PIX_PER_BASE;
int nextStart =
(blocks.get(i+1).getReferenceStart() + offset - refSeqStart)*ALIGNMENT_PIX_PER_BASE;
g2.drawLine(xstart+width, ypos-(BASE_HEIGHT/2), nextStart, ypos-(BASE_HEIGHT/2));
}
if(lastMousePoint != null && blocks.size() > 0)
refPos = blocks.get(0).getReferenceStart()+offset-refSeqStart;
refPos = blocks.get(blocks.size()-1).getReferenceStart()+
blocks.get(blocks.size()-1).getLength()+offset-refSeqStart;
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)
else if (baseQuality < 30)
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);
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)
g2.setColor(Color.LIGHT_GRAY);
int offset1 = getSequenceOffset(samRecord.getReferenceName());
int end1 = samRecord.getAlignmentEnd()+offset1-baseAtStartOfView;
int offset2 = getSequenceOffset(samNextRecord.getReferenceName());
int start2 = samNextRecord.getAlignmentStart()+offset2-baseAtStartOfView;
drawTranslucentLine(g2,
(int)(end1*pixPerBase), (int)(start2*pixPerBase), ypos);
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(samRecord));
else if( (samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
samNextRecord.getReadNegativeStrandFlag()) ||
(!samRecord.getReadNegativeStrandFlag() &&
!samNextRecord.getReadNegativeStrandFlag()))
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
drawRead(g2, samNextRecord, pixPerBase, ypos, baseAtStartOfView);
drawLoneRead(g2, samRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight);
drawLoneRead(g2, samRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight);
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);
int ydiff = 2;
if(isOrientation)
ydiff= 4;
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;
if(colourByCoverageColour.isSelected() ||
lstStart != recordStart || lstEnd != recordEnd)
tjc
committed
{
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(samRecord));
else if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped
tjc
committed
g2.setColor(Color.black);
else
g2.setColor(Color.blue);
if(maxEnd < recordStart)
{
ypos = (getHeight() - scaleHeight)-ydiff;
tjc
committed
maxEnd = recordEnd+2;
}
else
tjc
committed
lstStart = recordStart;
lstEnd = recordEnd;
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
/**
* 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);
int ydiff = 2;
if(isOrientation)
ydiff= 4;
drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -ydiff, pixPerBase, stroke);
drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), ydiff, 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();
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(colourByCoverageColour.isSelected() ||
lstStart != recordStart || lstEnd != recordEnd)
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(samRecord));
else 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
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
/**
* 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())
1422
1423
1424
1425
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
1460
1461
1462
1463
1464
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 ydiff = 3;
if(isOrientation)
ydiff= 5;
int ypos = getHeight() - scaleHeight - ydiff;
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 - ydiff;
if(pr.sam2 != null)
{
lastEnd = pr.sam2.getAlignmentEnd();
}
else
lastEnd = pr.sam1.getAlignmentEnd();
}
else
if(ypos > r.getMaxY() || ypos < r.getMinY())
g2.setStroke(originalStroke);
g2.setColor(Color.gray);
if(pr.sam2 != null)
{
int offset1 = getSequenceOffset(pr.sam1.getReferenceName());
int offset2 = getSequenceOffset(pr.sam2.getReferenceName());
drawTranslucentJointedLine(g2,
(int)((pr.sam1.getAlignmentEnd()+offset1-getBaseAtStartOfView())*pixPerBase),
(int)((pr.sam2.getAlignmentStart()+offset2-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();
int offset = getSequenceOffset(pr.sam1.getReferenceName());
drawTranslucentJointedLine(g2,
(int)( (prStart+offset-getBaseAtStartOfView())*pixPerBase),
(int)( (pr.sam1.getMateAlignmentStart()+offset-getBaseAtStartOfView())*pixPerBase), ypos);
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(pr.sam1));
else 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);
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
/**
* Draw the read coverage.
* @param g2
* @param start
* @param end
* @param pixPerBase
*/
private void drawCoverage(Graphics2D g2, int start, int end, float pixPerBase)
{
int scaleHeight = 0;
if(isShowScale())
{
drawScale(g2, start, end, pixPerBase, getHeight());
scaleHeight = 15;
}
int hgt = jspView.getVisibleRect().height-scaleHeight;
g2.translate(0, getHeight()-hgt-scaleHeight);
coverageView.draw(g2, getWidth(), hgt);
coverageView.drawMax(g2);
}
/**
* 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, int scaleHeight)
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();
}
{
offTheTop = true;
ypos = getHeight() - scaleHeight - 5;
}
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(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(samRecord));
else if(offTheTop)
tjc
committed
else if(samRecord.getReadNegativeStrandFlag() &&
samRecord.getMateNegativeStrandFlag()) // 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);
if(thisRead.getCigar().getCigarElements().size() == 1)
g2.drawLine((int)( thisStart * pixPerBase), ypos,
(int)( thisEnd * pixPerBase), ypos);
else
{
List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
Color c = g2.getColor();
int lastEnd = 0;
for(int i=0; i<blocks.size(); i++)
{
AlignmentBlock block = blocks.get(i);
int blockStart = block.getReferenceStart()+offset-baseAtStartOfView;
int blockEnd = blockStart + block.getLength() - 1;
g2.drawLine((int)( blockStart * pixPerBase), ypos,
(int)( blockEnd * pixPerBase), ypos);
if(i > 0 && blockStart != lastEnd)
{
g2.setColor(Color.gray);
g2.drawLine((int)( blockStart * pixPerBase), ypos,
(int)( lastEnd * pixPerBase), ypos);
g2.setColor(c);
}
lastEnd = blockEnd;
}
}
if(isOrientation)
drawArrow(g2, thisRead, thisStart, 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);
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
/**
* Draw arrow on the read to indicate orientation.
* @param g2
* @param thisRead
* @param thisStart
* @param thisEnd
* @param pixPerBase
* @param ypos
*/
private void drawArrow(Graphics2D g2,
SAMRecord thisRead,
int thisStart,
int thisEnd,
float pixPerBase,
int ypos)
{
if(thisRead.getReadNegativeStrandFlag())
{
int apos = ypos + 2;
g2.drawLine((int)( (thisStart+5) * pixPerBase), apos,
(int)( thisStart * pixPerBase), ypos);
}
else
{
int apos = ypos - 2;
g2.drawLine((int)( (thisEnd-5) * pixPerBase), apos,
(int)( thisEnd * pixPerBase), ypos);
}
}
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
/**
* 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());
}
}
}
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
1889
1890
1891
1892
1893
1894
1895
1896
1897
/**
* 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);
System.err.println(thisRead.getReadName()+" "+e.getMessage());
/**
* 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
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);
//
snpPanel = new SnpPanel(this, bases);
bottomPanel.add(snpPanel, BorderLayout.NORTH);
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();
if(isSNPplot)
snpPanel.repaint();
if(isCoverage)
coveragePanel.repaint();
}
});
bottomPanel.add(scrollBar, BorderLayout.SOUTH);
}
else
{
if(!concatSequences)
{
int seqLen = seqLengths.get((String) combo.getSelectedItem());
int artemisSeqLen = feature_display.getSequenceLength();
if(seqLen != artemisSeqLen)
{
int newIndex = -1;
for(int i=0; i<seqNames.size(); i++)
{
if(seqLengths.get(seqNames.get(i)) == artemisSeqLen)
{
// this looks like the correct sequence
combo.setSelectedIndex(i);
newIndex = i;
}
}
if(!Options.isBlackBeltMode())
{
String label[] = {
"The length of the sequence loaded does not match the length of",
"the default reference sequence in the BAM ("+seqNames.get(0)+").",
(newIndex == -1 ? "" : "The length does match the reference "+
};
new NonModalDialog(frame, label);
}
}
}
}
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
coveragePanel.setPreferredSize(new Dimension(900, 100));
coveragePanel.setVisible(false);
snpPanel.setPreferredSize(new Dimension(900, 100));
snpPanel.setVisible(false);
jspView.getVerticalScrollBar().setValue(
jspView.getVerticalScrollBar().getMaximum());
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;
repaint();
}
});
}
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, "BamView", "BAM");
List<String> bamFiles = bamFileSelection.getFiles(".*\\.bam$");
int count = bamList.size();
bamList.addAll(bamFileSelection.getFiles(".*\\.bam$"));
for(int i=0; i<bamFiles.size(); i++)
addToViewMenu(i+count);
repaint();
}
});
bamFilesMenu.setFont(addBam.getFont());
menu.add(bamFilesMenu);
final JMenu analyse = new JMenu("Analyse");
menu.add(analyse);
final JMenuItem readCount = new JMenuItem("Read count of selected features ...");
if(feature_display == null)
readCount.setEnabled(false);
readCount.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
FeatureVector features = feature_display.getSelection().getAllFeatures();
JCheckBox overlap = new JCheckBox("Include all overlapping reads", true);
overlap.setToolTipText("Include reads that partially overlap the feature");
JCheckBox spliced = new JCheckBox("Introns included", true);
Box yBox = Box.createVerticalBox();
yBox.add(overlap);
yBox.add(spliced);
JOptionPane.showMessageDialog(null, yBox, "Read Count Option", JOptionPane.INFORMATION_MESSAGE);
new MappedReads(features, (String)combo.getSelectedItem(), samFileReaderHash, bamList,
seqNames, offsetLengths, concatSequences, seqLengths,
samRecordFlagPredicate, samRecordMapQPredicate,
!overlap.isSelected(), spliced.isSelected());
}
});
final JMenuItem rpkm = new JMenuItem("RPKM value of selected features ...");
analyse.add(rpkm);
if(feature_display == null)
rpkm.setEnabled(false);
rpkm.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
FeatureVector features = feature_display.getSelection().getAllFeatures();
JCheckBox overlap = new JCheckBox("Include all overlapping reads", true);
overlap.setToolTipText("Include reads that partially overlap the feature");
JCheckBox spliced = new JCheckBox("Introns included", true);
JCheckBox allRefSeqs = new JCheckBox("Use reads mapped to all reference sequences", false);
Box yBox = Box.createVerticalBox();
yBox.add(overlap);
yBox.add(spliced);
if(seqLengths.size() > 1)
yBox.add(allRefSeqs);
JOptionPane.showMessageDialog(null, yBox, "Read Count Option", JOptionPane.INFORMATION_MESSAGE);
int seqlen = 0;
if(feature_display != null)
seqlen = feature_display.getSequenceLength();
else if(bases != null)
seqlen = bases.getLength();
new MappedReads(features, (String)combo.getSelectedItem(),
samFileReaderHash, bamList, seqNames, offsetLengths, concatSequences,
seqLengths, seqlen, samRecordFlagPredicate, samRecordMapQPredicate,
!overlap.isSelected(), spliced.isSelected(), allRefSeqs.isSelected());
for(int i=0; i<bamList.size(); i++)
addToViewMenu(i);
menu.add(new JSeparator());
cbStackView.setFont(viewMenu.getFont());
cbIsizeStackView.setFont(viewMenu.getFont());
cbPairedStackView.setFont(viewMenu.getFont());
cbStrandStackView.setFont(viewMenu.getFont());
cbCoverageView.setFont(viewMenu.getFont());
baseQualityColour.setFont(viewMenu.getFont());
colourByCoverageColour.setFont(viewMenu.getFont());
markInsertions.setFont(viewMenu.getFont());
cbIsizeStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
logMenuItem.setEnabled(isIsizeStackView());
viewMenu.add(cbIsizeStackView);
cbStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
logMenuItem.setEnabled(isIsizeStackView());
cbPairedStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
logMenuItem.setEnabled(isIsizeStackView());
repaint();
}
});
viewMenu.add(cbPairedStackView);
Loading
Loading full blame...