Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
index 5e33229..0a68792 100755 (executable)
@@ -25,6 +25,8 @@ import jalview.analysis.Conservation;
 import jalview.renderer.ResidueShader;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
+import jalview.util.MessageManager;
+import jalview.workers.InformationThread;
 
 import java.awt.Color;
 import java.beans.PropertyChangeListener;
@@ -43,11 +45,10 @@ import java.util.Map;
 public class SequenceGroup implements AnnotatedCollectionI
 {
   // TODO ideally this event notification functionality should be separated into
-  // a
-  // subclass of ViewportProperties similarly to ViewportRanges. Done here as
-  // quick fix for JAL-2665
+  // a subclass of ViewportProperties similarly to ViewportRanges.
+  // Done here as a quick fix for JAL-2665
   public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
-
+  
   protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
           this);
 
@@ -65,40 +66,46 @@ public class SequenceGroup implements AnnotatedCollectionI
   String groupName;
 
   String description;
-
+  
   Conservation conserve;
 
+  Conservation conservationData;
+
+  ProfilesI consensusProfiles;
+
+  ProfilesI hmmProfiles;
+
   boolean displayBoxes = true;
 
   boolean displayText = true;
 
   boolean colourText = false;
 
-  /**
-   * True if the group is defined as a group on the alignment, false if it is
-   * just a selection.
+  /*
+   * true if the group is defined as a group on the alignment, false if it is
+   * just a selection
    */
   boolean isDefined = false;
 
-  /**
+  /*
    * after Olivier's non-conserved only character display
    */
   boolean showNonconserved = false;
 
-  /**
-   * group members
+  /*
+   * sequences in the group
    */
   private List<SequenceI> sequences;
 
-  /**
+  /*
    * representative sequence for this group (if any)
    */
   private SequenceI seqrep = null;
 
   int width = -1;
 
-  /**
-   * Colourscheme applied to group if any
+  /*
+   * colour scheme applied to group if any
    */
   public ResidueShaderI cs;
 
@@ -122,22 +129,29 @@ public class SequenceGroup implements AnnotatedCollectionI
 
   public Color textColour2 = Color.white;
 
-  /**
-   * consensus calculation property
+  /*
+   * properties for consensus annotation
    */
   private boolean ignoreGapsInConsensus = true;
 
-  /**
-   * consensus calculation property
-   */
   private boolean showSequenceLogo = false;
 
-  /**
-   * flag indicating if logo should be rendered normalised
-   */
   private boolean normaliseSequenceLogo;
 
   /*
+   * properties for HMM information annotation
+   */
+  private boolean hmmIgnoreBelowBackground = true;
+
+  private boolean hmmUseInfoLetterHeight;
+
+  private boolean hmmShowSequenceLogo;
+
+  private boolean hmmNormaliseSequenceLogo;
+
+  private boolean hmmShowHistogram;
+
+  /*
    * visibility of rows or represented rows covered by group
    */
   private boolean hidereps = false;
@@ -145,18 +159,22 @@ public class SequenceGroup implements AnnotatedCollectionI
   /*
    * visibility of columns intersecting this group
    */
-  private boolean hidecols = false;
+  private boolean hidecols;
 
   AlignmentAnnotation consensus = null;
 
   AlignmentAnnotation conservation = null;
 
+  private AlignmentAnnotation hmmInformation;
+  
   private boolean showConsensusHistogram;
-
+  
   private AnnotatedCollectionI context;
 
+
   /**
-   * Creates a new SequenceGroup object.
+   * Constructor, assigning a generated default name of "JGroup:" with object
+   * hashcode appended
    */
   public SequenceGroup()
   {
@@ -199,10 +217,13 @@ public class SequenceGroup implements AnnotatedCollectionI
    * copy constructor
    * 
    * @param seqsel
+   * @param keepsequences
+   *          if false do not add sequences from seqsel to new instance
    */
   public SequenceGroup(SequenceGroup seqsel)
   {
     this();
+
     if (seqsel != null)
     {
       sequences = new ArrayList<>();
@@ -228,6 +249,9 @@ public class SequenceGroup implements AnnotatedCollectionI
       showSequenceLogo = seqsel.showSequenceLogo;
       normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
       showConsensusHistogram = seqsel.showConsensusHistogram;
+      hmmShowSequenceLogo = seqsel.hmmShowSequenceLogo;
+      hmmNormaliseSequenceLogo = seqsel.hmmNormaliseSequenceLogo;
+      hmmShowHistogram = seqsel.hmmShowHistogram;
       idColour = seqsel.idColour;
       outlineColour = seqsel.outlineColour;
       seqrep = seqsel.seqrep;
@@ -236,8 +260,12 @@ public class SequenceGroup implements AnnotatedCollectionI
       thresholdTextColour = seqsel.thresholdTextColour;
       width = seqsel.width;
       ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
+      hmmIgnoreBelowBackground = seqsel.hmmIgnoreBelowBackground;
+      hmmUseInfoLetterHeight = seqsel.hmmUseInfoLetterHeight;
       if (seqsel.conserve != null)
       {
+        // todo avoid doing this if we don't actually want derived calculations
+        // !
         recalcConservation(); // safer than
         // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
       }
@@ -581,8 +609,9 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * calculate residue conservation for group - but only if necessary. returns
-   * true if the calculation resulted in a visible change to group
+   * Recalculates column consensus, conservation, and HMM annotation for the
+   * group (as applicable). Returns true if the calculation resulted in a
+   * visible change to group.
    * 
    * @param defer
    *          when set, colourschemes for this group are not refreshed after
@@ -590,7 +619,8 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public boolean recalcConservation(boolean defer)
   {
-    if (cs == null && consensus == null && conservation == null)
+    if (cs == null && consensus == null && conservation == null
+            && hmmInformation == null)
     {
       return false;
     }
@@ -601,6 +631,16 @@ public class SequenceGroup implements AnnotatedCollectionI
     {
       ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
               endRes + 1, showSequenceLogo);
+      if (hmmInformation != null)
+      {
+        HiddenMarkovModel hmm = hmmInformation.sequenceRef.getHMM();
+
+        ProfilesI info = AAFrequency.calculateHMMProfiles(hmm,
+                (endRes + 1) - startRes, startRes, endRes + 1,
+                hmmIgnoreBelowBackground, hmmUseInfoLetterHeight);
+        _updateInformationRow(info);
+        upd = true;
+      }
       if (consensus != null)
       {
         _updateConsensusRow(cnsns, sequences.size());
@@ -701,6 +741,33 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
+   * Recalculates the information content on the HMM annotation
+   * 
+   * @param cnsns
+   */
+  private void _updateInformationRow(ProfilesI cnsns)
+  {
+    if (hmmInformation == null)
+    {
+      createInformationAnnotation();
+    }
+    hmmInformation.description = MessageManager
+            .getString("label.information_description");
+    setHmmProfiles(cnsns);
+    // preserve width if already set
+    int aWidth = (hmmInformation.annotations != null)
+            ? (endRes < hmmInformation.annotations.length
+                    ? hmmInformation.annotations.length : endRes + 1)
+            : endRes + 1;
+    hmmInformation.annotations = null;
+    hmmInformation.annotations = new Annotation[aWidth]; // should be alignment
+                                                      // width
+    hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+    AAFrequency.completeInformation(hmmInformation, cnsns, startRes,
+            endRes + 1);
+  }
+
+  /**
    * @param s
    *          sequence to either add or remove from group
    * @param recalc
@@ -1156,6 +1223,22 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
+   * Creates the Hidden Markov Model annotation for this group
+   */
+  void createInformationAnnotation()
+  {
+    hmmInformation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
+            6.25f, AlignmentAnnotation.BAR_GRAPH);
+    hmmInformation.hasText = true;
+    hmmInformation.autoCalculated = false;
+    hmmInformation.groupRef = this;
+    hmmInformation.label = getName();
+    hmmInformation.description = MessageManager
+            .getString("label.information_description");
+    hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+  }
+
+  /**
    * set this alignmentAnnotation object as the one used to render consensus
    * annotation
    * 
@@ -1242,6 +1325,26 @@ public class SequenceGroup implements AnnotatedCollectionI
     return ignoreGapsInConsensus;
   }
 
+  public void setIgnoreBelowBackground(boolean state)
+  {
+    hmmIgnoreBelowBackground = state;
+  }
+
+  public boolean isIgnoreBelowBackground()
+  {
+    return hmmIgnoreBelowBackground;
+  }
+
+  public void setInfoLetterHeight(boolean state)
+  {
+    hmmUseInfoLetterHeight = state;
+  }
+
+  public boolean isUseInfoLetterHeight()
+  {
+    return hmmUseInfoLetterHeight;
+  }
+
   /**
    * @param showSequenceLogo
    *          indicates if a sequence logo is shown for consensus annotation
@@ -1485,4 +1588,70 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
   }
+
+  public boolean isShowInformationHistogram()
+  {
+    return hmmShowHistogram;
+  }
+
+  public void setShowInformationHistogram(boolean state)
+  {
+    if (hmmShowHistogram != state && hmmInformation != null)
+    {
+      this.hmmShowHistogram = state;
+      // recalcConservation(); TODO don't know what to do here next
+    }
+    this.hmmShowHistogram = state;
+  }
+
+  public boolean isShowHMMSequenceLogo()
+  {
+    return hmmShowSequenceLogo;
+  }
+
+  public void setShowHMMSequenceLogo(boolean state)
+  {
+    hmmShowSequenceLogo = state;
+  }
+
+  public boolean isNormaliseHMMSequenceLogo()
+  {
+    return hmmNormaliseSequenceLogo;
+  }
+
+  public void setNormaliseHMMSequenceLogo(boolean state)
+  {
+    hmmNormaliseSequenceLogo = state;
+  }
+
+  public ProfilesI getConsensusData()
+  {
+    return consensusProfiles;
+  }
+
+  public ProfilesI getHmmProfiles()
+  {
+    return hmmProfiles;
+  }
+
+  public void setHmmProfiles(ProfilesI hmmProfiles)
+  {
+    this.hmmProfiles = hmmProfiles;
+  }
+
+  @Override
+  public List<SequenceI> getHmmSequences()
+  {
+    List<SequenceI> result = new ArrayList<>();
+    for (int i = 0; i < sequences.size(); i++)
+    {
+      SequenceI seq = sequences.get(i);
+      if (seq.hasHMMProfile())
+      {
+        result.add(seq);
+      }
+    }
+    return result;
+  }
+
 }