From 4cb2a65dd173394e043f2f5886f814866efcb771 Mon Sep 17 00:00:00 2001
From: tjc <tjc@ee4ac58c-ac51-4696-9907-e4b3aa274f04>
Date: Fri, 27 Aug 2010 08:19:40 +0000
Subject: [PATCH] changes for combining reference contigs in a vcf

git-svn-id: svn+ssh://svn.internal.sanger.ac.uk/repos/svn/pathsoft/artemis/trunk@14676 ee4ac58c-ac51-4696-9907-e4b3aa274f04
---
 .../artemis/components/variant/VCFview.java   | 404 ++++++++++++------
 1 file changed, 282 insertions(+), 122 deletions(-)

diff --git a/uk/ac/sanger/artemis/components/variant/VCFview.java b/uk/ac/sanger/artemis/components/variant/VCFview.java
index 008e73e5a..bfce3e16c 100644
--- a/uk/ac/sanger/artemis/components/variant/VCFview.java
+++ b/uk/ac/sanger/artemis/components/variant/VCFview.java
@@ -45,12 +45,14 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Hashtable;
 import java.util.List;
 import java.util.Vector;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JComboBox;
 import javax.swing.JComponent;
@@ -74,6 +76,7 @@ import uk.ac.sanger.artemis.Entry;
 import uk.ac.sanger.artemis.EntryGroup;
 import uk.ac.sanger.artemis.Feature;
 import uk.ac.sanger.artemis.FeatureKeyPredicate;
+import uk.ac.sanger.artemis.FeaturePredicate;
 import uk.ac.sanger.artemis.FeatureVector;
 import uk.ac.sanger.artemis.Options;
 import uk.ac.sanger.artemis.Selection;
@@ -143,6 +146,10 @@ public class VCFview extends JPanel
   private boolean showNonOverlappings = true;
   private float MIN_QUALITY = -10;
   
+  Hashtable<String, Integer> offsetLengths = null;
+  private boolean concatSequences = false;
+  private Point lastMousePoint;
+  
   private Pattern multiAllelePattern = Pattern.compile("^[AGCT]+,[AGCT,]+$");
   private static Pattern tabPattern = Pattern.compile("\t");
 
@@ -214,23 +221,10 @@ public class VCFview extends JPanel
     
     //
     //
-    final MouseMotionListener mouseMotionListener = new MouseMotionListener()
-    {
-      public void mouseDragged(MouseEvent event)
-      {
-        handleCanvasMouseDrag(event);
-      }
-      
-      public void mouseMoved(MouseEvent e)
-      {
-        findVariantAtPoint(e.getPoint());
-      }
-    };
-    addMouseMotionListener(mouseMotionListener);
     addMouseListener(new PopupListener());
     
     //
-    createMenus(frame);
+    createMenus(frame, jspView);
     setDisplay();
     
     if(feature_display == null)
@@ -248,85 +242,90 @@ public class VCFview extends JPanel
     }
   }
   
-  private void createMenus(JFrame frame)
+  private void createMenus(JFrame frame, final JScrollPane jspView)
   {
-    JComponent topPanel;
+    final JComponent topPanel;
     if(feature_display != null)
       topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
     else
     { 
       topPanel = new JMenuBar();
       frame.setJMenuBar((JMenuBar)topPanel);
-    }
+      
+      JMenu fileMenu = new JMenu("File");
+      topPanel.add(fileMenu);
     
-    JMenu fileMenu = new JMenu("File");
-    topPanel.add(fileMenu);
-  
-    JMenuItem printImage = new JMenuItem("Save As Image Files (png/jpeg)...");
-    printImage.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
+      JMenuItem printImage = new JMenuItem("Save As Image Files (png/jpeg)...");
+      printImage.addActionListener(new ActionListener()
       {
-        PrintVCFview part = new PrintVCFview(VCFview.this);
-        part.print();
-      }
-    });
-    fileMenu.add(printImage);
-    
-    JMenuItem printPS = new JMenuItem("Print...");
-    printPS.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
+        public void actionPerformed(ActionEvent e)
+        {
+          PrintVCFview part = new PrintVCFview(VCFview.this);
+          part.print();
+        }
+      });
+      fileMenu.add(printImage);
+      
+      JMenuItem printPS = new JMenuItem("Print...");
+      printPS.addActionListener(new ActionListener()
       {
-        PrintVCFview part = new PrintVCFview(VCFview.this);
-        part.validate();
-        part.doPrintActions();
-      }
-    });
-    fileMenu.add(printPS);
-    
+        public void actionPerformed(ActionEvent e)
+        {
+          PrintVCFview part = new PrintVCFview(VCFview.this);
+          part.validate();
+          part.doPrintActions();
+        }
+      });
+      fileMenu.add(printPS);
+      
 
-    JMenuItem close = new JMenuItem("Close");
-    fileMenu.add(close);
-    close.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
+      JMenuItem close = new JMenuItem("Close");
+      fileMenu.add(close);
+      close.addActionListener(new ActionListener()
       {
-        VCFview.this.setVisible(false);
-        Component comp = VCFview.this;
-        
-        while( !(comp instanceof JFrame) )
-          comp = comp.getParent();
-        ((JFrame)comp).dispose();
-      } 
-    });
-    
-    JButton zoomIn = new JButton("-");
-    Insets ins = new Insets(1,1,1,1);
-    zoomIn.setMargin(ins);
-    zoomIn.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
+        public void actionPerformed(ActionEvent e)
+        {
+          VCFview.this.setVisible(false);
+          Component comp = VCFview.this;
+          
+          while( !(comp instanceof JFrame) )
+            comp = comp.getParent();
+          ((JFrame)comp).dispose();
+        } 
+      });
+      
+      JButton zoomIn = new JButton("-");
+      Insets ins = new Insets(1,1,1,1);
+      zoomIn.setMargin(ins);
+      zoomIn.addActionListener(new ActionListener()
       {
-        setZoomLevel((int) (VCFview.this.nbasesInView * 1.1));
-      }
-    });
-    topPanel.add(zoomIn);
+        public void actionPerformed(ActionEvent e)
+        {
+          setZoomLevel((int) (VCFview.this.nbasesInView * 1.1));
+        }
+      });
+      topPanel.add(zoomIn);
 
-    JButton zoomOut = new JButton("+");
-    zoomOut.setMargin(ins);
-    zoomOut.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
+      JButton zoomOut = new JButton("+");
+      zoomOut.setMargin(ins);
+      zoomOut.addActionListener(new ActionListener()
       {
-        setZoomLevel((int) (VCFview.this.nbasesInView * .9));
-      }
-    });
-    topPanel.add(zoomOut);
+        public void actionPerformed(ActionEvent e)
+        {
+          setZoomLevel((int) (VCFview.this.nbasesInView * .9));
+        }
+      });
+      topPanel.add(zoomOut);
+    }
     
     final JComboBox combo = new JComboBox(tr[0].getmSeq());
+    
+    if(tr[0].getmSeq().length > 1)
+      combo.addItem("Combine All");
+    
     if(chr == null)
       this.chr = tr[0].getmSeq()[0];
+
     combo.setSelectedItem(this.chr);
     combo.setEditable(false);
     combo.setMaximumRowCount(20);
@@ -335,11 +334,52 @@ public class VCFview extends JPanel
     {
       public void itemStateChanged(ItemEvent e)
       {
-        VCFview.this.chr = (String) combo.getSelectedItem();
+        if(combo.getSelectedItem().equals("Combine All"))
+          concatSequences = true;
+        else 
+        {
+          VCFview.this.chr = (String) combo.getSelectedItem();
+          concatSequences = false;
+        }
         repaint();
       }
     });
     topPanel.add(combo);
+    if(topPanel instanceof JPanel)
+      vcfPanel.add(topPanel, BorderLayout.NORTH);
+    
+    // auto hide top panel
+    final JCheckBox buttonAutoHide = new JCheckBox("Hide", true);
+    buttonAutoHide.setToolTipText("Auto-Hide");
+    topPanel.add(buttonAutoHide);
+    final MouseMotionListener mouseMotionListener = new MouseMotionListener()
+    {
+      public void mouseDragged(MouseEvent event)
+      {
+        handleCanvasMouseDrag(event);
+      }
+      
+      public void mouseMoved(MouseEvent e)
+      {
+        findVariantAtPoint(e.getPoint());
+
+        int thisHgt = HEIGHT;
+        if (thisHgt < 5)
+          thisHgt = 15;
+
+        int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY());
+        if (y < thisHgt)
+          topPanel.setVisible(true);
+        else
+        {
+          if (buttonAutoHide.isSelected())
+            topPanel.setVisible(false);
+        }
+
+      }
+    };
+    addMouseMotionListener(mouseMotionListener);
+
     
     // popup menu
     popup = new JPopupMenu();
@@ -561,44 +601,111 @@ public class VCFview extends JPanel
     return msg;
   }
   
+  
+  
+  /**
+   * For VCF files with multiple references sequences, calculate
+   * the offset from the start of the concatenated sequence for 
+   * a given reference.
+   * @param refName
+   * @return
+   */
+  protected int getSequenceOffset(String refName)
+  {
+    if(!concatSequences)
+      return 0;
+    
+    if(offsetLengths == null)
+    {   
+      String[] contigs = tr[0].getmSeq();
+      FeatureVector features = entryGroup.getAllFeatures();
+      offsetLengths = new Hashtable<String, Integer>(contigs.length);
+      for(int i=0; i<contigs.length; i++)
+      {
+        FeatureContigPredicate predicate = new FeatureContigPredicate(contigs[i]);
+        for(int j=0; j<features.size(); j++)
+        {
+          if(predicate.testPredicate(features.elementAt(j)))
+          {
+            offsetLengths.put(contigs[i], features.elementAt(j).getFirstBase()-1);
+            break;
+          }
+        }
+      }
+    }
+    return offsetLengths.get(refName);
+  }
+  
   protected void paintComponent(Graphics g)
   {
     super.paintComponent(g);
     mouseOverVCFline = null;
 
     float pixPerBase = getPixPerBaseByWidth();
-    String s;
-    
     int start = getBaseAtStartOfView();
     int end   = start+nbasesInView;
-    String region = chr+":"+start+"-"+end;
        
     drawSelectionRange((Graphics2D)g, pixPerBase, start, end);
- 
+
     FeatureVector features = getCDSFeaturesInRange(start, end);
-    
-    // a region is specified; random access
     for (int i = 0; i < tr.length; i++)
     {
-      TabixReader.Iterator iter = tr[i].query(region); // get the iterator
-      if (iter == null)
-        return;
-      try
-      {
-        while ((s = iter.next()) != null)
-          drawVariantCall(g, s, start, i, pixPerBase, features);
-      }
-      catch (IOException e)
+      if(concatSequences) 
       {
-        // TODO Auto-generated catch block
-        e.printStackTrace();
-      }
+        String[] contigs = tr[0].getmSeq();
+        for(int j=0; j<contigs.length; j++)
+        {
+          int offset = getSequenceOffset(contigs[j]);
+          int nextOffset;
+          if(j<contigs.length-1)
+            nextOffset = getSequenceOffset(contigs[j+1]);
+          else
+            nextOffset = seqLength;
+          
+          if( (offset >= start && offset < end) ||
+              (offset < start && start < nextOffset) )
+          {
+            int thisStart = start - offset;
+            if(thisStart < 1)
+              thisStart = 1;
+            int thisEnd   = end - offset;
+            
+            drawRegion(g, contigs[j]+":"+thisStart+"-"+thisEnd, i, start, pixPerBase, features); 
+          }
+        }
+
+      } 
+      else
+        drawRegion(g, chr+":"+start+"-"+end, i, start, pixPerBase, features); 
     }
 
     if(feature_display == null)
       drawScale((Graphics2D)g, start, end, pixPerBase, getHeight());
   }
   
+  private void drawRegion(Graphics g, 
+                          String region,
+                          int i, 
+                          int start, 
+                          float pixPerBase, 
+                          FeatureVector features) 
+  {
+    String s;
+    TabixReader.Iterator iter = tr[i].query(region); // get the iterator
+    if (iter == null)
+      return;
+    try
+    {
+      while ((s = iter.next()) != null)
+        drawVariantCall(g, s, start, i, pixPerBase, features);
+    }
+    catch (IOException e)
+    {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }  
+  }
+  
   private FeatureVector getCDSFeaturesInRange(int start, int end)
   {
     try
@@ -780,7 +887,8 @@ public class VCFview extends JPanel
   {
     //String parts[] = line.split("\\t");
     String parts[] = tabPattern.split(line, 0);
-    int basePosition = Integer.parseInt(parts[1]);
+    
+    int basePosition = Integer.parseInt(parts[1]) + getSequenceOffset(parts[0]);
    
     if( !showVariant(parts[3], parts[4], features, basePosition, parts[5]) )
       return;
@@ -921,49 +1029,85 @@ public class VCFview extends JPanel
     return pos;
   }
   
-  private void findVariantAtPoint(Point lastMousePoint)
+  private void findVariantAtPoint(Point mousePoint)
   {
     float pixPerBase = getPixPerBaseByWidth();
-    String s;
     int start = getBaseAtStartOfView();
     int end   = start+nbasesInView;
-    String region = chr+":"+start+"-"+end;
     FeatureVector features = getCDSFeaturesInRange(start, end);
     
     for (int i = 0; i < tr.length; i++)
     {
-      TabixReader.Iterator iter = tr[i].query(region); // get the iterator
-      if (iter == null)
-        return;
-      try
+      if(concatSequences) 
       {
-        while ((s = iter.next()) != null)
+        String[] contigs = tr[0].getmSeq();
+        for(int j=0; j<contigs.length; j++)
         {
-          String parts[] = tabPattern.split(s, 7);
-          int basePosition = Integer.parseInt(parts[1]);
-
-          if( !showVariant(parts[3], parts[4], features, basePosition, parts[5]) )
-            continue;
+          int offset = getSequenceOffset(contigs[j]);
+          int nextOffset;
+          if(j<contigs.length-1)
+            nextOffset = getSequenceOffset(contigs[j+1]);
+          else
+            nextOffset = seqLength;
           
-          int pos[] = getScreenPosition(basePosition, pixPerBase, start, i);
-          if(lastMousePoint != null &&
-             lastMousePoint.getY() < pos[1] &&
-             lastMousePoint.getY() > pos[1]-LINE_HEIGHT &&
-             lastMousePoint.getX() > pos[0]-3 &&
-             lastMousePoint.getX() < pos[0]+3)
-           {
-             mouseOverVCFline = s;
-             mouseOverIndex = i;
-           }
+          if( (offset >= start && offset < end) ||
+              (offset < start && start < nextOffset) )
+          {
+            int thisStart = start - offset;
+            if(thisStart < 1)
+              thisStart = 1;
+            int thisEnd   = end - offset;
+            searchRegion(contigs[j]+":"+thisStart+"-"+thisEnd, i, mousePoint, features, start, pixPerBase);
+          }
         }
-      }
-      catch (IOException e)
-      {
-        e.printStackTrace();
-      }
+      } 
+      else
+        searchRegion(chr+":"+start+"-"+end, i, mousePoint, features, start, pixPerBase);
     }
   }
   
+  private void searchRegion(String region, int i, Point mousePoint, FeatureVector features,
+                            int start, float pixPerBase) 
+  {
+    TabixReader.Iterator iter = tr[i].query(region); // get the iterator
+    if (iter == null)
+      return;
+    try
+    { 
+      String s;
+      while ((s = iter.next()) != null)
+        isMouseOver(mousePoint, s, features, i, start, pixPerBase);
+    }
+    catch (IOException e)
+    {
+      e.printStackTrace();
+    }  
+  }
+  
+  private void isMouseOver(Point mousePoint, 
+                           String s, 
+                           FeatureVector features, 
+                           int i, 
+                           int start, float pixPerBase)
+  {
+    String parts[] = tabPattern.split(s, 7);
+    int basePosition = Integer.parseInt(parts[1]) + getSequenceOffset(parts[0]);
+
+    if( !showVariant(parts[3], parts[4], features, basePosition, parts[5]) )
+      return;
+    
+    int pos[] = getScreenPosition(basePosition, pixPerBase, start, i);
+    if(mousePoint != null &&
+       mousePoint.getY() < pos[1] &&
+       mousePoint.getY() > pos[1]-LINE_HEIGHT &&
+       mousePoint.getX() > pos[0]-3 &&
+       mousePoint.getX() < pos[0]+3)
+     {
+       mouseOverVCFline = s;
+       mouseOverIndex = i;
+     }
+  }
+  
   private void drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos)
   {
     g2.setColor(Color.black);
@@ -1260,6 +1404,22 @@ public class VCFview extends JPanel
      repaint();
    }
    
+  class FeatureContigPredicate implements FeaturePredicate
+  {
+    String contigName;
+    FeatureContigPredicate(String contigName)
+    {
+      this.contigName = contigName;
+    }
+    
+    public boolean testPredicate(Feature feature)
+    {
+      if(feature.getIDString().equals(contigName))
+        return true;
+      return false;
+    }
+  }
+   
   public static void main(String args[])
   {
     List<String> vcfFileList = new Vector<String>();
-- 
GitLab