Clover icon

Coverage Report

  1. Project Clover database Thu Nov 7 2024 10:11:34 GMT
  2. Package jalview.renderer

File ResidueShader.java

 

Coverage histogram

../../img/srcFileCovDistChart8.png
21% of files have more coverage

Code metrics

46
91
27
1
530
274
58
0.64
3.37
27
2.15

Classes

Class Line # Actions
ResidueShader 52 91 58
0.737804973.8%
 

Contributing tests

This file is covered by 269 tests. .

Source view

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.renderer;
22   
23    import jalview.analysis.AlignmentUtils;
24    import jalview.analysis.Conservation;
25    import jalview.api.ViewStyleI;
26    import jalview.datamodel.AnnotatedCollectionI;
27    import jalview.datamodel.ProfileI;
28    import jalview.datamodel.ProfilesI;
29    import jalview.datamodel.SequenceCollectionI;
30    import jalview.datamodel.SequenceI;
31    import jalview.schemes.ColourSchemeI;
32    import jalview.util.ColorUtils;
33    import jalview.util.Comparison;
34    import jalview.util.Constants;
35   
36    import java.awt.Color;
37    import java.util.Map;
38   
39    /**
40    * A class that computes the colouring of an alignment (or subgroup). Currently
41    * the factors that may influence residue colouring are
42    * <ul>
43    * <li>the colour scheme that provides a colour for each aligned residue</li>
44    * <li>any threshold for colour, based on percentage identity with
45    * consensus</li>
46    * <li>any graduation based on conservation of physico-chemical properties</li>
47    * </ul>
48    *
49    * @author gmcarstairs
50    *
51    */
 
52    public class ResidueShader implements ResidueShaderI
53    {
54    private static final int INITIAL_CONSERVATION = 30;
55   
56    /*
57    * the colour scheme that gives the colour of each residue
58    * before applying any conservation or PID shading
59    */
60    private ColourSchemeI colourScheme;
61   
62    /*
63    * the consensus data for each column
64    */
65    private ProfilesI consensus;
66   
67    /*
68    * the ss consensus data for each column for each source
69    */
70    private Map<String, ProfilesI> ssConsensusProfileMap;
71   
72    /*
73    * if true, apply shading of colour by conservation
74    */
75    private boolean conservationColouring;
76   
77    private boolean consensusSecondaryStructureColouring;
78   
 
79  27 toggle @Override
80    public boolean isConsensusSecondaryStructureColouring()
81    {
82  27 return consensusSecondaryStructureColouring;
83    }
84   
85    /*
86    * the physico-chemical property conservation scores for columns, with values
87    * 0-9, '+' (all properties conserved), '*' (residue fully conserved) or '-' (gap)
88    * (may be null if colour by conservation is not selected)
89    */
90    private char[] conservation;
91   
92    /*
93    * minimum percentage identity for colour to be applied;
94    * if above zero, residue must match consensus (or joint consensus)
95    * and column have >= pidThreshold identity with the residue
96    */
97    private int pidThreshold;
98   
99    /*
100    * if true, ignore gaps in percentage identity calculation
101    */
102    private boolean ignoreGaps;
103   
104    /*
105    * setting of the By Conservation slider
106    */
107    private int conservationIncrement = INITIAL_CONSERVATION;
108   
109    private int consensusSecondaryStructureThreshold = 30;
110   
 
111  2778 toggle public ResidueShader(ColourSchemeI cs)
112    {
113  2778 colourScheme = cs;
114    }
115   
116    /**
117    * Default constructor
118    */
 
119  1482 toggle public ResidueShader()
120    {
121    }
122   
123    /**
124    * Constructor given view style settings
125    *
126    * @param viewStyle
127    */
 
128  0 toggle public ResidueShader(ViewStyleI viewStyle)
129    {
130    // TODO remove duplicated storing of conservation / pid thresholds?
131  0 this();
132  0 setConservationApplied(viewStyle.isConservationColourSelected());
133    // setThreshold(viewStyle.getThreshold());
134    }
135   
136    /**
137    * Copy constructor
138    */
 
139  99 toggle public ResidueShader(ResidueShader rs)
140    {
141  99 this.colourScheme = rs.colourScheme;
142  99 this.consensus = rs.consensus;
143  99 this.conservation = rs.conservation;
144  99 this.conservationColouring = rs.conservationColouring;
145  99 this.conservationIncrement = rs.conservationIncrement;
146  99 this.ignoreGaps = rs.ignoreGaps;
147  99 this.pidThreshold = rs.pidThreshold;
148  99 this.ssConsensusProfileMap = rs.ssConsensusProfileMap;
149  99 this.consensusSecondaryStructureColouring = rs.consensusSecondaryStructureColouring;
150  99 this.consensusSecondaryStructureThreshold = rs.consensusSecondaryStructureThreshold;
151    }
152   
153    /**
154    * @see jalview.renderer.ResidueShaderI#setConsensus(jalview.datamodel.ProfilesI)
155    */
 
156  2488 toggle @Override
157    public void setConsensus(ProfilesI cons)
158    {
159  2488 consensus = cons;
160   
161    }
162   
163    /**
164    * @see jalview.renderer.ResidueShaderI#conservationApplied()
165    */
 
166  997 toggle @Override
167    public boolean conservationApplied()
168    {
169  997 return conservationColouring;
170    }
171   
172    /**
173    * @see jalview.renderer.ResidueShaderI#setConservationApplied(boolean)
174    */
 
175  194 toggle @Override
176    public void setConservationApplied(boolean conservationApplied)
177    {
178  194 conservationColouring = conservationApplied;
179    }
180   
181    /**
182    * @see jalview.renderer.ResidueShaderI#setConservation(jalview.analysis.Conservation)
183    */
 
184  29 toggle @Override
185    public void setConservation(Conservation cons)
186    {
187  29 if (cons == null)
188    {
189  5 conservationColouring = false;
190  5 conservation = null;
191    }
192    else
193    {
194  24 conservationColouring = true;
195  24 conservation = cons.getConsSequence().getSequenceAsString()
196    .toCharArray();
197    }
198   
199    }
200   
 
201  47 toggle @Override
202    public void setConsensusSecondaryStructureColouring(
203    boolean colourByConsensusSecondaryStructure)
204    {
205   
206  47 consensusSecondaryStructureColouring = colourByConsensusSecondaryStructure;
207   
208    }
209   
210    /**
211    * @see jalview.renderer.ResidueShaderI#alignmentChanged(jalview.datamodel.AnnotatedCollectionI,
212    * java.util.Map)
213    */
 
214  1194 toggle @Override
215    public void alignmentChanged(AnnotatedCollectionI alignment,
216    Map<SequenceI, SequenceCollectionI> hiddenReps)
217    {
218  1194 if (colourScheme != null)
219    {
220  336 colourScheme.alignmentChanged(alignment, hiddenReps);
221    }
222    }
223   
224    /**
225    * @see jalview.renderer.ResidueShaderI#setThreshold(int, boolean)
226    */
 
227  169 toggle @Override
228    public void setThreshold(int consensusThreshold, boolean ignoreGaps)
229    {
230  169 pidThreshold = consensusThreshold;
231  169 this.ignoreGaps = ignoreGaps;
232    }
233   
234    /**
235    * @see jalview.renderer.ResidueShaderI#setConservationInc(int)
236    */
 
237  72 toggle @Override
238    public void setConservationInc(int i)
239    {
240  72 conservationIncrement = i;
241    }
242   
243    /**
244    * @see jalview.renderer.ResidueShaderI#getConservationInc()
245    */
 
246  37 toggle @Override
247    public int getConservationInc()
248    {
249  37 return conservationIncrement;
250    }
251   
 
252  47 toggle @Override
253    public void setConsensusSecondaryStructureThreshold(int i)
254    {
255  47 consensusSecondaryStructureThreshold = i;
256    }
257   
 
258  0 toggle @Override
259    public int getConsensusSecondaryStructureThreshold()
260    {
261  0 return consensusSecondaryStructureThreshold;
262    }
263   
264    /**
265    * @see jalview.renderer.ResidueShaderI#getThreshold()
266    */
 
267  59 toggle @Override
268    public int getThreshold()
269    {
270  59 return pidThreshold;
271    }
272   
273    /**
274    * @see jalview.renderer.ResidueShaderI#findColour(char, int,
275    * jalview.datamodel.SequenceI)
276    */
 
277  621262 toggle @Override
278    public Color findColour(char symbol, int position, SequenceI seq)
279    {
280  621318 if (colourScheme == null)
281    {
282  0 return Color.white; // Colour is 'None'
283    }
284   
285    /*
286    * get 'base' colour
287    */
288  621290 ProfileI profile = consensus == null ? null : consensus.get(position);
289  621297 String modalResidue = profile == null ? null
290    : profile.getModalResidue();
291  621279 float pid = profile == null ? 0f
292    : profile.getPercentageIdentity(ignoreGaps);
293  621302 Color colour = colourScheme.findColour(symbol, position, seq,
294    modalResidue, pid);
295   
296    /*
297    * apply PID threshold and consensus fading if in force
298    */
299  621264 if (!Comparison.isGap(symbol))
300    {
301  592159 colour = adjustColour(symbol, position, colour, false);
302    }
303   
304  621292 return colour;
305    }
306   
 
307  0 toggle @Override
308    public Color findSSColour(char symbol, int position)
309    {
310  0 if (colourScheme == null)
311    {
312  0 return Color.white; // Colour is 'None'
313    }
314   
315  0 Color colour = AlignmentUtils.getSecondaryStructureAnnotationColour(symbol);
316   
317    /*
318    * apply PID threshold and consensus fading if in force
319    */
320  0 if (!Comparison.isGap(symbol))
321    {
322  0 colour = adjustColour(symbol, position, colour, true);
323    }
324   
325  0 return colour;
326    }
327   
328    /**
329    * Adjusts colour by applying thresholding or conservation shading, if in
330    * force. That is
331    * <ul>
332    * <li>if there is a threshold set for colouring, and the residue doesn't
333    * match the consensus (or a joint consensus) residue, or the consensus score
334    * is not above the threshold, then the colour is set to white</li>
335    * <li>if conservation colouring is selected, the colour is faded by an amount
336    * depending on the conservation score for the column, and the conservation
337    * colour threshold</li>
338    * </ul>
339    *
340    * @param symbol
341    * @param column
342    * @param colour
343    * @return
344    */
 
345  592154 toggle protected Color adjustColour(char symbol, int column, Color colour, boolean isSecondaryStructure)
346    {
347  592187 if (!isSecondaryStructure && !aboveThreshold(symbol, column))
348    {
349  502 colour = Color.white;
350    }
351   
352  592189 if (consensusSecondaryStructureColouring)
353    {
354  0 colour = applyByConsensusSecondaryStructure(colour, column);
355    }
356   
357  592186 if (conservationColouring)
358    {
359  2249 colour = applyConservation(colour, column);
360    }
361  592187 return colour;
362    }
363   
364    /**
365    * Answers true if there is a consensus profile for the specified column, and
366    * the given residue matches the consensus (or joint consensus) residue for
367    * the column, and the percentage identity for the profile is equal to or
368    * greater than the current threshold; else answers false. The percentage
369    * calculation depends on whether or not we are ignoring gapped sequences.
370    *
371    * @param residue
372    * @param column
373    * (index into consensus profiles)
374    *
375    * @return
376    * @see #setThreshold(int, boolean)
377    */
 
378  592180 toggle protected boolean aboveThreshold(char residue, int column)
379    {
380  592178 if (pidThreshold == 0)
381    {
382  591442 return true;
383    }
384  770 if ('a' <= residue && residue <= 'z')
385    {
386    // TO UPPERCASE !!!
387    // Faster than toUpperCase
388  4 residue -= ('a' - 'A');
389    }
390   
391  770 if (consensus == null)
392    {
393  0 return false;
394    }
395   
396  770 ProfileI profile = consensus.get(column);
397   
398    /*
399    * test whether this is the consensus (or joint consensus) residue
400    */
401  770 if (profile != null
402    && profile.getModalResidue().contains(String.valueOf(residue)))
403    {
404  490 if (profile.getPercentageIdentity(ignoreGaps) >= pidThreshold)
405    {
406  261 return true;
407    }
408    }
409   
410  509 return false;
411    }
412   
413    /**
414    * Applies a combination of column conservation score, and conservation
415    * percentage slider, to 'bleach' out the residue colours towards white.
416    * <p>
417    * If a column is fully conserved (identical residues, conservation score 11,
418    * shown as *), or all 10 physico-chemical properties are conserved
419    * (conservation score 10, shown as +), then the colour is left unchanged.
420    * <p>
421    * Otherwise a 'bleaching' factor is computed and applied to the colour. This
422    * is designed to fade colours for scores of 0-9 completely to white at slider
423    * positions ranging from 18% - 100% respectively.
424    *
425    * @param currentColour
426    * @param column
427    *
428    * @return bleached (or unmodified) colour
429    */
 
430  2290 toggle protected Color applyConservation(Color currentColour, int column)
431    {
432  2290 if (conservation == null || conservation.length <= column)
433    {
434  2 return currentColour;
435    }
436  2288 char conservationScore = conservation[column];
437   
438    /*
439    * if residues are fully conserved (* or 11), or all properties
440    * are conserved (+ or 10), leave colour unchanged
441    */
442  2288 if (conservationScore == '*' || conservationScore == '+'
443    || conservationScore == (char) 10
444    || conservationScore == (char) 11)
445    {
446  102 return currentColour;
447    }
448   
449  2186 if (Comparison.isGap(conservationScore))
450    {
451  669 return Color.white;
452    }
453   
454    /*
455    * convert score 0-9 to a bleaching factor 1.1 - 0.2
456    */
457  1517 float bleachFactor = (11 - (conservationScore - '0')) / 10f;
458   
459    /*
460    * scale this up by 0-5 (percentage slider / 20)
461    * as a result, scores of: 0 1 2 3 4 5 6 7 8 9
462    * fade to white at slider value: 18 20 22 25 29 33 40 50 67 100%
463    */
464  1517 bleachFactor *= (conservationIncrement / 20f);
465   
466  1517 return ColorUtils.bleachColour(currentColour, bleachFactor);
467    }
468   
 
469  0 toggle protected Color applyByConsensusSecondaryStructure(Color currentColour,
470    int column)
471    {
472  0 if (ssConsensusProfileMap == null && ssConsensusProfileMap
473    .get(Constants.SS_ALL_PROVIDERS) == null)
474    {
475  0 return currentColour;
476    }
477   
478  0 ProfilesI consensusSSProfileForAllSources = ssConsensusProfileMap
479    .get(Constants.SS_ALL_PROVIDERS);
480  0 ProfileI profile = consensusSSProfileForAllSources.get(column);
481   
482  0 if (profile != null)
483    {
484  0 float pid = profile.getSSPercentageIdentity(ignoreGaps);
485   
486  0 if (pid == 0)
487    {
488  0 return Color.white;
489    }
490   
491  0 float bleachFactor = 1f - (pid / 100);
492  0 bleachFactor *= (consensusSecondaryStructureThreshold / 20);
493   
494  0 return ColorUtils.bleachColour(currentColour, bleachFactor);
495   
496    }
497   
498  0 return currentColour;
499   
500    }
501   
502    /**
503    * @see jalview.renderer.ResidueShaderI#getColourScheme()
504    */
 
505  1170379 toggle @Override
506    public ColourSchemeI getColourScheme()
507    {
508  1170433 return this.colourScheme;
509    }
510   
511    /**
512    * @see jalview.renderer.ResidueShaderI#setColourScheme(jalview.schemes.ColourSchemeI)
513    */
 
514  340 toggle @Override
515    public void setColourScheme(ColourSchemeI cs)
516    {
517  340 colourScheme = cs;
518    }
519   
 
520  0 toggle public Map<String, ProfilesI> getSSConsensusProfileMap()
521    {
522  0 return ssConsensusProfileMap;
523    }
524   
 
525  1947 toggle public void setSSConsensusProfileMap(
526    Map<String, ProfilesI> ssConsensusProfileMap)
527    {
528  1947 this.ssConsensusProfileMap = ssConsensusProfileMap;
529    }
530    }