JAL-2629 tweaks to Information annotation updating
[jalview.git] / src / jalview / schemes / HMMERAlignmentColourScheme.java
1 package jalview.schemes;
2
3 import jalview.datamodel.AnnotatedCollectionI;
4 import jalview.datamodel.HiddenMarkovModel;
5 import jalview.datamodel.ResidueCount;
6 import jalview.datamodel.SequenceCollectionI;
7 import jalview.datamodel.SequenceI;
8 import jalview.util.ColorUtils;
9 import jalview.util.Comparison;
10
11 import java.awt.Color;
12 import java.util.HashMap;
13 import java.util.Map;
14
15 /**
16  * A colour scheme based on a selected Hidden Markov Model. The colour is
17  * <ul>
18  * <li>white for a gap</li>
19  * <li>red for an insertion</li>
20  * <li>orange for negative information content</li>
21  * <li>white to blue for increasing information content</li>
22  * </ul>
23  * where information content is the log ratio
24  * 
25  * <pre>
26  *   log(profile match emission probability / residue background probability>
27  * </pre>
28  * 
29  * using the alignment's background frequencies for residues.
30  * 
31  * @author tzvanaalten
32  *
33  */
34 public class HMMERAlignmentColourScheme extends ResidueColourScheme
35 {
36   /*
37    * the ratio, for each symbol, of its frequency to total symbol count
38    */
39   Map<Character, Double> frequency = new HashMap<>();
40
41   float logTotalCount;
42
43   HiddenMarkovModel hmm;
44
45   /**
46    * Constructor given a Hidden Markov Model
47    * 
48    * @param sg
49    * 
50    * @param markov
51    */
52   public HMMERAlignmentColourScheme(AnnotatedCollectionI sg,
53           HiddenMarkovModel markov)
54   {
55     hmm = markov;
56     countFrequencies(sg);
57   }
58
59   /**
60    * Default constructor (required by ColourSchemes.loadColourSchemes)
61    */
62   public HMMERAlignmentColourScheme()
63   {
64   }
65
66   @Override
67   public Color findColour(char symbol, int position, SequenceI seq,
68           String consensusResidue, float pid)
69   {
70     return findColour(symbol, position);
71   }
72
73   /**
74    * Returns the colour at a particular symbol at a column in the alignment:
75    * <ul>
76    * <li>white for a gap</li>
77    * <li>red for an insertion</li>
78    * <li>orange for negative information content</li>
79    * <li>white to blue for increasing information content</li>
80    * </ul>
81    * 
82    * @param symbol
83    * @param column
84    * @return
85    */
86   private Color findColour(char symbol, int column)
87   {
88     if (hmm == null || Comparison.isGap(symbol))
89     {
90       return Color.white;
91     }
92     if (Character.isLowerCase(symbol))
93     {
94       symbol = Character.toUpperCase(symbol);
95     }
96     double prob = hmm.getMatchEmissionProbability(column, symbol);
97     Double freq = frequency.get(symbol);
98     if (freq == null)
99     {
100       return Color.white;
101     }
102     if (prob == 0)
103     {
104       return new Color(230, 0, 0);
105     }
106     double value = Math.log(prob / freq.doubleValue());
107     Color colour = null;
108     if (value > 0)
109     {
110       colour = ColorUtils.getGraduatedColour((float) value, 0,
111               Color.WHITE, logTotalCount, Color.blue);
112     }
113     else if (value < 0)
114     {
115       return Color.ORANGE;
116     }
117     return colour;
118   }
119
120   @Override
121   public void alignmentChanged(AnnotatedCollectionI collection,
122           Map<SequenceI, SequenceCollectionI> hiddenReps)
123   {
124     /*
125      * ? no need to do anything if alignment is adjusted
126      * since findColour() handles everything 
127      */
128   }
129
130   @Override
131   public ColourSchemeI getInstance(AnnotatedCollectionI sg,
132           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
133   {
134     SequenceI hmmSeq = sg.getHmmConsensus();
135     HiddenMarkovModel model = hmmSeq == null ? null : hmmSeq.getHMM();
136     return new HMMERAlignmentColourScheme(sg, model);
137   }
138
139   /**
140    * Answers true if the sequence collection has an HMM consensus sequence, else
141    * false
142    */
143   @Override
144   public boolean isApplicableTo(AnnotatedCollectionI ac)
145   {
146     return ac.getHmmConsensus() != null;
147   }
148
149   @Override
150   public String getSchemeName()
151   {
152     return JalviewColourScheme.HMMERA.toString();
153   }
154
155   @Override
156   public boolean isSimple()
157   {
158     return false;
159   }
160
161   /**
162    * Counts and stores the relatively frequency of every residue in the
163    * alignment
164    * 
165    * @param sg
166    */
167   public void countFrequencies(AnnotatedCollectionI sg)
168   {
169     ResidueCount counts = new ResidueCount(sg.getSequences());
170     int total = counts.getTotalResidueCount(); // excludes gaps
171
172     for (char symbol : counts.getSymbolCounts().symbols)
173     {
174       double freq = counts.getCount(symbol) / (double) total;
175       frequency.put(symbol, freq);
176     }
177     logTotalCount = (float) Math.log(total);
178   }
179 }