Merge branch 'develop' into trialMerge
[jalview.git] / src / jalview / schemes / HMMERColourScheme.java
index a7fc8b8..ee6c873 100644 (file)
@@ -1,6 +1,5 @@
 package jalview.schemes;
 
-import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.SequenceCollectionI;
@@ -11,127 +10,161 @@ import jalview.util.Comparison;
 import java.awt.Color;
 import java.util.Map;
 
+/**
+ * A colour scheme based on a selected Hidden Markov Model. The colour is
+ * <ul>
+ * <li>white for a gap</li>
+ * <li>red for an insertion</li>
+ * <li>orange for negative information content</li>
+ * <li>white to blue for increasing information content</li>
+ * </ul>
+ * where information content is the log ratio
+ * 
+ * <pre>
+ *   log(profile match emission probability / residue background probability>
+ * </pre>
+ * 
+ * using global ('Uniprot') background frequencies for residues.
+ * 
+ * @author tzvanaalten
+ *
+ */
 public class HMMERColourScheme extends ResidueColourScheme
 {
+  /*
+   * The highest possible log ratio is when match emission probability in
+   * the HMM model is 1, and background (for W) is 0.0109 giving
+   * log(1/0.0109) = log(91.743) = 4.519
+   */
+  private static final float MAX_LOG_RATIO = 4.519f;
 
-  AnnotatedCollectionI alignment;
+  private static final Color REDDISH = new Color(230, 0, 0);
 
   HiddenMarkovModel hmm;
-  
-  boolean peptideSpecific;
-
-  boolean nucleotideSpecific;
 
+  /**
+   * Constructor given a Hidden Markov Model
+   * 
+   * @param markov
+   */
   public HMMERColourScheme(HiddenMarkovModel markov)
   {
     hmm = markov;
   }
+
+  /**
+   * Default constructor (required by ColourSchemes.loadColourSchemes)
+   */
   public HMMERColourScheme()
   {
-
   }
 
   @Override
   public Color findColour(char symbol, int position, SequenceI seq,
           String consensusResidue, float pid)
   {
-    if (hmm ==null)
-    {
-      return Color.white;
-    }
     return findColour(symbol, position);
   }
 
-  public Color findColour(char symbol, int position)
+  /**
+   * Returns the colour at a particular symbol at a column in the alignment:
+   * <ul>
+   * <li>white for a gap</li>
+   * <li>red for an insertion</li>
+   * <li>orange for negative information content</li>
+   * <li>white to blue for increasing information content</li>
+   * </ul>
+   * 
+   * @param symbol
+   * @param column
+   * @return
+   */
+  private Color findColour(char symbol, int column)
   {
-
-    if (Comparison.isGap(symbol))
+    if (hmm == null || Comparison.isGap(symbol))
     {
       return Color.white;
     }
-    Double prob;
-    prob = hmm.getMatchEmissionProbability(position, symbol);
-    double freq = ResidueProperties.aminoBackgroundFrequencies.get(symbol);
-    Double value = prob - freq;
+    if (Character.isLowerCase(symbol))
+    {
+      symbol = Character.toUpperCase(symbol);
+    }
 
+    double prob = hmm.getMatchEmissionProbability(column, symbol);
+    Float freq = 0f;
+    String alpha = hmm.getAlphabetType();
+    if (!ResidueProperties.backgroundFrequencies.get(alpha)
+            .containsKey(symbol))
+    {
+      return Color.WHITE;
+    }
+    else
+    {
+      freq = ResidueProperties.backgroundFrequencies.get(alpha).get(symbol);
+    }
+    if (prob == 0D)
+    {
+      return REDDISH;
+    }
+    double value = Math.log(prob / freq.floatValue());
     Color colour = null;
-    if (value >= 0)
+    if (value > 0)
     {
-
-      colour = ColorUtils.getGraduatedColour(value.floatValue(), 0,
-              Color.WHITE, 1f, Color.green);
+      colour = ColorUtils.getGraduatedColour((float) value, 0,
+              Color.WHITE, MAX_LOG_RATIO, Color.blue);
     }
     else if (value < 0)
     {
-      return Color.YELLOW;
-
+      return Color.ORANGE;
     }
     return colour;
-
   }
-    
-
-
-
-
 
   @Override
   public void alignmentChanged(AnnotatedCollectionI collection,
           Map<SequenceI, SequenceCollectionI> hiddenReps)
   {
-    AlignmentAnnotation[] annArr = collection.getAlignmentAnnotation();
-    for (AlignmentAnnotation ann : annArr)
-    {
-      if (ann.label.indexOf("Information Content") > -1)
-      {
-        hmm = ann.getHMM();
-      }
-    }
-
+    /*
+     * ? 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 sg that has an HMM
+   */
   @Override
   public ColourSchemeI getInstance(AnnotatedCollectionI sg,
           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
   {
-    HiddenMarkovModel markov = null;
-    AlignmentAnnotation[] annArr = sg.getAlignmentAnnotation();
-    for (AlignmentAnnotation ann : annArr)
-    {
-      if (ann.label.indexOf("Information Content") > -1)
-      {
-        markov = ann.getHMM();
-      }
-    }
-    
-    
-    
-    
+    SequenceI hmmSeq = sg.getHmmConsensus();
+    HiddenMarkovModel model = hmmSeq == null ? null : hmmSeq.getHMM();
 
-    HMMERColourScheme colour = new HMMERColourScheme(markov);
+    HMMERColourScheme colour = new HMMERColourScheme(model);
     return colour;
-
   }
 
   @Override
-  public boolean isApplicableTo(AnnotatedCollectionI ac)
+  public String getSchemeName()
   {
-      return true;
-
+    return JalviewColourScheme.HMMERU.toString();
   }
 
   @Override
-  public String getSchemeName()
+  public boolean isSimple()
   {
-
-    return JalviewColourScheme.HMMER.name();
+    return false;
   }
 
+  /**
+   * Answers true if the sequence collection has an HMM consensus sequence, else
+   * false
+   */
   @Override
-  public boolean isSimple()
+  public boolean isApplicableTo(AnnotatedCollectionI ac)
   {
-    // TODO Auto-generated method stub
-    return false;
+    return ac.getHmmConsensus() != null;
   }
 
 }