Skip to content
Snippets Groups Projects
BamView.java 131 KiB
Newer Older
tcarver's avatar
tcarver committed
	  {
	    if(isCoverageView(pixPerBase))
	      drawCoverage(g2,start, end, pixPerBase);
  	    else if(isStackView())  
	      drawStackView(g2, seqLength, pixPerBase, start, end);
	    else if(isPairedStackView())
	      drawPairedStackView(g2, seqLength, pixPerBase, start, end);
	    else if(isStrandStackView())
	      drawStrandStackView(g2, seqLength, pixPerBase, start, end);
	    else
	      drawLineView(g2, seqLength, pixPerBase, start, end);
	  }
    }
    
    if(isCoverage)
      coveragePanel.repaint();
    if(isSNPplot)
      snpPanel.repaint();
tjc's avatar
tjc committed
	if(waitingFrame.isVisible())
      waitingFrame.hideFrame();
tjc's avatar
tjc committed
	  popFrame.show(
          "Note :: Changed to the stack view to save memory.\n"+
          "Currently this is using "+ 
          (memory.getHeapMemoryUsage().getUsed()/1000000.f)+" Mb "+
          "and the maximum\nmemory limit is "+
          (memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.",
tjc's avatar
tjc committed
          mainPanel,
tjc's avatar
tjc committed
  }
  
tjc's avatar
tjc committed
  
  private float getPixPerBaseByWidth()
  {
    return (float)mainPanel.getWidth() / (float)nbasesInView;
tjc's avatar
tjc committed
  }
  
  
tjc's avatar
tjc committed
  private int getMaxBasesInPanel(int seqLength)
  {
    if(feature_display == null)
tjc's avatar
tjc committed
      return seqLength+nbasesInView;
tjc's avatar
tjc committed
  
tjc's avatar
tjc committed
  /**
   * Draw the zoomed-in base view.
   * @param g2
   * @param seqLength
   * @param pixPerBase
   * @param start
   * @param end
   */
  private void drawBaseAlignment(Graphics2D g2, 
                                 int seqLength, 
                                 float pixPerBase, 
                                 final int start, 
tjc's avatar
tjc committed
  {
tjc's avatar
tjc committed
    ruler.start = start;
    ruler.end = end;
    String refSeq = null;
tjc's avatar
tjc committed
    int refSeqStart = start;
    
    end = start + ( mainPanel.getWidth() * ALIGNMENT_PIX_PER_BASE );
tjc's avatar
tjc committed
    if(bases != null)
    {
      try
      {
        int seqEnd = end+2;
tjc's avatar
tjc committed
        if(seqEnd > bases.getLength())
          seqEnd = bases.getLength();
tjc's avatar
tjc committed

        if(refSeqStart < 1)
          refSeqStart = 1;
        refSeq = 
          bases.getSubSequence(new Range(refSeqStart, seqEnd), Bases.FORWARD).toUpperCase();
tjc's avatar
tjc committed
        
tjc's avatar
tjc committed
      }
      catch (OutOfRangeException e)
      {
        e.printStackTrace();
      }
    }
    ruler.repaint();
    drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end, Color.PINK);
    g2.setStroke(new BasicStroke (2.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
tjc's avatar
tjc committed
    
tjc's avatar
tjc committed
    boolean drawn[] = new boolean[readsInView.size()];
tjc's avatar
tjc committed
    for(int i=0; i<readsInView.size(); i++)
tjc's avatar
tjc committed
      drawn[i] = false;
tjc's avatar
tjc committed
    
    Rectangle r = jspView.getViewport().getViewRect();
    int nreads = readsInView.size();
    
tjc's avatar
tjc committed
    for (int i = 0; i < nreads; i++)
tjc's avatar
tjc committed
    {
tjc's avatar
tjc committed
      try
tjc's avatar
tjc committed
      {
tjc's avatar
tjc committed
        if (!drawn[i])
tjc's avatar
tjc committed
        {
tjc's avatar
tjc committed
          ypos += 11;

          BamViewRecord thisRead = readsInView.get(i);
tjc's avatar
tjc committed
          if (ypos < r.getMaxY() || ypos > r.getMinY())
            drawSequence(g2, thisRead, ypos, refSeq, refSeqStart);
          drawn[i] = true;

          int thisEnd = thisRead.sam.getAlignmentEnd();
tjc's avatar
tjc committed
          if (thisEnd == 0)
            thisEnd = thisRead.sam.getAlignmentStart() + thisRead.sam.getReadLength();
tjc's avatar
tjc committed

          for (int j = i + 1; j < nreads; j++)
tjc's avatar
tjc committed
          {
tjc's avatar
tjc committed
            if (!drawn[j])
tjc's avatar
tjc committed
            {
              BamViewRecord nextRead = readsInView.get(j);
              int nextStart = nextRead.sam.getAlignmentStart();
tjc's avatar
tjc committed
              if (nextStart > thisEnd + 1)
              {
                if (ypos < r.getMaxY() || ypos > r.getMinY())
                  drawSequence(g2, nextRead, ypos, refSeq, refSeqStart);

                drawn[j] = true;
                thisEnd = nextRead.sam.getAlignmentEnd();
tjc's avatar
tjc committed
                if (thisEnd == 0)
                  thisEnd = nextStart + nextRead.sam.getReadLength();
tjc's avatar
tjc committed
              }
              else if (ypos > r.getMaxY() || ypos < r.getMinY())
                break;
tjc's avatar
tjc committed
            }
          }
        }
      }
tjc's avatar
tjc committed
      catch (ArrayIndexOutOfBoundsException ae)
      {
        System.err.println(readsInView.size()+"  "+nreads);
        ae.printStackTrace();
      }
tjc's avatar
tjc committed
    }
tjc's avatar
tjc committed
    
    if(ypos > getHeight())
    {
tjc's avatar
tjc committed
      Dimension d = getPreferredSize();
      d.setSize(getPreferredSize().getWidth(), ypos);
      setPreferredSize(d);
tjc's avatar
tjc committed
      revalidate();
    }
tjc's avatar
tjc committed
  }
tjc's avatar
tjc committed
  
tjc's avatar
tjc committed
  /**
   * Draw the query sequence
   * @param g2
   * @param read
   * @param pixPerBase
   * @param ypos
   */
tcarver's avatar
tcarver committed
  private void drawSequence(final Graphics2D g2, final BamViewRecord bamViewRecord, 
                            int ypos, String refSeq, int refSeqStart)
tjc's avatar
tjc committed
  {
    SAMRecord samRecord = bamViewRecord.sam;
tjc's avatar
tjc committed
    if (!samRecord.getReadPairedFlag() ||  // read is not paired in sequencing
        samRecord.getMateUnmappedFlag() )  // mate is unmapped )  // mate is unmapped 
tjc's avatar
tjc committed
      g2.setColor(Color.black);
    else
      g2.setColor(Color.blue);
    
    final Color col = g2.getColor();
tjc's avatar
tjc committed
    int xpos;
    int len    = 0;
    int refPos = 0;
tcarver's avatar
tcarver committed
    final String readSeq = samRecord.getReadString();
    final int offset = getSequenceOffset(samRecord.getReferenceName());
    byte[] phredQuality = null;
    if(baseQualityColour.isSelected())
      phredQuality = samRecord.getBaseQualities();

    Hashtable<Integer, String> insertions = null;
    List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks();
    for(int i=0; i<blocks.size(); i++)
tjc's avatar
tjc committed
    {
      AlignmentBlock block = blocks.get(i);
      int blockStart = block.getReadStart();
      len += block.getLength();
      for(int j=0; j<block.getLength(); j++)
tjc's avatar
tjc committed
      {
        int readPos = blockStart-1+j;
        xpos = block.getReferenceStart() - 1 + j + offset;
        refPos = xpos - refSeqStart + 1;

        if(phredQuality != null)
          setColourByBaseQuality(g2, phredQuality[readPos]);
tjc's avatar
tjc committed
        if(isSNPs && refSeq != null && refPos > 0 && refPos < refSeq.length())
tcarver's avatar
tcarver committed
          if(Character.toUpperCase(readSeq.charAt(readPos)) != refSeq.charAt(refPos))
            g2.setColor(Color.red);
          else
            g2.setColor(col);
        }
tjc's avatar
tjc committed
        g2.drawString(readSeq.substring(readPos, readPos+1), 
                      refPos*ALIGNMENT_PIX_PER_BASE, ypos);
tjc's avatar
tjc committed
      }
      // look for insertions
      if(markInsertions.isSelected() && i < blocks.size()-1)
      {
        int blockEnd = blockStart+block.getLength();
        int nextBlockStart = blocks.get(i+1).getReadStart();
        int insertSize = nextBlockStart - blockEnd;
        if(insertSize > 0)
        {
          if(insertions == null)
            insertions = new Hashtable<Integer, String>();
tjc's avatar
tjc committed

          g2.setColor(DEEP_PINK);
tjc's avatar
tjc committed

          int xscreen = (refPos+1)*ALIGNMENT_PIX_PER_BASE;
          insertions.put(xscreen, 
              readSeq.substring(blockEnd-1, nextBlockStart-1));
          g2.drawLine(xscreen, ypos, xscreen, ypos-BASE_HEIGHT);
          
          // mark on reference sequence as well
          if(bases != null)
            g2.drawLine(xscreen, 11, xscreen, 11-BASE_HEIGHT);
          g2.setColor(col);
        }
      }
      
      // highlight
      if(highlightSAMRecord != null &&
         highlightSAMRecord.sam.getReadName().equals(samRecord.getReadName()))
      {
        refPos =  block.getReferenceStart() + offset - refSeqStart;
        int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
        int width  = block.getLength()*ALIGNMENT_PIX_PER_BASE;
        Color col1 = g2.getColor();
        g2.setColor(Color.red);
        g2.drawRect(xstart, ypos-BASE_HEIGHT, width, BASE_HEIGHT);        
        if(i < blocks.size()-1)
        {
          int nextStart = 
            (blocks.get(i+1).getReferenceStart() + offset - refSeqStart)*ALIGNMENT_PIX_PER_BASE;
          g2.drawLine(xstart+width, ypos-(BASE_HEIGHT/2), nextStart, ypos-(BASE_HEIGHT/2));
        }
        
        g2.setColor(col1);
      }
      else if(i < blocks.size()-1)
      {
        refPos =  block.getReferenceStart() + offset - refSeqStart;
        int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
        int width  = block.getLength()*ALIGNMENT_PIX_PER_BASE;
        int nextStart = 
          (blocks.get(i+1).getReferenceStart() + offset - refSeqStart)*ALIGNMENT_PIX_PER_BASE;
        g2.drawLine(xstart+width, ypos-(BASE_HEIGHT/2), nextStart, ypos-(BASE_HEIGHT/2));
      }
tjc's avatar
tjc committed
    }
    if(lastMousePoint != null && blocks.size() > 0)
      refPos = blocks.get(0).getReferenceStart()+offset-refSeqStart;
tjc's avatar
tjc committed
      int xstart = refPos*ALIGNMENT_PIX_PER_BASE;
      
      refPos = blocks.get(blocks.size()-1).getReferenceStart()+
               blocks.get(blocks.size()-1).getLength()+offset-refSeqStart;
tjc's avatar
tjc committed
      int xend   = (refPos+len)*ALIGNMENT_PIX_PER_BASE;

      if(lastMousePoint.getY() > ypos-11 && lastMousePoint.getY() < ypos)
      if(lastMousePoint.getX() > xstart &&
         lastMousePoint.getX() < xend)
      {
        mouseOverSAMRecord = bamViewRecord;
        if(insertions != null)
          mouseOverInsertion = insertions.get((int)lastMousePoint.getX());
tjc's avatar
tjc committed
  }
  
  /**
   * Colour bases on their mapping quality.
   * @param g2
   * @param baseQuality
   */
  private void setColourByBaseQuality(Graphics2D g2, byte baseQuality)
  {
    if (baseQuality < 10)
      g2.setColor(Color.blue);
    else if (baseQuality < 20)
      g2.setColor(DARK_GREEN);
    else if (baseQuality < 30)
      g2.setColor(DARK_ORANGE);
tjc's avatar
tjc committed
  /**
   * Draw inferred size view.
tjc's avatar
tjc committed
   * @param g2
   * @param seqLength
   * @param pixPerBase
   * @param start
   * @param end
   */
tjc's avatar
tjc committed
  private void drawLineView(Graphics2D g2, int seqLength, float pixPerBase, int start, int end)
    drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);
tjc's avatar
tjc committed
    if(isShowScale())
tjc's avatar
tjc committed
      drawScale(g2, start, end, pixPerBase, getHeight());
tjc's avatar
tjc committed
    
tcarver's avatar
tcarver committed
    final Stroke stroke =
      new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
    g2.setStroke(stroke);
    
tcarver's avatar
tcarver committed
    final int scaleHeight;
    if(isShowScale())
      scaleHeight = 15;
    else
      scaleHeight = 0;
tjc's avatar
tjc committed
    
    int baseAtStartOfView = getBaseAtStartOfView();
    Rectangle r = jspView.getViewport().getViewRect();
tjc's avatar
tjc committed
    for(int i=0; i<readsInView.size(); i++)
    {
      BamViewRecord bamViewRecord = readsInView.get(i);
      SAMRecord samRecord = bamViewRecord.sam;
      BamViewRecord bamViewNextRecord = null;
tjc's avatar
tjc committed
      SAMRecord samNextRecord = null;      
tjc's avatar
tjc committed

tcarver's avatar
tcarver committed
      List<Integer> snps = getSNPs(samRecord);
tjc's avatar
tjc committed
      if( !samRecord.getReadPairedFlag() ||  // read is not paired in sequencing
tjc's avatar
tjc committed
          samRecord.getMateUnmappedFlag() )  // mate is unmapped
tjc's avatar
tjc committed
      {
tjc's avatar
tjc committed
        if(isSingle)
tjc's avatar
tjc committed
        {
          int ypos = getYPos(scaleHeight, samRecord.getReadString().length()); // (getHeight() - scaleHeight) - samRecord.getReadString().length();
          if(ypos > r.getMaxY() || ypos < r.getMinY())
tjc's avatar
tjc committed
          g2.setColor(Color.black);
          drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
tjc's avatar
tjc committed
        }
tjc's avatar
tjc committed
        continue;
tjc's avatar
tjc committed
      }
tjc's avatar
tjc committed

      int ypos = getYPos(scaleHeight, Math.abs(samRecord.getInferredInsertSize()));
      if( (ypos > r.getMaxY() || ypos < r.getMinY()) && ypos > 0 )
tjc's avatar
tjc committed
      if(i < readsInView.size()-1)
      {
        bamViewNextRecord = readsInView.get(++i);
        samNextRecord = bamViewNextRecord.sam;
tjc's avatar
tjc committed

tjc's avatar
tjc committed
        if(samRecord.getReadName().equals(samNextRecord.getReadName()))
tjc's avatar
tjc committed
        { 
          // draw connection between paired reads
tjc's avatar
tjc committed
          if(samRecord.getAlignmentEnd() < samNextRecord.getAlignmentStart() && 
              (samNextRecord.getAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
tjc's avatar
tjc committed
          {
        	g2.setColor(Color.LIGHT_GRAY);
            int offset1 = getSequenceOffset(samRecord.getReferenceName());
            int end1   = samRecord.getAlignmentEnd()+offset1-baseAtStartOfView;
            
            int offset2 = getSequenceOffset(samNextRecord.getReferenceName());
            int start2  = samNextRecord.getAlignmentStart()+offset2-baseAtStartOfView;
            
            drawTranslucentLine(g2, 
                   (int)(end1*pixPerBase), (int)(start2*pixPerBase), ypos);
tjc's avatar
tjc committed
          }
tjc's avatar
tjc committed
          
          if(colourByCoverageColour.isSelected())
            g2.setColor(getColourByCoverageColour(bamViewRecord));
          else if( (samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
                    samNextRecord.getReadNegativeStrandFlag()) ||
                   (!samRecord.getReadNegativeStrandFlag() && 
                    !samNextRecord.getReadNegativeStrandFlag()))
tjc's avatar
tjc committed
            g2.setColor(Color.red);
          else
            g2.setColor(Color.blue);
          drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
          drawRead(g2, bamViewNextRecord, pixPerBase, ypos, baseAtStartOfView, getSNPs(samNextRecord));
tjc's avatar
tjc committed
        }
        else
tjc's avatar
tjc committed
        {
          drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps);
tjc's avatar
tjc committed
          i--;
tjc's avatar
tjc committed
        }
      }
      else
      {
        drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps);
tjc's avatar
tjc committed
      }
tjc's avatar
tjc committed
    }
tjc's avatar
tjc committed
    drawYScale(g2, scaleHeight);
tjc's avatar
tjc committed
  }
  
  private int getYPos(int scaleHeight, int size)
  {
    if(!logScale)
tcarver's avatar
tcarver committed
      return (getHeight() - scaleHeight) - size;
    else
    {
      int logInfSize = (int)( Math.log(size) * 100);
tcarver's avatar
tcarver committed
      return (getHeight() - scaleHeight) - logInfSize;
  /**
   * Draw the reads as lines in vertical stacks. The reads are colour 
   * coded as follows:
   * 
   * blue  - reads are unique and are paired with a mapped mate
   * black - reads are unique and are not paired or have an unmapped mate
   * green - reads are duplicates
   * 
   * @param g2
   * @param seqLength
   * @param pixPerBase
   * @param start
   * @param end
   */
  private void drawStackView(Graphics2D g2, 
tcarver's avatar
tcarver committed
                             final int seqLength, 
                             final float pixPerBase, 
                             final int start, 
                             final int end)
    drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);
    if(isShowScale())
tjc's avatar
tjc committed
      drawScale(g2, start, end, pixPerBase, getHeight());
tcarver's avatar
tcarver committed
    final BasicStroke stroke = new BasicStroke(
        1.3f,
        BasicStroke.CAP_BUTT, 
        BasicStroke.JOIN_MITER);
    g2.setStroke(stroke);
tcarver's avatar
tcarver committed
    final int scaleHeight;
    if(isShowScale())
      scaleHeight = 15;
    else
      scaleHeight = 0;
    
    int ypos = (getHeight() - scaleHeight);
    int ydiff = 2;
    if(isOrientation)
      ydiff= 4;
tcarver's avatar
tcarver committed
    final int baseAtStartOfView = getBaseAtStartOfView();
    g2.setColor(Color.blue);
tcarver's avatar
tcarver committed
    final Rectangle r = jspView.getViewport().getViewRect();
tcarver's avatar
tcarver committed
    for(BamViewRecord bamViewRecord: readsInView)
      SAMRecord samRecord = bamViewRecord.sam;
      int offset = getSequenceOffset(samRecord.getReferenceName());
      int recordStart = samRecord.getAlignmentStart()+offset;
      int recordEnd = samRecord.getAlignmentEnd()+offset;
tcarver's avatar
tcarver committed
      List<Integer> snps = getSNPs(samRecord);
      if(colourByCoverageColour.isSelected() || colourByStrandTag.isSelected() ||
         lstStart != recordStart || lstEnd != recordEnd || snps != null)
      {
        if(colourByStrandTag.isSelected())
        {
          if(samRecord.getAttribute("XS") == null)
            g2.setColor(Color.BLACK); 
          else if( ((Character)samRecord.getAttribute("XS")).equals('+') )
            g2.setColor(Color.BLUE);
          else if( ((Character)samRecord.getAttribute("XS")).equals('-') )
            g2.setColor(Color.RED);
          else 
            g2.setColor(Color.BLACK); 
        }
        else if(colourByCoverageColour.isSelected())
          g2.setColor(getColourByCoverageColour(bamViewRecord));
        else if (!samRecord.getReadPairedFlag() ||   // read is not paired in sequencing
                  samRecord.getMateUnmappedFlag() )  // mate is unmapped )  // mate is unmapped 
        if(maxEnd < recordStart || ypos < 0)
          ypos = (getHeight() - scaleHeight)-ydiff;
          ypos = ypos-ydiff;
        g2.setColor(DARK_GREEN);
      if(snps != null)
        lstStart = -1;
      else
      {
        lstStart = recordStart;
        lstEnd   = recordEnd;
      }
      if(ypos > r.getMaxY() || ypos < r.getMinY())
      drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
tjc's avatar
tjc committed
  /**
   * Draw the reads as lines in vertical stacks. The reads are colour 
   * coded as follows:
   * 
   * blue  - reads are unique and are paired with a mapped mate
   * black - reads are unique and are not paired or have an unmapped mate
   * green - reads are duplicates
   * 
   * @param g2
   * @param seqLength
   * @param pixPerBase
   * @param start
   * @param end
   */
  private void drawStrandStackView(Graphics2D g2, 
                                   int seqLength, 
                                   float pixPerBase, 
                                   int start, 
                                   int end)
  {
    drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);   
tcarver's avatar
tcarver committed
    final BasicStroke stroke = new BasicStroke(
tjc's avatar
tjc committed
        1.3f,
        BasicStroke.CAP_BUTT, 
        BasicStroke.JOIN_MITER);
    
tcarver's avatar
tcarver committed
    final int scaleHeight = 15;
tjc's avatar
tjc committed
    drawScale(g2, start, end, pixPerBase, ((getHeight()+scaleHeight)/2));

    int ymid = (getHeight()/ 2);
    int ydiff = 2;
    if(isOrientation)
      ydiff= 4;
tjc's avatar
tjc committed
    // positive strand    
    drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -ydiff, pixPerBase, stroke);
tjc's avatar
tjc committed
    
    // negative strand
    drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), ydiff, pixPerBase, stroke);
tjc's avatar
tjc committed
  }
  
tjc's avatar
tjc committed
  private void drawStrand(Graphics2D g2, 
                          boolean isStrandNegative, 
                          int scaleHeight,
                          int ymid,
                          int ystep,
                          float pixPerBase,
                          Stroke stroke)
  {
    int hgt = getHeight();
    int ypos = (hgt - scaleHeight);
tjc's avatar
tjc committed
    int maxEnd = 0;
    int lstStart = 0;
    int lstEnd = 0;
    int baseAtStartOfView = getBaseAtStartOfView();
tjc's avatar
tjc committed
    g2.setColor(Color.blue);
    Rectangle r = jspView.getViewport().getViewRect();
tjc's avatar
tjc committed
    
tcarver's avatar
tcarver committed
    for(BamViewRecord bamViewRecord: readsInView)
tjc's avatar
tjc committed
    {
      SAMRecord samRecord = bamViewRecord.sam;
      if( isNegativeStrand(samRecord) == isStrandNegative )
tjc's avatar
tjc committed
      {
tcarver's avatar
tcarver committed
        final int offset = getSequenceOffset(samRecord.getReferenceName());
        final int recordStart = samRecord.getAlignmentStart()+offset;
        final int recordEnd   = samRecord.getAlignmentEnd()+offset;
tcarver's avatar
tcarver committed
        List<Integer> snps = getSNPs(samRecord);
        if(colourByCoverageColour.isSelected() || colourByStrandTag.isSelected() ||
            lstStart != recordStart || lstEnd != recordEnd || snps != null)
        {
          if(colourByStrandTag.isSelected())
          {
            if(samRecord.getAttribute("XS") == null)
              g2.setColor(Color.BLACK); 
            else if( ((Character)samRecord.getAttribute("XS")).equals('+') )
              g2.setColor(Color.BLUE);
            else if( ((Character)samRecord.getAttribute("XS")).equals('-') )
              g2.setColor(Color.RED);
            else 
              g2.setColor(Color.BLACK); 
          }
          else if(colourByCoverageColour.isSelected())
            g2.setColor(getColourByCoverageColour(bamViewRecord));
          else if (!samRecord.getReadPairedFlag() ||   // read is not paired in sequencing
                    samRecord.getMateUnmappedFlag() )  // mate is unmapped 
tjc's avatar
tjc committed
            g2.setColor(Color.black);
          else
            g2.setColor(Color.blue);
        
          if(maxEnd < recordStart || ypos < 0 || ypos > hgt)
tjc's avatar
tjc committed
          {
            ypos = ymid + ystep;
            maxEnd = recordEnd+2;
          }
          else
            ypos = ypos + ystep;
        }
        else
          g2.setColor(DARK_GREEN);
tjc's avatar
tjc committed

        if(snps != null)
          lstStart = -1;
        else
        {
          lstStart = recordStart;
          lstEnd   = recordEnd;
        }
        if(ypos > r.getMaxY() || ypos < r.getMinY())

        drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
tjc's avatar
tjc committed
      }
    }
  }
   * Draw paired reads as lines in a vertical stacks. 
   * @param g2
   * @param seqLength
   * @param pixPerBase
   * @param start
   * @param end
   */
  private void drawPairedStackView(Graphics2D g2, 
tcarver's avatar
tcarver committed
                                   final int seqLength, 
                                   final float pixPerBase, 
                                   final int start, 
                                   final int end)
    drawSelectionRange(g2, pixPerBase,start, end, Color.PINK);
tjc's avatar
tjc committed
      drawScale(g2, start, end, pixPerBase, getHeight());

    final Vector<PairedRead> pairedReads = new Vector<PairedRead>();   
    for(int i=0; i<readsInView.size(); i++)
    {
      BamViewRecord bamViewRecord = readsInView.get(i);
      SAMRecord samRecord = bamViewRecord.sam;
      if( !samRecord.getReadPairedFlag() ||  // read is not paired in sequencing
          samRecord.getMateUnmappedFlag() )  // mate is unmapped
        continue;

      BamViewRecord bamViewNextRecord = null;      
      if(i < readsInView.size()-1)
      {
        bamViewNextRecord = readsInView.get(++i);
        SAMRecord samNextRecord = bamViewNextRecord.sam;
        
        final PairedRead pr = new PairedRead();
        if(samRecord.getReadName().equals(samNextRecord.getReadName()) && 
           isFromSameBamFile(bamViewRecord, bamViewNextRecord, bamList))
        { 
          if(samRecord.getAlignmentStart() < samNextRecord.getAlignmentStart())
          {
            pr.sam1 = bamViewRecord;
            pr.sam2 = bamViewNextRecord;
            pr.sam2 = bamViewRecord;
            pr.sam1 = bamViewNextRecord;
        pairedReads.add(pr);
      }
    }
    Collections.sort(pairedReads, new PairedReadComparator());
    
    Stroke originalStroke = new BasicStroke (1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); 
    Stroke stroke =
            new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
    g2.setStroke(stroke);
    
tcarver's avatar
tcarver committed
    final int scaleHeight;
    if(isShowScale())
      scaleHeight = 15;
    else
      scaleHeight = 0;
    
    int ydiff = 3;
    if(isOrientation)
      ydiff= 5;
    int ypos = getHeight() - scaleHeight - ydiff;
    int baseAtStartOfView = getBaseAtStartOfView();
    Rectangle r = jspView.getViewport().getViewRect();
tcarver's avatar
tcarver committed
    for(PairedRead pr: pairedReads)
      if(pr.sam1.sam.getAlignmentStart() > lastEnd)
        ypos = getHeight() - scaleHeight - ydiff;
          lastEnd = pr.sam2.sam.getAlignmentEnd();
          lastEnd = pr.sam1.sam.getAlignmentEnd();
        ypos = ypos - ydiff;
      if(ypos > r.getMaxY() || ypos < r.getMinY())
      g2.setStroke(originalStroke);
      
      if(highlightSAMRecord != null && 
          highlightSAMRecord.sam.getReadName().equals(pr.sam1.sam.getReadName()))
        g2.setColor(Color.black);
      else
        g2.setColor(Color.gray);
        if(!readsOverlap(pr.sam1.sam, pr.sam2.sam))
          int offset1 = getSequenceOffset(pr.sam1.sam.getReferenceName());
          int offset2 = getSequenceOffset(pr.sam2.sam.getReferenceName());
          drawTranslucentJointedLine(g2, 
              (int)((pr.sam1.sam.getAlignmentEnd()+offset1-getBaseAtStartOfView())*pixPerBase),
              (int)((pr.sam2.sam.getAlignmentStart()+offset2-getBaseAtStartOfView())*pixPerBase), ypos);
      else if(!pr.sam1.sam.getMateUnmappedFlag() &&
               pr.sam1.sam.getProperPairFlag() &&
               pr.sam1.sam.getMateReferenceName().equals(pr.sam1.sam.getReferenceName()))
        final int prStart, prEnd;
        if(pr.sam1.sam.getAlignmentStart() > pr.sam1.sam.getMateAlignmentStart())
          prStart = pr.sam1.sam.getMateAlignmentStart();
          prEnd = pr.sam1.sam.getAlignmentStart();
          prStart = pr.sam1.sam.getAlignmentEnd();
          prEnd = pr.sam1.sam.getMateAlignmentStart();
        int offset = getSequenceOffset(pr.sam1.sam.getReferenceName());
        drawTranslucentJointedLine(g2, 
              (int)( (prStart+offset-getBaseAtStartOfView())*pixPerBase),
              (int)( (prEnd  +offset-getBaseAtStartOfView())*pixPerBase), ypos);
      if(colourByCoverageColour.isSelected())
        g2.setColor(getColourByCoverageColour(pr.sam1));
      else if(colourByStrandTag.isSelected())
      {
        if( ((Character)pr.sam1.sam.getAttribute("XS")).equals('+') )
          g2.setColor(Color.BLUE);
        else if( ((Character)pr.sam1.sam.getAttribute("XS")).equals('-') )
          g2.setColor(Color.RED);
        else 
          g2.setColor(Color.BLACK); 
      }
      else if(   pr.sam2 != null && 
              !( pr.sam1.sam.getReadNegativeStrandFlag() ^ pr.sam2.sam.getReadNegativeStrandFlag() ) )
        g2.setColor(Color.red);
      else
        g2.setColor(Color.blue);

      drawRead(g2, pr.sam1, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam1.sam));
        drawRead(g2, pr.sam2, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam2.sam));
  /**
   * Check if a record is on the negative strand. If the RNA strand specific
   * checkbox is set then use the RNA strand.
   * @param samRecord
   * @return
   */
  private boolean isNegativeStrand(final SAMRecord samRecord) 
  {
    if(colourByStrandTag.isSelected())
    {
      if(samRecord.getAttribute("XS") == null)
        return samRecord.getReadNegativeStrandFlag();
      if( ((Character)samRecord.getAttribute("XS")).equals('+') )
        return false;
      else
        return true;
    }
    return samRecord.getReadNegativeStrandFlag();
  }
  
  /**
   * Check if two records are from the same BAM file
   * @param sam1
   * @param sam2
   * @param bamList
   * @return
   */
  private boolean isFromSameBamFile(final BamViewRecord sam1, 
                                    final BamViewRecord sam2, 
                                    final List<String> bamList)
  {
    if(bamList == null || bamList.size()<2)
      return true;

    final short o1 = sam1.bamIndex;
    final short o2 = sam2.bamIndex;
    if(o1 != -1 && o2 != -1)
      if( o1 != o2 )
        return false;
    
    return true;
  }
  
  
  /**
   * Check if two records overlap
   * @param s1
   * @param s2
   * @return true id the two reads overlap
   */
  private boolean readsOverlap(final SAMRecord s1, 
                               final SAMRecord s2)
  {
    if( (s2.getAlignmentStart() >= s1.getAlignmentStart() &&
         s2.getAlignmentStart() <= s1.getAlignmentEnd()) ||
        (s2.getAlignmentEnd()   >= s1.getAlignmentStart() &&
         s2.getAlignmentEnd()   <= s1.getAlignmentEnd()) )
      return true;
    
    if( (s1.getAlignmentStart() >= s2.getAlignmentStart() &&
         s1.getAlignmentStart() <= s2.getAlignmentEnd()) ||
        (s1.getAlignmentEnd()   >= s2.getAlignmentStart() &&
         s1.getAlignmentEnd()   <= s2.getAlignmentEnd()) )
     return true;
    return false;
  }
  
  /**
   * Draw the read coverage.
   * @param g2
   * @param start
   * @param end
   * @param pixPerBase
   */
  private void drawCoverage(Graphics2D g2, int start, int end, float pixPerBase)
  {
    int scaleHeight = 0;
    if(isShowScale())
    {
      drawScale(g2, start, end, pixPerBase, getHeight());
      scaleHeight = 15;
    }

    int hgt = jspView.getVisibleRect().height-scaleHeight;
tcarver's avatar
tcarver committed
    if(!cbCoverageStrandView.isSelected() && !coverageView.isPlotHeatMap())
      try
      {
        int y = getHeight()-6-( (hgt* MAX_COVERAGE)/(coverageView.max/coverageView.windowSize) );
        g2.setColor(Color.YELLOW);
        // draw the threshold for the coverage max read cut-off
        g2.fillRect(0, y, getWidth(), 8);
      }
      catch(Exception e){}
tcarver's avatar
tcarver committed
    g2.translate(0, getJspView().getViewport().getViewPosition().y);

    coverageView.drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK);
tcarver's avatar
tcarver committed
    coverageView.draw(g2, getWidth(), hgt, hideBamList);
tcarver's avatar
tcarver committed
    if(!coverageView.isPlotHeatMap())
      coverageView.drawMax(g2);  
tjc's avatar
tjc committed
  /**
   * 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, BamViewRecord bamViewRecord, int ypos, 
tcarver's avatar
tcarver committed
      float pixPerBase, int baseAtStartOfView, int scaleHeight, List<Integer> snps)
tjc's avatar
tjc committed
  {
    SAMRecord samRecord = bamViewRecord.sam;
    boolean offTheTop = false;
tjc's avatar
tjc committed
    int offset = getSequenceOffset(samRecord.getReferenceName());
    int thisStart = samRecord.getAlignmentStart()+offset;
    int thisEnd   = thisStart + samRecord.getReadString().length() -1;
tjc's avatar
tjc committed
    
    if(ypos <= 0)
    {
      offTheTop = true;
      ypos = samRecord.getReadString().length();
    }
tjc's avatar
tjc committed
    if(samRecord.getInferredInsertSize() == 0)
    {
      offTheTop = true;
      ypos = getHeight() - scaleHeight - 5;
    }
tjc's avatar
tjc committed
    if(samRecord.getInferredInsertSize() != 0 &&
      Math.abs(samRecord.getMateAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
tjc's avatar
tjc committed
    {
      g2.setColor(Color.LIGHT_GRAY);
      
      if(samRecord.getAlignmentEnd() < samRecord.getMateAlignmentStart())
      {
tjc's avatar
tjc committed
        int nextStart = 
          (int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
        drawTranslucentLine(g2, 
          (int)((thisEnd-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
tjc's avatar
tjc committed
      }
      else
      {
tjc's avatar
tjc committed
        int nextStart = 
            (int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
        drawTranslucentLine(g2, 
            (int)((thisStart-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
tjc's avatar
tjc committed
      }
    }
    
    if(colourByCoverageColour.isSelected())
      g2.setColor(getColourByCoverageColour(bamViewRecord));
    else if(offTheTop)
      g2.setColor(DARK_ORANGE); 
    else if(samRecord.getReadNegativeStrandFlag() &&
            samRecord.getMateNegativeStrandFlag()) // strand of the query (1 for reverse)
tjc's avatar
tjc committed
      g2.setColor(Color.red);
    else
      g2.setColor(Color.blue);
    drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps);
tjc's avatar
tjc committed
    
    /*if (isSNPs)
      showSNPsOnReads(g2, samRecord, pixPerBase, ypos, offset);*/
tjc's avatar
tjc committed
  }
tjc's avatar
tjc committed

tjc's avatar
tjc committed
  
tjc's avatar
tjc committed
  private void drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos)
tjc's avatar
tjc committed
  {
    g2.setColor(Color.black);
tjc's avatar
tjc committed
    g2.drawLine( 0, ypos-14,
                 (int)((end - getBaseAtStartOfView())*pixPerBase),   ypos-14);
tjc's avatar
tjc committed
    int interval = end-start;
    
    if(interval > 256000)
tjc's avatar
tjc committed
      drawTicks(g2, start, end, pixPerBase, 512000, ypos);
tjc's avatar
tjc committed
    else if(interval > 64000)
tjc's avatar
tjc committed
      drawTicks(g2, start, end, pixPerBase, 12800, ypos);
tjc's avatar
tjc committed
    else if(interval > 16000)
tjc's avatar
tjc committed
      drawTicks(g2, start, end, pixPerBase, 3200, ypos);
tjc's avatar
tjc committed
    else if(interval > 4000)
tjc's avatar
tjc committed
      drawTicks(g2, start, end, pixPerBase, 800, ypos);
tjc's avatar
tjc committed
    else if(interval > 1000)
tjc's avatar
tjc committed
      drawTicks(g2, start, end, pixPerBase, 400, ypos);
tjc's avatar
tjc committed
    else