Skip to content
Snippets Groups Projects
CoveragePanel.java 15.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* CoveragePanel
     *
     * 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;
    
    tjc's avatar
    tjc committed
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    tjc's avatar
    tjc committed
    import java.awt.geom.GeneralPath;
    
    import java.io.File;
    
    import java.util.Enumeration;
    import java.util.Hashtable;
    
    tjc's avatar
    tjc committed
    import java.util.List;
    
    import java.util.Vector;
    
    tjc's avatar
    tjc committed
    
    
    import javax.swing.JCheckBox;
    import javax.swing.JComponent;
    import javax.swing.JLabel;
    
    import javax.swing.JMenuItem;
    
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    tjc's avatar
    tjc committed
    
    
    tcarver's avatar
    tcarver committed
    import uk.ac.sanger.artemis.components.Plot;
    
    
    tjc's avatar
    tjc committed
    import net.sf.samtools.AlignmentBlock;
    
    tjc's avatar
    tjc committed
    import net.sf.samtools.SAMRecord;
    
    
      public class CoveragePanel extends AbstractGraphPanel
    
    tjc's avatar
    tjc committed
      {
        private static final long serialVersionUID = 1L;
    
        private static LineAttributes lines[];
    
        private boolean includeCombined = false;
    
        private Hashtable<String, int[][]> plots;
        private int combinedCoverage[][];
    
    tjc's avatar
    tjc committed
    
    
        private static boolean redraw = false;
    
        private boolean setMaxBases = false;
    
    tjc's avatar
    tjc committed
        
    
        private boolean plotByStrand = false;
    
    tcarver's avatar
    tcarver committed
        private boolean plotHeatMap = false;
    
        private List<HeatMapLn> heatPlots;
    
        protected CoveragePanel(final BamView bamView)
        {
          this();
          this.bamView = bamView;
    
          createMenus(popup);
    
          addMouseListener(new PopupListener());
        }
        
        protected CoveragePanel()
    
    tjc's avatar
    tjc committed
        {
          super();
    
          setMaxBases = true;
        }
        
    
        protected void createMenus(JComponent menu)
    
          final JMenuItem configure = new JMenuItem("Configure Line(s)...");
    
          configure.addActionListener(new ActionListener()
          {
            public void actionPerformed(ActionEvent e)
            {
    
              int size = bamView.bamList.size();
    
              if(includeCombined)
                size++;
    
                LineAttributes.configurePlots(bamView.bamList, 
    
                    getLineAttributes(size), CoveragePanel.this);
    
              bamView.refreshColourOfBamMenu();
    
          menu.add(configure);
    
          final JMenuItem optMenu = new JMenuItem("Options...");
    
          optMenu.addActionListener(new ActionListener()
    
            public void actionPerformed(ActionEvent e)
    
              defineOpts();
    
              bamView.repaint();
    
            }
          });
          menu.add(optMenu);
    
    tjc's avatar
    tjc committed
        }
        
        /**
         * Override
         */
        protected void paintComponent(Graphics g)
        {
          super.paintComponent(g);
          Graphics2D g2 = (Graphics2D)g;
    
          if(plots == null)
    
    tjc's avatar
    tjc committed
            return;
    
    tcarver's avatar
    tcarver committed
          if(!plotHeatMap)
            drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK);
    
          drawPlot(g2);
          drawMax(g2);
        }
        
        protected void init(BamView bamView, float pixPerBase, int start, int end)
        {
          this.bamView = bamView;
          setPixPerBase(pixPerBase);
          setStartAndEnd(start, end);
          init();
        }
        
        private void init()
        {
    
          if(autoWinSize)
          {
    
            windowSize = (bamView.getBasesInView()/300);
    
            userWinSize = windowSize;
          }
          else
            windowSize = userWinSize;
          
    
    tjc's avatar
    tjc committed
          if(windowSize < 1)
            windowSize = 1;
    
          nBins = Math.round((end-start+1.f)/windowSize);
    
          plots = new Hashtable<String, int[][]>();
    
          combinedCoverage = null;
          if(includeCombined)
          {
    
            combinedCoverage = new int[nBins][2];
    
            for(int k=0; k<combinedCoverage.length; k++)
    
              for(int l=0; l<2; l++)
                combinedCoverage[k][l] = 0;
    
            plots.put("-1", combinedCoverage);
          }
          max = 0;
        }
    
    tjc's avatar
    tjc committed
    
    
        private void drawPlot(Graphics2D g2)
        {
          max = 0;
          Enumeration<String> plotEum = plots.keys();
          while(plotEum.hasMoreElements())
          {
    
    tcarver's avatar
    tcarver committed
            String fileName = plotEum.nextElement();
    
            int[][] thisPlot = plots.get(fileName);
    
            for(int i=1; i<thisPlot.length; i++)
    
            {
              if(plotByStrand)
              {
                for(int j=0; j<2; j++)
                  if(max < thisPlot[i][j])
                    max = thisPlot[i][j];
              }
              else if(max < thisPlot[i][0])
                max = thisPlot[i][0];
            }
    
    tcarver's avatar
    tcarver committed
          
    
    tcarver's avatar
    tcarver committed
          draw(g2, getWidth(), getHeight(), null);
    
        protected void addRecord(SAMRecord thisRead, int offset, String fileName)
    
          int coverage[][] = plots.get(fileName);
    
          if(coverage == null)
    
            coverage = new int[nBins][2];
            for(int k=0; k<nBins; k++)
              for(int l=0; l<2; l++)
                coverage[k][l] = 0; 
    
            plots.put(fileName, coverage);
    
    tcarver's avatar
    tcarver committed
          if(plotByStrand && !isPlotHeatMap() && thisRead.getReadNegativeStrandFlag())
    
          final List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
    
          for(int j=0; j<blocks.size(); j++)
    
    tjc's avatar
    tjc committed
          {
    
            AlignmentBlock block = blocks.get(j);
    
            int refStart = block.getReferenceStart();
    
            for(int k=0; k<block.getLength(); k++)
    
    tjc's avatar
    tjc committed
            {
    
              int pos = refStart + k + offset;
    
              int bin = pos/windowSize;
              if(bin < 0 || bin > nBins-1)
                continue;
    
              coverage[bin][col]+=1;
              if(coverage[bin][col] > max)
                max = coverage[bin][col];
    
              if(includeCombined)
    
                combinedCoverage[bin][col]+=1;
                if(combinedCoverage[bin][col] > max)
                  max = combinedCoverage[bin][col];
    
    tjc's avatar
    tjc committed
          }
    
    tcarver's avatar
    tcarver committed
        protected void draw(Graphics2D g2, int wid, int hgt, List<Short> hideBamList)
    
        {
          int size = bamView.bamList.size();
    
          if(includeCombined)
          {
            lines = getLineAttributes(size+1);
            lines[size].setLineColour(Color.black);
          }
          else
            lines = getLineAttributes(size);
    
          if(plotHeatMap)
            heatPlots = new Vector<HeatMapLn>();
    
          Enumeration<String> plotEum = plots.keys();
          while(plotEum.hasMoreElements())
    
    tjc's avatar
    tjc committed
          {
    
            String fName = plotEum.nextElement();
            int[][] thisPlot = plots.get(fName);
    
    tcarver's avatar
    tcarver committed
            int idx;
    
            if(fName.equals("-1"))
    
    tcarver's avatar
    tcarver committed
              idx = lines.length-1;
    
              idx = bamView.bamList.indexOf(fName);
    
    tcarver's avatar
    tcarver committed
    
    
    tcarver's avatar
    tcarver committed
            final LineAttributes line = lines[idx];
    
    tcarver's avatar
    tcarver committed
            if(plotHeatMap)
    
    tcarver's avatar
    tcarver committed
            {
              if(hideBamList != null)
    
                idx = adjustIdx(idx, hideBamList);
              drawHeatMap(g2, hgt, line, idx, thisPlot, fName);
    
    tcarver's avatar
    tcarver committed
            }
    
    tcarver's avatar
    tcarver committed
            else
    
    tcarver's avatar
    tcarver committed
              drawLinePlot(g2, wid, hgt, line, thisPlot);
    
        /**
         * Adjust for BAM's that have been hidden
         * @param index
         * @param hideBamList
         * @return
         */
        private int adjustIdx(int index, List<Short> hideBamList)
        {
          int shiftIdx = index;
          for(short i=0; i<index; i++)
            if(hideBamList.contains(i))
              shiftIdx--;
          return shiftIdx;
        }
        
    
    tcarver's avatar
    tcarver committed
        private void drawLinePlot(final Graphics2D g2, int wid, int hgt, LineAttributes line, int[][] thisPlot)
    
    tcarver's avatar
    tcarver committed
        {
    
    tcarver's avatar
    tcarver committed
          g2.setColor(line.getLineColour());
    
    tcarver's avatar
    tcarver committed
          int hgt2 = hgt/2;
          float maxVal = getValue(max);
          
    
    tcarver's avatar
    tcarver committed
          if(line.getPlotType() == LineAttributes.PLOT_TYPES[0])
    
    tcarver's avatar
    tcarver committed
          {
    
    tcarver's avatar
    tcarver committed
            g2.setStroke(line.getStroke());
    
    tcarver's avatar
    tcarver committed
            for(int i=1; i<thisPlot.length; i++)
    
    tcarver's avatar
    tcarver committed
              int x0 = (int) ((((i-1)*(windowSize)) + windowSize/2.f)*pixPerBase);
              int x1 = (int) (((i*(windowSize)) + windowSize/2.f)*pixPerBase);
    
    tcarver's avatar
    tcarver committed
              int y0, y1;
              if(plotByStrand)
    
    tcarver's avatar
    tcarver committed
                for(int col=0; col<2; col++)
    
    tcarver's avatar
    tcarver committed
                  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);
                }
    
    tcarver's avatar
    tcarver committed
              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);
              }
    
    tcarver's avatar
    tcarver committed
          }
          else // filled plots
          {
            g2.setComposite(makeComposite(0.75f));
    
    tjc's avatar
    tjc committed
    
    
    tcarver's avatar
    tcarver committed
            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++)
    
    tcarver's avatar
    tcarver committed
                float xpos = i*windowSize*pixPerBase;
    
    tcarver's avatar
    tcarver committed
                for(int col=0; col<2; col++)
    
    tcarver's avatar
    tcarver committed
                  if(col == 0)
                    shapeFwd.lineTo(xpos,
                      hgt2 - ((getValue(thisPlot[i][col])/maxVal)*hgt2));
                  else
                    shapeBwd.lineTo(xpos,
                      hgt2 + ((getValue(thisPlot[i][col])/maxVal)*hgt2));
    
    tcarver's avatar
    tcarver committed
    
              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++)
    
    tcarver's avatar
    tcarver committed
                float xpos = i*windowSize*pixPerBase;
    
    tcarver's avatar
    tcarver committed
                shape.lineTo(xpos,
                    hgt - ((getValue(thisPlot[i][0])/maxVal)*hgt));
    
    tcarver's avatar
    tcarver committed
              shape.lineTo(wid,hgt);
              g2.fill(shape);
    
    tcarver's avatar
    tcarver committed
          
    
          if(plotByStrand)
          {
            g2.setColor(Color.GRAY);
            g2.drawLine(0, hgt2, wid+1, hgt2);
          }
    
    tcarver's avatar
    tcarver committed
        /**
         * Draw as heat map
         * @param g2
         * @param hgt
    
    tcarver's avatar
    tcarver committed
         * @param line
         * @param idx
    
    tcarver's avatar
    tcarver committed
         * @param thisPlot
         */
    
        private void drawHeatMap(final Graphics2D g2, int hgt, LineAttributes line, int idx, int[][] thisPlot, String fName)
    
    tcarver's avatar
    tcarver committed
        { // heat map
    
          int NSHADES = 240;
    
    tcarver's avatar
    tcarver committed
          int plotHgt = hgt/plots.size();
    
    tcarver's avatar
    tcarver committed
          int plotPos = plotHgt * idx;
    
          Color definedColours[] = Plot.makeColours(line.getLineColour(), NSHADES);
    
    tcarver's avatar
    tcarver committed
    
    
          heatPlots.add(new HeatMapLn(plotPos, plotPos+plotHgt, idx, fName));
          
    
    tcarver's avatar
    tcarver committed
          float maxVal = getValue(max);
          for(int i=0; i<thisPlot.length; i++)
          {
    
    tcarver's avatar
    tcarver committed
            int xpos = (int) (i*windowSize*pixPerBase);
    
    tcarver's avatar
    tcarver committed
            // this is a number between 0.0 and 1.0
            final float scaledValue = getValue(thisPlot[i][0]) / maxVal;
            // set color based on value
    
    tcarver's avatar
    tcarver committed
            int colourIdx = 
    
    tcarver's avatar
    tcarver committed
              (int)(definedColours.length * 0.999 * scaledValue);
    
    
    tcarver's avatar
    tcarver committed
            if(colourIdx > definedColours.length - 1)
              colourIdx = definedColours.length - 1;
            else if (colourIdx <= 0)
              continue;
    
            g2.setColor(definedColours[ colourIdx ]);
    
    tcarver's avatar
    tcarver committed
            g2.fillRect(xpos, plotPos, (int) (windowSize*2*pixPerBase), plotHgt);
    
        private AlphaComposite makeComposite(float alpha)
        {
          int type = AlphaComposite.SRC_OVER;
          return(AlphaComposite.getInstance(type, alpha));
    
        protected static LineAttributes[] getLineAttributes(int nsize)
    
        {
          if(lines == null)
    
            lines = LineAttributes.init(nsize);
          else if(lines.length < nsize)
    
            LineAttributes tmpLines[] = LineAttributes.init(nsize);
    
            for(int i=0;i<lines.length;i++)
              tmpLines[i] = lines[i];
            lines = tmpLines;
          }
    
    
        /**
         * @return the redraw
         */
    
        protected static boolean isRedraw()
    
    tjc's avatar
    tjc committed
          if(redraw)
          {
            redraw = false;
            return true;
          }
    
        /**
         * @param plotByStrand the plotByStrand to set
         */
        protected void setPlotByStrand(boolean plotByStrand)
        {
          redraw = true;
          this.plotByStrand = plotByStrand;
        }
        
    
    tcarver's avatar
    tcarver committed
        /**
         * @param plotHeatMap the plotHeatMap to set
         */
        protected void setPlotHeatMap(boolean plotHeatMap)
        {
          this.plotHeatMap = plotHeatMap;
        }
    
    
    tcarver's avatar
    tcarver committed
        /**
         * @return the plotHeatMap
         */
        protected boolean isPlotHeatMap()
        {
          return plotHeatMap;
        }
    
        
        /**
         * Return tooltip text for a given position
         * @param e
         * @return
         */
        public String getToolTipText(int ypos)
        {
    
    tcarver's avatar
    tcarver committed
          if(heatPlots == null)
            return null;
    
          for(HeatMapLn h: heatPlots)
          {
            if(ypos > h.yTop && ypos < h.yBtm)
              return h.toString();
          }
    
          return null;
        }
    
    tcarver's avatar
    tcarver committed
    
    
        private void defineOpts()
        {
          final JPanel opts = new JPanel(new GridBagLayout());
          final GridBagConstraints c = new GridBagConstraints();
    
          final JTextField newBaseMax = new JTextField(Integer.toString(bamView.getMaxBases()), 10);
          c.gridy = 0;
          if(setMaxBases)
          {
            final JLabel labMax1 = new JLabel("Zoom level before switching");
            final JLabel labMax2 = new JLabel("to coverage view (in bases):");
            c.anchor = GridBagConstraints.WEST;
            opts.add(labMax1, c);
            c.gridy = c.gridy+1;
            opts.add(labMax2, c);
            opts.add(newBaseMax, c);
          }
          
          final JTextField newWinSize = new JTextField(Integer.toString(userWinSize), 10);
          final JLabel lab = new JLabel("Window size:");
          lab.setEnabled(!autoWinSize);
          newWinSize.setEnabled(!autoWinSize);
          
          c.gridy = c.gridy+1;
          c.anchor = GridBagConstraints.EAST;
          opts.add(lab, c);
          opts.add(newWinSize, c);
    
          final JCheckBox autoSet = new JCheckBox("Automatically set window size", autoWinSize);
          autoSet.addActionListener(new ActionListener()
          {
            public void actionPerformed(ActionEvent e)
            {
              lab.setEnabled(!autoSet.isSelected());
              newWinSize.setEnabled(!autoSet.isSelected());
            }
          });
          c.anchor = GridBagConstraints.WEST;
          c.gridy = c.gridy+1;
          c.gridwidth = GridBagConstraints.REMAINDER;
          opts.add(autoSet, c);
    
    
          final JCheckBox showCombined = new JCheckBox("Show combined plot", includeCombined);
    
          if(bamView.bamList.size() == 1)
            showCombined.setEnabled(false);
          c.gridy = c.gridy+1;
          opts.add(showCombined, c);
    
          
          final JCheckBox byStrand = new JCheckBox("Plot by strand", plotByStrand);
          c.gridy = c.gridy+1;
          opts.add(byStrand, c);
    
          String window_options[] = { "OK", "Cancel" };
          int select = JOptionPane.showOptionDialog(null, opts, "Coverage Options",
              JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
              window_options, window_options[0]);
    
          if(select == 1)
            return;
          
          redraw = true;
          autoWinSize = autoSet.isSelected();
          includeCombined = showCombined.isSelected();
    
          plotByStrand = byStrand.isSelected();
    
          try
          {
            userWinSize = Integer.parseInt(newWinSize.getText().trim());
            if(setMaxBases)
              bamView.setMaxBases(Integer.parseInt(newBaseMax.getText().trim()));
          }
          catch (NumberFormatException nfe)
          {
            return;
          }
        }
    
        class HeatMapLn
        {
          int yTop, yBtm, idx;
          String fName;
          HeatMapLn(int yTop, int yBtm, int idx, String fName)
          {
            this.yTop = yTop;
            this.yBtm = yBtm;
            this.idx  = idx;
            this.fName = fName;
          }
          
          public String toString()
          {
            final File f = new File(fName);
            return f.getName();
          }
        }
    
    tjc's avatar
    tjc committed
      }