JAL-2629 InformationThread.findOrCreateAnnotation and related updates for a more...
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 20 Mar 2018 16:32:13 +0000 (16:32 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 20 Mar 2018 16:32:13 +0000 (16:32 +0000)
src/jalview/analysis/AAFrequency.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/hmmer/HMMBuild.java
src/jalview/renderer/ResidueShader.java
src/jalview/renderer/ResidueShaderI.java
src/jalview/workers/InformationThread.java
test/jalview/analysis/AAFrequencyTest.java

index 3e59274..963bfbd 100755 (executable)
@@ -344,20 +344,16 @@ public class AAFrequency
    * @param endCol
    *          end column (exclusive)
    * @param ignoreGaps
-   *          if true, normalise residue percentages 
+   *          if true, normalise residue percentages
    * @param showSequenceLogo
    *          if true include all information symbols, else just show modal
    *          residue
-   * @param nseq
-   *          number of sequences
    */
   public static float completeInformation(AlignmentAnnotation information,
-          ProfilesI profiles, int startCol, int endCol, long nseq,
-          float currentMax)
+          ProfilesI profiles, int startCol, int endCol)
   {
     // long now = System.currentTimeMillis();
-    if (information == null || information.annotations == null
-            || information.annotations.length < endCol)
+    if (information == null || information.annotations == null)
     {
       /*
        * called with a bad alignment annotation row 
@@ -368,10 +364,22 @@ public class AAFrequency
 
     float max = 0f;
     SequenceI hmmSeq = information.sequenceRef;
+
+    int seqLength = hmmSeq.getLength();
+    if (information.annotations.length < seqLength)
+    {
+      return 0;
+    }
+
     HiddenMarkovModel hmm = hmmSeq.getHMM();
 
     for (int column = startCol; column < endCol; column++)
     {
+      if (column >= seqLength)
+      {
+        // hmm consensus sequence is shorter than the alignment
+        break;
+      }
       ProfileI profile = profiles.get(column);
       if (profile == null)
       {
@@ -398,7 +406,6 @@ public class AAFrequency
               description, ' ', value);
     }
 
-    max = Math.max(max, currentMax);
     information.graphMax = max;
     return max;
   }
index 3ae46b8..6b100ea 100755 (executable)
@@ -1661,7 +1661,7 @@ public class Alignment implements AlignmentI
     annot.hasText = false;
     if (calcId != null)
     {
-      annot.setCalcId(new String(calcId));
+      annot.setCalcId(calcId);
     }
     annot.autoCalculated = autoCalc;
     if (seqRef != null)
index dd3692a..d591f42 100755 (executable)
@@ -504,8 +504,6 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          - null or specific sequence reference
    * @param groupRef
    *          - null or specific group reference
-   * @param method
-   *          - CalcId for the annotation (must match)
    * 
    * @return existing annotation matching the given attributes
    */
index b7ec9f9..4c38aa0 100755 (executable)
@@ -619,7 +619,7 @@ public class SequenceGroup implements AnnotatedCollectionI
                 (endRes + 1) - startRes, startRes, endRes + 1,
                 showHMMSequenceLogo, hmmIgnoreBelowBackground,
                 hmmUseInfoLetterHeight);
-        _updateInformationRow(info, sequences.size());
+        _updateInformationRow(info);
         upd = true;
       }
       if (consensus != null)
@@ -720,12 +720,11 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * Recalculates the information content on the HMM annotation.
+   * Recalculates the information content on the HMM annotation
    * 
    * @param cnsns
-   * @param nseq
    */
-  private void _updateInformationRow(ProfilesI cnsns, long nseq)
+  private void _updateInformationRow(ProfilesI cnsns)
   {
     if (hmmInformation == null)
     {
@@ -744,7 +743,7 @@ public class SequenceGroup implements AnnotatedCollectionI
                                                       // width
     hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
     AAFrequency.completeInformation(hmmInformation, cnsns, startRes,
-            endRes + 1, nseq, 0f);
+            endRes + 1);
   }
 
   /**
index b892e5c..6887cb5 100644 (file)
@@ -1,8 +1,8 @@
 package jalview.hmmer;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceGroup;
@@ -15,7 +15,6 @@ import jalview.io.DataSourceType;
 import jalview.io.FileParse;
 import jalview.io.HMMFile;
 import jalview.util.MessageManager;
-import jalview.workers.InformationThread;
 import jalview.ws.params.ArgumentI;
 
 import java.io.File;
@@ -58,6 +57,12 @@ public class HMMBuild extends HmmerCommand
   @Override
   public void run()
   {
+    if (params == null)
+    {
+      Cache.log.error("No parameters to HMMBuild!|");
+      return;
+    }
+
     long msgID = System.currentTimeMillis();
     af.setProgressBar(MessageManager.getString("status.running_hmmbuild"),
             msgID);
@@ -65,53 +70,11 @@ public class HMMBuild extends HmmerCommand
     AlignViewportI viewport = af.getViewport();
     try
     {
-      List<AnnotatedCollectionI> runBuildFor = new ArrayList<>();
-      if (params != null)
-      {
-        for (ArgumentI arg : params)
-        {
-          String name = arg.getName();
-          if (MessageManager.getString("label.hmmbuild_for").equals(name))
-          {
-            String value = arg.getValue();
-            if (MessageManager.getString("label.alignment")
-                    .equals(value))
-            {
-              runBuildFor.add(alignment);
-            }
-            else if (MessageManager.getString("label.groups_and_alignment")
-                    .equals(value))
-            {
-              runBuildFor.add(alignment);
-              runBuildFor.addAll(viewport.getAlignment().getGroups());
-            }
-            else if (MessageManager.getString("label.groups").equals(value))
-            {
-              runBuildFor.addAll(viewport.getAlignment().getGroups());
-            }
-            else if (MessageManager.getString("label.selected_group")
-                    .equals(value))
-            {
-              runBuildFor.add(viewport.getSelectionGroup());
-            }
-          }
-          else if (MessageManager.getString("label.use_reference")
-                  .equals(name))
-          {
-            // todo disable this option if no RF annotation on alignment
-            if (!af.getViewport().hasReferenceAnnotation())
-            {
-              JvOptionPane.showInternalMessageDialog(af, MessageManager
-                      .getString("warn.no_reference_annotation"));
-              // return;
-            }
-          }
-        }
-      }
-
       /*
        * run hmmbuild for alignment and/or groups as selected
        */
+      List<AnnotatedCollectionI> runBuildFor = parseParameters(viewport);
+
       for (AnnotatedCollectionI grp : runBuildFor)
       {
         runHMMBuild(grp);
@@ -125,6 +88,58 @@ public class HMMBuild extends HmmerCommand
   }
 
   /**
+   * Scans the parameters to determine whether to run hmmmbuild for the whole
+   * alignment or specified subgroup(s) or both
+   * 
+   * @param viewport
+   * @return
+   */
+  protected List<AnnotatedCollectionI> parseParameters(
+          AlignViewportI viewport)
+  {
+    List<AnnotatedCollectionI> runBuildFor = new ArrayList<>();
+    for (ArgumentI arg : params)
+    {
+      String name = arg.getName();
+      if (MessageManager.getString("label.hmmbuild_for").equals(name))
+      {
+        String value = arg.getValue();
+        if (MessageManager.getString("label.alignment").equals(value))
+        {
+          runBuildFor.add(alignment);
+        }
+        else if (MessageManager.getString("label.groups_and_alignment")
+                .equals(value))
+        {
+          runBuildFor.add(alignment);
+          runBuildFor.addAll(viewport.getAlignment().getGroups());
+        }
+        else if (MessageManager.getString("label.groups").equals(value))
+        {
+          runBuildFor.addAll(viewport.getAlignment().getGroups());
+        }
+        else if (MessageManager.getString("label.selected_group")
+                .equals(value))
+        {
+          runBuildFor.add(viewport.getSelectionGroup());
+        }
+      }
+      else if (MessageManager.getString("label.use_reference")
+              .equals(name))
+      {
+        // todo disable this option if no RF annotation on alignment
+        if (!af.getViewport().hasReferenceAnnotation())
+        {
+          JvOptionPane.showInternalMessageDialog(af, MessageManager
+                  .getString("warn.no_reference_annotation"));
+          // return;
+        }
+      }
+    }
+    return runBuildFor;
+  }
+
+  /**
    * Runs hmmbuild on the given sequences (alignment or group)
    * 
    * @param grp
@@ -153,8 +168,10 @@ public class HMMBuild extends HmmerCommand
 
       /*
        * copy over sequences, excluding hmm consensus sequences
-       * hmm sequences and their Information annotation are also deleted
-       * in preparation for re-adding them when recalculated
+       * hmm sequences are also deleted in preparation for 
+       * re-adding them when recalculated; Information annotation is not
+       * deleted, it will be updated to reference the new hmm sequence
+       * by InformationThread.findOrCreateAnnotation
        */
       Iterator<SequenceI> it = copy.iterator();
       while (it.hasNext())
@@ -162,19 +179,6 @@ public class HMMBuild extends HmmerCommand
         SequenceI seq = it.next();
         if (seq.isHMMConsensusSequence())
         {
-          // todo leave it to InformationThread to delete annotations?
-          AlignmentAnnotation[] seqAnnotations = seq
-                  .getAnnotation();
-          if (seqAnnotations != null)
-          {
-            for (AlignmentAnnotation ann : seqAnnotations)
-            {
-              if (InformationThread.HMM_CALC_ID.equals(ann.getCalcId()))
-              {
-                alignment.deleteAnnotation(ann);
-              }
-            }
-          }
           alignment.deleteSequence(seq);
           it.remove();
         }
index b312730..c031170 100644 (file)
@@ -415,11 +415,4 @@ public class ResidueShader implements ResidueShaderI
   {
     colourScheme = cs;
   }
-
-  @Override
-  public void setInformation(ProfilesI info)
-  {
-    // TODO Auto-generated method stub
-
-  }
 }
index d0b25b6..7e67598 100644 (file)
@@ -5,16 +5,16 @@
  * 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 
+ * modify it under the terms of the GNU General 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.
+ * PURPOSE.  See the GNU General License for more details.
  * 
- * You should have received a copy of the GNU General Public License
+ * You should have received a copy of the GNU General License
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
@@ -32,18 +32,15 @@ import java.util.Map;
 
 public interface ResidueShaderI
 {
+  void setConsensus(ProfilesI cons);
 
-  public abstract void setConsensus(ProfilesI cons);
+  boolean conservationApplied();
 
-  public abstract void setInformation(ProfilesI info);
+  void setConservationApplied(boolean conservationApplied);
 
-  public abstract boolean conservationApplied();
+  void setConservation(Conservation cons);
 
-  public abstract void setConservationApplied(boolean conservationApplied);
-
-  public abstract void setConservation(Conservation cons);
-
-  public abstract void alignmentChanged(AnnotatedCollectionI alignment,
+  void alignmentChanged(AnnotatedCollectionI alignment,
           Map<SequenceI, SequenceCollectionI> hiddenReps);
 
   /**
@@ -53,19 +50,19 @@ public interface ResidueShaderI
    * @param consensusThreshold
    * @param ignoreGaps
    */
-  public abstract void setThreshold(int consensusThreshold,
+  void setThreshold(int consensusThreshold,
           boolean ignoreGaps);
 
-  public abstract void setConservationInc(int i);
+  void setConservationInc(int i);
 
-  public abstract int getConservationInc();
+  int getConservationInc();
 
   /**
    * Get the percentage threshold for this colour scheme
    * 
    * @return Returns the percentage threshold
    */
-  public abstract int getThreshold();
+  int getThreshold();
 
   /**
    * Returns the possibly context dependent colour for the given symbol at the
@@ -77,11 +74,11 @@ public interface ResidueShaderI
    * @param seq
    * @return
    */
-  public abstract Color findColour(char symbol, int position,
+  Color findColour(char symbol, int position,
           SequenceI seq);
 
-  public abstract ColourSchemeI getColourScheme();
+  ColourSchemeI getColourScheme();
 
-  public abstract void setColourScheme(ColourSchemeI cs);
+  void setColourScheme(ColourSchemeI cs);
 
 }
\ No newline at end of file
index 6a86a61..28b4962 100644 (file)
@@ -10,7 +10,6 @@ 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;
@@ -91,9 +90,8 @@ public class InformationThread extends AlignCalcWorker
         return;
       }
 
-      eraseAnnotations(alignment);
       computeProfiles(alignment);
-      updateResultAnnotation(true);
+      updateAnnotation();
 
       if (ap != null)
       {
@@ -111,27 +109,12 @@ public class InformationThread extends AlignCalcWorker
   }
 
   /**
-   * Deletes any existing information annotations. These are sequence-related
-   * annotations which relate to HMM consensus sequences for either the
-   * alignment or a subgroup.
-   * 
-   * @param alignment
-   */
-  protected void eraseAnnotations(AlignmentI alignment)
-  {
-    Iterable<AlignmentAnnotation> anns = alignment
-            .findAnnotation(HMM_CALC_ID);
-    for (AlignmentAnnotation ann : anns)
-    {
-      alignment.deleteAnnotation(ann);
-    }
-  }
-
-  /**
    * Computes HMM profiles for any HMM consensus sequences (for alignment or
-   * subgroups)
+   * subgroups). Any alignment profile computed is stored on the viewport, any
+   * group profile computed is stored on the respective sequence group.
    * 
    * @param alignment
+   * @see AlignViewportI#setHmmProfiles(ProfilesI)
    */
   protected void computeProfiles(AlignmentI alignment)
   {
@@ -148,7 +131,6 @@ public class InformationThread extends AlignCalcWorker
               0, width, true, alignViewport.isIgnoreBelowBackground(),
               alignViewport.isInfoLetterHeight());
       alignViewport.setHmmProfiles(hmmProfiles);
-      // setColourSchemeInformation(hmmProfiles);
     }
 
     /*
@@ -165,7 +147,6 @@ public class InformationThread extends AlignCalcWorker
                 0, width, true, group.isIgnoreBelowBackground(),
                 group.isUseInfoLetterHeight());
         group.setHmmProfiles(hmmProfiles);
-        // setColourSchemeInformation(hmmProfiles);
       }
     }
   }
@@ -180,15 +161,6 @@ public class InformationThread extends AlignCalcWorker
     return alignViewport.getAlignment().getSequencesArray();
   }
 
-  protected void setColourSchemeInformation(ProfilesI information)
-  {
-    ResidueShaderI cs = alignViewport.getResidueShading();
-    if (cs != null)
-    {
-      cs.setInformation(information);
-    }
-  }
-
   /**
    * Get the Gap annotation for the alignment
    * 
@@ -200,36 +172,23 @@ public class InformationThread extends AlignCalcWorker
   }
 
   /**
-   * Updates the information annotation from the sequence profile data using
-   * current visualisation settings
+   * Computes Information Content annotation for any HMM consensus sequences
+   * (for alignment or groups), and updates (or adds) the annotation to the
+   * sequence and the alignment
    */
   @Override
   public void updateAnnotation()
   {
-    updateResultAnnotation(false);
-  }
-
-  /**
-   * 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)
-  {
     AlignmentI alignment = alignViewport.getAlignment();
 
+    this.max = 0f;
+
     /*
      * 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);
-    }
+    updateInformationAnnotation(hmmSeq, profile, null);
 
     /*
      * annotation for group HMM consensus if present
@@ -238,65 +197,96 @@ public class InformationThread extends AlignCalcWorker
     {
       hmmSeq = group.getHmmConsensus();
       ProfilesI profiles = group.getHmmProfiles();
-      ann = makeInformationAnnotation(hmmSeq, profiles);
-      if (ann != null)
-      {
-        ann.groupRef = group;
-        alignment.addAnnotation(ann);
-      }
+      updateInformationAnnotation(hmmSeq, profiles, group);
     }
+
+    /*
+     * todo: this.max is not used, but acquires the maximum value of
+     * information in any of the annotations; set this as graphMax in all
+     * annotations to have them all scaled the same
+     */
   }
 
   /**
-   * Constructs an HMM Profile information content annotation for a sequence
+   * Updates (and first constructs if necessary) an HMM Profile information
+   * content annotation for a sequence. The <code>group</code> argument is null
+   * for the whole alignment annotation, not null for a subgroup annotation.
    * 
    * @param seq
    * @param profile
+   * @param group
    * @return
    */
-  protected AlignmentAnnotation makeInformationAnnotation(SequenceI seq,
-          ProfilesI profile)
+  protected AlignmentAnnotation updateInformationAnnotation(SequenceI seq,
+          ProfilesI profile, SequenceGroup group)
   {
     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);
+    AlignmentAnnotation ann = findOrCreateAnnotation(seq, group);
+
     seq.addAlignmentAnnotation(ann);
 
-    long nseq = getSequences().length;
     max = AAFrequency.completeInformation(ann, profile,
-            profile.getStartColumn(), profile.getEndColumn() + 1, nseq,
-            max);
+            profile.getStartColumn(), profile.getEndColumn() + 1);
 
     return ann;
   }
 
   /**
-   * Convert the computed information data into the desired annotation for
-   * display.
+   * A helper method that first searches for the HMM annotation that matches the
+   * group reference (null for the whole alignment annotation). If found, its
+   * sequence reference is updated to the given sequence (the recomputed HMM
+   * consensus sequence). If not found, it is created. This supports both
+   * creating the annotation the first time hmmbuild is run, and updating it if
+   * hmmbuild is re-run.
    * 
-   * @param informationAnnotation
-   *          the annotation to be populated
-   * @param hinformation
-   *          the computed information data
+   * @param seq
+   * @param group
+   * @return
    */
-  protected void deriveInformation(
-          AlignmentAnnotation informationAnnotation, ProfilesI hinformation)
+  AlignmentAnnotation findOrCreateAnnotation(SequenceI seq,
+          SequenceGroup group)
   {
-    long nseq = getSequences().length;
-    max = AAFrequency.completeInformation(informationAnnotation,
-            hinformation, hinformation.getStartColumn(),
-            hinformation.getEndColumn() + 1, nseq, max);
+    /*
+     * can't use Alignment.findOrCreateAnnotation here because we 
+     * want to update, rather than match on, the sequence ref
+     */
+    AlignmentAnnotation info = null;
+
+    AlignmentI alignment = alignViewport.getAlignment();
+    AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+    if (anns != null)
+    {
+      for (AlignmentAnnotation ann : anns)
+      {
+        if (HMM_CALC_ID.equals(ann.getCalcId()) && group == ann.groupRef)
+        {
+          info = ann;
+          info.setSequenceRef(seq);
+          break;
+        }
+      }
+    }
+
+    if (info == null)
+    {
+      int aWidth = alignment.getWidth();
+      String desc = MessageManager
+              .getString("label.information_description");
+      float graphMax = 6.52f; // todo where does this value derive from?
+      info = new AlignmentAnnotation(seq.getName(), desc,
+              new Annotation[aWidth], 0f, graphMax,
+              AlignmentAnnotation.BAR_GRAPH);
+      info.setCalcId(HMM_CALC_ID);
+      info.setSequenceRef(seq);
+      info.groupRef = group;
+      info.hasText = true;
+      alignment.addAnnotation(info);
+    }
+
+    return info;
   }
 }
index fc4063a..2b6c512 100644 (file)
@@ -335,7 +335,7 @@ public class AAFrequencyTest
     seq.setHMM(hmm);
     AlignmentAnnotation annot = new AlignmentAnnotation("", "", annots);
     annot.setSequenceRef(seq);
-    AAFrequency.completeInformation(annot, profs, 0, 1, 1, 1f);
+    AAFrequency.completeInformation(annot, profs, 0, 1);
     float ic = annot.annotations[0].value;
     assertEquals(0.91532f, ic, 0.0001f);
     ic = annot.annotations[1].value;