Merge branch 'Jalview-JS/develop' into merge_js_develop
[jalview.git] / src / jalview / schemes / HmmerColourScheme.java
1 package jalview.schemes;
2
3 import jalview.api.AlignViewportI;
4 import jalview.datamodel.AnnotatedCollectionI;
5 import jalview.datamodel.HiddenMarkovModel;
6 import jalview.datamodel.SequenceI;
7 import jalview.util.ColorUtils;
8 import jalview.util.Comparison;
9
10 import java.awt.Color;
11 import java.util.List;
12 import java.util.Map;
13
14 /**
15  * Base class for colour schemes based on a selected Hidden Markov Model. The
16  * colour is with reference to an HMM consensus sequence and HMM profile
17  * <ul>
18  * <li>white for a gap</li>
19  * <li>red for an insertion (position is gapped in the HMM consensus)</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  * Sub-class implementations use either global ('Uniprot') or local
30  * ('alignment') background frequencies.
31  * 
32  * @author tzvanaalten
33  * @author gmcarstairs
34  */
35 public abstract class HmmerColourScheme extends ResidueColourScheme
36 {
37   private static final Color INSERTION_COLOUR = new Color(230, 0, 0); // reddish
38
39   /*
40    * the aligned HMM consensus sequence to use as reference for colouring
41    */
42   private SequenceI hmmSeq;
43
44   private HiddenMarkovModel hmm;
45
46   private Map<Character, Float> frequencies;
47
48   /**
49    * Constructor given a list of Hidden Markov Model consensus sequences. The
50    * first sequence provides the HMM profile from which we can read the emission
51    * probabilities that determine the colour.
52    * 
53    * @param hmmSeqs
54    */
55   public HmmerColourScheme(List<SequenceI> hmmSeqs)
56   {
57     hmmSeq = hmmSeqs.isEmpty() ? null : hmmSeqs.get(0);
58     hmm = hmmSeq == null ? null : hmmSeq.getHMM();
59   }
60
61   /**
62    * Default constructor (required by ColourSchemes.loadColourSchemes)
63    */
64   public HmmerColourScheme()
65   {
66   }
67
68   @Override
69   public Color findColour(char symbol, int column, SequenceI seq,
70           String consensusResidue, float pid)
71   {
72     return findColour(symbol, column);
73   }
74
75   /**
76    * Returns the colour at a particular symbol at a column in the alignment:
77    * <ul>
78    * <li>white for a gap</li>
79    * <li>red for an insertion</li>
80    * <li>orange for negative information content</li>
81    * <li>white to blue for increasing information content</li>
82    * </ul>
83    * 
84    * @param symbol
85    * @param column
86    * @return
87    */
88   private Color findColour(char symbol, int column)
89   {
90     if (getHmm() == null || Comparison.isGap(symbol))
91     {
92       return Color.white;
93     }
94     if (Comparison.isGap(hmmSeq.getCharAt(column)))
95     {
96       return INSERTION_COLOUR;
97     }
98     if (Character.isLowerCase(symbol))
99     {
100       symbol = Character.toUpperCase(symbol);
101     }
102
103     final double prob = getHmm().getMatchEmissionProbability(column,
104             symbol);
105
106     Float freq = 0f;
107
108     if (!frequencies.containsKey(symbol))
109     {
110       return Color.WHITE;
111     }
112     else
113     {
114       freq = frequencies.get(symbol);
115     }
116
117     /*
118      * Orange if match emission probability is less than background probability
119      */
120     double infoRatio = prob / freq.floatValue();
121     Color colour = Color.ORANGE;
122     if (infoRatio >= 1)
123     {
124       /*
125        * log-scale graduated shade of blue if prob is greater than background  
126        */
127       float infoLog = (float) Math.log(infoRatio);
128       colour = ColorUtils.getGraduatedColour(infoLog, 0, Color.WHITE,
129               getMaxInformationScore(), Color.blue);
130     }
131
132     return colour;
133   }
134
135   /**
136    * Answers the maximum possible value of information score (log ratio), for
137    * use in scaling a graduated colour range
138    * 
139    * @return
140    */
141   abstract float getMaxInformationScore();
142
143   /**
144    * Answers a new colour scheme instance based on the HMM of the first sequence
145    * in ac that has an HMM
146    */
147   @Override
148   public ColourSchemeI getInstance(AlignViewportI viewport,
149           AnnotatedCollectionI ac)
150   {
151     return newInstance(ac);
152   }
153
154   /**
155    * Answers a new instance of the colour scheme for the given HMM
156    * 
157    * @param ac
158    * @return
159    */
160   protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac);
161
162   @Override
163   public boolean isSimple()
164   {
165     return false;
166   }
167
168   /**
169    * Answers true if the sequence collection has an HMM consensus sequence, else
170    * false
171    */
172   @Override
173   public boolean isApplicableTo(AnnotatedCollectionI ac)
174   {
175     return !ac.getHmmSequences().isEmpty();
176   }
177
178   protected Map<Character, Float> getFrequencies()
179   {
180     return frequencies;
181   }
182
183   protected void setFrequencies(Map<Character, Float> frequencies)
184   {
185     this.frequencies = frequencies;
186   }
187
188   protected HiddenMarkovModel getHmm()
189   {
190     return hmm;
191   }
192
193   protected SequenceI getHmmSequence()
194   {
195     return hmmSeq;
196   }
197 }