JAL-2717 JAL-2668 fixes to HMMER colour scheme display names, enabled state, test...
[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     return new HMMERAlignmentColourScheme(sg, hmm);
135   }
136
137   @Override
138   public boolean isApplicableTo(AnnotatedCollectionI ac)
139   {
140     return !ac.getHMMConsensusSequences().isEmpty();
141   }
142
143   @Override
144   public String getSchemeName()
145   {
146     return JalviewColourScheme.HMMERA.toString();
147   }
148
149   @Override
150   public boolean isSimple()
151   {
152     return false;
153   }
154
155   /**
156    * Counts and stores the relatively frequency of every residue in the
157    * alignment
158    * 
159    * @param sg
160    */
161   public void countFrequencies(AnnotatedCollectionI sg)
162   {
163     ResidueCount counts = new ResidueCount(sg.getSequences());
164     int total = counts.getTotalCount(); // excludes gaps
165
166     for (char symbol : counts.getSymbolCounts().symbols)
167     {
168       double freq = counts.getCount(symbol) / (double) total;
169       frequency.put(symbol, freq);
170     }
171     logTotalCount = (float) Math.log(total);
172   }
173 }