Clover icon

Coverage Report

  1. Project Clover database Thu Nov 7 2024 13:01:17 GMT
  2. Package jalview.schemes

File AnnotationColourGradient.java

 

Coverage histogram

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

Code metrics

66
118
21
1
490
340
82
0.69
5.62
21
3.9

Classes

Class Line # Actions
AnnotationColourGradient 38 118 82
0.780487878%
 

Contributing tests

This file is covered by 9 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.schemes;
22   
23    import jalview.api.AlignViewportI;
24    import jalview.datamodel.AlignmentAnnotation;
25    import jalview.datamodel.AlignmentI;
26    import jalview.datamodel.AnnotatedCollectionI;
27    import jalview.datamodel.Annotation;
28    import jalview.datamodel.GraphLine;
29    import jalview.datamodel.SequenceCollectionI;
30    import jalview.datamodel.SequenceI;
31    import jalview.renderer.AnnotationRenderer;
32    import jalview.util.Comparison;
33   
34    import java.awt.Color;
35    import java.util.IdentityHashMap;
36    import java.util.Map;
37   
 
38    public class AnnotationColourGradient extends FollowerColourScheme
39    {
40    public static final int NO_THRESHOLD = -1;
41   
42    public static final int BELOW_THRESHOLD = 0;
43   
44    public static final int ABOVE_THRESHOLD = 1;
45   
46    private final AlignmentAnnotation annotation;
47   
48    private final int aboveAnnotationThreshold;
49   
50    public boolean thresholdIsMinMax = false;
51   
52    private GraphLine annotationThreshold;
53   
54    private int redMin;
55   
56    private int greenMin;
57   
58    private int blueMin;
59   
60    private int redRange;
61   
62    private int greenRange;
63   
64    private int blueRange;
65   
66    private boolean predefinedColours = false;
67   
68    private boolean seqAssociated = false;
69   
70    /**
71    * false if the scheme was constructed without a minColour and maxColour used
72    * to decide if existing colours should be taken from annotation elements when
73    * they exist
74    */
75    private boolean noGradient = false;
76   
77    private IdentityHashMap<SequenceI, AlignmentAnnotation> seqannot = null;
78   
 
79  4 toggle @Override
80    public ColourSchemeI getInstance(AlignViewportI view,
81    AnnotatedCollectionI sg)
82    {
83  4 AnnotationColourGradient acg = new AnnotationColourGradient(annotation,
84    getColourScheme(), aboveAnnotationThreshold);
85  4 acg.thresholdIsMinMax = thresholdIsMinMax;
86  4 acg.annotationThreshold = (annotationThreshold == null) ? null
87    : new GraphLine(annotationThreshold);
88  4 acg.redMin = redMin;
89  4 acg.greenMin = greenMin;
90  4 acg.blueMin = blueMin;
91  4 acg.redRange = redRange;
92  4 acg.greenRange = greenRange;
93  4 acg.blueRange = blueRange;
94  4 acg.predefinedColours = predefinedColours;
95  4 acg.seqAssociated = seqAssociated;
96  4 acg.noGradient = noGradient;
97  4 return acg;
98    }
99   
100    /**
101    * Creates a new AnnotationColourGradient object.
102    */
 
103  7 toggle public AnnotationColourGradient(AlignmentAnnotation annotation,
104    ColourSchemeI originalColour, int aboveThreshold)
105    {
106  7 if (originalColour instanceof AnnotationColourGradient)
107    {
108  0 setColourScheme(((AnnotationColourGradient) originalColour)
109    .getColourScheme());
110    }
111    else
112    {
113  7 setColourScheme(originalColour);
114    }
115   
116  7 this.annotation = annotation;
117   
118  7 aboveAnnotationThreshold = aboveThreshold;
119   
120  7 if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null)
121    {
122  4 annotationThreshold = annotation.threshold;
123    }
124    // clear values so we don't get weird black bands...
125  7 redMin = 254;
126  7 greenMin = 254;
127  7 blueMin = 254;
128  7 redRange = 0;
129  7 greenRange = 0;
130  7 blueRange = 0;
131   
132  7 noGradient = true;
133  7 checkLimits();
134    }
135   
136    /**
137    * Creates a new AnnotationColourGradient object.
138    */
 
139  10 toggle public AnnotationColourGradient(AlignmentAnnotation annotation,
140    Color minColour, Color maxColour, int aboveThreshold)
141    {
142  10 this.annotation = annotation;
143   
144  10 aboveAnnotationThreshold = aboveThreshold;
145   
146  10 if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null)
147    {
148  6 annotationThreshold = annotation.threshold;
149    }
150   
151  10 redMin = minColour.getRed();
152  10 greenMin = minColour.getGreen();
153  10 blueMin = minColour.getBlue();
154   
155  10 redRange = maxColour.getRed() - redMin;
156  10 greenRange = maxColour.getGreen() - greenMin;
157  10 blueRange = maxColour.getBlue() - blueMin;
158   
159  10 noGradient = false;
160  10 checkLimits();
161    }
162   
 
163  17 toggle private void checkLimits()
164    {
165  17 aamax = annotation.graphMax;
166  17 aamin = annotation.graphMin;
167  17 if (annotation.isRNA())
168    {
169    // reset colour palette
170  0 ColourSchemeProperty.resetRnaHelicesShading();
171  0 ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
172    }
173    }
174   
 
175  14 toggle @Override
176    public void alignmentChanged(AnnotatedCollectionI alignment,
177    Map<SequenceI, SequenceCollectionI> hiddenReps)
178    {
179  14 super.alignmentChanged(alignment, hiddenReps);
180   
181  14 if (seqAssociated && annotation.getCalcId() != null)
182    {
183  14 if (seqannot != null)
184    {
185  8 seqannot.clear();
186    }
187    else
188    {
189  6 seqannot = new IdentityHashMap<>();
190    }
191    // resolve the context containing all the annotation for the sequence
192  14 AnnotatedCollectionI alcontext = alignment instanceof AlignmentI
193    ? alignment
194    : alignment.getContext();
195  14 boolean f = true, rna = false;
196  14 for (AlignmentAnnotation alan : alcontext
197    .findAnnotation(annotation.getCalcId()))
198    {
199  360 if (alan.sequenceRef != null
200    && (alan.label != null && annotation != null
201    && alan.label.equals(annotation.label)))
202    {
203  154 if (!rna && alan.isRNA())
204    {
205  0 rna = true;
206    }
207  154 seqannot.put(alan.sequenceRef, alan);
208  154 if (f || alan.graphMax > aamax)
209    {
210  34 aamax = alan.graphMax;
211    }
212  154 if (f || alan.graphMin < aamin)
213    {
214  14 aamin = alan.graphMin;
215    }
216  154 f = false;
217    }
218    }
219  14 if (rna)
220    {
221  0 ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
222    }
223    }
224    }
225   
226    float aamin = 0f, aamax = 0f;
227   
 
228  3 toggle public AlignmentAnnotation getAnnotation()
229    {
230  3 return annotation;
231    }
232   
 
233  3 toggle public int getAboveThreshold()
234    {
235  3 return aboveAnnotationThreshold;
236    }
237   
 
238  3 toggle public float getAnnotationThreshold()
239    {
240  3 if (annotationThreshold == null)
241    {
242  1 return 0;
243    }
244    else
245    {
246  2 return annotationThreshold.value;
247    }
248    }
249   
 
250  3 toggle public Color getMinColour()
251    {
252  3 return new Color(redMin, greenMin, blueMin);
253    }
254   
 
255  3 toggle public Color getMaxColour()
256    {
257  3 return new Color(redMin + redRange, greenMin + greenRange,
258    blueMin + blueRange);
259    }
260   
261    /**
262    * DOCUMENT ME!
263    *
264    * @param n
265    * DOCUMENT ME!
266    *
267    * @return DOCUMENT ME!
268    */
 
269  0 toggle @Override
270    public Color findColour(char c)
271    {
272  0 return Color.red;
273    }
274   
275    /**
276    * Returns the colour for a given character and position in a sequence
277    *
278    * @param c
279    * the residue character
280    * @param j
281    * the aligned position
282    * @param seq
283    * the sequence
284    * @return
285    */
 
286  412 toggle @Override
287    public Color findColour(char c, int j, SequenceI seq)
288    {
289    /*
290    * locate the annotation we are configured to colour by
291    */
292  412 AlignmentAnnotation ann = (seqAssociated && seqannot != null
293    ? seqannot.get(seq)
294    : this.annotation);
295   
296    /*
297    * if gap or no annotation at position, no colour (White)
298    */
299  412 if (ann == null || ann.annotations == null
300    || j >= ann.annotations.length || ann.annotations[j] == null
301    || Comparison.isGap(c))
302    {
303  79 return Color.white;
304    }
305   
306  333 Annotation aj = ann.annotations[j];
307    // 'use original colours' => colourScheme != null
308    // -> look up colour to be used
309    // predefined colours => preconfigured shading
310    // -> only use original colours reference if thresholding enabled &
311    // minmax exists
312    // annotation.hasIcons => null or black colours replaced with glyph
313    // colours
314    // -> reuse original colours if present
315    // -> if thresholding enabled then return colour on non-whitespace glyph
316   
317    /*
318    * if threshold applies, and annotation fails the test - no colour (white)
319    */
320  333 if (annotationThreshold != null)
321    {
322  307 if ((aboveAnnotationThreshold == ABOVE_THRESHOLD
323    && aj.value <= annotationThreshold.value)
324    || (aboveAnnotationThreshold == BELOW_THRESHOLD
325    && aj.value >= annotationThreshold.value))
326    {
327  259 return Color.white;
328    }
329    }
330   
331    /*
332    * If 'use original colours' then return the colour of the annotation
333    * at the aligned position - computed using the background colour scheme
334    */
335  74 if (predefinedColours && aj.colour != null
336    && !aj.colour.equals(Color.black))
337    {
338  14 return aj.colour;
339    }
340   
341  60 Color result = Color.white;
342  60 if (ann.hasIcons && ann.graph == AlignmentAnnotation.NO_GRAPH)
343    {
344    /*
345    * secondary structure symbol colouring
346    */
347  0 if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
348    && aj.secondaryStructure != '-')
349    {
350  0 if (getColourScheme() != null)
351    {
352  0 result = getColourScheme().findColour(c, j, seq, null, 0f);
353    }
354    else
355    {
356  0 if (ann.isRNA())
357    {
358  0 result = ColourSchemeProperty.rnaHelices[(int) aj.value];
359    }
360    else
361    {
362  0 result = ann.annotations[j].secondaryStructure == 'H'
363    ? AnnotationRenderer.HELIX_COLOUR
364  0 : ann.annotations[j].secondaryStructure == 'E'
365    ? AnnotationRenderer.SHEET_COLOUR
366    : AnnotationRenderer.STEM_COLOUR;
367    }
368    }
369    }
370    else
371    {
372  0 return Color.white;
373    }
374    }
375  60 else if (noGradient)
376    {
377  0 if (getColourScheme() != null)
378    {
379  0 result = getColourScheme().findColour(c, j, seq, null, 0f);
380    }
381    else
382    {
383  0 if (aj.colour != null)
384    {
385  0 result = aj.colour;
386    }
387    }
388    }
389    else
390    {
391  60 result = shadeCalculation(ann, j);
392    }
393   
394  60 return result;
395    }
396   
397    /**
398    * Returns a graduated colour for the annotation at the given column. If there
399    * is a threshold value, and it is used as the top/bottom of the colour range,
400    * and the value satisfies the threshold condition, then a colour
401    * proportionate to the range from the threshold is calculated. For all other
402    * cases, a colour proportionate to the annotation's min-max range is
403    * calulated. Note that thresholding is _not_ done here (a colour is computed
404    * even if threshold is not passed).
405    *
406    * @param ann
407    * @param col
408    * @return
409    */
 
410  119 toggle Color shadeCalculation(AlignmentAnnotation ann, int col)
411    {
412  119 float range = 1f;
413  119 float value = ann.annotations[col].value;
414  119 if (thresholdIsMinMax && ann.threshold != null
415    && aboveAnnotationThreshold == ABOVE_THRESHOLD
416    && value >= ann.threshold.value)
417    {
418  12 range = ann.graphMax == ann.threshold.value ? 1f
419    : (value - ann.threshold.value)
420    / (ann.graphMax - ann.threshold.value);
421    }
422  107 else if (thresholdIsMinMax && ann.threshold != null
423    && aboveAnnotationThreshold == BELOW_THRESHOLD
424    && value <= ann.threshold.value)
425    {
426  12 range = ann.graphMin == ann.threshold.value ? 0f
427    : (value - ann.graphMin)
428    / (ann.threshold.value - ann.graphMin);
429    }
430    else
431    {
432  95 if (ann.graphMax != ann.graphMin)
433    {
434  95 range = (value - ann.graphMin) / (ann.graphMax - ann.graphMin);
435    }
436    else
437    {
438  0 range = 0f;
439    }
440    }
441   
442  119 int dr = (int) (redRange * range + redMin);
443  119 int dg = (int) (greenRange * range + greenMin);
444  119 int db = (int) (blueRange * range + blueMin);
445   
446  119 return new Color(dr, dg, db);
447    }
448   
 
449  4 toggle public boolean isPredefinedColours()
450    {
451  4 return predefinedColours;
452    }
453   
 
454  5 toggle public void setPredefinedColours(boolean predefinedColours)
455    {
456  5 this.predefinedColours = predefinedColours;
457    }
458   
 
459  6 toggle public boolean isSeqAssociated()
460    {
461  6 return seqAssociated;
462    }
463   
 
464  6 toggle public void setSeqAssociated(boolean sassoc)
465    {
466  6 seqAssociated = sassoc;
467    }
468   
 
469  0 toggle public boolean isThresholdIsMinMax()
470    {
471  0 return thresholdIsMinMax;
472    }
473   
 
474  6 toggle public void setThresholdIsMinMax(boolean minMax)
475    {
476  6 this.thresholdIsMinMax = minMax;
477    }
478   
 
479  8 toggle @Override
480    public String getSchemeName()
481    {
482  8 return ANNOTATION_COLOUR;
483    }
484   
 
485  0 toggle @Override
486    public boolean isSimple()
487    {
488  0 return false;
489    }
490    }