JAL-2629 storage and lifecycle of alignment/group HMM annotations revised
[jalview.git] / src / jalview / workers / InformationThread.java
index 09b428b..6a86a61 100644 (file)
@@ -8,11 +8,19 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ResidueShaderI;
+import jalview.util.MessageManager;
 
 import java.util.List;
 
+/**
+ * This class calculates HMM Information Content annotations, based on any HMM
+ * consensus sequences and their HMM models. HMM consensus sequences may be
+ * present for the whole alignment, or subgroups of it.
+ *
+ */
 public class InformationThread extends AlignCalcWorker
 {
   public static final String HMM_CALC_ID = "HMM";
@@ -20,7 +28,7 @@ public class InformationThread extends AlignCalcWorker
   private float max = 0f;
 
   /**
-   * Constructor for information thread.
+   * Constructor
    * 
    * @param alignViewport
    * @param alignPanel
@@ -31,6 +39,10 @@ public class InformationThread extends AlignCalcWorker
     super(alignViewport, alignPanel);
   }
 
+  /**
+   * Recomputes Information annotations for any HMM consensus sequences (for
+   * alignment and/or groups)
+   */
   @Override
   public void run()
   {
@@ -41,18 +53,17 @@ public class InformationThread extends AlignCalcWorker
     calcMan.notifyStart(this);
     // long started = System.currentTimeMillis();
 
-    List<AlignmentAnnotation> information = getInformationAnnotations();
     try
     {
-      if ((information == null) || calcMan.isPending(this))
+      if (calcMan.isPending(this))
       {
+        // another instance of this is waiting to run
         calcMan.workerComplete(this);
         return;
       }
       while (!calcMan.notifyWorking(this))
       {
-        // System.err.println("Thread
-        // (Information"+Thread.currentThread().getName()+") Waiting around.");
+        // another thread in progress, wait my turn
         try
         {
           if (ap != null)
@@ -72,20 +83,21 @@ public class InformationThread extends AlignCalcWorker
       }
       AlignmentI alignment = alignViewport.getAlignment();
 
-      int aWidth = -1;
+      int aWidth = alignment == null ? -1 : alignment.getWidth();
 
-      if (alignment == null || (aWidth = alignment.getWidth()) < 0)
+      if (aWidth < 0)
       {
         calcMan.workerComplete(this);
         return;
       }
 
-      eraseInformation(aWidth);
-      computeInformation(alignment);
+      eraseAnnotations(alignment);
+      computeProfiles(alignment);
       updateResultAnnotation(true);
 
       if (ap != null)
       {
+        ap.adjustAnnotationHeight();
         ap.paintAlignment(true, true);
       }
     } catch (OutOfMemoryError error)
@@ -99,41 +111,62 @@ public class InformationThread extends AlignCalcWorker
   }
 
   /**
-   * Clear out any existing information annotations
+   * Deletes any existing information annotations. These are sequence-related
+   * annotations which relate to HMM consensus sequences for either the
+   * alignment or a subgroup.
    * 
-   * @param aWidth
-   *          the width (number of columns) of the annotated alignment
+   * @param alignment
    */
-  protected void eraseInformation(int aWidth)
+  protected void eraseAnnotations(AlignmentI alignment)
   {
-
-    List<AlignmentAnnotation> information = getInformationAnnotations();
-    for (AlignmentAnnotation info : information)
+    Iterable<AlignmentAnnotation> anns = alignment
+            .findAnnotation(HMM_CALC_ID);
+    for (AlignmentAnnotation ann : anns)
     {
-      info.annotations = new Annotation[aWidth];
+      alignment.deleteAnnotation(ann);
     }
   }
 
   /**
-   * Computes the profiles from a HMM for an alignment.
+   * Computes HMM profiles for any HMM consensus sequences (for alignment or
+   * subgroups)
    * 
    * @param alignment
    */
-  protected void computeInformation(AlignmentI alignment)
+  protected void computeProfiles(AlignmentI alignment)
   {
     int width = alignment.getWidth();
-    List<SequenceI> hmmSeqs = alignment.getHMMConsensusSequences();
-    int index = 0;
 
-    for (SequenceI seq : hmmSeqs)
+    /*
+     * alignment HMM profile
+     */
+    SequenceI seq = alignment.getHmmConsensus();
+    if (seq != null)
     {
       HiddenMarkovModel hmm = seq.getHMM();
-      ProfilesI hinformation = AAFrequency.calculateHMMProfiles(hmm, width,
+      ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
               0, width, true, alignViewport.isIgnoreBelowBackground(),
               alignViewport.isInfoLetterHeight());
-      alignViewport.setSequenceInformationHash(hinformation, index);
-      // setColourSchemeInformation(hinformation);
-      index++;
+      alignViewport.setHmmProfiles(hmmProfiles);
+      // setColourSchemeInformation(hmmProfiles);
+    }
+
+    /*
+     * group HMM profiles
+     */
+    List<SequenceGroup> groups = alignment.getGroups();
+    for (SequenceGroup group : groups)
+    {
+      seq = group.getHmmConsensus();
+      if (seq != null)
+      {
+        HiddenMarkovModel hmm = seq.getHMM();
+        ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
+                0, width, true, group.isIgnoreBelowBackground(),
+                group.isUseInfoLetterHeight());
+        group.setHmmProfiles(hmmProfiles);
+        // setColourSchemeInformation(hmmProfiles);
+      }
     }
   }
 
@@ -157,28 +190,18 @@ public class InformationThread extends AlignCalcWorker
   }
 
   /**
-   * Get the Information annotation for the alignment
-   * 
-   * @return
-   */
-  protected List<AlignmentAnnotation> getInformationAnnotations()
-  {
-    return alignViewport.getInformationAnnotations();
-  }
-
-  /**
    * Get the Gap annotation for the alignment
    * 
    * @return
    */
   protected AlignmentAnnotation getGapAnnotation()
   {
-    return alignViewport.getAlignmentGapAnnotation();
+    return alignViewport.getOccupancyAnnotation();
   }
 
   /**
-   * update the information annotation from the sequence profile data using
-   * current visualization settings.
+   * Updates the information annotation from the sequence profile data using
+   * current visualisation settings
    */
   @Override
   public void updateAnnotation()
@@ -187,27 +210,79 @@ public class InformationThread extends AlignCalcWorker
   }
 
   /**
-   * Derives the information content for an information annotation.
+   * Constructs Information Content annotation for any HMM consensus sequences
+   * (for alignment or groups), and adds the annotation to the sequence and the
+   * alignment
    * 
    * @param immediate
    */
   public void updateResultAnnotation(boolean immediate)
   {
-    List<AlignmentAnnotation> annots = getInformationAnnotations();
-    int index = 0;
-    for (AlignmentAnnotation information : annots)
+    AlignmentI alignment = alignViewport.getAlignment();
+
+    /*
+     * annotation for alignment HMM consensus if present
+     */
+    SequenceI hmmSeq = alignment.getHmmConsensus();
+    ProfilesI profile = alignViewport.getHmmProfiles();
+    AlignmentAnnotation ann = makeInformationAnnotation(hmmSeq, profile);
+    if (ann != null)
+    {
+      alignment.addAnnotation(ann);
+    }
+
+    /*
+     * annotation for group HMM consensus if present
+     */
+    for (SequenceGroup group : alignment.getGroups())
     {
-      ProfilesI hinformation = getSequenceInformation(index);
-      if (immediate || !calcMan.isWorking(this) && information != null
-              && hinformation != null)
+      hmmSeq = group.getHmmConsensus();
+      ProfilesI profiles = group.getHmmProfiles();
+      ann = makeInformationAnnotation(hmmSeq, profiles);
+      if (ann != null)
       {
-        deriveInformation(information, hinformation);
+        ann.groupRef = group;
+        alignment.addAnnotation(ann);
       }
-      index++;
     }
   }
 
   /**
+   * Constructs an HMM Profile information content annotation for a sequence
+   * 
+   * @param seq
+   * @param profile
+   * @return
+   */
+  protected AlignmentAnnotation makeInformationAnnotation(SequenceI seq,
+          ProfilesI profile)
+  {
+    if (seq == null || profile == null)
+    {
+      return null;
+    }
+
+    AlignmentI alignment = alignViewport.getAlignment();
+    int aWidth = alignment == null ? 0 : alignment.getWidth();
+    AlignmentAnnotation ann = new AlignmentAnnotation(seq.getName(),
+            MessageManager.getString("label.information_description"),
+            new Annotation[aWidth], 0f, 6.52f,
+            AlignmentAnnotation.BAR_GRAPH);
+    ann.hasText = true;
+    ann.autoCalculated = false;
+    ann.sequenceRef = seq;
+    ann.setCalcId(InformationThread.HMM_CALC_ID);
+    seq.addAlignmentAnnotation(ann);
+
+    long nseq = getSequences().length;
+    max = AAFrequency.completeInformation(ann, profile,
+            profile.getStartColumn(), profile.getEndColumn() + 1, nseq,
+            max);
+
+    return ann;
+  }
+
+  /**
    * Convert the computed information data into the desired annotation for
    * display.
    * 
@@ -224,14 +299,4 @@ public class InformationThread extends AlignCalcWorker
             hinformation, hinformation.getStartColumn(),
             hinformation.getEndColumn() + 1, nseq, max);
   }
-
-  /**
-   * Get the information data stored on the viewport.
-   * 
-   * @return
-   */
-  protected ProfilesI getSequenceInformation(int index)
-  {
-    return alignViewport.getSequenceInformationHash(index);
-  }
 }