BamView.java 127.45 KiB
/* BamView
*
* 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.
*
*/
package uk.ac.sanger.artemis.components.alignment;
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.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
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.Options;
import uk.ac.sanger.artemis.Selection;
import uk.ac.sanger.artemis.SelectionChangeEvent;
import uk.ac.sanger.artemis.SelectionChangeListener;
import uk.ac.sanger.artemis.SimpleEntryGroup;
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.EntryFileDialog;
import uk.ac.sanger.artemis.components.FeatureDisplay;
import uk.ac.sanger.artemis.components.FileViewer;
import uk.ac.sanger.artemis.components.IndexReferenceEvent;
import uk.ac.sanger.artemis.components.MessageDialog;
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.io.EntryInformation;
import uk.ac.sanger.artemis.io.Range;
import uk.ac.sanger.artemis.sequence.Bases;
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;
import uk.ac.sanger.artemis.util.OutOfRangeException;
public class BamView extends JPanel
implements DisplayAdjustmentListener, SelectionChangeListener
{
private static final long serialVersionUID = 1L;
private List<BamViewRecord> readsInView;
private Hashtable<String, SAMFileReader> samFileReaderHash = new Hashtable<String, SAMFileReader>();
private HashMap<String, Integer> seqLengths = new HashMap<String, Integer>();
private HashMap<String, Integer> offsetLengths;
private Vector<String> seqNames = new Vector<String>();
protected List<String> bamList;
protected List<Short> hideBamList = new Vector<Short>();
private SAMRecordPredicate samRecordFlagPredicate;
private SAMRecordMapQPredicate samRecordMapQPredicate;
private SAMRecordFilter filterFrame;
private Bases bases;
private JScrollPane jspView;
private JScrollBar scrollBar;
private SequenceComboBox combo;
private boolean isOrientation = false;
private boolean isSingle = false;
private boolean isSNPs = false;
private boolean isCoverage = false;
private boolean isSNPplot = false;
private EntryEdit entry_edit;
private FeatureDisplay feature_display;
private Selection selection;
private JPanel mainPanel = new JPanel();
private CoveragePanel coveragePanel;
private SnpPanel snpPanel;
protected boolean logScale = false;
private Ruler ruler;
private int nbasesInView;
private int startBase = -1;
private int endBase = -1;
private int laststart;
private int lastend;
private boolean asynchronous = true;
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 colourByCoverageColour = new JCheckBoxMenuItem("Coverage Plot Colours");
private JCheckBoxMenuItem baseQualityColour = new JCheckBoxMenuItem("Base Quality");
private JCheckBoxMenuItem markInsertions = new JCheckBoxMenuItem("Mark Insertions", true);
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)$";
/** Used to colour the frames. */
private static Color LIGHT_GREY = new Color(200, 200, 200);
private static Color DARK_GREEN = new Color(0, 150, 0);
private static Color DARK_ORANGE = new Color(255,140,0);
private static Color DEEP_PINK = new Color(139,10,80);
private Point lastMousePoint = null;
private BamViewRecord mouseOverSAMRecord = null;
private BamViewRecord highlightSAMRecord = null;
private String mouseOverInsertion;
// record of where a mouse drag starts
protected int dragStart = -1;
private static int MAX_BASES = 26000;
private int maxHeight = 800;
private boolean concatSequences = false;
private int ALIGNMENT_PIX_PER_BASE;
private int BASE_HEIGHT;
private JPopupMenu popup;
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,
String reference,
int nbasesInView,
final FeatureDisplay feature_display,
final Bases bases,
final JPanel containerPanel,
final JFrame frame)
{
super();
setBackground(Color.white);
this.bamList = bamList;
this.nbasesInView = nbasesInView;
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));
if(reference != null)
{
System.setProperty("reference", reference); // for CRAM
EntryGroup entryGroup = new SimpleEntryGroup();
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();
Enumeration<Object> keys = UIManager.getDefaults().keys();
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());
FontMetrics fm = getFontMetrics(getFont());
ALIGNMENT_PIX_PER_BASE = fm.charWidth('M');
BASE_HEIGHT = fm.getMaxAscent();
selection = new Selection(null);
MultiLineToolTipUI.initialize();
setToolTipText("");
buttonGroup.add(cbStackView);
buttonGroup.add(cbPairedStackView);
buttonGroup.add(cbStrandStackView);
buttonGroup.add(cbIsizeStackView);
buttonGroup.add(cbCoverageView);
buttonGroup.add(cbCoverageStrandView);
buttonGroup.add(cbCoverageHeatMap);
addMouseListener(new PopupListener());
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);
addBamToPanel(frame);
}
public String getToolTipText()
{
if(isCoverageView(getPixPerBaseByWidth()) && lastMousePoint != null)
return coverageView.getToolTipText(
lastMousePoint.y-getJspView().getViewport().getViewPosition().y);
if(mouseOverSAMRecord == null)
return null;
String msg =
mouseOverSAMRecord.sam.getReadName() + "\n" +
mouseOverSAMRecord.sam.getAlignmentStart() + ".." +
mouseOverSAMRecord.sam.getAlignmentEnd() + "\nisize=" +
mouseOverSAMRecord.sam.getInferredInsertSize() + "\nmapq=" +
mouseOverSAMRecord.sam.getMappingQuality()+"\nrname="+
mouseOverSAMRecord.sam.getReferenceName();
if( mouseOverSAMRecord.sam.getReadPairedFlag() &&
mouseOverSAMRecord.sam.getProperPairFlag() &&
!mouseOverSAMRecord.sam.getMateUnmappedFlag())
{
msg = msg +
"\nstrand (read/mate): "+
(mouseOverSAMRecord.sam.getReadNegativeStrandFlag() ? "-" : "+")+" / "+
(mouseOverSAMRecord.sam.getMateNegativeStrandFlag() ? "-" : "+");
}
else
msg = msg +
"\nstrand (read/mate): "+
(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());
}
else
{
bamIndexFile = new File(bam + ".bai");
if(!bamIndexFile.exists())
{
final File cramIndexFile = new File(bam + ".crai");
if(cramIndexFile.exists())
return cramIndexFile;
}
}
return bamIndexFile;
}
/**
* 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);
File bamIndexFile = getBamIndexFile(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);
}
}
final SAMFileReader samFileReader;
if(feature_display != null && bam.endsWith("cram"))
{
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");
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"))
{
File bamFile = new File(bam);
samFileReader = new SAMFileReader(bamFile, bamIndexFile);
}
else
{
final URL urlBamFile = new URL(bam);
samFileReader = new SAMFileReader(urlBamFile, bamIndexFile, false);
}
samFileReader.setValidationStringency(ValidationStringency.SILENT);
samFileReaderHash.put(bam, samFileReader);
return samFileReader;
}
private void readHeaderPicard() throws IOException
{
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());
}
}
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();
}
}
}
/**
* Read a SAM or BAM file.
* @throws IOException
*/
private void readFromBamPicard(int start, int end, short bamIndex, float pixPerBase)
throws IOException
{
// Open the input file. Automatically detects whether input is SAM or BAM
// and delegates to a reader implementation for the appropriate format.
final String bam = bamList.get(bamIndex);
final SAMFileReader inputSam = getSAMFileReader(bam);
//final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
if(isConcatSequences())
{
for(String seq: seqNames)
{
int sLen = seqLengths.get(seq);
int offset = getSequenceOffset(seq);
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);
}
//inputSam.close();
}
/**
* 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 checkMemAfter = 8000;
final int seqOffset = getSequenceOffset(refName);
final int offset = seqOffset- getBaseAtStartOfView();
final boolean isCoverageView = isCoverageView(pixPerBase);
int cnt = 0;
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
{
while ( it.hasNext() )
{
try
{
cnt++;
SAMRecord samRecord = it.next();
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)
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);
if(isCoverage)
coveragePanel.addRecord(samRecord, offset, bam);
if(isSNPplot)
snpPanel.addRecord(samRecord, seqOffset);
if(!isCoverageView)
readsInView.add(new BamViewRecord(samRecord, bamIndex));
}
}
if(cnt > checkMemAfter)
{
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();
}
}
private int getSequenceLength()
{
if(isConcatSequences())
{
int len = 0;
for(String seq: seqNames)
len += seqLengths.get(seq);
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(!isConcatSequences())
return 0;
if(offsetLengths == null)
{
/* offsetLengths = new Hashtable<String, Integer>(combo.getItemCount());
int offset = 0;
for(int i=0; i<combo.getItemCount(); i++)
{
String thisSeqName = (String) combo.getItemAt(i);
offsetLengths.put(thisSeqName, offset);
offset += seqLengths.get(combo.getItemAt(i));
}*/
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);
}
});
//concatSequences = false;
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);
}
/**
* Override
*/
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
mouseOverSAMRecord = null;
final int seqLength = getSequenceLength();
int start;
int end;
if(startBase > 0)
start = startBase;
else
start = getBaseAtStartOfView();
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();
}
final float pixPerBase = getPixPerBaseByWidth();
boolean changeToStackView = false;
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
if(laststart != start ||
lastend != end ||
CoveragePanel.isRedraw())
{
if(isCoverageView(pixPerBase))
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)
{
cbStackView.setSelected(true);
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());
}
}
}
laststart = start;
lastend = end;
// this needs to be synchronized when cloning BAM window
synchronized(this)
{
if(showBaseAlignment)
drawBaseAlignment(g2, seqLength, pixPerBase, start, end);
else
{
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(waitingFrame.isVisible())
waitingFrame.hideFrame();
if(changeToStackView)
{
popFrame.show(
"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.",
mainPanel,
15000);
}
}
private float getPixPerBaseByWidth()
{
return (float)mainPanel.getWidth() / (float)nbasesInView;
}
private int getMaxBasesInPanel(int seqLength)
{
if(feature_display == null)
return seqLength+nbasesInView/3;
else
return seqLength+nbasesInView;
}
/**
* 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,
int end)
{
ruler.start = start;
ruler.end = end;
int ypos = 0;
String refSeq = null;
int refSeqStart = start;
end = start + ( mainPanel.getWidth() * ALIGNMENT_PIX_PER_BASE );
if(bases != null)
{
try
{
int seqEnd = end+2;
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));
boolean drawn[] = new boolean[readsInView.size()];
for(int i=0; i<readsInView.size(); i++)
drawn[i] = false;
Rectangle r = jspView.getViewport().getViewRect();
int nreads = readsInView.size();
for (int i = 0; i < nreads; i++)
{
try
{
if (!drawn[i])
{
ypos += 11;
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();
if (thisEnd == 0)
thisEnd = thisRead.sam.getAlignmentStart() + thisRead.sam.getReadLength();
for (int j = i + 1; j < nreads; j++)
{
if (!drawn[j])
{
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();
if (thisEnd == 0)
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();
}
}
if(ypos > getHeight())
{
Dimension d = getPreferredSize();
d.setSize(getPreferredSize().getWidth(), ypos);
setPreferredSize(d);
revalidate();
}
}
/**
* Draw the query sequence
* @param g2
* @param read
* @param pixPerBase
* @param ypos
*/
private void drawSequence(Graphics2D g2, 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
g2.setColor(Color.black);
else
g2.setColor(Color.blue);
final Color col = g2.getColor();
int xpos;
int len = 0;
int refPos = 0;
String readSeq = samRecord.getReadString();
int offset = getSequenceOffset(samRecord.getReferenceName());
byte[] phredQuality = null;
if(baseQualityColour.isSelected())
phredQuality = samRecord.getBaseQualities();
Hashtable<Integer, String> insertions = null;
List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks();
for(int i=0; i<blocks.size(); i++)
{
AlignmentBlock block = blocks.get(i);
int blockStart = block.getReadStart();
len += block.getLength();
for(int j=0; j<block.getLength(); j++)
{
int readPos = blockStart-1+j;
xpos = block.getReferenceStart() - 1 + j + offset;
refPos = xpos - refSeqStart + 1;
if(phredQuality != null)
setColourByBaseQuality(g2, phredQuality[readPos]);
if(isSNPs && refSeq != null && refPos > 0 && refPos < refSeq.length())
{
if(readSeq.charAt(readPos) != refSeq.charAt(refPos))
g2.setColor(Color.red);
else
g2.setColor(col);
}
g2.drawString(readSeq.substring(readPos, readPos+1),
refPos*ALIGNMENT_PIX_PER_BASE, ypos);
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>();
g2.setColor(DEEP_PINK);
int xscreen = (refPos+1)*ALIGNMENT_PIX_PER_BASE;
insertions.put(xscreen,
(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()))
{
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;
int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
refPos = blocks.get(blocks.size()-1).getReferenceStart()+
blocks.get(blocks.size()-1).getLength()+offset-refSeqStart;
int xend = (refPos+len)*ALIGNMENT_PIX_PER_BASE;
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)
g2.setColor(DARK_GREEN);
else if (baseQuality < 30)
g2.setColor(DARK_ORANGE);
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);
if(isShowScale())
drawScale(g2, start, end, pixPerBase, getHeight());
final Stroke stroke =
new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
g2.setStroke(stroke);
final int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
for(int i=0; i<readsInView.size(); i++)
{
BamViewRecord bamViewRecord = readsInView.get(i);
SAMRecord samRecord = bamViewRecord.sam;
BamViewRecord bamViewNextRecord = null;
SAMRecord samNextRecord = null;
List<Integer> snps = getSNPs(samRecord);
if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped
{
if(isSingle)
{
int ypos = getYPos(scaleHeight, samRecord.getReadString().length()); // (getHeight() - scaleHeight) - samRecord.getReadString().length();
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
g2.setColor(Color.black);
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
}
continue;
}
int ypos = getYPos(scaleHeight, Math.abs(samRecord.getInferredInsertSize()));
if( (ypos > r.getMaxY() || ypos < r.getMinY()) && ypos > 0 )
continue;
if(i < readsInView.size()-1)
{
bamViewNextRecord = readsInView.get(++i);
samNextRecord = bamViewNextRecord.sam;
if(samRecord.getReadName().equals(samNextRecord.getReadName()))
{
// draw connection between paired reads
if(samRecord.getAlignmentEnd() < samNextRecord.getAlignmentStart() &&
(samNextRecord.getAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
{
g2.setColor(Color.LIGHT_GRAY);
int offset1 = getSequenceOffset(samRecord.getReferenceName());
int end1 = samRecord.getAlignmentEnd()+offset1-baseAtStartOfView;
int offset2 = getSequenceOffset(samNextRecord.getReferenceName());
int start2 = samNextRecord.getAlignmentStart()+offset2-baseAtStartOfView;
drawTranslucentLine(g2,
(int)(end1*pixPerBase), (int)(start2*pixPerBase), ypos);
}
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(bamViewRecord));
else if( (samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
samNextRecord.getReadNegativeStrandFlag()) ||
(!samRecord.getReadNegativeStrandFlag() &&
!samNextRecord.getReadNegativeStrandFlag()))
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
drawRead(g2, bamViewNextRecord, pixPerBase, ypos, baseAtStartOfView, getSNPs(samNextRecord));
}
else
{
drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps);
i--;
}
}
else
{
drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps);
}
}
drawYScale(g2, scaleHeight);
}
private int getYPos(int scaleHeight, int size)
{
if(!logScale)
return (getHeight() - scaleHeight) - size;
else
{
int logInfSize = (int)( Math.log(size) * 100);
return (getHeight() - scaleHeight) - logInfSize;
}
}
/**
* 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 drawStackView(Graphics2D g2,
final int seqLength,
final float pixPerBase,
final int start,
final int end)
{
drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);
if(isShowScale())
drawScale(g2, start, end, pixPerBase, getHeight());
final BasicStroke stroke = new BasicStroke(
1.3f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
g2.setStroke(stroke);
final int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int ypos = (getHeight() - scaleHeight);
int ydiff = 2;
if(isOrientation)
ydiff= 4;
int maxEnd = 0;
int lstStart = 0;
int lstEnd = 0;
final int baseAtStartOfView = getBaseAtStartOfView();
g2.setColor(Color.blue);
final Rectangle r = jspView.getViewport().getViewRect();
for(BamViewRecord bamViewRecord: readsInView)
{
SAMRecord samRecord = bamViewRecord.sam;
int offset = getSequenceOffset(samRecord.getReferenceName());
int recordStart = samRecord.getAlignmentStart()+offset;
int recordEnd = samRecord.getAlignmentEnd()+offset;
List<Integer> snps = getSNPs(samRecord);
if(colourByCoverageColour.isSelected() ||
lstStart != recordStart || lstEnd != recordEnd || snps != null)
{
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(bamViewRecord));
else if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped
g2.setColor(Color.black);
else
g2.setColor(Color.blue);
if(maxEnd < recordStart || ypos < 0)
{
ypos = (getHeight() - scaleHeight)-ydiff;
maxEnd = recordEnd+2;
}
else
ypos = ypos-ydiff;
}
else
g2.setColor(DARK_GREEN);
if(snps != null)
lstStart = -1;
else
{
lstStart = recordStart;
lstEnd = recordEnd;
}
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
}
}
/**
* 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);
final BasicStroke stroke = new BasicStroke(
1.3f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
final int scaleHeight = 15;
drawScale(g2, start, end, pixPerBase, ((getHeight()+scaleHeight)/2));
int ymid = (getHeight()/ 2);
int ydiff = 2;
if(isOrientation)
ydiff= 4;
// positive strand
drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -ydiff, pixPerBase, stroke);
// negative strand
drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), ydiff, pixPerBase, stroke);
}
private void drawStrand(Graphics2D g2,
boolean isStrandNegative,
int scaleHeight,
int ymid,
int ystep,
float pixPerBase,
Stroke stroke)
{
int hgt = getHeight();
int ypos = (hgt - scaleHeight);
int maxEnd = 0;
int lstStart = 0;
int lstEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
g2.setColor(Color.blue);
Rectangle r = jspView.getViewport().getViewRect();
for(BamViewRecord bamViewRecord: readsInView)
{
SAMRecord samRecord = bamViewRecord.sam;
if( samRecord.getReadNegativeStrandFlag() == isStrandNegative )
{
final int offset = getSequenceOffset(samRecord.getReferenceName());
final int recordStart = samRecord.getAlignmentStart()+offset;
final int recordEnd = samRecord.getAlignmentEnd()+offset;
List<Integer> snps = getSNPs(samRecord);
if(colourByCoverageColour.isSelected() ||
lstStart != recordStart || lstEnd != recordEnd || snps != null)
{
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(bamViewRecord));
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
g2.setColor(DARK_GREEN);
if(snps != null)
lstStart = -1;
else
{
lstStart = recordStart;
lstEnd = recordEnd;
}
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
}
}
}
/**
* 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())
drawScale(g2, start, end, pixPerBase, getHeight());
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 (1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
Stroke stroke =
new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
g2.setStroke(stroke);
final int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
int ydiff = 3;
if(isOrientation)
ydiff= 5;
int ypos = getHeight() - scaleHeight - ydiff;
int lastEnd = 0;
int baseAtStartOfView = getBaseAtStartOfView();
Rectangle r = jspView.getViewport().getViewRect();
for(PairedRead pr: pairedReads)
{
if(pr.sam1.sam.getAlignmentStart() > lastEnd)
{
ypos = getHeight() - scaleHeight - ydiff;
if(pr.sam2 != null)
lastEnd = pr.sam2.sam.getAlignmentEnd();
else
lastEnd = pr.sam1.sam.getAlignmentEnd();
}
else
ypos = ypos - ydiff;
if(ypos > r.getMaxY() || ypos < r.getMinY())
continue;
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()))
{
final int prStart, prEnd;
if(pr.sam1.sam.getAlignmentStart() > pr.sam1.sam.getMateAlignmentStart())
{
prStart = pr.sam1.sam.getMateAlignmentStart();
prEnd = pr.sam1.sam.getAlignmentStart();
}
else
{
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( 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));
if(pr.sam2 != null)
drawRead(g2, pr.sam2, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam2.sam));
}
}
/**
* 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 )
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);
coverageView.draw(g2, getWidth(), hgt, hideBamList);
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)
{
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();
}
if(samRecord.getInferredInsertSize() == 0)
{
offTheTop = true;
ypos = getHeight() - scaleHeight - 5;
}
if(samRecord.getInferredInsertSize() != 0 &&
Math.abs(samRecord.getMateAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
{
g2.setColor(Color.LIGHT_GRAY);
if(samRecord.getAlignmentEnd() < samRecord.getMateAlignmentStart())
{
int nextStart =
(int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
drawTranslucentLine(g2,
(int)((thisEnd-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
}
else
{
int nextStart =
(int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
drawTranslucentLine(g2,
(int)((thisStart-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
}
}
if(colourByCoverageColour.isSelected())
g2.setColor(getColourByCoverageColour(bamViewRecord));
else if(offTheTop)
g2.setColor(DARK_ORANGE);
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);
/*if (isSNPs)
showSNPsOnReads(g2, samRecord, pixPerBase, ypos, offset);*/
}
private void drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos)
{
g2.setColor(Color.black);
g2.drawLine( 0, ypos-14,
(int)((end - getBaseAtStartOfView())*pixPerBase), ypos-14);
int interval = end-start;
if(interval > 256000)
drawTicks(g2, start, end, pixPerBase, 512000, ypos);
else if(interval > 64000)
drawTicks(g2, start, end, pixPerBase, 12800, ypos);
else if(interval > 16000)
drawTicks(g2, start, end, pixPerBase, 3200, ypos);
else if(interval > 4000)
drawTicks(g2, start, end, pixPerBase, 800, ypos);
else if(interval > 1000)
drawTicks(g2, start, end, pixPerBase, 400, ypos);
else
drawTicks(g2, start, end, pixPerBase, 100, ypos);
}
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;
int sm = markStart-(division/2);
float x;
if(sm > start)
{
x = (sm-getBaseAtStartOfView())*pixPerBase;
g2.drawLine((int)x, ypos-14,(int)x, ypos-12);
}
for(int m=markStart; m<end; m+=division)
{
x = (m-getBaseAtStartOfView())*pixPerBase;
g2.drawString(Integer.toString(m), x, ypos-1);
g2.drawLine((int)x, ypos-14,(int)x, ypos-11);
sm = m+(division/2);
if(sm < end)
{
x = (sm-getBaseAtStartOfView())*pixPerBase;
g2.drawLine((int)x, ypos-14,(int)x, ypos-12);
}
if(m == 1)
m = 0;
}
}
/**
* Draw a y-scale for inferred size (isize) of reads.
* @param g2
* @param xScaleHeight
*/
private void drawYScale(Graphics2D g2, int xScaleHeight)
{
g2.setColor(Color.black);
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 baseAtStartOfView
* @param snps
*/
private void drawRead(Graphics2D g2,
final BamViewRecord bamViewRecord,
final float pixPerBase,
final int ypos,
final int baseAtStartOfView,
final List<Integer> snps)
{
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 (3.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
g2.setStroke(stroke);
Color c = g2.getColor();
g2.setColor(Color.black);
g2.drawLine((int)( thisStart * pixPerBase), ypos,
(int)( thisEnd * pixPerBase), ypos);
g2.setColor(c);
g2.setStroke(originalStroke);
}
if(thisRead.getCigar().getCigarElements().size() == 1)
g2.drawLine((int)( thisStart * pixPerBase), ypos,
(int)( thisEnd * pixPerBase), ypos);
else
{
List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
Color c = g2.getColor();
int lastEnd = 0;
for(int i=0; i<blocks.size(); i++)
{
AlignmentBlock block = blocks.get(i);
int blockStart = block.getReferenceStart()+offset-baseAtStartOfView;
int blockEnd = blockStart + block.getLength() - 1;
g2.drawLine((int)( blockStart * pixPerBase), ypos,
(int)( blockEnd * pixPerBase), ypos);
if(i > 0 && blockStart != lastEnd)
{
g2.setColor(Color.gray);
g2.drawLine((int)( blockStart * pixPerBase), ypos,
(int)( lastEnd * pixPerBase), ypos);
g2.setColor(c);
}
lastEnd = blockEnd;
}
}
if(isOrientation)
drawArrow(g2, thisRead, thisStart, thisEnd, pixPerBase, ypos);
// test if the mouse is over this read
if(lastMousePoint != null)
{
if(lastMousePoint.getY()+2 > ypos && lastMousePoint.getY()-2 < ypos)
if(lastMousePoint.getX() > thisStart * pixPerBase &&
lastMousePoint.getX() < thisEnd * pixPerBase)
{
mouseOverSAMRecord = bamViewRecord;
}
}
if (isSNPs && snps != null)
showSNPsOnReads(snps, g2, pixPerBase, ypos);
}
/**
* Draw arrow on the read to indicate orientation.
* @param g2
* @param thisRead
* @param thisStart
* @param thisEnd
* @param pixPerBase
* @param ypos
*/
private void drawArrow(Graphics2D g2,
SAMRecord thisRead,
int thisStart,
int thisEnd,
float pixPerBase,
int ypos)
{
if(thisRead.getReadNegativeStrandFlag())
{
int apos = ypos + 2;
g2.drawLine((int)( (thisStart+5) * pixPerBase), apos,
(int)( thisStart * pixPerBase), ypos);
}
else
{
int apos = ypos - 2;
g2.drawLine((int)( (thisEnd-5) * pixPerBase), apos,
(int)( thisEnd * pixPerBase), ypos);
}
}
/**
* 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());
}
}
}
/**
* Draw a translucent line
* @param g2
* @param start
* @param end
* @param ypos
*/
private void drawTranslucentLine(Graphics2D g2, int start, int end, int ypos)
{
Composite origComposite = g2.getComposite();
g2.setComposite(translucent);
g2.drawLine(start, ypos, end, ypos);
g2.setComposite(origComposite);
}
/**
* Draw a translucent line
* @param g2
* @param start
* @param end
* @param ypos
*/
private void drawTranslucentJointedLine(Graphics2D g2, int start, int end, int ypos)
{
Composite origComposite = g2.getComposite();
g2.setComposite(translucent);
int mid = (int) ((end-start)/2.f)+start;
//g2.drawLine(start, ypos, end, ypos);
g2.drawLine(start, ypos, mid, ypos-5);
g2.drawLine(mid, ypos-5, end, ypos);
g2.setComposite(origComposite);
}
/**
* Display the SNPs for the given read.
* @param snps
* @param g2
* @param pixPerBase
* @param ypos
*/
private void showSNPsOnReads(final List<Integer> snps,
final Graphics2D g2,
float pixPerBase, int ypos)
{
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
try
{
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();
for(AlignmentBlock block: blocks)
{
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]) != readSeq[readPos])
{
if(snps == null)
snps = new ArrayList<Integer>();
snps.add(refPos+offset);
}
}
}
}
catch (OutOfRangeException e)
{
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 frame
* @param autohide automatically hide the top panel containing the buttons
* @param feature_display
*/
private void addBamToPanel(final JFrame frame)
{
final JComponent topPanel = bamTopPanel(frame);
mainPanel.setPreferredSize(new Dimension(900, 400));
setDisplay(1, nbasesInView, null);
mainPanel.setLayout(new BorderLayout());
if(topPanel instanceof JPanel)
mainPanel.add(topPanel, BorderLayout.NORTH);
mainPanel.add(jspView, BorderLayout.CENTER);
JPanel bottomPanel = new JPanel(new BorderLayout());
coveragePanel = new CoveragePanel(this);
bottomPanel.add(coveragePanel, BorderLayout.CENTER);
//
snpPanel = new SnpPanel(this, bases);
bottomPanel.add(snpPanel, BorderLayout.NORTH);
if(feature_display == null)
{
scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 1, nbasesInView, 1,
getMaxBasesInPanel(getSequenceLength()));
scrollBar.setUnitIncrement(nbasesInView/20);
scrollBar.addAdjustmentListener(new AdjustmentListener()
{
public void adjustmentValueChanged(AdjustmentEvent e)
{
repaint();
if(isSNPplot)
snpPanel.repaint();
if(isCoverage)
coveragePanel.repaint();
}
});
bottomPanel.add(scrollBar, BorderLayout.SOUTH);
}
else
{
if(!isConcatSequences())
{
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 "+
seqNames.get(newIndex)+" so this has been set as the default.")
};
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);
mainPanel.revalidate();
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())
hideBamList.remove(new Short(thisBamIndex));
else
hideBamList.add(new Short(thisBamIndex));
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
*/
private ImageIcon getImageIcon(Color c)
{
BufferedImage image = (BufferedImage)this.createImage(10, 10);
Graphics2D g2 = image.createGraphics();
g2.setColor(c);
g2.fillRect(0, 0, 10, 10);
return new ImageIcon(image);
}
private void createMenus(JComponent menu)
{
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));
laststart = -1;
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 ...");
analyse.add(readCount);
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());
}
});
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());
}
});
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());
JMenu viewMenu = new JMenu("Views");
cbStackView.setFont(viewMenu.getFont());
cbIsizeStackView.setFont(viewMenu.getFont());
cbPairedStackView.setFont(viewMenu.getFont());
cbStrandStackView.setFont(viewMenu.getFont());
cbCoverageView.setFont(viewMenu.getFont());
cbCoverageStrandView.setFont(viewMenu.getFont());
cbCoverageHeatMap.setFont(viewMenu.getFont());
baseQualityColour.setFont(viewMenu.getFont());
colourByCoverageColour.setFont(viewMenu.getFont());
markInsertions.setFont(viewMenu.getFont());
cbIsizeStackView.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
laststart = -1;
logMenuItem.setEnabled(isIsizeStackView());
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
repaint();
}
});
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);
repaint();
}
});
viewMenu.add(cbStackView);
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(isStrandStackView())
setViewportMidPoint();
if(cbStrandStackView.isSelected())
logMenuItem.setEnabled(false);
getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
repaint();
}
});
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);
menu.add(viewMenu);
final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNP marks");
//
JMenu colourMenu = new JMenu("Colour By");
colourMenu.add(colourByCoverageColour);
baseQualityColour.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(baseQualityColour.isSelected())
{
checkBoxSNPs.setSelected(false);
isSNPs = false;
}
repaint();
}
});
colourMenu.add(baseQualityColour);
menu.add(colourMenu);
//
JMenu showMenu = new JMenu("Show");
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);
menu.add(showMenu);
//
JMenu graphMenu = new JMenu("Graph");
JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage");
checkBoxCoverage.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
isCoverage = !isCoverage;
coveragePanel.setVisible(isCoverage);
if( isCoverage &&
!cbCoverageView.isSelected() &&
!cbCoverageStrandView.isSelected() &&
!cbCoverageHeatMap.isSelected())
laststart = -1;
repaint();
}
});
graphMenu.add(checkBoxCoverage);
JCheckBoxMenuItem checkBoxSNP = new JCheckBoxMenuItem("SNP");
checkBoxSNP.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
isSNPplot = !isSNPplot;
snpPanel.setVisible(isSNPplot);
laststart = -1;
repaint();
}
});
graphMenu.add(checkBoxSNP);
menu.add(graphMenu);
if(feature_display != null)
{
final JCheckBoxMenuItem checkBoxSync =
new JCheckBoxMenuItem("Asynchronous", asynchronous);
checkBoxSync.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
asynchronous = checkBoxSync.isSelected();
}
});
menu.add(checkBoxSync);
}
menu.add(new JSeparator());
JMenu maxHeightMenu = new JMenu("BamView Height");
menu.add(maxHeightMenu);
final String hgts[] =
{"500", "800", "1000", "1500", "2500", "5000", "50000"};
ButtonGroup bgroup = new ButtonGroup();
for(int i=0; i<hgts.length; i++)
{
final String hgt = hgts[i];
final JCheckBoxMenuItem maxHeightMenuItem = new JCheckBoxMenuItem(hgt);
bgroup.add(maxHeightMenuItem);
maxHeightMenuItem.setSelected(hgts[i].equals(Integer.toString(maxHeight)));
maxHeightMenu.add(maxHeightMenuItem);
maxHeightMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(maxHeightMenuItem.isSelected())
maxHeight = Integer.parseInt(hgt);
int start = getBaseAtStartOfView();
setDisplay(start, nbasesInView+start, null);
}
});
}
menu.add(new JSeparator());
logMenuItem.setFont(menu.getFont());
menu.add(logMenuItem);
logMenuItem.setEnabled(isIsizeStackView());
logMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
logScale = logMenuItem.isSelected();
repaint();
}
});
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);
}
});
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)
{
new SAMRecordList(BamView.this);
}
});
final JMenuItem bamSplitter = new JMenuItem("Clone window");
bamSplitter.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
cloneBamView();
}
});
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);
}
private JComponent bamTopPanel(final JFrame frame)
{
final JComponent topPanel;
if(frame == null)
{
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)
{
String[] s = { "NEW-BAMVIEW" };
BamView.main(s);
}
});
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());
}
});
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();
int thisHgt = HEIGHT;
if (thisHgt < 5)
thisHgt = 15;
int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY());
if (y < thisHgt)
{
topPanel.setVisible(true);
}
else
{
if (buttonAutoHide.isSelected())
topPanel.setVisible(false);
}
mainPanel.repaint();
mainPanel.revalidate();
}
};
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);
}
};
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);
if(feature_display != null)
{
JButton close = new JButton("Close");
topPanel.add(close);
close.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
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);
}
}
});
}
return topPanel;
}
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());
}
protected int getBaseAtStartOfView()
{
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
*/
private void setZoomLevel(final int nbasesInView)
{
int startValue = getBaseAtStartOfView();
this.nbasesInView = nbasesInView;
float pixPerBase = getPixPerBaseByWidth();
if(isBaseAlignmentView(pixPerBase))
{
pixPerBase = ALIGNMENT_PIX_PER_BASE;
this.nbasesInView = (int)(mainPanel.getWidth()/pixPerBase);
jspView.getVerticalScrollBar().setValue(0);
if(ruler == null)
ruler = new Ruler();
jspView.setColumnHeaderView(ruler);
showBaseAlignment = true;
baseQualityColour.setEnabled(true);
markInsertions.setEnabled(true);
}
else if(jspView != null)
{
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;
}
jspView.setColumnHeaderView(null);
if(!isStrandStackView())
setViewportBtm();
else
setViewportMidPoint();
showBaseAlignment = false;
baseQualityColour.setEnabled(false);
markInsertions.setEnabled(false);
}
if(scrollBar != null)
{
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;
this.nbasesInView = end-start+1;
lastMousePoint = null;
float pixPerBase;
if(jspView.getViewport().getViewRect().width > 0)
pixPerBase = getPixPerBaseByWidth();
else
{
if(feature_display == null)
pixPerBase = 1000.f/(float)(end-start+1);
else
pixPerBase = feature_display.getWidth()/(float)(end-start+1);
}
Dimension d = new Dimension();
d.setSize(nbasesInView*pixPerBase, maxHeight);
setPreferredSize(d);
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();
if(bases == null)
{
entry = new Entry(new_embl_entry);
bases = entry.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.
**/
private void handleCanvasMouseDrag(final MouseEvent event)
{
if(event.getButton() == MouseEvent.BUTTON3 || bases == null)
return;
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 seqLength = getSequenceLength();
float pixPerBase = getPixPerBaseByWidth();
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;
MarkerRange drag_range;
try
{
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();
}
catch (OutOfRangeException e)
{
e.printStackTrace();
}
}
/**
* 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;
if(bamList.size()>1)
fileIndex = samRecord.bamIndex;
return getColourByCoverageColour(fileIndex);
}
private Color getColourByCoverageColour(final short fileIndex)
{
LineAttributes lines[] = CoveragePanel.getLineAttributes(bamList.size());
return lines[fileIndex].getLineColour();
}
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();
}
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;
return cbCoverageStrandView;
}
protected Selection getSelection()
{
return selection;
}
protected List<BamViewRecord> getReadsInView()
{
return readsInView;
}
protected int getBasesInView()
{
return nbasesInView;
}
protected void setHighlightSAMRecord(BamViewRecord highlightSAMRecord)
{
this.highlightSAMRecord = highlightSAMRecord;
}
protected BamViewRecord getHighlightSAMRecord()
{
return highlightSAMRecord;
}
protected FeatureDisplay getFeatureDisplay()
{
return feature_display;
}
/**
* @return the combo
*/
public SequenceComboBox getCombo()
{
return combo;
}
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;
}
/**
* Open another BamView window
*/
public void cloneBamView()
{
BamView bamView = new BamView(new Vector<String>(bamList),
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);
}
}
/**
* Artemis event notification
*/
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;
}
SwingWorker worker = new SwingWorker()
{
public Object construct()
{
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if(event.getStart() != ((FeatureDisplay)event.getSource()).getForwardBaseAtLeftEdge())
{
waitingFrame.showWaiting("waiting...", mainPanel);
return null;
}
displayAdjustmentWork(event);
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();
}
private class Ruler extends JPanel
{
private static final long serialVersionUID = 1L;
protected int start;
protected int end;
protected String refSeq;
public Ruler()
{
super();
setPreferredSize(new Dimension(mainPanel.getWidth(), 26));
setBackground(Color.white);
}
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)
{
int xpos = (i-start)*ALIGNMENT_PIX_PER_BASE;
g2.drawString(Integer.toString(i), xpos, ypos);
}
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;
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)
{
dragStart = -1;
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e)
{
if(e.isPopupTrigger())
{
//
// main menu options
if(popup == null)
{
popup = new JPopupMenu();
createMenus(popup);
}
//
// coverage heatmap menu options
if(coverageMenu != null)
popup.remove(coverageMenu);
if(isCoverageView(getPixPerBaseByWidth()) && coverageView.isPlotHeatMap())
{
if(coverageMenu == null)
{
coverageMenu = new JMenu("Coverage HeatMap");
coverageView.createMenus(coverageMenu);
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()
{
private int n = 1;
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);
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);
}
if( mouseOverSAMRecord != null)
{
final BamViewRecord thisSAMRecord = mouseOverSAMRecord;
showDetails = new JMenuItem("Show details of : "+
thisSAMRecord.sam.getReadName());
showDetails.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
openFileViewer(thisSAMRecord.sam, getMate(thisSAMRecord), bamList);
}
});
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 thisSAMRecord,
final SAMRecord thisSAMRecordMate,
final FileViewer viewDetail,
final List<String> bamList)
{
if(bamList.size() > 1 && thisSAMRecord.getAttribute("FL") != null)
{
int bamIndex = (Integer)thisSAMRecord.getAttribute("FL");
if(bamIndex < bamList.size())
viewDetail.appendString("File "+bamList.get(bamIndex)+"\n\n", Level.INFO);
}
viewDetail.appendString("Read Name "+thisSAMRecord.getReadName()+"\n", Level.INFO);
viewDetail.appendString("Coordinates "+thisSAMRecord.getAlignmentStart()+".."+
thisSAMRecord.getAlignmentEnd()+"\n", Level.DEBUG);
viewDetail.appendString("Length "+thisSAMRecord.getReadLength()+"\n", Level.DEBUG);
viewDetail.appendString("Reference Name "+thisSAMRecord.getReferenceName()+"\n", Level.DEBUG);
viewDetail.appendString("Inferred Size "+thisSAMRecord.getInferredInsertSize()+"\n", Level.DEBUG);
viewDetail.appendString("Mapping Quality "+thisSAMRecord.getMappingQuality()+"\n", Level.DEBUG);
viewDetail.appendString("Cigar String "+thisSAMRecord.getCigarString()+"\n", Level.DEBUG);
viewDetail.appendString("Strand "+
(thisSAMRecord.getReadNegativeStrandFlag() ? "-\n\n" : "+\n\n"), Level.DEBUG);
if(thisSAMRecord.getReadPairedFlag() && !thisSAMRecord.getMateUnmappedFlag())
{
if(thisSAMRecordMate != null)
{
viewDetail.appendString("Mate Coordinates "+thisSAMRecordMate.getAlignmentStart()+".."+
thisSAMRecordMate.getAlignmentEnd()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Length "+thisSAMRecordMate.getReadLength()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Reference Name "+thisSAMRecordMate.getReferenceName()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Inferred Size "+thisSAMRecordMate.getInferredInsertSize()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Mapping Quality "+thisSAMRecordMate.getMappingQuality()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Cigar String "+thisSAMRecordMate.getCigarString()+"\n", Level.DEBUG);
}
else
{
viewDetail.appendString("Mate Start Coordinate "+thisSAMRecord.getMateAlignmentStart()+"\n", Level.DEBUG);
viewDetail.appendString("Mate Reference Name "+thisSAMRecord.getMateReferenceName()+"\n", Level.DEBUG);
}
viewDetail.appendString("Mate Strand "+
(thisSAMRecord.getMateNegativeStrandFlag() ? "-" : "+"), Level.DEBUG);
}
else
{
viewDetail.appendString("Mate Unmapped ", Level.DEBUG);
}
viewDetail.appendString("\n\nFlags:", Level.INFO);
viewDetail.appendString("\nDuplicate Read "+
(thisSAMRecord.getDuplicateReadFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nRead Paired "+
(thisSAMRecord.getReadPairedFlag() ? "yes" : "no"), Level.DEBUG);
if(thisSAMRecord.getReadPairedFlag())
{
viewDetail.appendString("\nFirst of Pair "+
(thisSAMRecord.getFirstOfPairFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nMate Unmapped "+
(thisSAMRecord.getMateUnmappedFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nProper Pair "+
(thisSAMRecord.getProperPairFlag() ? "yes" : "no"), Level.DEBUG);
}
viewDetail.appendString("\nRead Fails Vendor\nQuality Check "+
(thisSAMRecord.getReadFailsVendorQualityCheckFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\nRead Unmapped "+
(thisSAMRecord.getReadUnmappedFlag() ? "yes" : "no"), Level.DEBUG);
if(thisSAMRecord.getReadPairedFlag())
viewDetail.appendString("\nSecond Of Pair "+
(thisSAMRecord.getSecondOfPairFlag() ? "yes" : "no"), Level.DEBUG);
viewDetail.appendString("\n\nRead Bases:\n", Level.INFO);
// wrap the read bases
String seq = new String(thisSAMRecord.getReadBases());
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();
viewDetail.appendString(seq.substring(i, iend)+"\n", Level.DEBUG);
}
}
/**
* 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
thisSAMRecord.sam.getMateUnmappedFlag())
return null;
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)
{
e.printStackTrace();
}
return mate;
}
protected SAMRecordPredicate getSamRecordFlagPredicate()
{
return samRecordFlagPredicate;
}
protected void setSamRecordFlagPredicate(
SAMRecordPredicate samRecordFlagPredicate)
{
laststart = -1;
lastend = -1;
this.samRecordFlagPredicate = samRecordFlagPredicate;
}
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;
}
class CreateFeatures
{
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);
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);
}
}
public static void main(String[] args)
{
BamFrame frame = new BamFrame();
if(args.length == 0 && BamFrame.isMac())
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1) {}
if(frame.getBamFile() != null)
args = new String[]{ frame.getBamFile() };
}
List<String> bam = new Vector<String>();
String reference = null;
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");
bam = fileSelection.getFiles(BAM_SUFFIX);
reference = fileSelection.getReferenceFile();
if(reference == null || reference.equals(""))
reference = null;
if(bam == null || bam.size() < 1)
{
if(args.length > 0 && args[0].equals("NEW-BAMVIEW"))
return;
System.err.println("No files found.");
System.exit(0);
}
}
else if(!args[0].startsWith("-"))
{
for(int i=0; i< args.length; i++)
bam.add(args[i]);
}
int nbasesInView = 2000;
String chr = null;
String vw = null;
boolean orientation = false;
boolean covPlot = false;
boolean snpPlot = false;
int base = 0;
for(int i=0;i<args.length; i++)
{
if(args[i].equals("-a"))
{
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);
}
--i;
}
else if(args[i].equals("-r"))
reference = args[++i];
else if(args[i].equals("-n"))
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)");
System.exit(0);
}
}
final BamView view = new BamView(bam, reference, nbasesInView, null, null,
(JPanel)frame.getContentPane(), frame);
frame.setTitle("BamView v"+view.getVersion());
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){}
});*/
frame.pack();
view.jspView.getVerticalScrollBar().setValue(
view.jspView.getVerticalScrollBar().getMaximum());
frame.setVisible(true);
}
}