Newer
Older
/* JamView
*
* created: 2009
*
* This file is part of Artemis
*
* Copyright(C) 2009 Genome Research Limited
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or(at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package uk.ac.sanger.artemis.components.alignment;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import net.sf.samtools.AlignmentBlock;
import net.sf.samtools.SAMFileHeader;
import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.SAMSequenceRecord;
import net.sf.samtools.SAMFileReader.ValidationStringency;
import net.sf.samtools.util.CloseableIterator;
import uk.ac.sanger.artemis.Entry;
import uk.ac.sanger.artemis.EntryGroup;
import uk.ac.sanger.artemis.Options;
import uk.ac.sanger.artemis.Selection;
import uk.ac.sanger.artemis.SelectionChangeEvent;
import uk.ac.sanger.artemis.SelectionChangeListener;
import uk.ac.sanger.artemis.components.DisplayAdjustmentEvent;
import uk.ac.sanger.artemis.components.DisplayAdjustmentListener;
import uk.ac.sanger.artemis.components.FeatureDisplay;
import uk.ac.sanger.artemis.components.MessageDialog;
import uk.ac.sanger.artemis.io.EntryInformation;
import uk.ac.sanger.artemis.sequence.MarkerRange;
import uk.ac.sanger.artemis.sequence.NoSequenceException;
import uk.ac.sanger.artemis.util.Document;
import uk.ac.sanger.artemis.util.DocumentFactory;
import uk.ac.sanger.artemis.util.OutOfRangeException;
public class JamView extends JPanel
implements Scrollable, DisplayAdjustmentListener, SelectionChangeListener
private Hashtable<String, Integer> seqLengths = new Hashtable<String, Integer>();
private Vector<String> seqNames = new Vector<String>();
private String bam;
private FeatureDisplay feature_display;
private Selection selection;
private JPanel mainPanel;
private boolean showScale = true;
private int startBase = -1;
private int endBase = -1;
private Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR);
private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR);
private boolean showBaseAlignment = false;
/** Used to colour the frames. */
private Color light_grey = new Color(200, 200, 200);
public JamView(String bam,
String reference,
int nbasesInView)
{
super();
setBackground(Color.white);
this.bam = bam;
this.nbasesInView = nbasesInView;
if(reference != null)
{
try
{
getEntry(reference,entryGroup);
}
catch (NoSequenceException e)
{
e.printStackTrace();
}
}
if(PICARD)
readHeaderPicard();
else
readHeader();
//Options.getOptions().getFontUIResource();
//final javax.swing.plaf.FontUIResource font_ui_resource =
// new javax.swing.plaf.FontUIResource(getFont());
final javax.swing.plaf.FontUIResource font_ui_resource =
Options.getOptions().getFontUIResource();
while(keys.hasMoreElements())
{
Object key = keys.nextElement();
Object value = UIManager.get(key);
if(value instanceof javax.swing.plaf.FontUIResource)
UIManager.put(key, font_ui_resource);
}
setFont(Options.getOptions().getFont());
ALIGNMENT_PIX_PER_BASE = fm.charWidth('M');
selection = new Selection(null);
String samtoolCmd = "";
if(System.getProperty("samtoolDir") != null)
samtoolCmd = System.getProperty("samtoolDir");
String cmd[] = { samtoolCmd+File.separator+"samtools",
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
if(samtools.getProcessStderr() != null)
System.out.println(samtools.getProcessStderr());
String header = samtools.getProcessStdout();
StringReader samReader = new StringReader(header);
BufferedReader buff = new BufferedReader(samReader);
String line;
try
{
while((line = buff.readLine()) != null)
{
if(line.indexOf("LN:") > -1)
{
String parts[] = line.split("\t");
String name = "";
int seqLength = 0;
for(int i=0; i<parts.length; i++)
{
if(parts[i].startsWith("LN:"))
seqLength = Integer.parseInt( parts[i].substring(3) );
else if(parts[i].startsWith("SN:"))
name = parts[i].substring(3);
}
seqLengths.put(name, seqLength);
seqNames.add(name);
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
private void readHeaderPicard()
{
File bamFile = new File(bam);
File indexFile = new File(bam+".bai");
final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
SAMFileHeader header = inputSam.getFileHeader();
List<SAMSequenceRecord> readGroups = header.getSequenceDictionary().getSequences();
for(int i=0; i<readGroups.size(); i++)
{
seqLengths.put(readGroups.get(i).getSequenceName(),
readGroups.get(i).getSequenceLength());
seqNames.add(readGroups.get(i).getSequenceName());
}
inputSam.close();
}
/**
* Read data from BAM/SAM file for a region.
* @param start
* @param end
* @param pair_sort
*/
private void readFromBam(int start, int end)
String samtoolCmd = "";
if(System.getProperty("samtoolDir") != null)
samtoolCmd = System.getProperty("samtoolDir");
for(int i=0; i<cmd.length;i++)
System.out.print(cmd[i]+" ");
System.out.println();
else
readsInView.clear();
RunSamTools samtools = new RunSamTools(cmd, null, null, readsInView);
if(samtools.getProcessStderr() != null)
System.out.println(samtools.getProcessStderr());
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/**
* Read a SAM or BAM file.
*/
private void readFromBamPicard(int start, int end)
{
// Open the input file. Automatically detects whether input is SAM or BAM
// and delegates to a reader implementation for the appropriate format.
File bamFile = new File(bam);
File indexFile = new File(bam+".bai");
final SAMFileReader inputSam = new SAMFileReader(bamFile, indexFile);
inputSam.setValidationStringency(ValidationStringency.SILENT);
if(readsInView == null)
readsInView = new Vector<SAMRecord>();
else
readsInView.clear();
String refName = (String) combo.getSelectedItem();
CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end);
while ( it.hasNext() )
{
try
{
SAMRecord samRecord = it.next();
readsInView.add(samRecord);
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
inputSam.close();
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
String refName = (String) combo.getSelectedItem();
int seqLength = seqLengths.get(refName);
int start;
final int end;
if(startBase > 0)
start = startBase;
else
{
double x = jspView.getViewport().getViewRect().getX();
}
if(endBase > 0)
end = endBase;
else
{
int width = jspView.getViewport().getViewRect().width;
if(jspView.getVerticalScrollBar() != null)
width += jspView.getVerticalScrollBar().getWidth();
end = (int) (start + ((float)width / (float)pixPerBase));
}
System.out.println("paintComponent "+start+".."+end+" "+startBase+".."+endBase+" pixPerBase="+pixPerBase);
if(PICARD)
readFromBamPicard(start, end);
else
readFromBam(start, end);
Collections.sort(readsInView, new SAMRecordComparator());
}
catch(OutOfMemoryError ome)
{
JOptionPane.showMessageDialog(this, "Out of Memory");
return;
}
}
if(pixPerBase*2 >= ALIGNMENT_PIX_PER_BASE)
/**
* Draw the zoomed-in base view.
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
if(bases != null)
{
// draw the reference sequence
ypos+=11;
if(seqEnd > bases.getLength())
seqEnd = bases.getLength();
bases.getSubSequence(new Range(refSeqStart, seqEnd), Bases.FORWARD).toUpperCase();
int xpos = (refSeqStart-1)*ALIGNMENT_PIX_PER_BASE;
g2.setColor(light_grey);
g2.fillRect(xpos, ypos-11,
jspView.getViewport().getWidth()+(ALIGNMENT_PIX_PER_BASE*2), 11);
drawSelectionRange(g2, pixPerBase, start, end);
g2.setColor(Color.black);
g2.drawString(refSeq, xpos, ypos);
//for(int i=0;i<refSeq.length(); i++)
//{
//xpos = ((refSeqStart-1) + i)*ALIGNMENT_PIX_PER_BASE;
//g2.drawString(refSeq.substring(i, i+1), xpos, ypos);
//}
}
catch (OutOfRangeException e)
{
e.printStackTrace();
}
}
else
drawSelectionRange(g2, pixPerBase, start, end);
drawSequence(g2, thisRead, pixPerBase, ypos, refSeq, refSeqStart);
drawn[i] = true;
int thisEnd = thisRead.getAlignmentEnd();
if(thisEnd == 0)
thisEnd = thisRead.getAlignmentStart()+thisRead.getReadLength();
SAMRecord nextRead = readsInView.get(j);
if(nextRead.getAlignmentStart() > thisEnd+1)
drawSequence(g2, nextRead, pixPerBase, ypos, refSeq, refSeqStart);
drawn[j] = true;
thisEnd = nextRead.getAlignmentEnd();
if(thisEnd == 0)
thisEnd = nextRead.getAlignmentStart()+nextRead.getReadLength();
if(ypos > getHeight())
{
setPreferredSize(new Dimension(getWidth(), ypos));
revalidate();
}
/**
* Draw the query sequence
* @param g2
* @param read
* @param pixPerBase
* @param ypos
*/
private void drawSequence(Graphics2D g2, SAMRecord samRecord,
float pixPerBase, int ypos, String refSeq, int refSeqStart)
if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing
samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped
String readSeq = samRecord.getReadString();
List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks();
for(int i=0; i<blocks.size(); i++)
AlignmentBlock block = blocks.get(i);
for(int j=0; j<block.getLength(); j++)
int readPos = block.getReadStart()-1+j;
xpos = block.getReferenceStart()-1+j;
int refPos = xpos-refSeqStart+1;
if(checkBoxSNPs.isSelected() && 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), xpos*ALIGNMENT_PIX_PER_BASE, ypos);
/**
* Draw zoomed-out view.
* @param g2
* @param seqLength
* @param pixPerBase
* @param start
* @param end
*/
private void drawLineView(Graphics2D g2, int seqLength, float pixPerBase, int start, int end)
{
drawSelectionRange(g2, pixPerBase,start, end);
if(isShowScale())
drawScale(g2, start, end, pixPerBase);
Stroke originalStroke = new BasicStroke (1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
int scaleHeight;
if(isShowScale())
scaleHeight = 15;
else
scaleHeight = 0;
SAMRecord samRecord = readsInView.get(i);
SAMRecord samNextRecord = null;
if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing
int ypos = (getHeight() - scaleHeight) - samRecord.getReadString().length();
int ypos = (getHeight() - scaleHeight) - ( Math.abs(samRecord.getInferredInsertSize()) );
if(samRecord.getReadName().equals(samNextRecord.getReadName()))
if(samRecord.getAlignmentEnd() < samNextRecord.getAlignmentStart() &&
(samNextRecord.getAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
{
g2.setStroke(originalStroke);
g2.setColor(Color.LIGHT_GRAY);
g2.drawLine((int)(samRecord.getAlignmentEnd()*pixPerBase), ypos,
(int)(samNextRecord.getAlignmentStart()*pixPerBase), ypos);
if( samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
samNextRecord.getReadNegativeStrandFlag() )
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
drawRead(g2, samRecord, pixPerBase, stroke, ypos);
drawRead(g2, samNextRecord, pixPerBase, stroke, ypos);
drawLoneRead(g2, samRecord, ypos, pixPerBase, originalStroke, stroke);
drawLoneRead(g2, samRecord, ypos, pixPerBase, originalStroke, stroke);
/**
* Draw a read that apparently has a read mate that is not in view.
* @param g2
* @param thisRead
* @param ypos
* @param pixPerBase
* @param originalStroke
* @param stroke
*/
private void drawLoneRead(Graphics2D g2, SAMRecord samRecord, int ypos,
boolean drawLine = true;
int thisStart = samRecord.getAlignmentStart()-1;
int thisEnd = thisStart + samRecord.getReadString().length();
if(drawLine &&
(samRecord.getMateAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
{
g2.setStroke(originalStroke);
g2.setColor(Color.LIGHT_GRAY);
if(samRecord.getAlignmentEnd() < samRecord.getMateAlignmentStart())
{
int nextStart = (int) ((samRecord.getMateAlignmentStart()-1)*pixPerBase);
g2.drawLine((int)(thisStart*pixPerBase), ypos, nextStart, ypos);
}
else
{
int nextStart = (int) ((samRecord.getMateAlignmentStart()-1)*pixPerBase);
g2.drawLine((int)(thisEnd*pixPerBase), ypos, nextStart, ypos);
}
}
if(samRecord.getReadNegativeStrandFlag()) // strand of the query (1 for reverse)
g2.setColor(Color.red);
else
g2.setColor(Color.blue);
private void drawScale(Graphics2D g2, int start, int end, float pixPerBase)
{
g2.setColor(Color.black);
g2.drawLine( (int)(start*pixPerBase), getHeight()-14,
(int)(end*pixPerBase), getHeight()-14);
int interval = end-start;
if(interval > 256000)
drawTicks(g2, start, end, pixPerBase, 512000);
else if(interval > 64000)
drawTicks(g2, start, end, pixPerBase, 12800);
else if(interval > 16000)
drawTicks(g2, start, end, pixPerBase, 3200);
else if(interval > 4000)
drawTicks(g2, start, end, pixPerBase, 800);
else if(interval > 1000)
drawTicks(g2, start, end, pixPerBase, 400);
drawTicks(g2, start, end, pixPerBase, 100);
}
private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division)
{
int markStart = (Math.round(start/division)*division);
if(markStart < 1)
markStart = 1;
int sm = markStart-(division/2);
if(sm > start)
g2.drawLine((int)(sm*pixPerBase), getHeight()-14,(int)(sm*pixPerBase), getHeight()-12);
for(int m=markStart; m<end; m+=division)
{
g2.drawString(Integer.toString(m), m*pixPerBase, getHeight()-1);
g2.drawLine((int)(m*pixPerBase), getHeight()-14,(int)(m*pixPerBase), getHeight()-11);
sm = m+(division/2);
if(sm < end)
g2.drawLine((int)(sm*pixPerBase), getHeight()-14,(int)(sm*pixPerBase), getHeight()-12);
int thisStart = thisRead.getAlignmentStart()-1;
int thisEnd = thisRead.getAlignmentEnd();
g2.drawLine((int) (thisStart * pixPerBase), ypos,
(int) (thisEnd * pixPerBase), ypos);
if (checkBoxSNPs.isSelected())
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
/**
* Highlight a selected range
* @param g2
* @param pixPerBase
* @param start
* @param end
*/
private void drawSelectionRange(Graphics2D g2, float pixPerBase, int start, int end)
{
if(getSelection() != null)
{
Range selectedRange = getSelection().getSelectionRange();
if(selectedRange != null)
{
int rangeStart = selectedRange.getStart();
int rangeEnd = selectedRange.getEnd();
if(end < rangeStart || start > rangeEnd)
return;
int x = (int) (pixPerBase*(rangeStart-1));
int width = (int) (pixPerBase*(rangeEnd-rangeStart+1));
g2.setColor(Color.pink);
g2.fillRect(x, 0, width, getHeight());
}
}
}
/**
* Display the SNPs for the given read.
* @param g2
* @param thisRead
* @param pixPerBase
* @param ypos
*/
private void showSNPsOnReads(Graphics2D g2, SAMRecord thisRead,
float pixPerBase, int ypos)
{
int thisStart = thisRead.getAlignmentStart();
int thisEnd = thisRead.getAlignmentEnd();
// use alignment blocks of the contiguous alignment of
// subsets of read bases to a reference sequence
List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
char[] refSeq = bases.getSubSequenceC(
new Range(thisStart, thisEnd), Bases.FORWARD);
byte[] readSeq = thisRead.getReadBases();
for(int i=0; i<blocks.size(); i++)
AlignmentBlock block = blocks.get(i);
for(int j=0; j<block.getLength(); j++)
int readPos = block.getReadStart()-1+j;
int refPos = block.getReferenceStart()+j;
if (Character.toUpperCase(refSeq[refPos-thisStart]) != readSeq[readPos])
{
g2.drawLine((int) ((thisStart + i) * pixPerBase), ypos + 2,
(int) ((thisStart + i) * pixPerBase), ypos - 2);
}
g2.setColor(col);
}
catch (OutOfRangeException e)
{
e.printStackTrace();
/**
* Add the alignment view to the supplied <code>JPanel</code> in
* a <code>JScrollPane</code>.
* @param mainPanel panel to add the alignment to
* @param autohide automatically hide the top panel containing the buttons
*/
public void addJamToPanel(final JPanel mainPanel,
final boolean autohide,
final FeatureDisplay feature_display)
if(feature_display != null)
{
this.feature_display = feature_display;
this.selection = feature_display.getSelection();
}
final JPanel topPanel = new JPanel(new GridBagLayout());
// auto hide top panel
final JCheckBox buttonAutoHide = new JCheckBox("Auto-hide", autohide);
final MouseMotionListener mouseMotionListener = new MouseMotionListener()
handleCanvasMouseDragOrClick(event);
}
public void mouseMoved(MouseEvent e)
{
int thisHgt = HEIGHT;
if (thisHgt < 5)
thisHgt = 15;
int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY());
if (y < thisHgt)
if (!containsComponent(topPanel, mainPanel))
mainPanel.add(topPanel, BorderLayout.NORTH);
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
else
{
if (buttonAutoHide.isSelected() && containsComponent(topPanel, mainPanel))
mainPanel.remove(topPanel);
}
mainPanel.repaint();
mainPanel.revalidate();
}
};
addMouseMotionListener(mouseMotionListener);
combo = new JComboBox(seqNames);
combo.setEditable(false);
combo.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
laststart = -1;
lastend = -1;
setZoomLevel(JamView.this.nbasesInView);
}
});
gc.fill = GridBagConstraints.NONE;
gc.anchor = GridBagConstraints.NORTHWEST;
topPanel.add(combo, gc);
checkBoxSingle = new JCheckBox("Single Reads");
checkBoxSingle.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaint();
}
});
checkBoxSNPs = new JCheckBox("SNPs");
checkBoxSNPs.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (checkBoxSNPs.isSelected() && bases == null)
{
JOptionPane.showMessageDialog(null,
"No reference sequence supplied to identify SNPs.", "SNPs",
JOptionPane.INFORMATION_MESSAGE);
topPanel.add(checkBoxSNPs, gc);
if (feature_display == null)
final JTextField baseText = new JTextField(10);
JButton goTo = new JButton("GoTo:");
goTo.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
try
{
int basePosition = Integer.parseInt(baseText.getText());
goToBasePosition(basePosition);
}
catch (NumberFormatException nfe)
{
JOptionPane.showMessageDialog(JamView.this,
"Expecting a base number!", "Number Format",
JOptionPane.WARNING_MESSAGE);
}
});
topPanel.add(goTo, gc);
topPanel.add(baseText, gc);
JButton zoomIn = new JButton("+");
Insets ins = new Insets(0,0,0,0);
zoomIn.setMargin(ins);
zoomIn.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
int startBase = getBaseAtStartOfView();
setZoomLevel((int) (JamView.this.nbasesInView * 1.1));
goToBasePosition(startBase);
}
});
topPanel.add(zoomIn, gc);
JButton zoomOut = new JButton("-");
zoomOut.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
{
if (showBaseAlignment)
return;
int startBase = getBaseAtStartOfView();
setZoomLevel((int) (JamView.this.nbasesInView * .9));
goToBasePosition(startBase);
}
});
topPanel.add(zoomOut, gc);
}
jspView = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
setDisplay(1, nbasesInView, null);
mainPanel.setLayout(new BorderLayout());
mainPanel.add(topPanel, BorderLayout.NORTH);
mainPanel.add(jspView, BorderLayout.CENTER);
jspView.getVerticalScrollBar().setValue(
jspView.getVerticalScrollBar().getMaximum());
public void keyPressed(final KeyEvent event)
setZoomLevel((int) (JamView.this.nbasesInView * 1.1));
setZoomLevel((int) (JamView.this.nbasesInView * .9));
addMouseListener(new PopupListener());
public void focusGained(FocusEvent fe){}
public void focusLost(FocusEvent fe){}
}
/**
* Check to see if this component is contained by the display
* (FeatureDisplay) component.
* @return
*/
private boolean containsComponent(JPanel topPanel, JPanel mainPanel)
{
Component[] c = mainPanel.getComponents();
for(int i=0; i<c.length; i++)
if(c[i].equals(topPanel))
return true;
}
return false;
}
public void setVisible(boolean visible)
{
super.setVisible(visible);
mainPanel.setVisible(visible);
private int getBaseAtStartOfView()
{
String refName = (String) combo.getSelectedItem();
int seqLength = seqLengths.get(refName);
double x = jspView.getViewport().getViewRect().getX();
}
/**
* Set the panel size based on the number of bases visible
* and repaint.
* @param nbasesInView
*/
private void setZoomLevel(final int nbasesInView)
{
this.nbasesInView = nbasesInView;
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
System.out.println("setZoomLevel "+start);
String refName = (String) combo.getSelectedItem();
int seqLength = seqLengths.get(refName);
int width = jspView.getViewport().getViewRect().width;
if(jspView.getVerticalScrollBar() != null)
width += jspView.getVerticalScrollBar().getWidth();
float pixPerBase = ((float)width)/(float)(nbasesInView);
if(pixPerBase*2 > ALIGNMENT_PIX_PER_BASE)
{
pixPerBase = ALIGNMENT_PIX_PER_BASE;
checkBoxSingle.setVisible(false);
jspView.getVerticalScrollBar().setValue(0);
jspView.setColumnHeaderView(ruler);
showBaseAlignment = true;
}
else if(jspView != null)
{
checkBoxSingle.setVisible(true);
jspView.setColumnHeaderView(null);
showBaseAlignment = false;
}
Dimension d = new Dimension();
d.setSize((seqLength*pixPerBase), 800.d);
setPreferredSize(d);
}
/**
* Set the ViewPort so it starts at the given base position.
* @param base
*/
String refName = (String) combo.getSelectedItem();
int seqLength = seqLengths.get(refName);
int width = getPreferredSize().width;
p.x = Math.round((float)width*((float)(base-1)/(float)seqLength));
System.out.println("goToBasePosition*** "+base+" "+
getBaseAtStartOfView()+" "+p.x+" pixPerBase="+((float)width/(float)seqLength));
* 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;
String refName = (String) combo.getSelectedItem();
int seqLength = seqLengths.get(refName);
double pixPerBase = getPixPerBase(start, end);
if(pixPerBase*2 > ALIGNMENT_PIX_PER_BASE)
Dimension d = new Dimension();
d.setSize((seqLength*pixPerBase), 800.d);
setPreferredSize(d);
if(event == null)
{
this.startBase = -1;
this.endBase = -1;
}
}
private double getPixPerBase(int start, int end)
{
if(feature_display == null)
return 1000.d/(double)(end-start+1);
else
return feature_display.getWidth()/(double)(end-start+1);
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
/**
* Return an Artemis entry from a file
* @param entryFileName
* @param entryGroup
* @return
* @throws NoSequenceException
*/
private Entry getEntry(final String entryFileName, final EntryGroup entryGroup)
throws NoSequenceException
{
final Document entry_document = DocumentFactory.makeDocument(entryFileName);
final EntryInformation artemis_entry_information =
Options.getArtemisEntryInformation();
System.out.println(entryFileName);
final uk.ac.sanger.artemis.io.Entry new_embl_entry =
EntryFileDialog.getEntryFromFile(null, entry_document,
artemis_entry_information,
false);
if(new_embl_entry == null) // the read failed
return null;
Entry entry = null;
try
{
if(entryGroup.getSequenceEntry() != null)
bases = entryGroup.getSequenceEntry().getBases();
else
entry = new Entry(bases,new_embl_entry);
entryGroup.add(entry);
}
catch(OutOfRangeException e)
{
new MessageDialog(null, "read failed: one of the features in " +
entryFileName + " has an out of range " +
"location: " + e.getMessage());
}
return entry;
}
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
private boolean isShowScale()
{
return showScale;
}
public void setShowScale(boolean showScale)
{
this.showScale = showScale;
}
public JScrollPane getJspView()
{
return jspView;
}
public void setBases(Bases bases)
{
this.bases = bases;
}
/**
* Remove JScrollPane border
*/
public void removeBorder()
{
Border empty = new EmptyBorder(0,0,0,0);
jspView.setBorder(empty);
}
/**
* Handle a mouse drag event on the drawing canvas.
**/
private void handleCanvasMouseDragOrClick(final MouseEvent event)
if(!showBaseAlignment || event.isShiftDown())
String refName = (String) combo.getSelectedItem();
int seqLength = seqLengths.get(refName);
float pixPerBase = ((float)getWidth())/(float)(seqLength);
int start = (int) Math.round(event.getPoint().getX()/pixPerBase);
if(start < 1)
start = 1;
if(start > seqLength)
start = seqLength;
drag_range = new MarkerRange (bases.getForwardStrand(), start, start);
getSelection().setMarkerRange(drag_range);
repaint();
}
}
private Selection getSelection()
{
return selection;
}
public Ruler()
{
super();
setPreferredSize(new Dimension(getPreferredSize().width, 15));
setBackground(Color.white);
setFont(getFont().deriveFont(11.f));
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
drawBaseScale(g2, start, end, 12);
}
private void drawBaseScale(Graphics2D g2, int start, int end, int ypos)
{
int startMark = (((int)(start/10))*10)+1;
for(int i=startMark; i<end; i+=10)
{
int xpos = (i-1-start)*ALIGNMENT_PIX_PER_BASE;
g2.drawString(Integer.toString(i), xpos, ypos);
xpos+=(ALIGNMENT_PIX_PER_BASE/2);
g2.drawLine(xpos, ypos+1, xpos, ypos+5);
}
}
/**
* Popup menu listener
*/
class PopupListener extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{
JamView.this.requestFocus();
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
}
public void mousePressed(MouseEvent e)
{
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e)
{
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e)
{
if(e.isPopupTrigger())
popup.show(e.getComponent(),
e.getX(), e.getY());
}
}
SAMRecord pr1 = (SAMRecord) o1;
SAMRecord pr2 = (SAMRecord) o2;
int cmp = pr1.getReadName().compareTo(pr2.getReadName());
if(cmp == 0)
{
if(pr1.getAlignmentStart() < pr2.getAlignmentStart())
return -1;
else
return 1;
}
return cmp;
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
public Dimension getPreferredScrollableViewportSize()
{
return getPreferredSize();
}
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction)
{
if (orientation == SwingConstants.HORIZONTAL)
return visibleRect.width - maxUnitIncrement;
else
return visibleRect.height - maxUnitIncrement;
}
public boolean getScrollableTracksViewportHeight()
{
return false;
}
public boolean getScrollableTracksViewportWidth()
{
return false;
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction)
{
//Get the current position.
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL)
currentPosition = visibleRect.x;
else
currentPosition = visibleRect.y;
//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0)
{
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
}
else
{
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}
/**
* Artemis event notification
*/
public void displayAdjustmentValueChanged(DisplayAdjustmentEvent event)
{
System.out.println("displayAdjustmentValueChanged() "+event.getStart()+".."+event.getEnd());
if(event.getType() == DisplayAdjustmentEvent.SCALE_ADJUST_EVENT)
{
this.nbasesInView = event.getWidthInBases();
setDisplay(event.getStart(), event.getEnd(), event);
}
else
{
setDisplay(event.getStart(), event.getEnd(), event);
}
public static void main(String[] args)
{
String bam = args[0];
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
for(int i=0;i<args.length; i++)
{
if(args[i].equals("-a"))
bam = args[++i];
else if(args[i].equals("-r"))
reference = args[++i];
else if(args[i].equals("-v"))
nbasesInView = Integer.parseInt(args[++i]);
else if(args[i].equals("-s"))
System.setProperty("samtoolDir", args[++i]);
else if(args[i].startsWith("-h"))
{
System.out.println("-h\t show help");
System.out.println("-a\t BAM/SAM file to display");
System.out.println("-r\t reference file (optional)");
System.out.println("-v\t number of bases to display in the view (optional)");
System.out.println("-s\t samtool directory");
System.exit(0);
}
}
frame.addWindowFocusListener(new WindowFocusListener()
{
public void windowGainedFocus(WindowEvent e)
{
view.requestFocus();
}
public void windowLostFocus(WindowEvent e){}
});
view.addJamToPanel((JPanel)frame.getContentPane(), false, null);
view.jspView.getVerticalScrollBar().setValue(
view.jspView.getVerticalScrollBar().getMaximum());
public void selectionChanged(SelectionChangeEvent event)
{
repaint();
}