JAL-885; Implementation of StructureFrequency.java and according
authorjanengelhardt <engelhardt87@googlemail.com>
Fri, 29 Jul 2011 12:38:27 +0000 (14:38 +0200)
committerjanengelhardt <engelhardt87@googlemail.com>
Fri, 29 Jul 2011 12:38:27 +0000 (14:38 +0200)
methods in AlignViewport.java; completeConsensus is still missing;

Change-Id: I3678fc75af84aa5a8974faed6b76f47c76a74638

src/jalview/analysis/AAFrequency.java
src/jalview/analysis/Conservation.java
src/jalview/analysis/Rna.java
src/jalview/analysis/StructureFrequency.java [new file with mode: 0644]
src/jalview/datamodel/SequenceFeature.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/Desktop.java

index 31cb7ad..16754a1 100755 (executable)
@@ -85,6 +85,7 @@ public class AAFrequency
   public static final void calculate(SequenceI[] sequences, int start,
           int end, Hashtable[] result, boolean profile)
   {
+       System.out.println("AAFrequence.calculate");
     Hashtable residueHash;
     int maxCount, nongap, i, j, v, jSize = sequences.length;
     String maxResidue;
index 2ffb4de..04914fc 100755 (executable)
@@ -87,7 +87,6 @@ public class Conservation
   public Conservation(String name, Hashtable propHash, int threshold,
           Vector sequences, int start, int end)
   {
-
     this.name = name;
     this.propHash = propHash;
     this.threshold = threshold;
index 69a228b..f2ef118 100644 (file)
@@ -76,8 +76,8 @@ public class Rna
     {
       int begin = Integer.parseInt(pairs.elementAt(p).toString());
       int end = Integer.parseInt(pairs.elementAt(p + 1).toString());
-
-      outPairs[p / 2] = new SequenceFeature("RNA helix", "", "", begin,
+      
+       outPairs[p / 2] = new SequenceFeature("RNA helix", "", "", begin,
               end, "");
     }
 
@@ -159,7 +159,6 @@ public class Rna
 
       // Record helix as featuregroup
       pairs[i].setFeatureGroup(Integer.toString(helix));
-      pairs[i].setFeatureGroup(Integer.toString(helix));
 
       lastopen = open;
       lastclose = close;
diff --git a/src/jalview/analysis/StructureFrequency.java b/src/jalview/analysis/StructureFrequency.java
new file mode 100644 (file)
index 0000000..a666bc0
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
+ * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * 
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package jalview.analysis;
+
+import java.util.*;
+
+import jalview.datamodel.*;
+
+/**
+ * Takes in a vector or array of sequences and column start and column end and
+ * returns a new Hashtable[] of size maxSeqLength, if Hashtable not supplied.
+ * This class is used extensively in calculating alignment colourschemes that
+ * depend on the amount of conservation in each alignment column.
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class StructureFrequency
+{
+  // No need to store 1000s of strings which are not
+  // visible to the user.
+  public static final String MAXCOUNT = "C";
+
+  public static final String MAXRESIDUE = "R";
+
+  public static final String PID_GAPS = "G";
+
+  public static final String PID_NOGAPS = "N";
+
+  public static final String PROFILE = "P";
+
+  public static final Hashtable[] calculate(Vector sequences, int start,
+          int end)
+  {
+    return calculate(sequences, start, end, false);
+  }
+
+  public static final Hashtable[] calculate(Vector sequences, int start,
+          int end, boolean profile)
+  {
+    SequenceI[] seqs = new SequenceI[sequences.size()];
+    int width = 0;
+    for (int i = 0; i < sequences.size(); i++)
+    {
+      seqs[i] = (SequenceI) sequences.elementAt(i);
+      if (seqs[i].getLength() > width)
+      {
+        width = seqs[i].getLength();
+      }
+    }
+
+    Hashtable[] reply = new Hashtable[width];
+
+    if (end >= width)
+    {
+      end = width;
+    }
+
+    calculate(seqs, start, end, reply, profile);
+
+    return reply;
+  }
+
+  public static final void calculate(SequenceI[] sequences, int start,
+          int end, Hashtable[] result)
+  {
+    calculate(sequences, start, end, result, false);
+  }
+
+  public static final void calculate(SequenceI[] sequences, int start,
+          int end, Hashtable[] result, boolean profile)
+  {
+    Hashtable residueHash;
+    int maxCount, nongap, i, j, v, jSize = sequences.length;
+    String maxResidue;
+    char c;
+    float percentage;
+
+    int[] values = new int[255];
+
+    char[] seq;
+
+    for (i = start; i < end; i++)
+    {
+      residueHash = new Hashtable();
+      maxCount = 0;
+      maxResidue = "";
+      nongap = 0;
+      values = new int[255];
+
+      for (j = 0; j < jSize; j++)
+      {
+        if (sequences[j]==null)
+        {
+          System.err.println("WARNING: Consensus skipping null sequence - possible race condition.");
+          continue;
+        }
+        seq = sequences[j].getSequence();
+        if (seq.length > i)
+        {
+          c = seq[i];
+
+          if (c == '.' || c == ' ')
+          {
+            c = '-';
+          }
+
+          if (c == '-')
+          {
+            values['-']++;
+            continue;
+          }
+          else if ('a' <= c && c <= 'z')
+          {
+            c -= 32; // ('a' - 'A');
+          }
+
+          nongap++;
+          values[c]++;
+
+        }
+        else
+        {
+          values['-']++;
+        }
+      }
+
+      for (v = 'A'; v < 'Z'; v++)
+      {
+        if (values[v] < 2 || values[v] < maxCount)
+        {
+          continue;
+        }
+
+        if (values[v] > maxCount)
+        {
+          maxResidue = String.valueOf((char) v);
+        }
+        else if (values[v] == maxCount)
+        {
+          maxResidue += String.valueOf((char) v);
+        }
+        maxCount = values[v];
+      }
+
+      if (maxResidue.length() == 0)
+      {
+        maxResidue = "-";
+      }
+      if (profile)
+      {
+        residueHash.put(PROFILE, new int[][]
+        { values, new int[]
+        { jSize, nongap } });
+      }
+      residueHash.put(MAXCOUNT, new Integer(maxCount));
+      residueHash.put(MAXRESIDUE, maxResidue);
+
+      percentage = ((float) maxCount * 100) / (float) jSize;
+      residueHash.put(PID_GAPS, new Float(percentage));
+
+      percentage = ((float) maxCount * 100) / (float) nongap;
+      residueHash.put(PID_NOGAPS, new Float(percentage));
+      result[i] = residueHash;
+    }
+  }
+  
+  /**
+   * Method to calculate a 'base pair consensus row', very similar 
+   * to nucleotide consensus but takes into account a given structure
+   * @param sequences
+   * @param start
+   * @param end
+   * @param result
+   * @param profile
+   * @param rnaStruc
+   */
+  public static final void calculate(SequenceI[] sequences, int start,
+          int end, Hashtable[] result, boolean profile, AlignmentAnnotation rnaStruc){
+         //TODO Consider to use AlignmentAnnotation instead of structure string
+
+         Hashtable residueHash;
+
+         char[] seq, struc=rnaStruc.getRNAStruc().toCharArray();
+         SequenceFeature[] rna =rnaStruc._rnasecstr;
+         char c,s,cEnd;
+         int count,nonGap,i,j,jSize = sequences.length;
+         int[] values = new int[255];
+         float percentage;
+
+
+         for (i = start; i < end; i++) //foreach column
+           {
+             residueHash = new Hashtable();
+             for (j = 0; j < jSize; j++) //foreach row
+             {
+               if (sequences[j]==null)
+               {
+                 System.err.println("WARNING: Consensus skipping null sequence - possible race condition.");
+                 continue;
+               }
+               seq = sequences[j].getSequence();
+               if (seq.length > i)
+               {
+                       c = seq[i];
+                       s = struc[i];
+                       nonGap=0;
+                       
+                       //standard representation for gaps in sequence and structure
+                       if (c == '.' || c == ' ')
+                   {
+                     c = '-';
+                   }
+                       if (s == '.' || s == ' ')
+                   {
+                     s = '-';
+                   }
+                       
+                       if (c == '-')
+                   {
+                     values['-']++;
+                     continue;
+                   }
+                       if(s == '-'){
+                               values['-']++;
+                               continue;
+                       }
+                       nonGap++;
+                       cEnd=seq[rna[i].getEnd()];
+                       if(checkBpType(c,cEnd)){
+                               values['H']++; //H means it's a helix (structured)
+                       }
+               }
+             }
+             /*UPDATE this for new values
+             if (profile)
+             {
+               residueHash.put(PROFILE, new int[][]
+               { values, new int[]
+               { jSize, nongap } });
+             }
+             */
+             
+             count=values['H'];
+
+             percentage = ((float) count * 100) / (float) jSize;
+             residueHash.put(PID_GAPS, new Float(percentage));
+
+             //percentage = ((float) count * 100) / (float) nongap;
+             //residueHash.put(PID_NOGAPS, new Float(percentage));
+             result[i] = residueHash;
+             
+           }
+  }
+                       
+        
+  /**
+   * Method to check if a base-pair is a canonical or a wobble bp 
+   * @param up 5' base
+   * @param down 3' base
+   * @return True if it is a canonical/wobble bp
+   */
+  public static boolean checkBpType(char up, char down){
+         if(up>'Z'){up-=32;}
+         if(down>'Z'){down-=32;}
+         
+         switch (up){
+               case 'A': 
+                       switch (down){
+                               case 'T': return true;
+                               case 'U': return true;
+                       }
+               break;
+               case 'C': 
+                       switch (down){
+                               case 'G': return true;
+                       }
+               break;
+               case 'T': 
+                       switch (down){
+                               case 'A': return true;
+                               case 'G': return true;
+                               }
+               break;
+               case 'G': 
+                       switch (down){
+                               case 'C': return true;
+                               case 'T': return true;
+                               case 'U': return true;
+                       }
+               break;
+               case 'U': 
+                       switch (down){
+                               case 'A': return true;
+                               case 'G': return true;
+                       }
+               break;
+         }       
+         return false;
+  }
+  
+  /**
+   * Compute all or part of the annotation row from the given consensus
+   * hashtable
+   * 
+   * @param consensus
+   *          - pre-allocated annotation row
+   * @param hconsensus
+   * @param iStart
+   * @param width
+   * @param ignoreGapsInConsensusCalculation
+   * @param includeAllConsSymbols
+   */
+  public static void completeConsensus(AlignmentAnnotation consensus,
+          Hashtable[] hconsensus, int iStart, int width,
+          boolean ignoreGapsInConsensusCalculation,
+          boolean includeAllConsSymbols)
+  {
+    completeConsensus(consensus, hconsensus, iStart, width,
+            ignoreGapsInConsensusCalculation, includeAllConsSymbols, null); // new
+                                                                            // char[]
+    // { 'A', 'C', 'G', 'T', 'U' });
+  }
+
+  public static void completeConsensus(AlignmentAnnotation consensus,
+          Hashtable[] hconsensus, int iStart, int width,
+          boolean ignoreGapsInConsensusCalculation,
+          boolean includeAllConsSymbols, char[] alphabet)
+  {
+    float tval, value;
+    if (consensus == null || consensus.annotations == null
+            || consensus.annotations.length < width)
+    {
+      // called with a bad alignment annotation row - wait for it to be
+      // initialised properly
+      return;
+    }
+    for (int i = iStart; i < width; i++)
+    {
+      if (i >= hconsensus.length)
+      {
+        // happens if sequences calculated over were shorter than alignment
+        // width
+        consensus.annotations[i] = null;
+        continue;
+      }
+      value = 0;
+      if (ignoreGapsInConsensusCalculation)
+      {
+        value = ((Float) hconsensus[i].get(StructureFrequency.PID_NOGAPS))
+                .floatValue();
+      }
+      else
+      {
+        value = ((Float) hconsensus[i].get(StructureFrequency.PID_GAPS))
+                .floatValue();
+      }
+
+      String maxRes = hconsensus[i].get(StructureFrequency.MAXRESIDUE).toString();
+      String mouseOver = hconsensus[i].get(StructureFrequency.MAXRESIDUE) + " ";
+      if (maxRes.length() > 1)
+      {
+        mouseOver = "[" + maxRes + "] ";
+        maxRes = "+";
+      }
+      int[][] profile = (int[][]) hconsensus[i].get(StructureFrequency.PROFILE);
+      if (profile != null && includeAllConsSymbols)
+      {
+        mouseOver = "";
+        if (alphabet != null)
+        {
+          for (int c = 0; c < alphabet.length; c++)
+          {
+            tval = ((float) profile[0][alphabet[c]])
+                    * 100f
+                    / (float) profile[1][ignoreGapsInConsensusCalculation ? 1
+                            : 0];
+            mouseOver += ((c == 0) ? "" : "; ") + alphabet[c] + " "
+                    + ((int) tval) + "%";
+          }
+        }
+        else
+        {
+          Object[] ca = new Object[profile[0].length];
+          float[] vl = new float[profile[0].length];
+          for (int c = 0; c < ca.length; c++)
+          {
+            ca[c] = new char[]
+            { (char) c };
+            vl[c] = (float) profile[0][c];
+          }
+          ;
+          jalview.util.QuickSort.sort(vl, ca);
+          for (int p = 0, c = ca.length - 1; profile[0][((char[]) ca[c])[0]] > 0; c--)
+          {
+            if (((char[]) ca[c])[0] != '-')
+            {
+              tval = ((float) profile[0][((char[]) ca[c])[0]])
+                      * 100f
+                      / (float) profile[1][ignoreGapsInConsensusCalculation ? 1
+                              : 0];
+              mouseOver += ((p == 0) ? "" : "; ") + ((char[]) ca[c])[0]
+                      + " " + ((int) tval) + "%";
+              p++;
+
+            }
+          }
+
+        }
+      }
+      else
+      {
+        mouseOver += ((int) value + "%");
+      }
+      consensus.annotations[i] = new Annotation(maxRes, mouseOver, ' ',
+              value);
+    }
+  }
+
+  /**
+   * get the sorted profile for the given position of the consensus
+   * 
+   * @param hconsensus
+   * @return
+   */
+  public static int[] extractProfile(Hashtable hconsensus,
+          boolean ignoreGapsInConsensusCalculation)
+  {
+    int[] rtnval = new int[64];
+    int[][] profile = (int[][]) hconsensus.get(StructureFrequency.PROFILE);
+    if (profile == null)
+      return null;
+    Object[] ca = new Object[profile[0].length];
+    float[] vl = new float[profile[0].length];
+    for (int c = 0; c < ca.length; c++)
+    {
+      ca[c] = new char[]
+      { (char) c };
+      vl[c] = (float) profile[0][c];
+    }
+    ;
+    jalview.util.QuickSort.sort(vl, ca);
+    rtnval[0] = 1;
+    for (int c = ca.length - 1; profile[0][((char[]) ca[c])[0]] > 0; c--)
+    {
+      if (((char[]) ca[c])[0] != '-')
+      {
+        rtnval[rtnval[0]++] = ((char[]) ca[c])[0];
+        rtnval[rtnval[0]++] = (int) (((float) profile[0][((char[]) ca[c])[0]]) * 100f / (float) profile[1][ignoreGapsInConsensusCalculation ? 1
+                : 0]);
+      }
+    }
+    return rtnval;
+  }
+
+  enum base {A,T,g,C};
+  
+  public static void main(String args[]){
+         //Short test to see if checkBpType works
+         ArrayList<String> test = new ArrayList<String>();
+         test.add("A");
+         test.add("c");
+         test.add("g");
+         test.add("T");
+         test.add("U");
+         for (String i : test) {
+                 for (String j : test) {
+                         System.out.println(i+"-"+j+": "+StructureFrequency.checkBpType(i.charAt(0),j.charAt(0)));
+                 }
+         }
+  }
+}
index 012f842..184123f 100755 (executable)
@@ -40,7 +40,7 @@ public class SequenceFeature
   public Hashtable otherDetails;
 
   public java.util.Vector links;
-
+  
   // Feature group can be set from a features file
   // as a group of features between STARTGROUP and ENDGROUP markers
   public String featureGroup;
@@ -95,7 +95,7 @@ public class SequenceFeature
       }
     }
   }
-
+  
   public SequenceFeature(String type, String desc, String status,
           int begin, int end, String featureGroup)
   {
index f05d873..5beebfd 100755 (executable)
@@ -589,6 +589,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       ap.av.updateConservation(ap);
       ap.av.updateConsensus(ap);
+      ap.av.updateStrucConsensus(ap);
     }
   }
 
index 26fb890..98cae4c 100644 (file)
@@ -139,8 +139,12 @@ public class AlignViewport implements SelectionSource, VamsasSource
 
   /** DOCUMENT ME!! */
   public Hashtable[] hconsensus;
-
+  
+  public Hashtable[] hStrucConsensus;
+  
   AlignmentAnnotation consensus;
+  
+  AlignmentAnnotation strucConsensus;
 
   AlignmentAnnotation conservation;
 
@@ -151,6 +155,9 @@ public class AlignViewport implements SelectionSource, VamsasSource
   AlignmentAnnotation[] groupConservation;
 
   boolean autoCalculateConsensus = true;
+  
+  boolean autoCalculateStrucConsensus = true;
+
 
   /** DOCUMENT ME!! */
   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
@@ -458,14 +465,20 @@ public class AlignViewport implements SelectionSource, VamsasSource
   ConservationThread conservationThread;
 
   ConsensusThread consensusThread;
+  
+  StrucConsensusThread strucConsensusThread;
 
   boolean consUpdateNeeded = false;
 
   static boolean UPDATING_CONSENSUS = false;
+  
+  static boolean UPDATING_STRUC_CONSENSUS = false;
 
   static boolean UPDATING_CONSERVATION = false;
 
   boolean updatingConsensus = false;
+  
+  boolean updatingStrucConsensus = false;
 
   boolean updatingConservation = false;
 
@@ -559,7 +572,6 @@ public class AlignViewport implements SelectionSource, VamsasSource
         AAFrequency.calculate(alignment.getSequencesArray(), 0,
                 alignment.getWidth(), hconsensus, true);
         updateAnnotation(true);
-
         if (globalColourScheme != null)
         {
           globalColourScheme.setConsensus(hconsensus);
@@ -604,6 +616,116 @@ public class AlignViewport implements SelectionSource, VamsasSource
       }
     }
   }
+  
+  //--------START Structure Conservation
+  public void updateStrucConsensus(final AlignmentPanel ap)
+  {
+    // see note in mantis : issue number 8585
+    if (strucConsensus == null || !autoCalculateStrucConsensus)
+    {
+      return;
+    }
+    strucConsensusThread = new StrucConsensusThread(ap);
+    strucConsensusThread.start();
+  }
+
+  class StrucConsensusThread extends Thread
+  {
+    AlignmentPanel ap;
+
+    public StrucConsensusThread(AlignmentPanel ap)
+    {
+      this.ap = ap;
+    }
+
+    public void run()
+    {
+      updatingStrucConsensus = true;
+      while (UPDATING_STRUC_CONSENSUS)
+      {
+        try
+        {
+          if (ap != null)
+          {
+            ap.paintAlignment(false);
+          }
+
+          Thread.sleep(200);
+        } catch (Exception ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+
+      UPDATING_STRUC_CONSENSUS = true;
+
+      try
+      {
+        int aWidth = (alignment != null) ? alignment.getWidth() : -1; // null
+        // pointer
+        // possibility
+        // here.
+        if (aWidth <= 0)
+        {
+          updatingStrucConsensus = false;
+          UPDATING_STRUC_CONSENSUS = false;
+          return;
+        }
+
+        strucConsensus.annotations = null;
+        strucConsensus.annotations = new Annotation[aWidth];
+
+        hStrucConsensus = new Hashtable[aWidth];
+        AlignmentAnnotation rna = ap.av.getAlignment().getAlignmentAnnotation()[0];
+        StructureFrequency.calculate(alignment.getSequencesArray(), 0,
+                alignment.getWidth(), hStrucConsensus, true, rna);
+        //TODO AlignmentAnnotation rnaStruc!!!
+        updateAnnotation(true);
+        if (globalColourScheme != null)
+        {
+          globalColourScheme.setConsensus(hStrucConsensus);
+        }
+
+      } catch (OutOfMemoryError error)
+      {
+        alignment.deleteAnnotation(consensus);
+
+        strucConsensus = null;
+        hStrucConsensus = null;
+        new OOMWarning("calculating structure consensus", error);
+      }
+      UPDATING_STRUC_CONSENSUS = false;
+      updatingStrucConsensus = false;
+
+      if (ap != null)
+      {
+        ap.paintAlignment(true);
+      }
+    }
+
+    /**
+     * update the consensus annotation from the sequence profile data using
+     * current visualization settings.
+     */
+    public void updateAnnotation()
+    {
+      updateAnnotation(false);
+    }
+
+    protected void updateAnnotation(boolean immediate)
+    {
+      // TODO: make calls thread-safe, so if another thread calls this method,
+      // it will either return or wait until one calculation is finished.
+      if (immediate
+              || (!updatingConsensus && consensus != null && hconsensus != null))
+      {
+        StructureFrequency.completeConsensus(strucConsensus, hStrucConsensus, 0,
+                hStrucConsensus.length, ignoreGapsInConsensusCalculation,
+                showSequenceLogo);
+      }
+    }
+  }
+  //--------END   Structure Conservation
 
   /**
    * get the consensus sequence as displayed under the PID consensus annotation
@@ -1732,6 +1854,11 @@ public class AlignViewport implements SelectionSource, VamsasSource
     {
       updateConsensus(ap);
     }
+    
+    if(autoCalculateStrucConsensus)
+    {
+       updateStrucConsensus(ap);
+    }
 
     // Reset endRes of groups if beyond alignment width
     int alWidth = alignment.getWidth();
index 32f9514..6418c76 100755 (executable)
@@ -671,11 +671,11 @@ public class AnnotationPanel extends JPanel implements MouseListener,
            AlignmentAnnotation anot = aa[activeRow];
            
            if(anot.description.equals("secondary structure")){
-               System.out.println(anot.description+" "+anot.getRNAStruc());       
+               //System.out.println(anot.description+" "+anot.getRNAStruc());     
            }
          }
   }
-         
+         //TODO mouseClicked-content and drawCursor are quite experimental!
          public void drawCursor(Graphics graphics, SequenceI seq, int res, int x1, int y1)
          {
            int pady = av.charHeight / 5;
index 45aed9c..9797694 100644 (file)
@@ -951,7 +951,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     }
     // TODO: update this text for each release or centrally store it for lite
     // and application
-    message.append("\nAuthors:  Jim Procter, Jan Engelhardt2, Lauren Lui, Andrew Waterhouse, Michele Clamp, James Cuff, Steve Searle,\n    David Martin & Geoff Barton."
+    message.append("\nAuthors:  Jim Procter, Jan Engelhardt, Lauren Lui, Andrew Waterhouse, Michele Clamp, James Cuff, Steve Searle,\n    David Martin & Geoff Barton."
             + "\nDevelopment managed by The Barton Group, University of Dundee, Scotland, UK.\n"
             + "\nFor help, see the FAQ at www.jalview.org and/or join the jalview-discuss@jalview.org mailing list\n"
             + "\nIf  you use Jalview, please cite:"