Skip to content
Snippets Groups Projects
JamView.java 47.7 KiB
Newer Older
tjc's avatar
tjc committed
          topPanel.setVisible(true);
tjc's avatar
tjc committed
          if (buttonAutoHide.isSelected())
            topPanel.setVisible(false);
        }
        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);
      }
    });
tjc's avatar
tjc committed
    topPanel.add(combo);
    if(feature_display == null)
tjc's avatar
tjc committed
    {
tjc's avatar
tjc committed
      final JTextField baseText = new JTextField(8);
      JButton goTo = new JButton("GoTo:");
tjc's avatar
tjc committed
      goTo.setToolTipText("Go to base position");
      goTo.addActionListener(new ActionListener()
tjc's avatar
tjc committed
      {
        public void actionPerformed(ActionEvent e)
tjc's avatar
tjc committed
        {
          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);
          }
tjc's avatar
tjc committed
        }
tjc's avatar
tjc committed
      topPanel.add(goTo);
      topPanel.add(baseText);
      JButton zoomIn = new JButton("-");
      Insets ins = new Insets(1,1,1,1);
      zoomIn.setMargin(ins);
      zoomIn.addActionListener(new ActionListener()
tjc's avatar
tjc committed
      {
        public void actionPerformed(ActionEvent e)
        {
          int startBase = getBaseAtStartOfView();
          setZoomLevel((int) (JamView.this.nbasesInView * 1.1));
          goToBasePosition(startBase);
        }
      });
tjc's avatar
tjc committed
      topPanel.add(zoomIn);
      JButton zoomOut = new JButton("+");
      zoomOut.setMargin(ins);
      zoomOut.addActionListener(new ActionListener()
tjc's avatar
tjc committed
      {
        public void actionPerformed(ActionEvent e)
        {
          if (showBaseAlignment)
            return;
          int startBase = getBaseAtStartOfView();
          setZoomLevel((int) (JamView.this.nbasesInView * .9));
          goToBasePosition(startBase);
        }
      });
tjc's avatar
tjc committed
      topPanel.add(zoomOut);
tjc's avatar
tjc committed
    topPanel.add(buttonAutoHide);
tjc's avatar
tjc committed

tjc's avatar
tjc committed
    mainPanel.setPreferredSize(new Dimension(900, 400));
tjc's avatar
tjc committed
    
    jspView = new JScrollPane(this, 
        JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
tjc's avatar
tjc committed
        JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    setDisplay(1, nbasesInView, null);
    mainPanel.setLayout(new BorderLayout());
tjc's avatar
tjc committed
    if(topPanel instanceof JPanel)
      mainPanel.add(topPanel, BorderLayout.NORTH);
    mainPanel.add(jspView, BorderLayout.CENTER);
    
    coveragePanel = new CoveragePanel();
    mainPanel.add(coveragePanel, BorderLayout.SOUTH);
    coveragePanel.setPreferredSize(new Dimension(900, 100));
    coveragePanel.setVisible(false);
    
tjc's avatar
tjc committed
    jspView.getVerticalScrollBar().setValue(
        jspView.getVerticalScrollBar().getMaximum());
tjc's avatar
tjc committed

tjc's avatar
tjc committed
    if(feature_display == null)
tjc's avatar
tjc committed
    {
      addKeyListener(new KeyAdapter()
tjc's avatar
tjc committed
      {
        public void keyPressed(final KeyEvent event)
tjc's avatar
tjc committed
        {
          switch (event.getKeyCode())
          {
tjc's avatar
tjc committed
          case KeyEvent.VK_UP:
tjc's avatar
tjc committed
            int startBase = getBaseAtStartOfView();
            setZoomLevel((int) (JamView.this.nbasesInView * 1.1));
tjc's avatar
tjc committed
            goToBasePosition(startBase);
tjc's avatar
tjc committed
            break;
          case KeyEvent.VK_DOWN:
            if (showBaseAlignment)
              break;
tjc's avatar
tjc committed
            startBase = getBaseAtStartOfView();
            setZoomLevel((int) (JamView.this.nbasesInView * .9));
tjc's avatar
tjc committed
            goToBasePosition(startBase);
tjc's avatar
tjc committed
            break;
          default:
            break;
tjc's avatar
tjc committed
        }
    addMouseListener(new PopupListener());
tjc's avatar
tjc committed
    setFocusable(true);
    requestFocusInWindow();
    addFocusListener(new FocusListener()
tjc's avatar
tjc committed
    {
      public void focusGained(FocusEvent fe){}
      public void focusLost(FocusEvent fe){}
tjc's avatar
tjc committed
    });
tjc's avatar
tjc committed
  private void createViewMenu(JComponent view)
tjc's avatar
tjc committed
    JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads");
    checkBoxSingle.addActionListener(new ActionListener()
tjc's avatar
tjc committed
      public void actionPerformed(ActionEvent e)
      {
        repaint();
        isSingle = !isSingle;
      }
    });
    view.add(checkBoxSingle);

    JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNPs");
    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;
        repaint();
      }
    });
    view.add(checkBoxSNPs);
tjc's avatar
tjc committed
    JCheckBoxMenuItem checkBoxStackView = new JCheckBoxMenuItem("Stack View");
    checkBoxStackView.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        laststart = -1;
        lastend = -1;
        isStackView = !isStackView;
        repaint();
      }
    });
    view.add(checkBoxStackView);
    
    
    JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage");
    checkBoxCoverage.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        isCoverage = !isCoverage;
        coveragePanel.setVisible(isCoverage);
        repaint();
      }
    });
    view.add(checkBoxCoverage);
  }
  
  public void setVisible(boolean visible)
  {
    super.setVisible(visible);
    mainPanel.setVisible(visible);
tjc's avatar
tjc committed
  }
  
tjc's avatar
tjc committed
  private int getBaseAtStartOfView()
  {
    String refName = (String) combo.getSelectedItem();
    int seqLength = seqLengths.get(refName);
    double x = jspView.getViewport().getViewRect().getX();
    return (int) (getMaxBasesInPanel(seqLength) * ( x / getWidth())) + 1;
tjc's avatar
tjc committed
  }
  
  /**
   * Set the panel size based on the number of bases visible
   * and repaint.
   * @param nbasesInView
   */
tjc's avatar
tjc committed
  private void setZoomLevel(final int nbasesInView)
  {
    this.nbasesInView = nbasesInView;
tjc's avatar
tjc committed

    //System.out.println("setZoomLevel "+nbasesInView+"  "+getBaseAtStartOfView());
tjc's avatar
tjc committed
    String refName = (String) combo.getSelectedItem();
    int seqLength = seqLengths.get(refName);
tjc's avatar
tjc committed
    float pixPerBase = getPixPerBaseByBasesInView(); 

    if(pixPerBase*3 > ALIGNMENT_PIX_PER_BASE)
tjc's avatar
tjc committed
    {
      pixPerBase = ALIGNMENT_PIX_PER_BASE;
      jspView.getVerticalScrollBar().setValue(0);
      jspView.setColumnHeaderView(ruler);
      showBaseAlignment = true;
    }
    else if(jspView != null)
    {
      jspView.setColumnHeaderView(null);
tjc's avatar
tjc committed
      jspView.getVerticalScrollBar().setValue(
          jspView.getVerticalScrollBar().getMaximum());
tjc's avatar
tjc committed
      showBaseAlignment = false;
    }
    
    Dimension d = new Dimension();
    d.setSize((getMaxBasesInPanel(seqLength)*pixPerBase), 800.d);
tjc's avatar
tjc committed
    setPreferredSize(d);
tjc's avatar
tjc committed
    revalidate();
tjc's avatar
tjc committed
  }
  
  /**
   * Set the ViewPort so it starts at the given base position.
   * @param base
   */
tjc's avatar
tjc committed
  private void goToBasePosition(int base)
tjc's avatar
tjc committed
  {
    Point p = jspView.getViewport().getViewPosition();
tjc's avatar
tjc committed
    String refName = (String) combo.getSelectedItem();
    int seqLength = seqLengths.get(refName);
tjc's avatar
tjc committed
    
tjc's avatar
tjc committed
    float width = (float) getPreferredSize().getWidth();
tjc's avatar
tjc committed
    
    p.x = Math.round(width*((float)(base-1)/(getMaxBasesInPanel(seqLength))));
    //System.out.println("goToBasePosition "+base);
tjc's avatar
tjc committed
    jspView.getViewport().setViewPosition(p);
  }
tjc's avatar
tjc committed
  /**
   * Set the start and end base positions to display.
   * @param start
   * @param end
   * @param event
tjc's avatar
tjc committed
   */
  public void setDisplay(int start,
                         int end,
                         DisplayAdjustmentEvent event)
tjc's avatar
tjc committed
  {
    this.startBase = start;
    this.endBase   = end;
tjc's avatar
tjc committed
    this.nbasesInView = end-start+1;
tjc's avatar
tjc committed
    String refName = (String) combo.getSelectedItem();
    int seqLength = seqLengths.get(refName);
tjc's avatar
tjc committed
    float pixPerBase;
    if(jspView.getViewport().getViewRect().width > 0)
      pixPerBase = this.getPixPerBaseByWidth(); // getPixPerBaseByBasesInView();
tjc's avatar
tjc committed
    else
    {
      if(feature_display == null)
        pixPerBase = 1000.f/(float)(end-start+1);
      else
        pixPerBase = feature_display.getWidth()/(float)(end-start+1);
    }

    if(pixPerBase*3 > ALIGNMENT_PIX_PER_BASE)
tjc's avatar
tjc committed
    {
tjc's avatar
tjc committed
      pixPerBase = ALIGNMENT_PIX_PER_BASE;
tjc's avatar
tjc committed
      jspView.getVerticalScrollBar().setValue(0);
tjc's avatar
tjc committed
      jspView.setColumnHeaderView(ruler);
      showBaseAlignment = true;
tjc's avatar
tjc committed
    }
tjc's avatar
tjc committed
    else if(jspView != null)
tjc's avatar
tjc committed
    {
tjc's avatar
tjc committed
      jspView.setColumnHeaderView(null);
      showBaseAlignment = false;
tjc's avatar
tjc committed
    }
tjc's avatar
tjc committed
    Dimension d = new Dimension();
    d.setSize((getMaxBasesInPanel(seqLength)*pixPerBase), 800.d);
    
    //System.out.println("setDisplay() "+d.getWidth());
tjc's avatar
tjc committed
    setPreferredSize(d);
    goToBasePosition(startBase);
    
tjc's avatar
tjc committed
    if(event == null)
    {
      this.startBase = -1;
      this.endBase   = -1;
    }
tjc's avatar
tjc committed
  /**
   * 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);
tjc's avatar
tjc committed
    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();
tjc's avatar
tjc committed
      
tjc's avatar
tjc committed
      if(bases == null)
tjc's avatar
tjc committed
      {
tjc's avatar
tjc committed
        entry = new Entry(new_embl_entry);
tjc's avatar
tjc committed
        bases = entry.getBases();
      }
tjc's avatar
tjc committed
      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 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()) 
tjc's avatar
tjc committed

    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;
    MarkerRange drag_range;
    try
      drag_range = new MarkerRange (bases.getForwardStrand(), start, start);
      getSelection().setMarkerRange(drag_range);
      repaint();
    catch (OutOfRangeException e)
      e.printStackTrace();
    }
  }
  
  private Selection getSelection()
  {
    return selection;
  }
  
  private class CoveragePanel extends JPanel
  {
	private static final long serialVersionUID = 1L;

    private int start;
	private int end;
	private float pixPerBase;
    
    public CoveragePanel()
    {
      super();
      setBackground(Color.white);
    }
    
	/**
	 * Override
	 */
	protected void paintComponent(Graphics g)
	{
	  super.paintComponent(g);
	  Graphics2D g2 = (Graphics2D)g;
	  
	  if(readsInView == null)
	    return;
	  
	  int windowSize = 10;
	  int nBins = Math.round((end-start+1.f)/windowSize);
	  float coverage[] = new float[nBins];
	  for(int i=0; i<coverage.length; i++)
	    coverage[i] = 0.f;
	  
	  float max = 0.f;
	  for(int i=0; i<readsInView.size(); i++)
	  {
	    SAMRecord thisRead = readsInView.get(i);
	    int length = thisRead.getReadLength();
	    
	    for(int j=0; j<length;j++)
	    {
	      int bin = 
	        Math.round(((thisRead.getAlignmentStart()-start) + j) / windowSize);

	      if(bin < 0 || bin > coverage.length-1)
	        continue;   
	      coverage[bin]+=1;
	      if(coverage[bin] > max)
	        max = coverage[bin];
	    }
	  }
	  
	  g2.setColor(Color.blue);
	  GeneralPath shape = new GeneralPath();
	  shape.moveTo(0,getHeight());
	  for(int i=0; i<coverage.length; i++)
	  {
	    float xpos = ((i*(windowSize)) - windowSize/2.f)*pixPerBase;
	    shape.lineTo(xpos,
	        getHeight() - ((coverage[i]/max)*getHeight()));
	  }
	        

	  shape.lineTo(getWidth(),getHeight());
	  g2.fill(shape);
	}
	
	protected void setStartAndEnd(int start, int end)
	{
	  this.start = start;
	  this.end = end;
	}

	public void setPixPerBase(float pixPerBase)
	{
	  this.pixPerBase = pixPerBase;
	}
  }
  
tjc's avatar
tjc committed
  private class Ruler extends JPanel
tjc's avatar
tjc committed
  {
tjc's avatar
tjc committed
    private static final long serialVersionUID = 1L;
tjc's avatar
tjc committed
    int start;
    int end;
tjc's avatar
tjc committed

tjc's avatar
tjc committed
    public Ruler()
    {
      super();
      setPreferredSize(new Dimension(getPreferredSize().width, 15));
      setBackground(Color.white);
      setFont(getFont().deriveFont(11.f));
    }
tjc's avatar
tjc committed

tjc's avatar
tjc committed
    public void paintComponent(Graphics g)
    {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D)g;
      drawBaseScale(g2, start, end, 12);
    }
tjc's avatar
tjc committed

    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)
      {
tjc's avatar
tjc committed
        int xpos = (i-start)*ALIGNMENT_PIX_PER_BASE;
tjc's avatar
tjc committed
        g2.drawString(Integer.toString(i), xpos, ypos);
        
        xpos+=(ALIGNMENT_PIX_PER_BASE/2);
        g2.drawLine(xpos, ypos+1, xpos, ypos+5);
      }
    }
tjc's avatar
tjc committed
  }
  
  /**
  * Popup menu listener
  */
  class PopupListener extends MouseAdapter
  {
    public void mouseClicked(MouseEvent e)
    {
      JamView.this.requestFocus();
      handleCanvasMouseDragOrClick(e);
    }
    
    public void mousePressed(MouseEvent e)
    {
      maybeShowPopup(e);
    }

    public void mouseReleased(MouseEvent e)
    {
      maybeShowPopup(e);
    }

    private void maybeShowPopup(MouseEvent e)
    {
      if(e.isPopupTrigger())
tjc's avatar
tjc committed
      {
        if(popup == null)
        {
          popup = new JPopupMenu();
          createViewMenu(popup);
        }
        popup.show(e.getComponent(),
                e.getX(), e.getY());
tjc's avatar
tjc committed
      }
tjc's avatar
tjc committed
  class SAMRecordComparator implements Comparator<Object>
tjc's avatar
tjc committed
  {
    public int compare(Object o1, Object o2) 
    {
tjc's avatar
tjc committed
      SAMRecord pr1 = (SAMRecord) o1;
      SAMRecord pr2 = (SAMRecord) o2;
tjc's avatar
tjc committed
      
tjc's avatar
tjc committed
      int cmp = pr1.getReadName().compareTo(pr2.getReadName());
      if(cmp == 0)
      {
        if(pr1.getAlignmentStart() < pr2.getAlignmentStart())
          return -1;
        else
          return 1;
tjc's avatar
tjc committed
      return cmp;
tjc's avatar
tjc committed
    }
  }
tjc's avatar
tjc committed

tjc's avatar
tjc committed

tjc's avatar
tjc committed
  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;
    }
  }
tjc's avatar
tjc committed
  
  /**
   * Artemis event notification
   */
  public void displayAdjustmentValueChanged(DisplayAdjustmentEvent event)
  {
tjc's avatar
tjc committed
    if(event.getType() == DisplayAdjustmentEvent.SCALE_ADJUST_EVENT)
    {
tjc's avatar
tjc committed
      laststart = -1;
      lastend = -1;
      this.startBase = event.getStart();
      this.endBase   = event.getEnd();
      
      //int width = event.getEnd()-event.getStart()+1;
      int width = feature_display.getMaxVisibleBases();
      //System.out.println("displayAdjustmentValueChanged() "+event.getStart()+".."+event.getEnd()+"  +width");
tjc's avatar
tjc committed

      setZoomLevel(width);
      goToBasePosition(event.getStart());
tjc's avatar
tjc committed
      setDisplay(event.getStart(), event.getEnd(), event);
tjc's avatar
tjc committed
      repaint();
tjc's avatar
tjc committed
    }
    else
    {
      setDisplay(event.getStart(), event.getEnd(), event);
tjc's avatar
tjc committed
      repaint();
tjc's avatar
tjc committed
    }
  public void selectionChanged(SelectionChangeEvent event)
  {
    repaint();
  }
  
  
  
tjc's avatar
tjc committed
  public static void main(String[] args)
  {
    String bam = args[0];
tjc's avatar
tjc committed
    int nbasesInView = 1000;
tjc's avatar
tjc committed
    String reference = null;
    
tjc's avatar
tjc committed
    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");*/
tjc's avatar
tjc committed

        System.exit(0);
      }
    }

tjc's avatar
tjc committed
    final JamView view = new JamView(bam, reference, nbasesInView);
tjc's avatar
tjc committed
    JFrame frame = new JFrame("JAM");
tjc's avatar
tjc committed
    
    frame.addWindowFocusListener(new WindowFocusListener()
    {
      public void windowGainedFocus(WindowEvent e)
      {
        view.requestFocus();
      }
      public void windowLostFocus(WindowEvent e){}
    });
    
tjc's avatar
tjc committed
    view.addJamToPanel((JPanel)frame.getContentPane(), frame, false, null);
tjc's avatar
tjc committed
    frame.pack();
tjc's avatar
tjc committed
    view.jspView.getVerticalScrollBar().setValue(
        view.jspView.getVerticalScrollBar().getMaximum());
    frame.setVisible(true);
tjc's avatar
tjc committed
  }
tjc's avatar
tjc committed
}