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