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.GridBagConstraints;
import java.awt.GridBagLayout;
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.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
tjc
committed
import org.apache.log4j.Level;
import net.sf.picard.reference.ReferenceSequenceFile;
import net.sf.picard.sam.BuildBamIndex;
import net.sf.samtools.AlignmentBlock;
import net.sf.samtools.SAMException;
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.circular.TextFieldInt;
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.IndexReferenceEvent;
import uk.ac.sanger.artemis.components.NonModalDialog;
import uk.ac.sanger.artemis.components.SequenceComboBox;
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.FTPSeekableStream;
public class BamView extends JPanel
implements DisplayAdjustmentListener, SelectionChangeListener
private List<BamViewRecord> readsInView;
private Hashtable<String, SAMFileReader> samFileReaderHash = new Hashtable<String, SAMFileReader>();
private List<SAMReadGroupRecord> readGroups = new Vector<SAMReadGroupRecord>();
private HashMap<String, Integer> seqLengths = new HashMap<String, Integer>();
private HashMap<String, Integer> offsetLengths;
tjc
committed
protected List<String> bamList;
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 cbCoverageStrandView = new JCheckBoxMenuItem("Coverage by Strand", false);
private JCheckBoxMenuItem cbCoverageHeatMap = new JCheckBoxMenuItem("Coverage Heat Map", false);
private JCheckBoxMenuItem cbLastSelected;
private ButtonGroup buttonGroup = new ButtonGroup();
private JCheckBoxMenuItem colourByReadGrp = new JCheckBoxMenuItem("Read Group");
private JCheckBoxMenuItem colourByStrandTag = new JCheckBoxMenuItem("RNASeq Strand Specific Tag (XS)");
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 GroupBamFrame groupsFrame = new GroupBamFrame(this, bamFilesMenu);
private CoveragePanel coverageView = new CoveragePanel();
protected static String BAM_SUFFIX = ".*\\.(bam|cram)$";
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 BamViewRecord mouseOverSAMRecord = null;
private BamViewRecord highlightSAMRecord = null;
private String mouseOverInsertion;
// record of where a mouse drag starts
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...");
private ExecutorService bamReadTaskExecutor;
private int MAX_COVERAGE = Integer.MAX_VALUE;
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));
System.setProperty("reference", reference); // for CRAM
try
{
getEntry(reference,entryGroup);
}
catch (NoSequenceException e)
{
e.printStackTrace();
}
}
if(Options.getOptions().getIntegerProperty("bam_read_thread") != null)
{
logger4j.debug("BAM READ THREADS="+Options.getOptions().getIntegerProperty("bam_read_thread"));
bamReadTaskExecutor = Executors.newFixedThreadPool(
Options.getOptions().getIntegerProperty("bam_read_thread"));
}
else
bamReadTaskExecutor = Executors.newFixedThreadPool(1);
if(Options.getOptions().getIntegerProperty("bam_max_coverage") != null)
{
logger4j.debug("BAM MAX COVERAGE="+Options.getOptions().getIntegerProperty("bam_max_coverage"));
MAX_COVERAGE = Options.getOptions().getIntegerProperty("bam_max_coverage");
}
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);
buttonGroup.add(cbCoverageStrandView);
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);
// apply command line options
if(System.getProperty("show_snps") != null)
isSNPs = true;
if(System.getProperty("show_snp_plot") != null)
{
isSNPplot = true;
snpPanel.setVisible(isSNPplot);
}
if(System.getProperty("show_cov_plot") != null)
{
isCoverage = true;
coveragePanel.setVisible(isCoverage);
}
}
public String getToolTipText()
{
if(isCoverageView(getPixPerBaseByWidth()) && lastMousePoint != null)
return coverageView.getToolTipText(
lastMousePoint.y-getJspView().getViewport().getViewPosition().y);
tjc
committed
if(mouseOverSAMRecord == null)
return null;
String msg =
mouseOverSAMRecord.sam.getReadName() + "\n" +
mouseOverSAMRecord.sam.getAlignmentStart() + ".." +
mouseOverSAMRecord.sam.getAlignmentEnd() +
(mouseOverSAMRecord.sam.getReadGroup() != null ? "\nRG="+mouseOverSAMRecord.sam.getReadGroup().getId() : "") +
"\nisize=" + mouseOverSAMRecord.sam.getInferredInsertSize() +
"\nmapq=" + mouseOverSAMRecord.sam.getMappingQuality()+
"\nrname="+ mouseOverSAMRecord.sam.getReferenceName();
tjc
committed
if( mouseOverSAMRecord.sam.getReadPairedFlag() &&
mouseOverSAMRecord.sam.getProperPairFlag() &&
!mouseOverSAMRecord.sam.getMateUnmappedFlag())
tjc
committed
{
msg = msg +
(mouseOverSAMRecord.sam.getReadNegativeStrandFlag() ? "-" : "+")+" / "+
(mouseOverSAMRecord.sam.getMateNegativeStrandFlag() ? "-" : "+");
tjc
committed
}
else
msg = msg +
(mouseOverSAMRecord.sam.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());
{
final File cramIndexFile = new File(bam + ".crai");
if(cramIndexFile.exists())
{
logger4j.debug(
"ERROR: CRAM INDEX FILE ("+cramIndexFile.getName()+
") EXPECTING A BAM INDEX FILE (USE THIS OPTION --bam-style-index) ");
/**
* Get the SAM file reader.
* @param bam
* @return
* @throws IOException
*/
private SAMFileReader getSAMFileReader(final String bam) throws IOException
{
// parsing of the header happens during SAMFileReader construction,
// so need to set the default stringency
SAMFileReader.setDefaultValidationStringency(ValidationStringency.LENIENT);
if(samFileReaderHash.containsKey(bam))
return samFileReaderHash.get(bam);
if(!bamIndexFile.exists())
{
try
{
logger4j.warn("Index file not found so using picard to index the BAM.");
// Use Picard to index the file
// requires reads to be sorted by coordinate
new BuildBamIndex().instanceMain(
new String[]{ "I="+bam, "O="+bamIndexFile.getAbsolutePath(), "MAX_RECORDS_IN_RAM=50000", "VALIDATION_STRINGENCY=SILENT" });
}
catch(SAMException e)
{
String ls = System.getProperty("line.separator");
String msg =
"BAM index file is missing. The BAM file needs to be sorted and indexed"+ls+
"This can be done using samtools (http://samtools.sf.net/):"+ls+ls+
"samtools sort <in.bam> <out.prefix>"+ls+
"samtools index <sorted.bam>";
throw new SAMException(msg);
}
if(feature_display != null && bam.endsWith("cram"))
{
// set log level
net.sf.picard.util.Log.setGlobalLogLevel(
net.sf.picard.util.Log.LogLevel.ERROR);
final CRAMReferenceSequenceFile ref = new CRAMReferenceSequenceFile(
feature_display.getEntryGroup().getSequenceEntry(), this);
final Map<Object, ReferenceSequenceFile> referenceFactory =
new HashMap<Object, ReferenceSequenceFile>();
referenceFactory.put(bamIndexFile, ref);
try
{
Class<?> cls = getClass().getClassLoader().loadClass("net.sf.samtools.ReferenceDiscovery");
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
Field f = cls.getDeclaredField("referenceFactory");
f.set(null, referenceFactory);
}
catch (ClassNotFoundException e)
{
System.err.println("Check cramtools.jar is in the CLASSPATH. "+e.getMessage());
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (NoSuchFieldException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
//net.sf.samtools.ReferenceDiscovery.referenceFactory.put(bamIndexFile, ref);
}
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);
readGroups.addAll(samFileReader.getFileHeader().getReadGroups());
final SAMFileReader inputSam = getSAMFileReader(bamList.get(0));
final SAMFileHeader header = inputSam.getFileHeader();
for(SAMSequenceRecord seq: header.getSequenceDictionary().getSequences())
seqLengths.put(seq.getSequenceName(),
seq.getSequenceLength());
seqNames.add(seq.getSequenceName());
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
class BamReadTask implements Runnable
{
private int start;
private int end;
private short bamIndex;
private float pixPerBase;
private CountDownLatch latch;
BamReadTask(int start, int end, short bamIndex, float pixPerBase, CountDownLatch latch)
{
this.start = start;
this.end = end;
this.bamIndex = bamIndex;
this.pixPerBase = pixPerBase;
this.latch = latch;
}
public void run()
{
try
{
readFromBamPicard(start, end, bamIndex, pixPerBase) ;
}
catch (OutOfMemoryError ome)
{
throw ome;
}
catch(IOException me)
{
me.printStackTrace();
}
finally
{
latch.countDown();
}
}
}
private void readFromBamPicard(int start, int end, short 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.
final SAMFileReader inputSam = getSAMFileReader(bam);
//final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
int sBeg = offset+1;
int sEnd = sBeg+sLen-1;
if( (sBeg >= start && sBeg <= end) ||
(sBeg <= start && sEnd >= start) )
{
int thisStart = start - offset;
if(thisStart < 1)
thisStart = 1;
int thisEnd = end - offset;
if(thisEnd > sLen)
thisEnd = sLen;
iterateOverBam(inputSam, seq, thisStart, thisEnd, bamIndex, pixPerBase, bam);
//System.out.println("READ "+seq+" "+thisStart+".."+thisEnd+" "+start+" --- "+offset);
}
}
}
else
{
String refName = (String) combo.getSelectedItem();
iterateOverBam(inputSam, refName, start, end, bamIndex, pixPerBase, bam);
}
/**
* 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,
final String refName, final int start, final int end,
final short bamIndex, final float pixPerBase,
final String bam)
{
final MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
final int seqOffset = getSequenceOffset(refName);
final int offset = seqOffset- getBaseAtStartOfView();
final boolean isCoverageView = isCoverageView(pixPerBase);
int nbins = 800;
int binSize = ((end-start)/nbins)+1;
if(binSize < 1)
{
binSize = 1;
nbins = end-start+1;
}
int max = MAX_COVERAGE*binSize;
if(max < 1)
max = Integer.MAX_VALUE;
final int cov[] = new int[nbins];
for(int i=0; i<nbins; i++)
cov[i] = 0;
final CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end);
try
{
cnt++;
SAMRecord samRecord = it.next();
if(readGrpFrame != null && !readGrpFrame.isReadGroupVisible(samRecord.getReadGroup()))
continue;
if( samRecordFlagPredicate == null ||
!samRecordFlagPredicate.testPredicate(samRecord))
if(samRecordMapQPredicate == null ||
samRecordMapQPredicate.testPredicate(samRecord))
{
int abeg = samRecord.getAlignmentStart();
int aend = samRecord.getAlignmentEnd();
boolean over = false;
for(int i=abeg; i<aend; i++)
{
int bin = ((i-start)/binSize)-1;
if(bin < 0)
else if(bin > nbins-1)
bin = nbins-1;
cov[bin]++;
if(cov[bin] > max)
{
over = true;
break;
}
}
if(over)
continue;
if(isCoverageView)
coverageView.addRecord(samRecord, offset, bam, colourByStrandTag.isSelected());
coveragePanel.addRecord(samRecord, offset, bam, colourByStrandTag.isSelected());
if(isSNPplot)
snpPanel.addRecord(samRecord, seqOffset);
if(!isCoverageView)
readsInView.add(new BamViewRecord(samRecord, bamIndex));
}
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)
{
popFrame.show(
"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.",
mainPanel,
15000);
break;
}
catch(Exception e)
{
System.err.println(e.getMessage());
}
finally
{
it.close();
}
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(offsetLengths == null)
offsetLengths = new HashMap<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);
}
final FeatureVector features = feature_display.getEntryGroup().getAllFeatures();
final HashMap<String, Integer> lookup = new HashMap<String, Integer>();
for(int i=0; i<features.size(); i++)
lookup.put(features.elementAt(i).getIDString(), features.elementAt(i).getFirstBase());
offsetLengths = new HashMap<String, Integer>(seqNames.size());
for(int i=0; i<seqNames.size(); i++)
{
final Integer pos = lookup.get(seqNames.get(i));
if(pos != null)
offsetLengths.put(seqNames.get(i), pos-1);
/*final 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());
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JOptionPane.showMessageDialog(BamView.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);
}
});
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 0;
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;
if(feature_display != null && nbasesInView < feature_display.getMaxVisibleBases())
nbasesInView = feature_display.getMaxVisibleBases();
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<BamViewRecord>();
else
readsInView.clear();
final CountDownLatch latch = new CountDownLatch(bamList.size()-hideBamList.size());
//long ms = System.currentTimeMillis();
for(short i=0; i<bamList.size(); i++)
{
if(!hideBamList.contains(i))
bamReadTaskExecutor.execute(
new BamReadTask(start, end, i, pixPerBase, latch));
try
{
latch.await();
}
catch (InterruptedException e) {} // TODO
//System.out.println("===== NO. THREADS="+
// ((java.util.concurrent.ThreadPoolExecutor)bamReadTaskExecutor).getPoolSize()+" TIME="+(System.currentTimeMillis()-ms));
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(net.sf.samtools.util.RuntimeIOException re)
{
JOptionPane.showMessageDialog(this, re.getMessage());
}
// this needs to be synchronized when cloning BAM window
synchronized(this)
{
if(showBaseAlignment)
drawBaseAlignment(g2, seqLength, pixPerBase, start, end);
{
if(isCoverageView(pixPerBase))
drawCoverage(g2,start, end, pixPerBase);
else if(isStackView())
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.repaint();
if(isSNPplot)
snpPanel.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.",
protected void repaintBamView()
{
laststart = -1;
repaint();
}
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
int ypos = 0;
end = start + ( mainPanel.getWidth() * ALIGNMENT_PIX_PER_BASE );
if(seqEnd > bases.getLength())
seqEnd = bases.getLength();
if(refSeqStart < 1)
refSeqStart = 1;
refSeq =
bases.getSubSequence(new Range(refSeqStart, seqEnd), Bases.FORWARD).toUpperCase();
ruler.refSeq = refSeq;
}
catch (OutOfRangeException e)
{
e.printStackTrace();
}
}
ruler.repaint();
drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end, Color.PINK);
g2.setStroke(new BasicStroke (2.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
Rectangle r = jspView.getViewport().getViewRect();
int nreads = readsInView.size();
BamViewRecord thisRead = readsInView.get(i);
if (ypos < r.getMaxY() || ypos > r.getMinY())
drawSequence(g2, thisRead, ypos, refSeq, refSeqStart);
drawn[i] = true;
int thisEnd = thisRead.sam.getAlignmentEnd();
thisEnd = thisRead.sam.getAlignmentStart() + thisRead.sam.getReadLength();
BamViewRecord nextRead = readsInView.get(j);
int nextStart = nextRead.sam.getAlignmentStart();
if (nextStart > thisEnd + 1)
{
if (ypos < r.getMaxY() || ypos > r.getMinY())
drawSequence(g2, nextRead, ypos, refSeq, refSeqStart);
drawn[j] = true;
thisEnd = nextRead.sam.getAlignmentEnd();
thisEnd = nextStart + nextRead.sam.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(final Graphics2D g2, final BamViewRecord bamViewRecord,
int ypos, String refSeq, int refSeqStart)
SAMRecord samRecord = bamViewRecord.sam;
if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped
final Color col = g2.getColor();
int len = 0;
int refPos = 0;
final String readSeq = samRecord.getReadString();
final 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(Character.toUpperCase(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);
if(isSNPs)
g2.setColor(col);
// 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);
}
}
// highlight
if(highlightSAMRecord != null &&
highlightSAMRecord.sam.getReadName().equals(samRecord.getReadName()))
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
{
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 = bamViewRecord;
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 inferred size 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, Color.PINK);
new BasicStroke (readLnHgt, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
BamViewRecord bamViewRecord = readsInView.get(i);
SAMRecord samRecord = bamViewRecord.sam;
BamViewRecord bamViewNextRecord = 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, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff);
int ypos = getYPos(scaleHeight, Math.abs(samRecord.getInferredInsertSize()));
if( (ypos > r.getMaxY() || ypos < r.getMinY()) && ypos > 0 )
bamViewNextRecord = readsInView.get(++i);
samNextRecord = bamViewNextRecord.sam;
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);
g2.setColor(getColourByCoverageColour(bamViewRecord));
else if( (samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
samNextRecord.getReadNegativeStrandFlag()) ||
(!samRecord.getReadNegativeStrandFlag() &&
!samNextRecord.getReadNegativeStrandFlag()))
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff);
drawRead(g2, bamViewNextRecord, pixPerBase, ypos, baseAtStartOfView, getSNPs(samNextRecord), ydiff);
drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps, ydiff);
drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps, ydiff);
private int getYPos(int scaleHeight, int size)
{
if(!logScale)
else
{
int logInfSize = (int)( Math.log(size) * 100);
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,
final int seqLength,
final float pixPerBase,
final int start,
final int end)
drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int ypos = (getHeight() - scaleHeight);
int ydiff = (int) Math.round(1.5*readLnHgt);
tjc
committed
int maxEnd = 0;
int lstStart = 0;
int lstEnd = 0;
SAMRecord samRecord = bamViewRecord.sam;
int offset = getSequenceOffset(samRecord.getReferenceName());
tjc
committed
int recordStart = samRecord.getAlignmentStart()+offset;
int recordEnd = samRecord.getAlignmentEnd()+offset;
if(colourByCoverageColour.isSelected() ||
colourByStrandTag.isSelected() ||
colourByReadGrp.isSelected() ||
lstStart != recordStart || lstEnd != recordEnd || snps != null)
{
if(colourByStrandTag.isSelected())
{
if(samRecord.getAttribute("XS") == null)
g2.setColor(Color.BLACK);
else if( ((Character)samRecord.getAttribute("XS")).equals('+') )
g2.setColor(Color.BLUE);
else if( ((Character)samRecord.getAttribute("XS")).equals('-') )
g2.setColor(Color.RED);
else
g2.setColor(Color.BLACK);
}
else if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(bamViewRecord));
else if(colourByReadGrp.isSelected())
g2.setColor(getReadGroupFrame().getReadGroupColour(readGroups, samRecord.getReadGroup()));
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 < 0)
tjc
committed
{
ypos = (getHeight() - scaleHeight)-ydiff;
tjc
committed
maxEnd = recordEnd+2;
}
else
if(snps != null)
lstStart = -1;
else
{
lstStart = recordStart;
lstEnd = recordEnd;
}
if(ypos > r.getMaxY() || ypos < r.getMinY())
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff);
/**
* 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, Color.PINK);
drawScale(g2, start, end, pixPerBase, ((getHeight()+scaleHeight)/2));
int ymid = (getHeight()/ 2);
drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -ydiff, pixPerBase);
drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), ydiff, pixPerBase);
private void drawStrand(Graphics2D g2,
boolean isStrandNegative,
int scaleHeight,
int ymid,
int ystep,
int hgt = getHeight();
int ypos = (hgt - scaleHeight);
int maxEnd = 0;
int lstStart = 0;
int lstEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
SAMRecord samRecord = bamViewRecord.sam;
if( isNegativeStrand(samRecord, colourByStrandTag.isSelected()) == isStrandNegative )
final int offset = getSequenceOffset(samRecord.getReferenceName());
final int recordStart = samRecord.getAlignmentStart()+offset;
final int recordEnd = samRecord.getAlignmentEnd()+offset;
if(colourByCoverageColour.isSelected() ||
colourByStrandTag.isSelected() ||
colourByReadGrp.isSelected() ||
lstStart != recordStart || lstEnd != recordEnd || snps != null)
{
if(colourByStrandTag.isSelected())
{
if(samRecord.getAttribute("XS") == null)
g2.setColor(Color.BLACK);
else if( ((Character)samRecord.getAttribute("XS")).equals('+') )
g2.setColor(Color.BLUE);
else if( ((Character)samRecord.getAttribute("XS")).equals('-') )
g2.setColor(Color.RED);
else
g2.setColor(Color.BLACK);
}
else if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(bamViewRecord));
else if(colourByReadGrp.isSelected())
g2.setColor(getReadGroupFrame().getReadGroupColour(readGroups, samRecord.getReadGroup()));
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 < 0 || ypos > hgt)
{
ypos = ymid + ystep;
maxEnd = recordEnd+2;
}
else
ypos = ypos + ystep;
}
else
if(snps != null)
lstStart = -1;
else
{
lstStart = recordStart;
lstEnd = recordEnd;
}
if(ypos > r.getMaxY() || ypos < r.getMinY())
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ystep);
* Draw paired reads as lines in a vertical stacks.
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawPairedStackView(Graphics2D g2,
final int seqLength,
final float pixPerBase,
final int start,
final int end)
drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);
if(isShowScale())
final Vector<PairedRead> pairedReads = new Vector<PairedRead>();
for(int i=0; i<readsInView.size(); i++)
{
BamViewRecord bamViewRecord = readsInView.get(i);
SAMRecord samRecord = bamViewRecord.sam;
if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped
continue;
BamViewRecord bamViewNextRecord = null;
if(i < readsInView.size()-1)
{
bamViewNextRecord = readsInView.get(++i);
SAMRecord samNextRecord = bamViewNextRecord.sam;
final PairedRead pr = new PairedRead();
if(samRecord.getReadName().equals(samNextRecord.getReadName()) &&
isFromSameBamFile(bamViewRecord, bamViewNextRecord, bamList))
{
if(samRecord.getAlignmentStart() < samNextRecord.getAlignmentStart())
{
pr.sam1 = bamViewRecord;
pr.sam2 = bamViewNextRecord;
}
else
{
pr.sam2 = bamViewRecord;
pr.sam1 = bamViewNextRecord;
}
}
else
{
--i;
pr.sam1 = bamViewRecord;
pr.sam2 = null;
}
pairedReads.add(pr);
}
}
Collections.sort(pairedReads, new PairedReadComparator());
Stroke originalStroke = new BasicStroke (readLnHgt, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
g2.setStroke( new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int ypos = getHeight() - scaleHeight - ydiff;
int lastEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
if(pr.sam1.sam.getAlignmentStart() > lastEnd)
ypos = getHeight() - scaleHeight - ydiff;
if(pr.sam2 != null)
lastEnd = pr.sam2.sam.getAlignmentEnd();
lastEnd = pr.sam1.sam.getAlignmentEnd();
if(ypos > r.getMaxY() || ypos < r.getMinY())
g2.setStroke(originalStroke);
if(highlightSAMRecord != null &&
highlightSAMRecord.sam.getReadName().equals(pr.sam1.sam.getReadName()))
g2.setColor(Color.black);
else
g2.setColor(Color.gray);
if(pr.sam2 != null)
{
if(!readsOverlap(pr.sam1.sam, pr.sam2.sam))
int offset1 = getSequenceOffset(pr.sam1.sam.getReferenceName());
int offset2 = getSequenceOffset(pr.sam2.sam.getReferenceName());
drawTranslucentJointedLine(g2,
(int)((pr.sam1.sam.getAlignmentEnd()+offset1-getBaseAtStartOfView())*pixPerBase),
(int)((pr.sam2.sam.getAlignmentStart()+offset2-getBaseAtStartOfView())*pixPerBase), ypos);
else if(!pr.sam1.sam.getMateUnmappedFlag() &&
pr.sam1.sam.getProperPairFlag() &&
pr.sam1.sam.getMateReferenceName().equals(pr.sam1.sam.getReferenceName()))
if(pr.sam1.sam.getAlignmentStart() > pr.sam1.sam.getMateAlignmentStart())
prStart = pr.sam1.sam.getMateAlignmentStart();
prEnd = pr.sam1.sam.getAlignmentStart();
prStart = pr.sam1.sam.getAlignmentEnd();
prEnd = pr.sam1.sam.getMateAlignmentStart();
int offset = getSequenceOffset(pr.sam1.sam.getReferenceName());
drawTranslucentJointedLine(g2,
(int)( (prStart+offset-getBaseAtStartOfView())*pixPerBase),
(int)( (prEnd +offset-getBaseAtStartOfView())*pixPerBase), ypos);
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(pr.sam1));
else if(colourByStrandTag.isSelected())
{
if( ((Character)pr.sam1.sam.getAttribute("XS")).equals('+') )
g2.setColor(Color.BLUE);
else if( ((Character)pr.sam1.sam.getAttribute("XS")).equals('-') )
g2.setColor(Color.RED);
else
g2.setColor(Color.BLACK);
}
else if(colourByReadGrp.isSelected())
g2.setColor(getReadGroupFrame().getReadGroupColour(readGroups, pr.sam1.sam.getReadGroup()));
else if( pr.sam2 != null &&
!( pr.sam1.sam.getReadNegativeStrandFlag() ^ pr.sam2.sam.getReadNegativeStrandFlag() ) )
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
drawRead(g2, pr.sam1, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam1.sam), ydiff);
if(pr.sam2 != null)
drawRead(g2, pr.sam2, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam2.sam), ydiff);
/**
* Check if a record is on the negative strand. If the RNA strand specific
* checkbox is set then use the RNA strand.
* @param samRecord
* @param useStrandTag - strand specific tag
* @return
*/
protected static boolean isNegativeStrand(final SAMRecord samRecord,
final boolean useStrandTag)
if(useStrandTag)
{
if(samRecord.getAttribute("XS") == null)
return samRecord.getReadNegativeStrandFlag();
if( ((Character)samRecord.getAttribute("XS")).equals('+') )
return false;
else
return true;
}
return samRecord.getReadNegativeStrandFlag();
}
/**
* Check if two records are from the same BAM file
* @param sam1
* @param sam2
* @param bamList
* @return
*/
private boolean isFromSameBamFile(final BamViewRecord sam1,
final BamViewRecord sam2,
final List<String> bamList)
{
if(bamList == null || bamList.size()<2)
return true;
final short o1 = sam1.bamIndex;
final short o2 = sam2.bamIndex;
if(o1 != -1 && o2 != -1)
if( o1 != o2 )
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
return false;
return true;
}
/**
* Check if two records overlap
* @param s1
* @param s2
* @return true id the two reads overlap
*/
private boolean readsOverlap(final SAMRecord s1,
final SAMRecord s2)
{
if( (s2.getAlignmentStart() >= s1.getAlignmentStart() &&
s2.getAlignmentStart() <= s1.getAlignmentEnd()) ||
(s2.getAlignmentEnd() >= s1.getAlignmentStart() &&
s2.getAlignmentEnd() <= s1.getAlignmentEnd()) )
return true;
if( (s1.getAlignmentStart() >= s2.getAlignmentStart() &&
s1.getAlignmentStart() <= s2.getAlignmentEnd()) ||
(s1.getAlignmentEnd() >= s2.getAlignmentStart() &&
s1.getAlignmentEnd() <= s2.getAlignmentEnd()) )
return true;
return false;
}
/**
* 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;
if(!cbCoverageStrandView.isSelected() && !coverageView.isPlotHeatMap())
try
{
int y = getHeight()-6-( (hgt* MAX_COVERAGE)/(coverageView.max/coverageView.windowSize) );
g2.setColor(Color.YELLOW);
// draw the threshold for the coverage max read cut-off
g2.fillRect(0, y, getWidth(), 8);
}
catch(Exception e){}
g2.translate(0, getJspView().getViewport().getViewPosition().y);
coverageView.drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK);
if(!coverageView.isPlotHeatMap())
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, BamViewRecord bamViewRecord, int ypos,
float pixPerBase, int baseAtStartOfView, int scaleHeight, List<Integer> snps, int ydiff)
SAMRecord samRecord = bamViewRecord.sam;
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);
g2.setColor(getColourByCoverageColour(bamViewRecord));
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, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff);
/*if (isSNPs)
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
* @param snps
final BamViewRecord bamViewRecord,
final int baseAtStartOfView,
SAMRecord thisRead = bamViewRecord.sam;
int offset = getSequenceOffset(thisRead.getReferenceName());
int thisStart = thisRead.getAlignmentStart()+offset-baseAtStartOfView;
int thisEnd = thisRead.getAlignmentEnd()+offset-baseAtStartOfView;
if(highlightSAMRecord != null &&
highlightSAMRecord.sam.getReadName().equals(thisRead.getReadName()))
Stroke originalStroke = g2.getStroke();
Stroke stroke =
new BasicStroke (readLnHgt*1.6f, 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;
}
}
drawArrow(g2, thisRead, thisStart, thisEnd, pixPerBase, ypos, ydiff);
// 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 = bamViewRecord;
if (isSNPs && snps != null)
showSNPsOnReads(snps, g2, pixPerBase, ypos);
private void drawArrow(final Graphics2D g2,
final SAMRecord thisRead,
final int thisStart,
final int thisEnd,
final float pixPerBase,
int ypos,
int ydiff)
if(thisRead.getReadNegativeStrandFlag())
{
ypos-=readLnHgt/2;
int apos = ypos + ydiff - 1;
g2.drawLine((int)( (thisStart+5) * pixPerBase), apos,
(int)( thisStart * pixPerBase), ypos);
}
else
{
ypos+=readLnHgt/2;
int apos = ypos - ydiff + 1;
g2.drawLine((int)( (thisEnd-5) * pixPerBase), apos,
(int)( thisEnd * pixPerBase), ypos);
}
}
/**
* Highlight a selected range
* @param g2
* @param pixPerBase
* @param start
* @param end
*/
private void drawSelectionRange(Graphics2D g2, float pixPerBase, int start, int end, Color c)
{
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(c);
g2.fillRect(x, 0, width, getHeight());
}
}
}
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
/**
* 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);
}
* @param snps
private void showSNPsOnReads(final List<Integer> snps,
final Graphics2D g2,
final Stroke originalStroke = g2.getStroke();
final BasicStroke stroke = new BasicStroke(
1.3f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
g2.setStroke(stroke);
g2.setColor(Color.red);
for(int pos: snps)
g2.drawLine((int) (pos * pixPerBase), ypos + 2,
(int) (pos * pixPerBase), ypos - 2);
}
/**
* Get the SNP positions
* @param samRecord
*/
private List<Integer> getSNPs(final SAMRecord samRecord)
{
if(!isSNPs) // return null if not displaying SNPs
return null;
int rbeg = samRecord.getAlignmentStart();
int rend = samRecord.getAlignmentEnd();
int offset = getSequenceOffset(samRecord.getReferenceName());
ArrayList<Integer> snps = null;
// use alignment blocks of the contiguous alignment of
// subsets of read bases to a reference sequence
final char[] refSeq = bases.getSubSequenceC(
new Range(rbeg+offset, rend+offset), Bases.FORWARD);
final byte[] readSeq = samRecord.getReadBases();
offset = offset - getBaseAtStartOfView();
final List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks();
int readStart = block.getReadStart();
int refStart = block.getReferenceStart();
int len = block.getLength();
for(int j=0; j<len; j++)
int readPos = readStart-1+j;
int refPos = refStart+j;
if (Character.toUpperCase(refSeq[refPos-rbeg]) != Character.toUpperCase( (char)readSeq[readPos]) )
if(snps == null)
snps = new ArrayList<Integer>();
snps.add(refPos+offset);
System.err.println(samRecord.getReadName()+" "+e.getMessage());
return snps;
/**
* 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
{
{
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 short thisBamIndex)
final File f = new File(bamList.get(thisBamIndex));
final JCheckBoxMenuItem cbBam = new JCheckBoxMenuItem(
f.getName(),
getImageIcon(getColourByCoverageColour(thisBamIndex)),
true);
bamFilesMenu.add(cbBam);
cbBam.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if(cbBam.isSelected())
laststart = -1;
repaint();
}
});
}
/**
* Refresh the colour of the icons used to identify the
* BAM files.
*/
protected void refreshColourOfBamMenu()
{
final Component cs[] = bamFilesMenu.getMenuComponents();
for(Component c : cs)
{
if(c instanceof JCheckBoxMenuItem)
{
final JCheckBoxMenuItem cbBam = (JCheckBoxMenuItem) c;
final Color col = getColorByJCheckBoxMenuItem(cbBam);
if(col != null)
cbBam.setIcon(getImageIcon(col));
protected Color getColorByJCheckBoxMenuItem(JCheckBoxMenuItem cbBam)
{
final String bam = cbBam.getText();
for(short i=0; i<bamList.size(); i++)
{
final File f = new File(bamList.get(i));
if(f.getName().equals(bam))
return getColourByCoverageColour(i);
}
return null;
}
/**
* Create an icon of a box using the given colour.
* @param c
* @return
*/
{
BufferedImage image = (BufferedImage)this.createImage(10, 10);
Graphics2D g2 = image.createGraphics();
g2.setColor(c);
g2.fillRect(0, 0, 10, 10);
return new ImageIcon(image);
}
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_SUFFIX);
short count = (short) bamList.size();
bamList.addAll(bamFileSelection.getFiles(BAM_SUFFIX));
for(short i=0; i<bamFiles.size(); i++)
addToViewMenu((short) (i+count));
repaint();
}
});
bamFilesMenu.setFont(addBam.getFont());
final JMenuItem groupBams = new JMenuItem("Group BAMs ...");
groupBams.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
groupsFrame.updateAndDisplay();
}
});
bamFilesMenu.add(groupBams);
bamFilesMenu.addSeparator();
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);
final ReadCountDialog opts = new ReadCountDialog(new JFrame(),
"Read Count Options", feature_display, yBox);
if(opts.getStatus() == -1)
return;
//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(), colourByStrandTag.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)
{
final 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);
final ReadCountDialog opts = new ReadCountDialog(new JFrame(),
"RPKM Options", feature_display, yBox);
if(opts.getStatus() == -1)
return;
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(),
colourByStrandTag.isSelected());
final JMenuItem createFeatures = new JMenuItem("Create features from coverage peaks ...");
analyse.add(createFeatures);
if(feature_display == null)
createFeatures.setEnabled(false);
createFeatures.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(feature_display == null)
return;
new CreateFeatures(groupsFrame);
for(short 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());
cbCoverageStrandView.setFont(viewMenu.getFont());
colourByStrandTag.setFont(viewMenu.getFont());
cbIsizeStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
logMenuItem.setEnabled(isIsizeStackView());
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
viewMenu.add(cbIsizeStackView);
cbStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
if(cbStackView.isSelected())
logMenuItem.setEnabled(false);
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
cbPairedStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
if(cbPairedStackView.isSelected())
logMenuItem.setEnabled(false);
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
repaint();
}
});
viewMenu.add(cbPairedStackView);
cbStrandStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
if(cbStrandStackView.isSelected())
logMenuItem.setEnabled(false);
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
viewMenu.add(cbStrandStackView);
cbCoverageView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
if(cbCoverageView.isSelected())
{
logMenuItem.setEnabled(true);
coverageView.setPlotHeatMap(false);
coverageView.setPlotByStrand(false);
setViewportBtm();
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
repaint();
}
});
viewMenu.add(cbCoverageView);
cbCoverageStrandView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
if(cbCoverageStrandView.isSelected())
{
logMenuItem.setEnabled(true);
coverageView.setPlotHeatMap(false);
coverageView.setPlotByStrand(true);
setViewportBtm();
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
repaint();
}
});
viewMenu.add(cbCoverageStrandView);
cbCoverageHeatMap.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
if(cbCoverageHeatMap.isSelected())
{
logMenuItem.setEnabled(true);
coverageView.setPlotHeatMap(true);
setViewportBtm();
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
}
repaint();
}
});
viewMenu.add(cbCoverageHeatMap);
final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNP marks", isSNPs);
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
final JMenu colourMenu = new JMenu("Colour By");
final JCheckBoxMenuItem colourDefault = new JCheckBoxMenuItem ("Default", true);
final ButtonGroup grp = new ButtonGroup();
grp.add(colourByReadGrp);
grp.add(colourByCoverageColour);
grp.add(colourByStrandTag);
grp.add(colourDefault);
colourMenu.add(colourDefault);
colourDefault.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaintBamView();
}
});
colourMenu.add(colourByReadGrp);
colourByReadGrp.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaintBamView();
}
});
colourByCoverageColour.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
colourMenu.add(colourByStrandTag);
colourByStrandTag.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
baseQualityColour.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(baseQualityColour.isSelected())
{
checkBoxSNPs.setSelected(false);
isSNPs = false;
}
repaint();
}
});
colourMenu.add(baseQualityColour);
menu.add(colourMenu);
//
JCheckBoxMenuItem checkBoxOrientation = new JCheckBoxMenuItem("Orientation");
checkBoxOrientation.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
isOrientation = !isOrientation;
repaint();
}
});
showMenu.add(checkBoxOrientation);
JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads");
checkBoxSingle.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaint();
isSingle = !isSingle;
}
});
showMenu.add(checkBoxSingle);
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);
markInsertions.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaint();
}
});
showMenu.add(markInsertions);
JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage", isCoverage);
checkBoxCoverage.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
isCoverage = !isCoverage;
coveragePanel.setVisible(isCoverage);
if( isCoverage &&
!cbCoverageView.isSelected() &&
!cbCoverageStrandView.isSelected() &&
!cbCoverageHeatMap.isSelected())
JCheckBoxMenuItem checkBoxSNP = new JCheckBoxMenuItem("SNP", isSNPplot);
checkBoxSNP.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
isSNPplot = !isSNPplot;
snpPanel.setVisible(isSNPplot);
repaint();
}
});
graphMenu.add(checkBoxSNP);
menu.add(graphMenu);
final JCheckBoxMenuItem checkBoxSync =
new JCheckBoxMenuItem("Asynchronous", asynchronous);
checkBoxSync.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
asynchronous = checkBoxSync.isSelected();
}
});
JMenu maxHeightMenu = new JMenu("BamView Height");
final String hgts[] =
{"500", "800", "1000", "1500", "2500", "5000", "50000"};
ButtonGroup bgroup = new ButtonGroup();
for(int i=0; i<hgts.length; i++)
{
final String hgt = hgts[i];
final JCheckBoxMenuItem maxHeightMenuItem = new JCheckBoxMenuItem(hgt);
bgroup.add(maxHeightMenuItem);
maxHeightMenuItem.setSelected(hgts[i].equals(Integer.toString(maxHeight)));
maxHeightMenu.add(maxHeightMenuItem);
maxHeightMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(maxHeightMenuItem.isSelected())
maxHeight = Integer.parseInt(hgt);
int start = getBaseAtStartOfView();
setDisplay(start, nbasesInView+start, null);
}
});
}
logMenuItem.setEnabled(isIsizeStackView());
logMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
logScale = logMenuItem.isSelected();
repaint();
}
});
final JMenuItem readGroupsMenu = new JMenuItem("Read Groups ...");
readGroupsMenu.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
f.setVisible(true);
}
});
menu.add(readGroupsMenu);
JMenuItem filter = new JMenuItem("Filter Reads ...");
menu.add(filter);
filter.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(filterFrame == null)
filterFrame = new SAMRecordFilter(BamView.this);
else
filterFrame.setVisible(true);
}
});
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
JMenuItem maxReadCoverage = new JMenuItem("Read Coverage Threshold ...");
menu.add(maxReadCoverage);
maxReadCoverage.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
final TextFieldInt maxRead = new TextFieldInt();
maxRead.setValue(MAX_COVERAGE);
int status = JOptionPane.showConfirmDialog(null, maxRead,
"Read Coverage Threshold", JOptionPane.OK_CANCEL_OPTION);
if(status == JOptionPane.OK_OPTION &&
maxRead.getValue() != MAX_COVERAGE)
{
MAX_COVERAGE = maxRead.getValue();
if(MAX_COVERAGE < 1)
MAX_COVERAGE = Integer.MAX_VALUE;
laststart = -1;
repaint();
}
}
});
JMenuItem readList = new JMenuItem("List Reads ...");
menu.add(readList);
readList.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
final JMenuItem bamSplitter = new JMenuItem("Clone window");
bamSplitter.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
openBamView(new Vector<String>(bamList));
}
});
menu.add(new JSeparator());
menu.add(bamSplitter);
//
JMenu coverageMenu = new JMenu("Coverage Options");
coverageView.init(this, 0.f, 0, 0);
coverageView.createMenus(coverageMenu);
viewMenu.add(new JSeparator());
viewMenu.add(coverageMenu);
readGrpFrame = new ReadGroupsFrame(readGroups, BamView.this);
private JComponent bamTopPanel(final JFrame frame)
{
final JComponent topPanel;
{
topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
if(feature_display != null)
this.selection = feature_display.getSelection();
}
else
{
topPanel = new JMenuBar();
frame.setJMenuBar((JMenuBar)topPanel);
JMenu fileMenu = new JMenu("File");
topPanel.add(fileMenu);
JMenuItem readBam = new JMenuItem("Open new BamView ...");
fileMenu.add(readBam);
readBam.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JMenuItem saveAs = new JMenuItem("Save As Image File (png/jpeg/svg) ...");
fileMenu.add(saveAs);
saveAs.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
PrintBamView.print((JPanel)mainPanel.getParent());
}
});
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
JMenuItem close = new JMenuItem("Close");
fileMenu.add(close);
close.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
BamView.this.setVisible(false);
Component comp = BamView.this;
while( !(comp instanceof JFrame) )
comp = comp.getParent();
((JFrame)comp).dispose();
}
});
JMenuItem exit = new JMenuItem("Exit");
fileMenu.add(new JSeparator());
fileMenu.add(exit);
exit.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
int status = JOptionPane.showConfirmDialog(BamView.this,
"Exit BamView?", "Exit",
JOptionPane.OK_CANCEL_OPTION);
if(status != JOptionPane.OK_OPTION)
return;
System.exit(0);
}
});
addKeyListener(new KeyAdapter()
{
public void keyPressed(final KeyEvent event)
{
switch (event.getKeyCode())
{
case KeyEvent.VK_UP:
setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
break;
case KeyEvent.VK_DOWN:
if (showBaseAlignment)
break;
setZoomLevel((int) (BamView.this.nbasesInView * .9));
break;
default:
break;
}
}
});
}
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;
}
// auto hide top panel
final JCheckBox buttonAutoHide = new JCheckBox("Hide", (frame == null));
buttonAutoHide.setToolTipText("Auto-Hide");
final MouseMotionListener mouseMotionListener = new MouseMotionListener()
{
public void mouseDragged(MouseEvent event)
{
handleCanvasMouseDrag(event);
}
public void mouseMoved(MouseEvent e)
{
lastMousePoint = e.getPoint();
if (thisHgt < 5)
thisHgt = 15;
int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY());
Point p = jspView.getViewport().getViewPosition();
boolean isVis = topPanel.isVisible();
if (y < thisHgt)
{
topPanel.setVisible(true);
}
else
{
if (buttonAutoHide.isSelected())
}
};
addMouseMotionListener(mouseMotionListener);
combo = new SequenceComboBox(seqNames){
private static final long serialVersionUID = 1L;
public void update(IndexReferenceEvent event)
{
laststart = -1;
if(feature_display != null)
setZoomLevel(feature_display.getMaxVisibleBases());
else
setZoomLevel(BamView.this.nbasesInView);
}
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
topPanel.add(combo);
if(feature_display == null)
{
final JTextField baseText = new JTextField(8);
JButton goTo = new JButton("GoTo:");
goTo.setToolTipText("Go to base position");
goTo.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
int basePosition = Integer.parseInt(baseText.getText());
scrollBar.setValue(basePosition);
}
catch (NumberFormatException nfe)
{
JOptionPane.showMessageDialog(BamView.this,
"Expecting a base number!", "Number Format",
JOptionPane.WARNING_MESSAGE);
}
}
});
topPanel.add(goTo);
topPanel.add(baseText);
JButton zoomIn = new JButton("-");
zoomIn.setToolTipText("Zoom out (up arrow)");
Insets ins = new Insets(1,1,1,1);
zoomIn.setMargin(ins);
zoomIn.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
}
});
topPanel.add(zoomIn);
JButton zoomOut = new JButton("+");
zoomOut.setToolTipText("Zoom in (down arrow)");
zoomOut.setMargin(ins);
zoomOut.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (showBaseAlignment)
return;
setZoomLevel((int) (BamView.this.nbasesInView * .9));
}
});
topPanel.add(zoomOut);
}
topPanel.add(buttonAutoHide);
final JSlider slider = new JSlider(13, 52, (int) (readLnHgt*10));
slider.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent arg0)
{
readLnHgt = (slider.getValue()/10.f);
repaintBamView();
}
});
if(feature_display != null)
{
JButton close = new JButton("Close");
topPanel.add(close);
close.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
int status = JOptionPane.showConfirmDialog(frame,
"Close the BAM panel?", "Close",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(status == JOptionPane.CANCEL_OPTION)
return;
final JPanel containerPanel = (JPanel) mainPanel.getParent();
feature_display.removeDisplayAdjustmentListener(BamView.this);
feature_display.getSelection().removeSelectionChangeListener(BamView.this);
containerPanel.remove(mainPanel);
if(containerPanel.getComponentCount() > 0)
containerPanel.revalidate();
else
{
if(entry_edit != null)
entry_edit.setNGDivider();
else
containerPanel.setVisible(false);
}
}
});
}
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);
}
private void setViewportBtm()
{
jspView.getVerticalScrollBar().setValue(
jspView.getVerticalScrollBar().getMaximum());
}
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(isBaseAlignmentView(pixPerBase))
this.nbasesInView = (int)(mainPanel.getWidth()/pixPerBase);
if(ruler == null)
ruler = new Ruler();
jspView.setColumnHeaderView(ruler);
showBaseAlignment = true;
markInsertions.setEnabled(true);
if(!isCoverageView(pixPerBase) && nbasesInView >= MAX_BASES)
{
cbLastSelected = getSelectedCheckBoxMenuItem();
cbCoverageView.setSelected(true);
coverageView.setPlotByStrand(false);
else if(isCoverageView(pixPerBase) && nbasesInView < MAX_BASES && cbLastSelected != null)
{
cbLastSelected.setSelected(true);
cbLastSelected = null;
}
else
setViewportMidPoint();
markInsertions.setEnabled(false);
{
scrollBar.setValues(startValue, nbasesInView, 1,
getMaxBasesInPanel(getSequenceLength()));
scrollBar.setUnitIncrement(nbasesInView/20);
scrollBar.setBlockIncrement(nbasesInView);
}
* Set the start and end base positions to display.
* @param start
* @param end
* @param event
public void setDisplay(int start,
int end,
DisplayAdjustmentEvent event)
this.startBase = start;
this.endBase = end;
else
{
if(feature_display == null)
pixPerBase = 1000.f/(float)(end-start+1);
else
pixPerBase = feature_display.getWidth()/(float)(end-start+1);
}
if(event == null)
{
this.startBase = -1;
this.endBase = -1;
}
/**
* Return an Artemis entry from a file
* @param entryFileName
* @param entryGroup
* @return
* @throws NoSequenceException
*/
private Entry getEntry(final String entryFileName, final EntryGroup entryGroup)
throws NoSequenceException
{
final Document entry_document = DocumentFactory.makeDocument(entryFileName);
final EntryInformation artemis_entry_information =
Options.getArtemisEntryInformation();
//System.out.println(entryFileName);
final uk.ac.sanger.artemis.io.Entry new_embl_entry =
EntryFileDialog.getEntryFromFile(null, entry_document,
artemis_entry_information,
false);
if(new_embl_entry == null) // the read failed
return null;
Entry entry = null;
try
{
if(entryGroup.getSequenceEntry() != null)
bases = entryGroup.getSequenceEntry().getBases();
else
entry = new Entry(bases,new_embl_entry);
entryGroup.add(entry);
}
catch(OutOfRangeException e)
{
new MessageDialog(null, "read failed: one of the features in " +
entryFileName + " has an out of range " +
"location: " + e.getMessage());
}
return entry;
}
private boolean isShowScale()
{
return (feature_display == null ? true : false);
}
public JScrollPane getJspView()
{
return jspView;
}
/**
* Handle a mouse drag event on the drawing canvas.
**/
if(event.getButton() == MouseEvent.BUTTON3 || bases == null)
tjc
committed
highlightSAMRecord = null;
if(event.getClickCount() > 1)
{
getSelection().clear();
repaint();
return;
}
highlightRange(event,
MouseEvent.BUTTON1_DOWN_MASK & MouseEvent.BUTTON2_DOWN_MASK);
}
/**
*
* @param event
* @param onmask
*/
protected 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();
/**
* Get the colour for the given read given to it by the coverage plot.
* @param samRecord
* @return
*/
private Color getColourByCoverageColour(BamViewRecord samRecord)
short fileIndex = 0;
fileIndex = samRecord.bamIndex;
return getColourByCoverageColour(fileIndex);
}
private Color getColourByCoverageColour(final short fileIndex)
{
LineAttributes lines[] = CoveragePanel.getLineAttributes(bamList.size());
return lines[fileIndex].getLineColour();
}
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
protected int getMaxBases()
{
return MAX_BASES;
}
protected void setMaxBases(int max)
{
MAX_BASES = max;
}
private boolean isStackView()
{
return cbStackView.isSelected();
}
private boolean isPairedStackView()
{
return cbPairedStackView.isSelected();
}
private boolean isStrandStackView()
{
return cbStrandStackView.isSelected();
}
private boolean isCoverageView(float pixPerBase)
{
if(isBaseAlignmentView(pixPerBase))
return false;
return cbCoverageView.isSelected() || cbCoverageStrandView.isSelected() || cbCoverageHeatMap.isSelected();
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
}
private boolean isIsizeStackView()
{
return cbIsizeStackView.isSelected();
}
private boolean isBaseAlignmentView(float pixPerBase)
{
if(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
return true;
return false;
}
private JCheckBoxMenuItem getSelectedCheckBoxMenuItem()
{
if(isStackView())
return cbStackView;
if(isPairedStackView())
return cbPairedStackView;
if(isStrandStackView())
return cbStrandStackView;
if(isIsizeStackView())
return cbIsizeStackView;
if(cbCoverageView.isSelected())
return cbCoverageView;
if(cbCoverageHeatMap.isSelected())
return cbCoverageHeatMap;
protected List<BamViewRecord> getReadsInView()
protected int getBasesInView()
{
return nbasesInView;
}
protected void setHighlightSAMRecord(BamViewRecord highlightSAMRecord)
{
this.highlightSAMRecord = highlightSAMRecord;
}
protected BamViewRecord getHighlightSAMRecord()
protected FeatureDisplay getFeatureDisplay()
{
return feature_display;
}
/**
* @return the combo
*/
public SequenceComboBox getCombo()
{
return combo;
}
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
private String getVersion()
{
final ClassLoader cl = this.getClass().getClassLoader();
try
{
String line;
InputStream in = cl.getResourceAsStream("etc/versions");
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while((line = reader.readLine()) != null)
{
if(line.startsWith("BamView"))
return line.substring( "BamView".length() ).trim();
}
reader.close();
in.close();
}
catch (Exception ex)
{
}
return null;
}
public void openBamView(final List<String> bamsList)
BamView bamView = new BamView(bamsList,
null, nbasesInView, entry_edit,
feature_display, bases, (JPanel) mainPanel.getParent(), null);
bamView.getJspView().getVerticalScrollBar().setValue(
bamView.getJspView().getVerticalScrollBar().getMaximum());
getJspView().getVerticalScrollBar().setValue(
bamView.getJspView().getVerticalScrollBar().getMaximum());
int start = getBaseAtStartOfView();
setDisplay(start, nbasesInView+start, null);
if(feature_display != null)
{
feature_display.addDisplayAdjustmentListener(bamView);
feature_display.getSelection().addSelectionChangeListener(bamView);
if(entry_edit != null)
entry_edit.getOneLinePerEntryDisplay().addDisplayAdjustmentListener(bamView);
if(feature_display.getEntryGroup().getSequenceEntry().getEMBLEntry().getSequence()
instanceof uk.ac.sanger.artemis.io.IndexFastaStream)
{
{
// add reference sequence selection listeners
entry_edit.getEntryGroupDisplay().getIndexFastaCombo().addIndexReferenceListener(bamView.getCombo());
bamView.getCombo().addIndexReferenceListener(entry_edit.getEntryGroupDisplay().getIndexFastaCombo());
public void displayAdjustmentValueChanged(final DisplayAdjustmentEvent event)
if(event.getType() == DisplayAdjustmentEvent.REV_COMP_EVENT &&
event.isRevCompDisplay())
JOptionPane.showMessageDialog(this,
"Flipping the display is not supported by BamView.", "Warning",
JOptionPane.WARNING_MESSAGE);
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();
/**
* Carry out the display agjustment event action.
* @param event
*/
private void displayAdjustmentWork(final DisplayAdjustmentEvent event)
{
if(event.getType() == DisplayAdjustmentEvent.SCALE_ADJUST_EVENT)
{
laststart = -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();
protected int start;
protected int end;
protected String refSeq;
setPreferredSize(new Dimension(mainPanel.getWidth(), 26));
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();
for(int i=startMark; i<end; i+=20)
for(int i=startMark; i<end; i+=10)
{
int xpos = (i-start)*ALIGNMENT_PIX_PER_BASE;
xpos+=(ALIGNMENT_PIX_PER_BASE/2);
g2.drawLine(xpos, ypos+1, xpos, ypos+5);
}
if(refSeq != null)
{
ypos+=15;
g2.setColor(LIGHT_GREY);
g2.fillRect(0, ypos-11, getWidth(), 11);
g2.translate(0, 16);
drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end, Color.yellow);
g2.translate(0, -16);
g2.setColor(Color.black);
g2.drawString(refSeq, 0, ypos-2);
}
/**
* Popup menu listener
*/
class PopupListener extends MouseAdapter
{
private JMenuItem gotoMateMenuItem;
private JMenuItem showDetails;
private JMenu coverageMenu;
private JMenuItem createGroup;
tjc
committed
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)
{
if(isCoverageView(getPixPerBaseByWidth()))
coverageView.singleClick(e.isShiftDown(),
e.getPoint().y-getJspView().getViewport().getViewPosition().y);
else
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())
//
// coverage heatmap menu options
if(coverageMenu != null)
popup.remove(coverageMenu);
if(isCoverageView(getPixPerBaseByWidth()) && coverageView.isPlotHeatMap())
{
if(coverageMenu == null)
{
coverageMenu = new JMenu("Coverage HeatMap");
final JCheckBoxMenuItem coverageGrid = new JCheckBoxMenuItem("Show heatmap grid", false);
coverageGrid.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
coverageView.showLabels(coverageGrid.isSelected());
}
});
coverageMenu.add(coverageGrid);
createGroup = new JMenuItem("Create group from selected BAMs");
createGroup.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String groupName = "group_"+n;
groupsFrame.addGroup(groupName);
final List<String> selected = coverageView.getSelected();
for(String sel: selected)
groupsFrame.addToGroup((new File(sel)).getName(), groupName);
groupsFrame.updateAndDisplay();
n++;
}
});
coverageMenu.add(createGroup);
}
createGroup.setEnabled(coverageView.hasSelectedBams());
popup.add(coverageMenu);
}
if(gotoMateMenuItem != null)
popup.remove(gotoMateMenuItem);
tjc
committed
if(showDetails != null)
popup.remove(showDetails);
if( mouseOverSAMRecord != null &&
mouseOverSAMRecord.sam.getReadPairedFlag() &&
!mouseOverSAMRecord.sam.getMateUnmappedFlag() )
{
final BamViewRecord thisSAMRecord = mouseOverSAMRecord;
gotoMateMenuItem = new JMenuItem("Go to mate of : "+
thisSAMRecord.sam.getReadName());
gotoMateMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String name = thisSAMRecord.sam.getMateReferenceName();
if(name.equals("="))
name = thisSAMRecord.sam.getReferenceName();
int offset = getSequenceOffset(name);
if(feature_display != null)
feature_display.makeBaseVisible(
thisSAMRecord.sam.getMateAlignmentStart()+offset);
else
scrollBar.setValue(
thisSAMRecord.sam.getMateAlignmentStart()+offset-
(nbasesInView/2));
highlightSAMRecord = thisSAMRecord;
}
});
popup.add(gotoMateMenuItem);
tjc
committed
}
if( mouseOverSAMRecord != null)
{
final BamViewRecord thisSAMRecord = mouseOverSAMRecord;
tjc
committed
showDetails = new JMenuItem("Show details of : "+
thisSAMRecord.sam.getReadName());
tjc
committed
showDetails.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
openFileViewer(thisSAMRecord.sam, getMate(thisSAMRecord), bamList);
tjc
committed
});
popup.add(showDetails);
}
popup.show(e.getComponent(),
e.getX(), e.getY());
protected static void openFileViewer(SAMRecord readRecord, SAMRecord mateRecord, List<String> bamList)
{
FileViewer viewDetail = new FileViewer(readRecord.getReadName(), true, false, false);
appendToDetailView(readRecord, mateRecord, viewDetail, bamList);
private static void appendToDetailView(final SAMRecord sam,
final SAMRecord mate,
final FileViewer viewer,
final List<String> bamList)
tjc
committed
{
if(bamList.size() > 1 && sam.getAttribute("FL") != null)
int bamIdx = (Integer)sam.getAttribute("FL");
if(bamIdx < bamList.size())
viewer.appendString("File "+bamList.get(bamIdx)+"\n\n", Level.INFO);
viewer.appendString("Read Name "+sam.getReadName()+"\n", Level.INFO);
viewer.appendString("Coordinates "+sam.getAlignmentStart()+".."+
sam.getAlignmentEnd()+"\n", Level.DEBUG);
if(sam.getReadGroup() != null)
viewer.appendString("Read Group "+sam.getReadGroup().getId()+"\n", Level.DEBUG);
viewer.appendString("Length "+sam.getReadLength()+"\n", Level.DEBUG);
viewer.appendString("Reference Name "+sam.getReferenceName()+"\n", Level.DEBUG);
viewer.appendString("Inferred Size "+sam.getInferredInsertSize()+"\n", Level.DEBUG);
viewer.appendString("Mapping Quality "+sam.getMappingQuality()+"\n", Level.DEBUG);
viewer.appendString("Cigar String "+sam.getCigarString()+"\n", Level.DEBUG);
viewer.appendString("Strand "+
(sam.getReadNegativeStrandFlag() ? "-\n\n" : "+\n\n"), Level.DEBUG);
viewer.appendString("Mate Coordinates "+mate.getAlignmentStart()+".."+
mate.getAlignmentEnd()+"\n", Level.DEBUG);
viewer.appendString("Mate Length "+mate.getReadLength()+"\n", Level.DEBUG);
viewer.appendString("Mate Reference Name "+mate.getReferenceName()+"\n", Level.DEBUG);
viewer.appendString("Mate Inferred Size "+mate.getInferredInsertSize()+"\n", Level.DEBUG);
viewer.appendString("Mate Mapping Quality "+mate.getMappingQuality()+"\n", Level.DEBUG);
viewer.appendString("Mate Cigar String "+mate.getCigarString()+"\n", Level.DEBUG);
viewer.appendString("Mate Start Coordinate "+sam.getMateAlignmentStart()+"\n", Level.DEBUG);
viewer.appendString("Mate Reference Name "+sam.getMateReferenceName()+"\n", Level.DEBUG);
viewer.appendString("Mate Strand "+
(sam.getMateNegativeStrandFlag() ? "-" : "+"), Level.DEBUG);
tjc
committed
}
viewer.appendString("\n\nFlags:", Level.INFO);
viewer.appendString("\nDuplicate Read "+
(sam.getDuplicateReadFlag() ? "yes" : "no"), Level.DEBUG);
viewer.appendString("\nRead Paired "+
(sam.getReadPairedFlag() ? "yes" : "no"), Level.DEBUG);
if(sam.getReadPairedFlag())
viewer.appendString("\nFirst of Pair "+
(sam.getFirstOfPairFlag() ? "yes" : "no"), Level.DEBUG);
viewer.appendString("\nMate Unmapped "+
(sam.getMateUnmappedFlag() ? "yes" : "no"), Level.DEBUG);
viewer.appendString("\nProper Pair "+
(sam.getProperPairFlag() ? "yes" : "no"), Level.DEBUG);
viewer.appendString("\nRead Fails Vendor\nQuality Check "+
(sam.getReadFailsVendorQualityCheckFlag() ? "yes" : "no"), Level.DEBUG);
viewer.appendString("\nRead Unmapped "+
(sam.getReadUnmappedFlag() ? "yes" : "no"), Level.DEBUG);
if(sam.getReadPairedFlag())
viewer.appendString("\nSecond Of Pair "+
(sam.getSecondOfPairFlag() ? "yes" : "no"), Level.DEBUG);
viewer.appendString("\n\nRead Bases:\n", Level.INFO);
wrapReadBases(sam, viewer);
if(sam.getReadPairedFlag() && mate != null)
{
viewer.appendString("\nMate Read Bases:\n", Level.INFO);
wrapReadBases(mate, viewer);
}
}
private static void wrapReadBases(final SAMRecord sam,
final FileViewer viewer)
{
final String seq = new String(sam.getReadBases());
final int MAX_SEQ_LINE_LENGTH = 100;
for(int i=0; i<=seq.length(); i+=MAX_SEQ_LINE_LENGTH)
{
int iend = i+MAX_SEQ_LINE_LENGTH;
if(iend > seq.length())
iend = seq.length();
viewer.appendString(seq.substring(i, iend)+"\n", Level.DEBUG);
tjc
committed
}
/**
* Query for the mate of a read
* @param mate
* @return
*/
protected SAMRecord getMate(BamViewRecord thisSAMRecord)
if(!thisSAMRecord.sam.getReadPairedFlag()) // read is not paired in sequencing
SAMRecord mate = null;
try
{
short fileIndex = 0;
if(bamList.size()>1 && thisSAMRecord.bamIndex > 0)
fileIndex = thisSAMRecord.bamIndex;
String bam = bamList.get(fileIndex);
final SAMFileReader inputSam = getSAMFileReader(bam);
mate = inputSam.queryMate(thisSAMRecord.sam);
}
catch (Exception e)
{
logger4j.warn("WARN: BamView.getMate() :: "+e.getMessage());
e.printStackTrace();
}
return mate;
}
protected SAMRecordPredicate getSamRecordFlagPredicate()
{
return samRecordFlagPredicate;
}
protected void setSamRecordFlagPredicate(
{
laststart = -1;
lastend = -1;
this.samRecordFlagPredicate = samRecordFlagPredicate;
}
tjc
committed
protected SAMRecordMapQPredicate getSamRecordMapQPredicate()
{
return samRecordMapQPredicate;
}
protected void setSamRecordMapQPredicate(
SAMRecordMapQPredicate samRecordMapQPredicate)
{
laststart = -1;
lastend = -1;
this.samRecordMapQPredicate = samRecordMapQPredicate;
}
/**
* @return the concatSequences
*/
protected boolean isConcatSequences()
{
return concatSequences;
}
class PairedRead
{
BamViewRecord sam1;
BamViewRecord sam2;
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
CreateFeatures(final GroupBamFrame groupsFrame)
{
final TextFieldInt threshold = new TextFieldInt();
final TextFieldInt minSize = new TextFieldInt();
final TextFieldInt minBams = new TextFieldInt();
threshold.setValue(6);
minSize.setValue(6);
minBams.setValue( (groupsFrame.getNumberOfGroups() == 1 ?
bamList.size() : groupsFrame.getMaximumBamsInGroup()) );
final JPanel gridPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
gridPanel.add(new JLabel("Minimum number of reads:"), c);
c.gridy++;
gridPanel.add(threshold, c);
c.gridy++;
gridPanel.add(new JSeparator(), c);
c.gridy++;
gridPanel.add(new JLabel("Minimum number of BAMs for reads to be present in:"), c);
c.gridy++;
gridPanel.add(minBams, c);
JRadioButton useAllBams = new JRadioButton("out of all BAMs", (groupsFrame.getNumberOfGroups() == 1));
JRadioButton useGroup = new JRadioButton("within a group", (groupsFrame.getNumberOfGroups() != 1));
if(groupsFrame.getNumberOfGroups() == 1)
useGroup.setEnabled(false);
final ButtonGroup group = new ButtonGroup();
group.add(useAllBams);
group.add(useGroup);
final Box xBox = Box.createHorizontalBox();
xBox.add(useAllBams);
xBox.add(useGroup);
xBox.add(Box.createHorizontalGlue());
c.gridy++;
gridPanel.add(xBox, c);
c.gridy++;
gridPanel.add(new JSeparator(), c);
c.gridy++;
gridPanel.add(new JLabel("Minimum feature size:"), c);
c.gridy++;
gridPanel.add(minSize, c);
final JCheckBox cbOpposite = new JCheckBox("Assume reads on opposite strand", false);
cbOpposite.setToolTipText("for cDNA experiments when the reads are on the opposite strand");
c.gridy++;
gridPanel.add(cbOpposite, c);
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
int status =
JOptionPane.showConfirmDialog(feature_display, gridPanel,
"Options", JOptionPane.OK_CANCEL_OPTION);
if(status == JOptionPane.CANCEL_OPTION)
return;
if(!useGroup.isSelected() && minBams.getValue() > bamList.size())
{
status =
JOptionPane.showConfirmDialog(feature_display,
"The minimum number of BAMs setting can not be\n"+
"greater than the total number of BAM files.\n"+
"Set this to the number of BAMs (i.e. "+bamList.size()+").",
"Options", JOptionPane.OK_CANCEL_OPTION);
if(status == JOptionPane.CANCEL_OPTION)
return;
minBams.setValue(bamList.size());
}
else if(useGroup.isSelected() && minBams.getValue() > groupsFrame.getMaximumBamsInGroup())
{
status =
JOptionPane.showConfirmDialog(feature_display,
"Minimum number of BAMs setting can not be greater than\n"+
"the total number of BAM files found in any of the groups.\n"+
"Set this to the greatest number of BAM files in any\n"+
"group (i.e. "+groupsFrame.getMaximumBamsInGroup()+").",
"Options", JOptionPane.OK_CANCEL_OPTION);
if(status == JOptionPane.CANCEL_OPTION)
return;
minBams.setValue(groupsFrame.getMaximumBamsInGroup());
}
new MappedReads((String)combo.getSelectedItem(),BamView.this, samFileReaderHash,
seqNames, offsetLengths, concatSequences, seqLengths,
(useGroup.isSelected() ? groupsFrame : null), threshold.getValue(),
minSize.getValue(), minBams.getValue(), cbOpposite.isSelected(), true);
}
}
if(args.length == 0 && BamFrame.isMac())
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1) {}
if(args.length == 0 || args[0].equals("NEW-BAMVIEW"))
System.setProperty("default_directory", System.getProperty("user.dir"));
FileSelectionDialog fileSelection = new FileSelectionDialog(
null, true, "BamView", "BAM");
reference = fileSelection.getReferenceFile();
if(reference == null || reference.equals(""))
reference = null;
if(args.length > 0 && args[0].equals("NEW-BAMVIEW"))
return;
System.err.println("No files found.");
else if(!args[0].startsWith("-"))
{
for(int i=0; i< args.length; i++)
bam.add(args[i]);
}
String chr = null;
String vw = null;
boolean orientation = false;
boolean covPlot = false;
boolean snpPlot = false;
int base = 0;
{
while(i < args.length-1 && !args[++i].startsWith("-"))
{
String filename = args[i];
if(FileSelectionDialog.isListOfFiles(filename))
bam.addAll(FileSelectionDialog.getListOfFiles(filename));
else
bam.add(filename);
}
nbasesInView = Integer.parseInt(args[++i]);
else if(args[i].equals("-s"))
System.setProperty("samtoolDir", args[++i]);
else if(args[i].equals("-c"))
chr = args[++i].trim();
else if(args[i].equals("-b"))
base = Integer.parseInt(args[++i].trim());
else if(args[i].equals("-v"))
vw = args[++i].trim();
else if(args[i].equals("-o"))
orientation = true;
else if(args[i].equals("-pc"))
covPlot = true;
else if(args[i].equals("-ps"))
snpPlot = true;
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("-n\t number of bases to display in the view (optional)");
System.out.println("-c\t chromosome name (optional)");
System.out.println("-v\t view (optional - IS (inferred size), S (stack, default), PS (paired stack), ST (strand), C (coverage))");
System.out.println("-b\t base position (optional)");
System.out.println("-o\t show orientation (optional)");
System.out.println("-pc\t plot coverage (optional)");
System.out.println("-ps\t plot SNP (optional and only with -r)");
final BamView view = new BamView(bam, reference, nbasesInView, null, null,
(JPanel)frame.getContentPane(), frame);
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
if(chr != null)
view.combo.setSelectedItem(chr);
if(vw != null)
{
if(vw.equalsIgnoreCase("IS"))
view.cbIsizeStackView.setSelected(true);
if(vw.equalsIgnoreCase("PS"))
view.cbPairedStackView.setSelected(true);
if(vw.equalsIgnoreCase("ST"))
view.cbStrandStackView.setSelected(true);
if(vw.equalsIgnoreCase("C"))
view.cbCoverageView.setSelected(true);
}
if(base > 0)
view.scrollBar.setValue(base);
if(orientation)
view.isOrientation = true;
if(covPlot)
{
view.isCoverage = true;
view.coveragePanel.setVisible(true);
}
if(snpPlot)
{
view.isSNPplot = true;
view.snpPanel.setVisible(true);
}
// 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.jspView.getVerticalScrollBar().setValue(
view.jspView.getVerticalScrollBar().getMaximum());