Skip to content
Snippets Groups Projects
BamView.java 123 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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
       */
    
      private void drawSequence(Graphics2D g2, 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;
    
        String readSeq = samRecord.getReadString();
    
    tjc's avatar
    tjc committed
        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())
    
              if(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() ||
    
             lstStart != recordStart || lstEnd != recordEnd || snps != null)
    
            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
      }
      
      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;
    
    tjc's avatar
    tjc committed
          if( samRecord.getReadNegativeStrandFlag() == isStrandNegative )
          {
    
    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() ||
    
                lstStart != recordStart || lstEnd != recordEnd || snps != null)
    
    tjc's avatar
    tjc committed
            { 
    
              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(   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 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;
    
        if(!cbCoverageStrandView.isSelected())
        {
    
          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){}
    
        g2.translate(0, getHeight()-hgt-scaleHeight);
    
        coverageView.drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK);
    
        coverageView.draw(g2, getWidth(), hgt);
        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
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 100, ypos);
    
    tjc's avatar
    tjc committed
      }
      
    
    tjc's avatar
    tjc committed
      private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division, int ypos)
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        int markStart = (Math.round(start/division)*division);
        
        if(markStart < 1)
          markStart = 1;
        
    
    tjc's avatar
    tjc committed
        int sm = markStart-(division/2);
    
    tjc's avatar
    tjc committed
        float x;
    
    tjc's avatar
    tjc committed
        if(sm > start)
    
    tjc's avatar
    tjc committed
        {
          x = (sm-getBaseAtStartOfView())*pixPerBase;
    
    tjc's avatar
    tjc committed
          g2.drawLine((int)x, ypos-14,(int)x, ypos-12);
    
    tjc's avatar
    tjc committed
        }
    
    tjc's avatar
    tjc committed
        
        for(int m=markStart; m<end; m+=division)
        {
    
    tjc's avatar
    tjc committed
          x = (m-getBaseAtStartOfView())*pixPerBase;
    
    tjc's avatar
    tjc committed
          g2.drawString(Integer.toString(m), x, ypos-1);
          g2.drawLine((int)x, ypos-14,(int)x, ypos-11);
    
    tjc's avatar
    tjc committed
          
          sm = m+(division/2);
          
          if(sm < end)
    
    tjc's avatar
    tjc committed
          {
            x = (sm-getBaseAtStartOfView())*pixPerBase;
    
    tjc's avatar
    tjc committed
            g2.drawLine((int)x, ypos-14,(int)x, ypos-12);
    
    tjc's avatar
    tjc committed
          }
    
    tjc's avatar
    tjc committed
          
          if(m == 1)
            m = 0;
    
      /**
       * Draw a y-scale for inferred size (isize) of reads.
       * @param g2
       * @param xScaleHeight
       */
    
    tjc's avatar
    tjc committed
      private void drawYScale(Graphics2D g2, int xScaleHeight)
    
      {
        g2.setColor(Color.black);
    
        int maxY = getPreferredSize().height-xScaleHeight;
    
        if(logScale)
        {
          int start = 10;
          int count = 0;
          int ypos = getYPos(xScaleHeight, start);
          
          while(ypos > 0 && count < 15 && start > 1)
          {
            g2.drawLine(0, ypos, 2, ypos);
            g2.drawString(Integer.toString(start), 3, ypos);
            start = start*5;
            ypos = getYPos(xScaleHeight, start);
            count++;
          }
          return;
        }
        
    
        for(int i=100; i<maxY; i+=100)
        {
    
          int ypos = getHeight()-i-xScaleHeight;
    
    tjc's avatar
    tjc committed
          g2.drawLine(0, ypos, 2, ypos);
          g2.drawString(Integer.toString(i), 3, ypos);
    
      /**
       * Draw a given read.
       * @param g2
       * @param thisRead
       * @param pixPerBase
       * @param ypos
    
       * @param baseAtStartOfView
    
    tcarver's avatar
    tcarver committed
      private void drawRead(Graphics2D g2, 
    
          final BamViewRecord bamViewRecord,
    
    tcarver's avatar
    tcarver committed
          final float pixPerBase,
          final int ypos,
    
    tcarver's avatar
    tcarver committed
          final List<Integer> snps)