From b0c7373e59297d8a226ba3497bb3cf365df27e8b Mon Sep 17 00:00:00 2001
From: tjc <tjc@ee4ac58c-ac51-4696-9907-e4b3aa274f04>
Date: Tue, 9 Mar 2010 12:37:32 +0000
Subject: [PATCH] add SNP graph
git-svn-id: svn+ssh://svn.internal.sanger.ac.uk/repos/svn/pathsoft/artemis/trunk@13445 ee4ac58c-ac51-4696-9907-e4b3aa274f04
---
.../artemis/components/alignment/BamView.java | 53 +++-
.../components/alignment/SnpPanel.java | 288 ++++++++++++++++++
2 files changed, 335 insertions(+), 6 deletions(-)
create mode 100644 uk/ac/sanger/artemis/components/alignment/SnpPanel.java
diff --git a/uk/ac/sanger/artemis/components/alignment/BamView.java b/uk/ac/sanger/artemis/components/alignment/BamView.java
index 77b1be334..a65f8e95a 100644
--- a/uk/ac/sanger/artemis/components/alignment/BamView.java
+++ b/uk/ac/sanger/artemis/components/alignment/BamView.java
@@ -149,11 +149,13 @@ public class BamView extends JPanel
private boolean isPairedStackView = false;
private boolean isStrandStackView = false;
private boolean isCoverage = false;
+ private boolean isSNPplot = false;
private FeatureDisplay feature_display;
private Selection selection;
private JPanel mainPanel;
private CoveragePanel coveragePanel;
+ private SnpPanel snpPanel;
private boolean showScale = true;
private boolean logScale = false;
private Ruler ruler;
@@ -665,6 +667,12 @@ public class BamView extends JPanel
coveragePanel.setPixPerBase(pixPerBase);
coveragePanel.repaint();
}
+ if(isSNPplot)
+ {
+ snpPanel.setStartAndEnd(start, end);
+ snpPanel.setPixPerBase(pixPerBase);
+ snpPanel.repaint();
+ }
}
if(waitingFrame.isVisible())
@@ -1892,6 +1900,10 @@ public class BamView extends JPanel
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,
@@ -1902,6 +1914,10 @@ public class BamView extends JPanel
public void adjustmentValueChanged(AdjustmentEvent e)
{
repaint();
+ if(coveragePanel != null)
+ coveragePanel.repaint();
+ if(snpPanel != null)
+ snpPanel.repaint();
}
});
bottomPanel.add(scrollBar, BorderLayout.SOUTH);
@@ -1910,7 +1926,9 @@ public class BamView extends JPanel
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
coveragePanel.setPreferredSize(new Dimension(900, 100));
coveragePanel.setVisible(false);
-
+ snpPanel.setPreferredSize(new Dimension(900, 100));
+ snpPanel.setVisible(false);
+
jspView.getVerticalScrollBar().setValue(
jspView.getVerticalScrollBar().getMaximum());
jspView.getVerticalScrollBar().setUnitIncrement(maxUnitIncrement);
@@ -2096,7 +2114,7 @@ public class BamView extends JPanel
viewMenu.add(checkBoxStrandStackView);
menu.add(viewMenu);
- final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNPs");
+ final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNP marks");
//
JMenu colourMenu = new JMenu("Colour By");
colourMenu.add(colourByCoverageColour);
@@ -2156,8 +2174,10 @@ public class BamView extends JPanel
}
});
showMenu.add(markInsertions);
- showMenu.add(new JSeparator());
+ menu.add(showMenu);
+ //
+ JMenu graphMenu = new JMenu("Graph");
JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage");
checkBoxCoverage.addActionListener(new ActionListener()
{
@@ -2168,8 +2188,21 @@ public class BamView extends JPanel
repaint();
}
});
- showMenu.add(checkBoxCoverage);
- menu.add(showMenu);
+ graphMenu.add(checkBoxCoverage);
+
+ JCheckBoxMenuItem checkBoxSNP = new JCheckBoxMenuItem("SNP");
+ checkBoxSNP.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ isSNPplot = !isSNPplot;
+ snpPanel.setVisible(isSNPplot);
+ repaint();
+ }
+ });
+ graphMenu.add(checkBoxSNP);
+ menu.add(graphMenu);
+
if(feature_display != null)
{
@@ -2250,7 +2283,7 @@ public class BamView extends JPanel
jspView.getViewport().setViewPosition(p);
}
- private int getBaseAtStartOfView()
+ protected int getBaseAtStartOfView()
{
if(feature_display != null)
return feature_display.getForwardBaseAtLeftEdge();
@@ -2581,12 +2614,20 @@ public class BamView extends JPanel
int width = feature_display.getMaxVisibleBases();
setZoomLevel(width);
repaint();
+ if(coveragePanel != null && coveragePanel.isVisible())
+ coveragePanel.repaint();
+ if(snpPanel != null && snpPanel.isVisible())
+ snpPanel.repaint();
}
else
{
setDisplay(event.getStart(),
event.getStart()+feature_display.getMaxVisibleBases(), event);
repaint();
+ if(coveragePanel != null && coveragePanel.isVisible())
+ coveragePanel.repaint();
+ if(snpPanel != null && snpPanel.isVisible())
+ snpPanel.repaint();
}
}
diff --git a/uk/ac/sanger/artemis/components/alignment/SnpPanel.java b/uk/ac/sanger/artemis/components/alignment/SnpPanel.java
new file mode 100644
index 000000000..2463665ea
--- /dev/null
+++ b/uk/ac/sanger/artemis/components/alignment/SnpPanel.java
@@ -0,0 +1,288 @@
+/* SnpPanel
+ *
+ * created: 2010
+ *
+ * This file is part of Artemis
+ *
+ * Copyright(C) 2010 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.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.GeneralPath;
+import java.util.List;
+
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JTextField;
+
+import uk.ac.sanger.artemis.io.Range;
+import uk.ac.sanger.artemis.sequence.Bases;
+import uk.ac.sanger.artemis.util.OutOfRangeException;
+
+import net.sf.samtools.AlignmentBlock;
+import net.sf.samtools.SAMRecord;
+
+ public class SnpPanel extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+
+ private int start;
+ private int end;
+ private float pixPerBase;
+ private BamView bamView;
+ private JPopupMenu popup;
+ private Bases bases;
+ private float minBaseQualityFilter = 0;
+
+ public SnpPanel(final BamView bamView, Bases bases)
+ {
+ super();
+ setBackground(Color.white);
+ this.bamView = bamView;
+ this.bases = bases;
+
+ popup = new JPopupMenu();
+ JMenuItem configure = new JMenuItem("Filter by Base Quality...");
+ configure.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ // filter by base quality
+ JTextField filterField = new JTextField(Float.toString(minBaseQualityFilter));
+
+ int status = JOptionPane.showConfirmDialog(SnpPanel.this,
+ filterField, "Base Quality Filter",
+ JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
+ if(status == JOptionPane.CANCEL_OPTION)
+ return;
+ try
+ {
+ minBaseQualityFilter = Float.parseFloat(filterField.getText());
+ }
+ catch(NumberFormatException nfe)
+ {
+ JOptionPane.showMessageDialog(SnpPanel.this, nfe.getMessage(),
+ "Number Format", JOptionPane.WARNING_MESSAGE);
+ }
+ }
+ });
+ popup.add(configure);
+ addMouseListener(new PopupListener());
+ }
+
+ /**
+ * Override
+ */
+ protected void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ Graphics2D g2 = (Graphics2D)g;
+
+ if(bases == null)
+ return;
+
+ List<SAMRecord> readsInView = bamView.getReadsInView();
+ if(readsInView == null)
+ return;
+
+ int windowSize = (bamView.getBasesInView()/300);
+ if(windowSize < 1)
+ windowSize = 1;
+
+ int nBins = Math.round((end-start+1.f)/windowSize);
+ int max = drawPlot(g2, nBins, windowSize);
+
+ String maxStr = Float.toString(max/windowSize);
+ FontMetrics fm = getFontMetrics(getFont());
+ g2.setColor(Color.black);
+
+ int xpos = getWidth() - fm.stringWidth(maxStr) -
+ bamView.getJspView().getVerticalScrollBar().getWidth();
+ g2.drawString(maxStr, xpos, fm.getHeight());
+ }
+
+
+ private int drawPlot(Graphics2D g2, int nBins, int windowSize)
+ {
+ //lines = CoveragePanel.getLineAttributes(bamView.bamList.size());
+ List<SAMRecord> readsInView = bamView.getReadsInView();
+
+ int snpCount[] = new int[nBins];
+ for(int i=0; i<snpCount.length; i++)
+ snpCount[i] = 0;
+
+ int max = 0;
+ for(int i=0; i<readsInView.size(); i++)
+ {
+ SAMRecord thisRead = readsInView.get(i);
+ max = calculateSNPs(thisRead, windowSize, nBins, snpCount, max);
+ }
+
+ g2.setColor(Color.red);
+ g2.setStroke(new BasicStroke(1.f));
+
+ if(windowSize == 1)
+ {
+ GeneralPath shape = new GeneralPath();
+ shape.moveTo(0,getHeight());
+ for(int i=0; i<snpCount.length; i++)
+ {
+ float xpos1 = ((i*windowSize) )*pixPerBase;
+ float xpos2 = ((i*windowSize) + windowSize)*pixPerBase;
+
+ shape.lineTo(xpos1,getHeight());
+ shape.lineTo(xpos1,
+ getHeight() - (((float)snpCount[i]/(float)max)*getHeight()));
+ shape.lineTo(xpos2,
+ getHeight() - (((float)snpCount[i]/(float)max)*getHeight()));
+
+ shape.lineTo(xpos2,getHeight());
+ }
+
+ shape.lineTo(getWidth(),getHeight());
+ g2.fill(shape);
+ }
+ else
+ {
+ for(int i=1; i<snpCount.length; i++)
+ {
+ int x0 = (int) (((i*windowSize) - windowSize/2.f)*pixPerBase);
+ int y0 = (int) (getHeight() - (((float)snpCount[i-1]/(float)max)*getHeight()));
+ int x1 = (int) ((((i+1)*windowSize) - windowSize/2.f)*pixPerBase);
+ int y1 = (int) (getHeight() - (((float)snpCount[i]/(float)max)*getHeight()));
+
+ g2.drawLine(x0, y0, x1, y1);
+ }
+ }
+
+ return max;
+ }
+
+ /**
+ * Display the SNPs for the given read.
+ * @param g2
+ * @param thisRead
+ * @param pixPerBase
+ * @param ypos
+ */
+ private int calculateSNPs(SAMRecord thisRead,
+ int windowSize,
+ int nBins,
+ int[] snpCount,
+ int max)
+ {
+ int thisStart = thisRead.getAlignmentStart();
+ int thisEnd = thisRead.getAlignmentEnd();
+ int offset = bamView.getSequenceOffset(thisRead.getReferenceName());
+ // use alignment blocks of the contiguous alignment of
+ // subsets of read bases to a reference sequence
+ List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
+ byte[] phredQuality = thisRead.getBaseQualities();
+ try
+ {
+ char[] refSeq = bases.getSubSequenceC(
+ new Range(thisStart+offset, thisEnd+offset), Bases.FORWARD);
+ byte[] readSeq = thisRead.getReadBases();
+
+ offset = offset - bamView.getBaseAtStartOfView();
+ for(int i=0; i<blocks.size(); i++)
+ {
+ AlignmentBlock block = blocks.get(i);
+ for(int j=0; j<block.getLength(); j++)
+ {
+ int readPos = block.getReadStart()-1+j;
+ int refPos = block.getReferenceStart()+j;
+
+ if (Character.toUpperCase(refSeq[refPos-thisStart]) != readSeq[readPos])
+ {
+ if(phredQuality[readPos] < minBaseQualityFilter)
+ continue;
+ int bin = (int)((refPos+offset) / windowSize);
+
+ if(bin < 0 || bin > nBins-1)
+ continue;
+
+ snpCount[bin]+=1;
+ if(snpCount[bin] > max)
+ max = snpCount[bin];
+ }
+ }
+ }
+ }
+ catch (OutOfRangeException e)
+ {
+ e.printStackTrace();
+ }
+ return max;
+ }
+
+ protected void setStartAndEnd(int start, int end)
+ {
+ this.start = start;
+ this.end = end;
+ }
+
+ protected void setPixPerBase(float pixPerBase)
+ {
+ this.pixPerBase = pixPerBase;
+ }
+
+ /**
+ * Popup menu listener
+ */
+ class PopupListener extends MouseAdapter
+ {
+ JMenuItem gotoMateMenuItem;
+ JMenuItem showDetails;
+
+ public void mouseClicked(MouseEvent e)
+ {
+ }
+
+ 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());
+ }
+ }
+ }
+ }
\ No newline at end of file
--
GitLab