diff --git a/uk/ac/sanger/artemis/components/Plot.java b/uk/ac/sanger/artemis/components/Plot.java index b5a726915d514c500e50e94def7f14827dda16cb..175832128117d16e0d85edf50e66e3b67e3f612a 100644 --- a/uk/ac/sanger/artemis/components/Plot.java +++ b/uk/ac/sanger/artemis/components/Plot.java @@ -930,7 +930,7 @@ public abstract class Plot extends JPanel * @param NUMBER_OF_SHADES * @return */ - protected Color[] makeColours(Color col, int NUMBER_OF_SHADES) + public static Color[] makeColours(Color col, int NUMBER_OF_SHADES) { Color definedColour[] = new Color[NUMBER_OF_SHADES]; for(int i = 0; i < NUMBER_OF_SHADES; ++i) diff --git a/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java b/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java index 9f651b952c224c2165f1eb87abc1fc3944c29335..1f53fe42e48a02f0e8769c767cb3cbb5d6c7a639 100644 --- a/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java +++ b/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java @@ -161,6 +161,18 @@ public class AbstractGraphPanel extends JPanel g2.drawString(maxStr, xpos, fm.getHeight()); } + /** + * Return the log value if the log scale is selected + * @param val + * @return + */ + protected float getValue(int val) + { + if(val == 0) + return 0.f; + return (float) (bamView.logScale ? Math.log(val) : val); + } + protected void drawSelectionRange(final Graphics2D g2, final float pixPerBase, final int start, diff --git a/uk/ac/sanger/artemis/components/alignment/BamView.java b/uk/ac/sanger/artemis/components/alignment/BamView.java index 19d8734db49ed791c2e3eb97a6382c897c8b0be0..be26ccb51dd06460dbd098b89afdaa398e385aa1 100644 --- a/uk/ac/sanger/artemis/components/alignment/BamView.java +++ b/uk/ac/sanger/artemis/components/alignment/BamView.java @@ -184,7 +184,7 @@ public class BamView extends JPanel private CoveragePanel coveragePanel; private SnpPanel snpPanel; - private boolean logScale = false; + protected boolean logScale = false; private Ruler ruler; private int nbasesInView; @@ -205,6 +205,7 @@ public class BamView extends JPanel 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(); @@ -354,6 +355,7 @@ public class BamView extends JPanel buttonGroup.add(cbIsizeStackView); buttonGroup.add(cbCoverageView); buttonGroup.add(cbCoverageStrandView); + buttonGroup.add(cbCoverageHeatMap); addMouseListener(new PopupListener()); jspView = new JScrollPane(this, @@ -1807,7 +1809,7 @@ public class BamView extends JPanel } int hgt = jspView.getVisibleRect().height-scaleHeight; - if(!cbCoverageStrandView.isSelected()) + if(!cbCoverageStrandView.isSelected() && !cbCoverageHeatMap.isSelected()) { try { @@ -1819,7 +1821,8 @@ public class BamView extends JPanel catch(Exception e){} } - g2.translate(0, getHeight()-hgt-scaleHeight); + g2.translate(0, getJspView().getViewport().getViewPosition().y); + coverageView.drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK); coverageView.draw(g2, getWidth(), hgt); coverageView.drawMax(g2); @@ -2519,6 +2522,7 @@ public class BamView extends JPanel cbStrandStackView.setFont(viewMenu.getFont()); cbCoverageView.setFont(viewMenu.getFont()); cbCoverageStrandView.setFont(viewMenu.getFont()); + cbCoverageHeatMap.setFont(viewMenu.getFont()); baseQualityColour.setFont(viewMenu.getFont()); colourByCoverageColour.setFont(viewMenu.getFont()); @@ -2530,6 +2534,7 @@ public class BamView extends JPanel { laststart = -1; logMenuItem.setEnabled(isIsizeStackView()); + getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); repaint(); } }); @@ -2541,7 +2546,9 @@ public class BamView extends JPanel public void actionPerformed(ActionEvent e) { laststart = -1; - logMenuItem.setEnabled(isIsizeStackView()); + if(cbStackView.isSelected()) + logMenuItem.setEnabled(false); + getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); repaint(); } }); @@ -2553,7 +2560,9 @@ public class BamView extends JPanel public void actionPerformed(ActionEvent e) { laststart = -1; - logMenuItem.setEnabled(isIsizeStackView()); + if(cbPairedStackView.isSelected()) + logMenuItem.setEnabled(false); + getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); repaint(); } }); @@ -2567,7 +2576,9 @@ public class BamView extends JPanel if(isStrandStackView()) setViewportMidPoint(); - logMenuItem.setEnabled(isIsizeStackView()); + if(cbStrandStackView.isSelected()) + logMenuItem.setEnabled(false); + getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); repaint(); } }); @@ -2578,11 +2589,13 @@ public class BamView extends JPanel public void actionPerformed(ActionEvent e) { laststart = -1; - logMenuItem.setEnabled(isIsizeStackView()); if(cbCoverageView.isSelected()) { + logMenuItem.setEnabled(true); + coverageView.setPlotHeatMap(false); coverageView.setPlotByStrand(false); setViewportBtm(); + getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); } repaint(); } @@ -2594,17 +2607,39 @@ public class BamView extends JPanel public void actionPerformed(ActionEvent e) { laststart = -1; - logMenuItem.setEnabled(isIsizeStackView()); 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"); @@ -2692,7 +2727,8 @@ public class BamView extends JPanel if( isCoverage && !cbCoverageView.isSelected() && - !cbCoverageStrandView.isSelected() ) + !cbCoverageStrandView.isSelected() && + !cbCoverageHeatMap.isSelected()) laststart = -1; repaint(); } @@ -2825,6 +2861,8 @@ public class BamView extends JPanel 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); @@ -3124,13 +3162,13 @@ public class BamView extends JPanel } else if(jspView != null) { - if(!cbCoverageView.isSelected() && !cbCoverageStrandView.isSelected() && nbasesInView >= MAX_BASES) + if(isCoverageView(pixPerBase) && nbasesInView >= MAX_BASES) { cbLastSelected = getSelectedCheckBoxMenuItem(); cbCoverageView.setSelected(true); coverageView.setPlotByStrand(false); } - else if((cbCoverageView.isSelected() || cbCoverageStrandView.isSelected()) && nbasesInView < MAX_BASES && cbLastSelected != null) + else if(isCoverageView(pixPerBase) && nbasesInView < MAX_BASES && cbLastSelected != null) { cbLastSelected.setSelected(true); cbLastSelected = null; @@ -3358,7 +3396,7 @@ public class BamView extends JPanel { if(isBaseAlignmentView(pixPerBase)) return false; - return cbCoverageView.isSelected() || cbCoverageStrandView.isSelected(); + return cbCoverageView.isSelected() || cbCoverageStrandView.isSelected() || cbCoverageHeatMap.isSelected(); } private boolean isIsizeStackView() @@ -3385,6 +3423,8 @@ public class BamView extends JPanel return cbIsizeStackView; if(cbCoverageView.isSelected()) return cbCoverageView; + if(cbCoverageHeatMap.isSelected()) + return cbCoverageHeatMap; return cbCoverageStrandView; } diff --git a/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java b/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java index 6e29e5b8f889116805a6f000f8688a7c2f0493fc..ffd7bd4e0ea917ee01645654f383f73c4723a255 100644 --- a/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java +++ b/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java @@ -45,6 +45,8 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; +import uk.ac.sanger.artemis.components.Plot; + import net.sf.samtools.AlignmentBlock; import net.sf.samtools.SAMRecord; @@ -60,6 +62,7 @@ import net.sf.samtools.SAMRecord; private boolean setMaxBases = false; private boolean plotByStrand = false; + private boolean plotHeatMap = false; protected CoveragePanel(final BamView bamView) { @@ -115,7 +118,8 @@ import net.sf.samtools.SAMRecord; if(plots == null) return; - drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK); + if(!plotHeatMap) + drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK); drawPlot(g2); drawMax(g2); } @@ -175,6 +179,7 @@ import net.sf.samtools.SAMRecord; max = thisPlot[i][0]; } } + draw(g2, getWidth(), getHeight()); } @@ -232,7 +237,6 @@ import net.sf.samtools.SAMRecord; else lines = getLineAttributes(size); - int hgt2 = hgt/2; Enumeration<String> plotEum = plots.keys(); while(plotEum.hasMoreElements()) @@ -245,86 +249,97 @@ import net.sf.samtools.SAMRecord; index = lines.length-1; else index = bamView.bamList.indexOf(fileName); - - g2.setColor(lines[index].getLineColour()); - if(lines[index].getPlotType() == LineAttributes.PLOT_TYPES[0]) + + if(plotHeatMap) + drawHeatMap(g2, hgt, index, thisPlot); + else + drawLinePlot(g2, wid, hgt, index, thisPlot); + } + } + + private void drawLinePlot(final Graphics2D g2, int wid, int hgt, int index, int[][] thisPlot) + { + g2.setColor(lines[index].getLineColour()); + int hgt2 = hgt/2; + float maxVal = getValue(max); + + if(lines[index].getPlotType() == LineAttributes.PLOT_TYPES[0]) + { + g2.setStroke(lines[index].getStroke()); + for(int i=1; i<thisPlot.length; i++) { - g2.setStroke(lines[index].getStroke()); - for(int i=1; i<thisPlot.length; i++) + int x0 = (int) ((((i-1)*(windowSize)) - windowSize/2.f)*pixPerBase); + int x1 = (int) (((i*(windowSize)) - windowSize/2.f)*pixPerBase); + int y0, y1; + if(plotByStrand) { - int x0 = (int) ((((i-1)*(windowSize)) - windowSize/2.f)*pixPerBase); - int x1 = (int) (((i*(windowSize)) - windowSize/2.f)*pixPerBase); - int y0, y1; - if(plotByStrand) - { - for(int col=0; col<2; col++) - { - final int factor; - if(col == 0) - factor = 1; // fwd strand - else - factor = -1; // reverse strand - - y0 = (int) (hgt2 - (factor)*(((float)thisPlot[i-1][col]/(float)max)*hgt2)); - y1 = (int) (hgt2 - (factor)*(((float)thisPlot[i][col]/(float)max)*hgt2)); - - g2.drawLine(x0, y0, x1, y1); - } - } - else + for(int col=0; col<2; col++) { - y0 = (int) (hgt - (((float)(thisPlot[i-1][0])/(float)max)*hgt)); - y1 = (int) (hgt - (((float)(thisPlot[i][0])/(float)max)*hgt)); + final int factor; + if(col == 0) + factor = 1; // fwd strand + else + factor = -1; // reverse strand + + y0 = (int) (hgt2 - (factor)*((getValue(thisPlot[i-1][col])/maxVal)*hgt2)); + y1 = (int) (hgt2 - (factor)*((getValue(thisPlot[i][col])/maxVal)*hgt2)); + g2.drawLine(x0, y0, x1, y1); } } + else + { + y0 = (int) (hgt - ((getValue(thisPlot[i-1][0])/maxVal)*hgt)); + y1 = (int) (hgt - ((getValue(thisPlot[i][0])/maxVal)*hgt)); + g2.drawLine(x0, y0, x1, y1); + } } - else // filled plots - { - g2.setComposite(makeComposite(0.75f)); + } + else // filled plots + { + g2.setComposite(makeComposite(0.75f)); - if(plotByStrand) + if(plotByStrand) + { + final GeneralPath shapeFwd = new GeneralPath(); + shapeFwd.moveTo(0,hgt2); + final GeneralPath shapeBwd = new GeneralPath(); + shapeBwd.moveTo(0,hgt2); + + for(int i=0; i<thisPlot.length; i++) { - final GeneralPath shapeFwd = new GeneralPath(); - shapeFwd.moveTo(0,hgt2); - final GeneralPath shapeBwd = new GeneralPath(); - shapeBwd.moveTo(0,hgt2); - - for(int i=0; i<thisPlot.length; i++) + float xpos = ((i*(windowSize)) - windowSize/2.f)*pixPerBase; + for(int col=0; col<2; col++) { - float xpos = ((i*(windowSize)) - windowSize/2.f)*pixPerBase; - for(int col=0; col<2; col++) - { - if(col == 0) - shapeFwd.lineTo(xpos, - hgt2 - (((float)thisPlot[i][col]/(float)max)*hgt2)); - else - shapeBwd.lineTo(xpos, - hgt2 + (((float)thisPlot[i][col]/(float)max)*hgt2)); - } + if(col == 0) + shapeFwd.lineTo(xpos, + hgt2 - ((getValue(thisPlot[i][col])/maxVal)*hgt2)); + else + shapeBwd.lineTo(xpos, + hgt2 + ((getValue(thisPlot[i][col])/maxVal)*hgt2)); } - - shapeBwd.lineTo(wid,hgt2); - shapeFwd.lineTo(wid,hgt2); - g2.fill(shapeBwd); - g2.fill(shapeFwd); } - else + + shapeBwd.lineTo(wid,hgt2); + shapeFwd.lineTo(wid,hgt2); + g2.fill(shapeBwd); + g2.fill(shapeFwd); + } + else + { + final GeneralPath shape = new GeneralPath(); + shape.moveTo(0,hgt); + for(int i=0; i<thisPlot.length; i++) { - final GeneralPath shape = new GeneralPath(); - shape.moveTo(0,hgt); - for(int i=0; i<thisPlot.length; i++) - { - float xpos = ((i*(windowSize)) - windowSize/2.f)*pixPerBase; - shape.lineTo(xpos, - hgt - (((float)thisPlot[i][0]/(float)max)*hgt)); - } - shape.lineTo(wid,hgt); - g2.fill(shape); + float xpos = ((i*(windowSize)) - windowSize/2.f)*pixPerBase; + shape.lineTo(xpos, + hgt - ((getValue(thisPlot[i][0])/maxVal)*hgt)); } + shape.lineTo(wid,hgt); + g2.fill(shape); } } - + if(plotByStrand) { g2.setColor(Color.GRAY); @@ -332,6 +347,42 @@ import net.sf.samtools.SAMRecord; } } + /** + * Draw as heat map + * @param g2 + * @param hgt + * @param index + * @param thisPlot + */ + private void drawHeatMap(final Graphics2D g2, int hgt, int index, int[][] thisPlot) + { // heat map + int NUMBER_OF_SHADES = 254; + int plotHgt = hgt/plots.size(); + int plotPos = plotHgt * index; + Color definedColours[] = Plot.makeColours(lines[index].getLineColour(), + NUMBER_OF_SHADES); + + float maxVal = getValue(max); + for(int i=0; i<thisPlot.length; i++) + { + int xpos = (int) ((((i-1)*(windowSize)) - windowSize/2.f)*pixPerBase); + + // this is a number between 0.0 and 1.0 + final float scaledValue = getValue(thisPlot[i][0]) / maxVal; + // set color based on value + int colourIndex = + (int)(definedColours.length * 0.999 * scaledValue); + + if(colourIndex > definedColours.length - 1) + colourIndex = definedColours.length - 1; + else if (colourIndex < 0) + colourIndex = 0; + + g2.setColor(definedColours[ colourIndex ]); + g2.fillRect(xpos, plotPos, windowSize, plotHgt); + } + } + private AlphaComposite makeComposite(float alpha) { int type = AlphaComposite.SRC_OVER; @@ -374,6 +425,14 @@ import net.sf.samtools.SAMRecord; this.plotByStrand = plotByStrand; } + /** + * @param plotHeatMap the plotHeatMap to set + */ + protected void setPlotHeatMap(boolean plotHeatMap) + { + this.plotHeatMap = plotHeatMap; + } + private void defineOpts() { final JPanel opts = new JPanel(new GridBagLayout());