JAL-2629 some tidying of Hmmer commands (wip)
[jalview.git] / src / jalview / schemes / HMMERColourScheme.java
index b2d0742..2ba0898 100644 (file)
@@ -4,105 +4,167 @@ 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;
 
+/**
+ * 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 new Color(255, 255, 255);
-    }
     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 (hmm == null || Comparison.isGap(symbol))
+    {
+      return Color.white;
+    }
+    if (Character.isLowerCase(symbol))
+    {
+      symbol = Character.toUpperCase(symbol);
+    }
 
-
-    Double probability;
-    probability = hmm.getMatchEmissionProbability(position, symbol);
-    // Double redModifier = Math.pow(probability, 0.9);
-    Double doubleGreenModifier;
-    float greenModifier;
-    if (probability < 0.5)
+    double prob = hmm.getMatchEmissionProbability(column, symbol);
+    Float freq = 0f;
+    String alpha = hmm.getAlphabetType();
+    if (!ResidueProperties.backgroundFrequencies.get(alpha)
+            .containsKey(symbol))
     {
-      doubleGreenModifier = probability;
-      greenModifier = doubleGreenModifier.floatValue();
+      return Color.WHITE;
     }
     else
     {
-      doubleGreenModifier = Math.pow(probability, 1 / 1.9);
-      greenModifier = doubleGreenModifier.floatValue();
+      freq = ResidueProperties.backgroundFrequencies.get(alpha).get(symbol);
     }
-    // Double blueModifier = Math.pow(probability, 0.9);
-    return new Color(1f, 1f - greenModifier, 0.f);
-
+    if (prob == 0D)
+    {
+      return REDDISH;
+    }
+    double value = Math.log(prob / freq.floatValue());
+    Color colour = null;
+    if (value > 0)
+    {
+      colour = ColorUtils.getGraduatedColour((float) value, 0,
+              Color.WHITE, MAX_LOG_RATIO, Color.blue);
+    }
+    else if (value < 0)
+    {
+      return Color.ORANGE;
+    }
+    return colour;
   }
 
   @Override
   public void alignmentChanged(AnnotatedCollectionI collection,
           Map<SequenceI, SequenceCollectionI> hiddenReps)
   {
-
-    collection.setHMM(hmm);
+    /*
+     * ? 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)
   {
-    
-    
-    
-    
-
-    HMMERColourScheme markov = new HMMERColourScheme(sg.getHMM());
-    return markov;
+    HiddenMarkovModel model = null;
+    List<SequenceI> seqs = sg.getHMMConsensusSequences();
+    if (!seqs.isEmpty())
+    {
+      model = seqs.get(0).getHMM();
+    }
+    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;
   }
 
   @Override
-  public boolean isSimple()
+  public boolean isApplicableTo(AnnotatedCollectionI ac)
   {
-    // TODO Auto-generated method stub
-    return false;
+    return !ac.getHMMConsensusSequences().isEmpty();
   }
 
 }