JAL-3665 sequences shaded according to emmission probability of first aligned hmm...
[jalview.git] / src / jalview / schemes / HmmerColourScheme.java
index 011c071..fa62071 100644 (file)
@@ -1,21 +1,22 @@
 package jalview.schemes;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.HiddenMarkovModel;
-import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 import jalview.util.ColorUtils;
 import jalview.util.Comparison;
 
 import java.awt.Color;
+import java.util.List;
 import java.util.Map;
 
 /**
  * Base class for colour schemes based on a selected Hidden Markov Model. The
- * colour is
+ * colour is with reference to an HMM consensus sequence and HMM profile
  * <ul>
  * <li>white for a gap</li>
- * <li>red for an insertion</li>
+ * <li>red for an insertion (position is gapped in the HMM consensus)</li>
  * <li>orange for negative information content</li>
  * <li>white to blue for increasing information content</li>
  * </ul>
@@ -35,18 +36,41 @@ public abstract class HmmerColourScheme extends ResidueColourScheme
 {
   private static final Color INSERTION_COLOUR = new Color(230, 0, 0); // reddish
 
-  private HiddenMarkovModel hmm;
+  /*
+   * the aligned HMM consensus sequence to use as reference for colouring
+   */
+  private List<SequenceI> hmmSeqs=null;
 
+  
   private Map<Character, Float> frequencies;
 
   /**
-   * Constructor given a Hidden Markov Model
+   * Constructor given a list of Hidden Markov Model consensus sequences. The
+   * first sequence provides the HMM profile from which we can read the emission
+   * probabilities that determine the colour.
    * 
-   * @param markov
+   * @param hmmSeqs
    */
-  public HmmerColourScheme(HiddenMarkovModel markov)
+  public HmmerColourScheme(List<SequenceI> hmmSeqs)
   {
-    hmm = markov;
+    this.hmmSeqs = hmmSeqs.isEmpty() ? null : hmmSeqs;
+  }
+
+  protected   String getAlphabetType()
+  {
+    if (hmmSeqs!=null) {
+      for (SequenceI hmmSeq : hmmSeqs)
+    {
+      if (hmmSeq != null)
+      {
+        HiddenMarkovModel hmm = hmmSeq.getHMM();
+        if (hmm != null)
+        {
+          return hmm.getAlphabetType();
+        }
+      }
+    }}
+    return ResidueProperties.ALPHABET_AMINO;
   }
 
   /**
@@ -57,10 +81,10 @@ public abstract class HmmerColourScheme extends ResidueColourScheme
   }
 
   @Override
-  public Color findColour(char symbol, int position, SequenceI seq,
+  public Color findColour(char symbol, int column, SequenceI seq,
           String consensusResidue, float pid)
   {
-    return findColour(symbol, position);
+    return findColour(symbol, column);
   }
 
   /**
@@ -78,42 +102,59 @@ public abstract class HmmerColourScheme extends ResidueColourScheme
    */
   private Color findColour(char symbol, int column)
   {
-    if (getHmm() == null || Comparison.isGap(symbol))
+    if (hmmSeqs==null || Comparison.isGap(symbol))
     {
+      // todo: could return probability of seeing a gap ?
       return Color.white;
     }
-    if (Character.isLowerCase(symbol))
-    {
-      symbol = Character.toUpperCase(symbol);
-    }
-
-    double prob = getHmm().getMatchEmissionProbability(column, symbol);
-    Float freq = 0f;
-
-    if (!frequencies.containsKey(symbol))
-    {
-      return Color.WHITE;
-    }
-    else
-    {
-      freq = frequencies.get(symbol);
-    }
-    if (prob == 0D)
+    // locate first hmm with a non-gap at this position
+    for (SequenceI hmmSeq:hmmSeqs)
     {
-      return INSERTION_COLOUR;
+      if (hmmSeq==null)
+      {
+        continue;
+      }
+      if (!Comparison.isGap(hmmSeq.getCharAt(column)))
+      {
+        if (symbol >= 'a')
+        {
+          symbol += 'A' - 'a';
+        }
+
+        final double prob = hmmSeq.getHMM()
+                .getMatchEmissionProbability(column, symbol);
+
+        Float freq = 0f;
+
+        if (!frequencies.containsKey(symbol))
+        {
+          return Color.WHITE;
+        }
+        else
+        {
+          freq = frequencies.get(symbol);
+        }
+
+        /*
+         * Orange if match emission probability is less than background probability
+         */
+        double infoRatio = prob / freq.floatValue();
+        Color colour = Color.ORANGE;
+        if (infoRatio >= 1)
+        {
+          /*
+           * log-scale graduated shade of blue if prob is greater than background  
+           */
+          float infoLog = (float) Math.log(infoRatio);
+          colour = ColorUtils.getGraduatedColour(infoLog, 0, Color.WHITE,
+                  getMaxInformationScore(), Color.blue);
+        }
+
+        return colour;
+      }
     }
-    double value = Math.log(prob / freq.floatValue());
-    Color colour = null;
-    if (value > 0)
-    {
-      colour = ColorUtils.getGraduatedColour((float) value, 0,
-              Color.WHITE, getMaxInformationScore(), Color.blue);
-    }
-    else if (value < 0)
-    {
-      return Color.ORANGE;
-    }
-    return colour;
+    // no more seqs. so
+    return INSERTION_COLOUR;
   }
 
   /**
@@ -124,39 +165,24 @@ public abstract class HmmerColourScheme extends ResidueColourScheme
    */
   abstract float getMaxInformationScore();
 
-  @Override
-  public void alignmentChanged(AnnotatedCollectionI collection,
-          Map<SequenceI, SequenceCollectionI> hiddenReps)
-  {
-    /*
-     * ? no need to do anything if alignment is adjusted
-     * since findColour() handles everything 
-     */
-  }
-
   /**
    * Answers a new colour scheme instance based on the HMM of the first sequence
    * in ac that has an HMM
    */
   @Override
-  public ColourSchemeI getInstance(AnnotatedCollectionI ac,
-          Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
+  public ColourSchemeI getInstance(AlignViewportI viewport,
+          AnnotatedCollectionI ac)
   {
-    SequenceI hmmSeq = ac.getHmmConsensus();
-    HiddenMarkovModel model = hmmSeq == null ? null : hmmSeq.getHMM();
-
-    return newInstance(ac, model);
+    return newInstance(ac);
   }
 
   /**
    * Answers a new instance of the colour scheme for the given HMM
    * 
    * @param ac
-   * @param model
    * @return
    */
-  protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac,
-          HiddenMarkovModel model);
+  protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac);
 
   @Override
   public boolean isSimple()
@@ -171,7 +197,7 @@ public abstract class HmmerColourScheme extends ResidueColourScheme
   @Override
   public boolean isApplicableTo(AnnotatedCollectionI ac)
   {
-    return ac.getHmmConsensus() != null;
+    return !ac.getHmmSequences().isEmpty();
   }
 
   protected Map<Character, Float> getFrequencies()
@@ -183,10 +209,4 @@ public abstract class HmmerColourScheme extends ResidueColourScheme
   {
     this.frequencies = frequencies;
   }
-
-  protected HiddenMarkovModel getHmm()
-  {
-    return hmm;
-  }
-
 }