JAL-4368 Only set a colour scheme if --colour is given or it's not a JVP file
[jalview.git] / src / jalview / schemes / ClustalxColourScheme.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.schemes;
22
23 import java.awt.Color;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import jalview.api.AlignViewportI;
29 import jalview.datamodel.AnnotatedCollectionI;
30 import jalview.datamodel.SequenceCollectionI;
31 import jalview.datamodel.SequenceI;
32 import jalview.util.ColorUtils;
33 import jalview.util.Comparison;
34
35 public class ClustalxColourScheme extends ResidueColourScheme
36 {
37   private static final int EIGHTY_FIVE = 85;
38
39   private static final int FIFTY = 50;
40
41   private static final int EIGHTY = 80;
42
43   private static final int SIXTY = 60;
44
45   enum ClustalColour
46   {
47     RED(0.9f, 0.2f, 0.1f), BLUE(0.5f, 0.7f, 0.9f), GREEN(0.1f, 0.8f, 0.1f),
48     ORANGE(0.9f, 0.6f, 0.3f), CYAN(0.1f, 0.7f, 0.7f),
49     PINK(0.9f, 0.5f, 0.5f), MAGENTA(0.8f, 0.3f, 0.8f),
50     YELLOW(0.8f, 0.8f, 0.0f);
51
52     final Color colour;
53
54     ClustalColour(float r, float g, float b)
55     {
56       colour = new Color(r, g, b);
57     }
58   }
59
60   private class ConsensusColour
61   {
62     Consensus[] cons;
63
64     Color c;
65
66     public ConsensusColour(ClustalColour col, Consensus[] conses)
67     {
68       this.cons = conses;
69       this.c = col.colour;
70     }
71   }
72
73   private int[][] cons2;
74
75   private ConsensusColour[] colours;
76
77   private ConsensusColour[] residueColour;
78
79   private int size;
80
81   private Consensus[] conses = new Consensus[32];
82
83   private boolean includeGaps = true;
84
85   /**
86    * Default constructor (required for Class.newInstance())
87    */
88   public ClustalxColourScheme()
89   {
90
91   }
92
93   public ClustalxColourScheme(AnnotatedCollectionI alignment,
94           Map<SequenceI, SequenceCollectionI> hiddenReps)
95   {
96     alignmentChanged(alignment, hiddenReps);
97   }
98
99   @Override
100   public synchronized void alignmentChanged(AnnotatedCollectionI alignment,
101           Map<SequenceI, SequenceCollectionI> hiddenReps)
102   {
103     int maxWidth = alignment.getWidth();
104     List<SequenceI> seqs = alignment.getSequences(hiddenReps);
105     cons2 = new int[maxWidth][24];
106     includeGaps = isIncludeGaps(); // does nothing - TODO replace with call to
107     // get the current setting of the
108     // includeGaps param.
109     int res = 0;
110
111     for (SequenceI sq : seqs)
112     {
113       int end_j = sq.getLength() - 1;
114       int length = sq.getLength();
115
116       for (int i = 0; i <= end_j; i++)
117       {
118         if (length - 1 < i)
119         {
120           res = 23;
121         }
122         else
123         {
124           res = ResidueProperties.aaIndex[sq.getCharAt(i)];
125         }
126         cons2[i][res]++;
127       }
128     }
129
130     this.size = seqs.size();
131     makeColours();
132   }
133
134   void makeColours()
135   {
136     conses[0] = new Consensus("WLVIMAFCYHP", SIXTY);
137     conses[1] = new Consensus("WLVIMAFCYHP", EIGHTY);
138     conses[2] = new Consensus("ED", FIFTY);
139     conses[3] = new Consensus("KR", SIXTY);
140     conses[4] = new Consensus("G", FIFTY);
141     conses[5] = new Consensus("N", FIFTY);
142     conses[6] = new Consensus("QE", FIFTY);
143     conses[7] = new Consensus("P", FIFTY);
144     conses[8] = new Consensus("TS", FIFTY);
145
146     conses[26] = new Consensus("A", EIGHTY_FIVE);
147     conses[27] = new Consensus("C", EIGHTY_FIVE);
148     conses[10] = new Consensus("E", EIGHTY_FIVE);
149     conses[11] = new Consensus("F", EIGHTY_FIVE);
150     conses[12] = new Consensus("G", EIGHTY_FIVE);
151     conses[13] = new Consensus("H", EIGHTY_FIVE);
152     conses[14] = new Consensus("I", EIGHTY_FIVE);
153     conses[15] = new Consensus("L", EIGHTY_FIVE);
154     conses[16] = new Consensus("M", EIGHTY_FIVE);
155     conses[17] = new Consensus("N", EIGHTY_FIVE);
156     conses[18] = new Consensus("P", EIGHTY_FIVE);
157     conses[19] = new Consensus("Q", EIGHTY_FIVE);
158     conses[20] = new Consensus("R", EIGHTY_FIVE);
159     conses[21] = new Consensus("S", EIGHTY_FIVE);
160     conses[22] = new Consensus("T", EIGHTY_FIVE);
161     conses[23] = new Consensus("V", EIGHTY_FIVE);
162     conses[24] = new Consensus("W", EIGHTY_FIVE);
163     conses[25] = new Consensus("Y", EIGHTY_FIVE);
164     conses[28] = new Consensus("K", EIGHTY_FIVE);
165     conses[29] = new Consensus("D", EIGHTY_FIVE);
166
167     conses[30] = new Consensus("G", 0);
168     conses[31] = new Consensus("P", 0);
169
170     // We now construct the colours
171     colours = new ConsensusColour[11];
172
173     Consensus[] tmp8 = new Consensus[1];
174     tmp8[0] = conses[30]; // G
175     colours[7] = new ConsensusColour(ClustalColour.ORANGE, tmp8);
176
177     Consensus[] tmp9 = new Consensus[1];
178     tmp9[0] = conses[31]; // P
179     colours[8] = new ConsensusColour(ClustalColour.YELLOW, tmp9);
180
181     Consensus[] tmp10 = new Consensus[1];
182     tmp10[0] = conses[27]; // C
183     colours[9] = new ConsensusColour(ClustalColour.PINK, tmp8);
184
185     Consensus[] tmp1 = new Consensus[14];
186     tmp1[0] = conses[0]; // %
187     tmp1[1] = conses[1]; // #
188     tmp1[2] = conses[26]; // A
189     tmp1[3] = conses[27]; // C
190     tmp1[4] = conses[11]; // F
191     tmp1[5] = conses[13]; // H
192     tmp1[6] = conses[14]; // I
193     tmp1[7] = conses[15]; // L
194     tmp1[8] = conses[16]; // M
195     tmp1[9] = conses[23]; // V
196     tmp1[10] = conses[24]; // W
197     tmp1[11] = conses[25]; // Y
198     tmp1[12] = conses[18]; // P
199     tmp1[13] = conses[19]; // p
200     colours[0] = new ConsensusColour(ClustalColour.BLUE, tmp1);
201
202     colours[10] = new ConsensusColour(ClustalColour.CYAN, tmp1);
203
204     Consensus[] tmp2 = new Consensus[5];
205     tmp2[0] = conses[8]; // t
206     tmp2[1] = conses[21]; // S
207     tmp2[2] = conses[22]; // T
208     tmp2[3] = conses[0]; // %
209     tmp2[4] = conses[1]; // #
210     colours[1] = new ConsensusColour(ClustalColour.GREEN, tmp2);
211
212     Consensus[] tmp3 = new Consensus[3];
213
214     tmp3[0] = conses[17]; // N
215     tmp3[1] = conses[29]; // D
216     tmp3[2] = conses[5]; // n
217     colours[2] = new ConsensusColour(ClustalColour.GREEN, tmp3);
218
219     Consensus[] tmp4 = new Consensus[6];
220     tmp4[0] = conses[6]; // q = QE
221     tmp4[1] = conses[19]; // Q
222     tmp4[2] = conses[22]; // E
223     tmp4[3] = conses[3]; // +
224     tmp4[4] = conses[28]; // K
225     tmp4[5] = conses[20]; // R
226     colours[3] = new ConsensusColour(ClustalColour.GREEN, tmp4);
227
228     Consensus[] tmp5 = new Consensus[4];
229     tmp5[0] = conses[3]; // +
230     tmp5[1] = conses[28]; // K
231     tmp5[2] = conses[20]; // R
232     tmp5[3] = conses[19]; // Q
233     colours[4] = new ConsensusColour(ClustalColour.RED, tmp5);
234
235     Consensus[] tmp6 = new Consensus[6];
236     tmp6[0] = conses[3]; // -
237     tmp6[1] = conses[29]; // D
238     tmp6[2] = conses[10]; // E
239     tmp6[3] = conses[6]; // QE
240     tmp6[4] = conses[19]; // Q
241     tmp6[5] = conses[2]; // DE
242     colours[5] = new ConsensusColour(ClustalColour.MAGENTA, tmp6);
243
244     Consensus[] tmp7 = new Consensus[5];
245     tmp7[0] = conses[3]; // -
246     tmp7[1] = conses[29]; // D
247     tmp7[2] = conses[10]; // E
248     tmp7[3] = conses[17]; // N
249     tmp7[4] = conses[2]; // DE
250     colours[6] = new ConsensusColour(ClustalColour.MAGENTA, tmp7);
251
252     // Now attach the ConsensusColours to the residue letters
253     residueColour = new ConsensusColour[20];
254     residueColour[0] = colours[0]; // A
255     residueColour[1] = colours[4]; // R
256     residueColour[2] = colours[2]; // N
257     residueColour[3] = colours[6]; // D
258     residueColour[4] = colours[0]; // C
259     residueColour[5] = colours[3]; // Q
260     residueColour[6] = colours[5]; // E
261     residueColour[7] = colours[7]; // G
262     residueColour[8] = colours[10]; // H
263     residueColour[9] = colours[0]; // I
264     residueColour[10] = colours[0]; // L
265     residueColour[11] = colours[4]; // K
266     residueColour[12] = colours[0]; // M
267     residueColour[13] = colours[0]; // F
268     residueColour[14] = colours[8]; // P
269     residueColour[15] = colours[1]; // S
270     residueColour[16] = colours[1]; // T
271     residueColour[17] = colours[0]; // W
272     residueColour[18] = colours[10]; // Y
273     residueColour[19] = colours[0]; // V
274   }
275
276   @Override
277   public Color findColour(char c)
278   {
279     return Color.pink;
280   }
281
282   @Override
283   protected synchronized Color findColour(char c, int j, SequenceI seq)
284   {
285     // TODO why the test for includeGaps here?
286     if (cons2.length <= j || Comparison.isGap(c)
287     /*|| (includeGaps && threshold != 0 && !aboveThreshold(c, j))*/)
288     {
289       return Color.white;
290     }
291
292     int i = ResidueProperties.aaIndex[c];
293
294     Color colour = Color.white;
295
296     if (i > 19)
297     {
298       return colour;
299     }
300
301     for (int k = 0; k < residueColour[i].cons.length; k++)
302     {
303       if (residueColour[i].cons[k].isConserved(cons2, j, size, includeGaps))
304       {
305         colour = residueColour[i].c;
306       }
307     }
308
309     if (i == 4)
310     {
311       /*
312        * override to colour C pink if >85% conserved
313        */
314       if (conses[27].isConserved(cons2, j, size, includeGaps))
315       {
316         colour = ClustalColour.PINK.colour;
317       }
318     }
319
320     return colour;
321   }
322
323   /**
324    * @return the includeGaps
325    */
326   protected boolean isIncludeGaps()
327   {
328     return includeGaps;
329   }
330
331   /**
332    * @param includeGaps
333    *          the includeGaps to set
334    */
335   protected void setIncludeGaps(boolean includeGaps)
336   {
337     this.includeGaps = includeGaps;
338   }
339
340   @Override
341   public ColourSchemeI getInstance(AlignViewportI view,
342           AnnotatedCollectionI sg)
343   {
344     ClustalxColourScheme css = new ClustalxColourScheme(sg,
345             view == null ? null : view.getHiddenRepSequences());
346     css.includeGaps = includeGaps;
347     return css;
348   }
349
350   @Override
351   public boolean isPeptideSpecific()
352   {
353     return true;
354   }
355
356   @Override
357   public String getSchemeName()
358   {
359     return JalviewColourScheme.Clustal.toString();
360   }
361
362   @Override
363   public boolean isSimple()
364   {
365     return false;
366   }
367   public String toRuleRep()
368   {
369     makeColours();
370     HashMap<String, String> cols=new HashMap();
371     for (String res:ResidueProperties.aa) {
372       StringBuilder sb = new StringBuilder();
373       
374       int rnum=ResidueProperties.aaIndex[res.charAt(0)];
375       if (rnum<0 || rnum>=residueColour.length)
376       {
377         continue;
378       }
379       
380       ConsensusColour cc = residueColour[rnum];
381       if (cc==null)
382       {
383         continue;
384       }
385       //sb.append("Residue "+res+" ("+rnum+")");
386       //sb.append("\t");
387       sb.append(cc.c.toString());
388       double f=0;
389       sb.append("\t");
390       for (Consensus cons: cc.cons) {
391         if (cons.threshold==0 || f!=cons.threshold)
392         {
393           if (f!=0)
394           {
395
396               sb.append("}, {");
397           } else {
398           sb.append("{");
399           }
400         sb.append(cons.threshold);
401         sb.append(",");
402         f=cons.threshold;
403         } else {
404           sb.append(",");
405         }
406         sb.append(cons.maskstr);
407       }
408       sb.append("}");
409       String clxrep=sb.toString();
410       String xres = cols.get(clxrep);
411       if (xres==null) { xres = "";}
412       xres+=res;
413       cols.put(clxrep, xres);
414     }
415     StringBuilder sb = new StringBuilder();
416     for (String clxrep:cols.keySet())
417     {
418       sb.append(cols.get(clxrep));
419       sb.append("\t");
420       sb.append(clxrep);
421       sb.append("\n");
422       
423     }
424     return sb.toString();
425   }
426 }