Clover icon

Coverage Report

  1. Project Clover database Wed Jan 7 2026 02:49:01 GMT
  2. Package jalview.analysis

File Conservation.java

 

Coverage histogram

../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

148
260
19
1
946
563
107
0.41
13.68
19
5.63

Classes

Class Line # Actions
Conservation 49 260 107
0.953161695.3%
 

Contributing tests

This file is covered by 204 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.analysis;
22   
23    import java.util.Locale;
24   
25    import jalview.analysis.scoremodels.ScoreMatrix;
26    import jalview.analysis.scoremodels.ScoreModels;
27    import jalview.api.analysis.ScoreModelI;
28    import jalview.datamodel.AlignmentAnnotation;
29    import jalview.datamodel.Annotation;
30    import jalview.datamodel.ResidueCount;
31    import jalview.datamodel.ResidueCount.SymbolCounts;
32    import jalview.datamodel.Sequence;
33    import jalview.datamodel.SequenceI;
34    import jalview.schemes.ResidueProperties;
35    import jalview.util.Comparison;
36    import jalview.util.Format;
37   
38    import java.awt.Color;
39    import java.util.List;
40    import java.util.Map;
41    import java.util.Map.Entry;
42    import java.util.SortedMap;
43    import java.util.TreeMap;
44    import java.util.Vector;
45   
46    /**
47    * Calculates conservation values for a given set of sequences
48    */
 
49    public class Conservation
50    {
51    /*
52    * need to have a minimum of 3% of sequences with a residue
53    * for it to be included in the conservation calculation
54    */
55    private static final int THRESHOLD_PERCENT = 3;
56   
57    private static final int TOUPPERCASE = 'a' - 'A';
58   
59    private static final int GAP_INDEX = -1;
60   
61    private static final Format FORMAT_3DP = new Format("%2.5f");
62   
63    SequenceI[] sequences;
64   
65    int start;
66   
67    int end;
68   
69    /*
70    * a list whose i'th element is an array whose first entry is the checksum
71    * of the i'th sequence, followed by residues encoded to score matrix index
72    */
73    Vector<int[]> seqNums;
74   
75    int maxLength = 0; // used by quality calcs
76   
77    boolean seqNumsChanged = false; // updated after any change via calcSeqNum;
78   
79    /*
80    * a map per column with {property, conservation} where conservation value is
81    * 1 (property is conserved), 0 (absence of property is conserved) or -1
82    * (property is not conserved i.e. column has residues with and without it)
83    */
84    Map<String, Integer>[] total;
85   
86    /*
87    * if true then conservation calculation will map all symbols to canonical aa
88    * numbering rather than consider conservation of that symbol
89    */
90    boolean canonicaliseAa = true;
91   
92    private Vector<Double> quality;
93   
94    private double qualityMinimum;
95   
96    private double qualityMaximum;
97   
98    private Sequence consSequence;
99   
100    /*
101    * percentage of residues in a column to qualify for counting conservation
102    */
103    private int threshold;
104   
105    private String name = "";
106   
107    /*
108    * an array, for each column, of counts of symbols (by score matrix index)
109    */
110    private int[][] cons2;
111   
112    /*
113    * gap counts for each column
114    */
115    private int[] cons2GapCounts;
116   
117    private String[] consSymbs;
118   
119    /**
120    * Constructor using default threshold of 3%
121    *
122    * @param name
123    * Name of conservation
124    * @param sequences
125    * sequences to be used in calculation
126    * @param start
127    * start residue position
128    * @param end
129    * end residue position
130    */
 
131  890 toggle public Conservation(String name, List<SequenceI> sequences, int start,
132    int end)
133    {
134  890 this(name, THRESHOLD_PERCENT, sequences, start, end);
135    }
136   
137    /**
138    * Constructor
139    *
140    * @param name
141    * Name of conservation
142    * @param threshold
143    * percentage of sequences at or below which property conservation is
144    * ignored
145    * @param sequences
146    * sequences to be used in calculation
147    * @param start
148    * start column position
149    * @param end
150    * end column position
151    */
 
152  894 toggle public Conservation(String name, int threshold, List<SequenceI> sequences,
153    int start, int end)
154    {
155  894 this.name = name;
156  894 this.threshold = threshold;
157  894 this.start = start;
158  894 this.end = end;
159   
160  894 maxLength = end - start + 1; // default width includes bounds of
161    // calculation
162   
163  894 int s, sSize = sequences.size();
164  894 SequenceI[] sarray = new SequenceI[sSize];
165  894 this.sequences = sarray;
166  894 try
167    {
168  9945 for (s = 0; s < sSize; s++)
169    {
170  9050 sarray[s] = sequences.get(s);
171  9050 if (sarray[s].getLength() > maxLength)
172    {
173  6 maxLength = sarray[s].getLength();
174    }
175    }
176    } catch (ArrayIndexOutOfBoundsException ex)
177    {
178    // bail - another thread has modified the sequence array, so the current
179    // calculation is probably invalid.
180  0 this.sequences = new SequenceI[0];
181  0 maxLength = 0;
182    }
183    }
184   
185    /**
186    * Translate sequence i into score matrix indices and store it in the i'th
187    * position of the seqNums array.
188    *
189    * @param i
190    * @param sm
191    */
 
192  8977 toggle private void calcSeqNum(int i, ScoreMatrix sm)
193    {
194  8977 int sSize = sequences.length;
195   
196  8977 if ((i > -1) && (i < sSize))
197    {
198  8977 String sq = sequences[i].getSequenceAsString();
199   
200  8977 if (seqNums.size() <= i)
201    {
202  8977 seqNums.addElement(new int[sq.length() + 1]);
203    }
204   
205    /*
206    * the first entry in the array is the sequence's hashcode,
207    * following entries are matrix indices of sequence characters
208    */
209  8977 if (sq.hashCode() != seqNums.elementAt(i)[0])
210    {
211  8977 int j;
212  8977 int len;
213  8977 seqNumsChanged = true;
214  8977 len = sq.length();
215   
216  8977 if (maxLength < len)
217    {
218  0 maxLength = len;
219    }
220   
221  8977 int[] sqnum = new int[len + 1]; // better to always make a new array -
222    // sequence can change its length
223  8977 sqnum[0] = sq.hashCode();
224   
225  1462619 for (j = 1; j <= len; j++)
226    {
227    // sqnum[j] = ResidueProperties.aaIndex[sq.charAt(j - 1)];
228  1453643 char residue = sq.charAt(j - 1);
229  1453817 if (Comparison.isGap(residue))
230    {
231  292291 sqnum[j] = GAP_INDEX;
232    }
233    else
234    {
235  1161516 sqnum[j] = sm.getMatrixIndex(residue);
236  1161515 if (sqnum[j] == -1)
237    {
238  58 sqnum[j] = GAP_INDEX;
239    }
240    }
241    }
242   
243  8977 seqNums.setElementAt(sqnum, i);
244    }
245    else
246    {
247  0 jalview.bin.Console.outPrintln("SEQUENCE HAS BEEN DELETED!!!");
248    }
249    }
250    else
251    {
252    // JBPNote INFO level debug
253  0 jalview.bin.Console.errPrintln(
254    "ERROR: calcSeqNum called with out of range sequence index for Alignment\n");
255    }
256    }
257   
258    /**
259    * Calculates the conservation values for given set of sequences
260    */
 
261  890 toggle public void calculate()
262    {
263  890 int height = sequences.length;
264   
265  890 total = new Map[maxLength];
266   
267  120812 for (int column = start; column <= end; column++)
268    {
269  119919 ResidueCount values = countResidues(column);
270   
271    /*
272    * percentage count at or below which we ignore residues
273    */
274  119916 int thresh = (threshold * height) / 100;
275   
276    /*
277    * check observed residues in column and record whether each
278    * physico-chemical property is conserved (+1), absence conserved (0),
279    * or not conserved (-1)
280    * Using TreeMap means properties are displayed in alphabetical order
281    */
282  119914 SortedMap<String, Integer> resultHash = new TreeMap<>();
283  119911 SymbolCounts symbolCounts = values.getSymbolCounts();
284  119896 char[] symbols = symbolCounts.symbols;
285  119895 int[] counts = symbolCounts.values;
286  381366 for (int j = 0; j < symbols.length; j++)
287    {
288  261420 char c = symbols[j];
289  261403 if (counts[j] > thresh)
290    {
291  261394 recordConservation(resultHash, String.valueOf(c));
292    }
293    }
294  119929 if (values.getGapCount() > thresh)
295    {
296  64523 recordConservation(resultHash, "-");
297    }
298   
299  119925 if (total.length > 0)
300    {
301  119924 total[column - start] = resultHash;
302    }
303    }
304    }
305   
306    /**
307    * Updates the conservation results for an observed residue
308    *
309    * @param resultMap
310    * a map of {property, conservation} where conservation value is +1
311    * (all residues have the property), 0 (no residue has the property)
312    * or -1 (some do, some don't)
313    * @param res
314    */
 
315  325874 toggle protected static void recordConservation(Map<String, Integer> resultMap,
316    String res)
317    {
318  325882 res = res.toUpperCase(Locale.ROOT);
319  325881 for (Entry<String, Map<String, Integer>> property : ResidueProperties.propHash
320    .entrySet())
321    {
322  3255733 String propertyName = property.getKey();
323  3256539 Integer residuePropertyValue = property.getValue().get(res);
324   
325  3259276 if (!resultMap.containsKey(propertyName))
326    {
327    /*
328    * first time we've seen this residue - note whether it has this property
329    */
330  1199142 if (residuePropertyValue != null)
331    {
332  1198862 resultMap.put(propertyName, residuePropertyValue);
333    }
334    else
335    {
336    /*
337    * unrecognised residue - use default value for property
338    */
339  360 resultMap.put(propertyName, property.getValue().get("-"));
340    }
341    }
342    else
343    {
344  2060098 Integer currentResult = resultMap.get(propertyName);
345  2060881 if (currentResult.intValue() != -1
346    && !currentResult.equals(residuePropertyValue))
347    {
348    /*
349    * property is unconserved - residues seen both with and without it
350    */
351  595695 resultMap.put(propertyName, Integer.valueOf(-1));
352    }
353    }
354    }
355    }
356   
357    /**
358    * Counts residues (upper-cased) and gaps in the given column
359    *
360    * @param column
361    * @return
362    */
 
363  119916 toggle protected ResidueCount countResidues(int column)
364    {
365  119917 ResidueCount values = new ResidueCount(false);
366   
367  1605787 for (int row = 0; row < sequences.length; row++)
368    {
369  1485844 if (sequences[row].getLength() > column)
370    {
371  1460178 char c = sequences[row].getCharAt(column);
372  1460090 if (canonicaliseAa)
373    {
374  1460094 int index = ResidueProperties.aaIndex[c];
375  1460606 c = index > 20 ? '-' : ResidueProperties.aa[index].charAt(0);
376    }
377    else
378    {
379  0 c = toUpperCase(c);
380    }
381  1460570 if (Comparison.isGap(c))
382    {
383  293525 values.addGap();
384    }
385    else
386    {
387  1167063 values.add(c);
388    }
389    }
390    else
391    {
392  25702 values.addGap();
393    }
394    }
395  119918 return values;
396    }
397   
398    /**
399    * Counts conservation and gaps for a column of the alignment
400    *
401    * @return { 1 if fully conserved, else 0, gap count }
402    */
 
403  119923 toggle public int[] countConservationAndGaps(int column)
404    {
405  119923 int gapCount = 0;
406  119923 boolean fullyConserved = true;
407  119922 int iSize = sequences.length;
408   
409  119922 if (iSize == 0)
410    {
411  0 return new int[] { 0, 0 };
412    }
413   
414  119922 char lastRes = '0';
415  1605645 for (int i = 0; i < iSize; i++)
416    {
417  1485610 if (column >= sequences[i].getLength())
418    {
419  25702 gapCount++;
420  25702 continue;
421    }
422   
423  1459915 char c = sequences[i].getCharAt(column); // gaps do not have upper/lower
424    // case
425   
426  1460345 if (Comparison.isGap((c)))
427    {
428  293478 gapCount++;
429    }
430    else
431    {
432  1166866 c = toUpperCase(c);
433  1167086 if (lastRes == '0')
434    {
435  115345 lastRes = c;
436    }
437  1167650 if (c != lastRes)
438    {
439  359338 fullyConserved = false;
440    }
441    }
442    }
443   
444  119922 int[] r = new int[] { fullyConserved ? 1 : 0, gapCount };
445  119916 return r;
446    }
447   
448    /**
449    * Returns the upper-cased character if between 'a' and 'z', else the
450    * unchanged value
451    *
452    * @param c
453    * @return
454    */
 
455  1166858 toggle char toUpperCase(char c)
456    {
457  1166865 if ('a' <= c && c <= 'z')
458    {
459  5159 c -= TOUPPERCASE;
460    }
461  1166885 return c;
462    }
463   
464    /**
465    * Calculates the conservation sequence
466    *
467    * @param positiveOnly
468    * if true, calculate positive conservation; else calculate both
469    * positive and negative conservation
470    * @param maxPercentageGaps
471    * the percentage of gaps in a column, at or above which no
472    * conservation is asserted
473    */
 
474  888 toggle public void verdict(boolean positiveOnly, float maxPercentageGaps)
475    {
476    // TODO call this at the end of calculate(), should not be a public method
477   
478  888 StringBuilder consString = new StringBuilder(end);
479   
480    // NOTE THIS SHOULD CHECK IF THE CONSEQUENCE ALREADY
481    // EXISTS AND NOT OVERWRITE WITH '-', BUT THIS CASE
482    // DOES NOT EXIST IN JALVIEW 2.1.2
483  978 for (int i = 0; i < start; i++)
484    {
485  90 consString.append('-');
486    }
487  888 consSymbs = new String[end - start + 1];
488  120807 for (int i = start; i <= end; i++)
489    {
490  119919 int[] gapcons = countConservationAndGaps(i);
491  119912 boolean fullyConserved = gapcons[0] == 1;
492  119913 int totGaps = gapcons[1];
493  119913 float pgaps = (totGaps * 100f) / sequences.length;
494   
495  119920 if (maxPercentageGaps > pgaps)
496    {
497  87597 Map<String, Integer> resultHash = total[i - start];
498  87598 int count = 0;
499  87598 StringBuilder positives = new StringBuilder(64);
500  87600 StringBuilder negatives = new StringBuilder(32);
501  87600 for (String type : resultHash.keySet())
502    {
503  875751 int result = resultHash.get(type).intValue();
504  875936 if (result == -1)
505    {
506    /*
507    * not conserved (present or absent)
508    */
509  367735 continue;
510    }
511  508156 count++;
512  508202 if (result == 1)
513    {
514    /*
515    * positively conserved property (all residues have it)
516    */
517  155897 positives.append(positives.length() == 0 ? "" : " ");
518  155897 positives.append(type);
519    }
520  508216 if (result == 0 && !positiveOnly)
521    {
522    /*
523    * absense of property is conserved (all residues lack it)
524    */
525  352324 negatives.append(negatives.length() == 0 ? "" : " ");
526  352320 negatives.append("!").append(type);
527    }
528    }
529  87600 if (negatives.length() > 0)
530    {
531  55142 positives.append(" ").append(negatives);
532    }
533  87595 consSymbs[i - start] = positives.toString();
534   
535  87598 if (count < 10)
536    {
537  54311 consString.append(count); // Conserved props!=Identity
538    }
539    else
540    {
541  33285 consString.append(fullyConserved ? "*" : "+");
542    }
543    }
544    else
545    {
546  32325 consString.append('-');
547    }
548    }
549   
550  888 consSequence = new Sequence(name, consString.toString(), start, end);
551    }
552   
553    /**
554    *
555    *
556    * @return Conservation sequence
557    */
 
558  905 toggle public SequenceI getConsSequence()
559    {
560  905 return consSequence;
561    }
562   
563    // From Alignment.java in jalview118
 
564  876 toggle public void findQuality()
565    {
566  876 findQuality(0, maxLength - 1, getQualitySubstMat());
567    }
568   
 
569  876 toggle public ScoreMatrix getQualitySubstMat()
570    {
571  876 return sm;
572    }
573   
574    /**
575    * default for quality calc is BLOSUM
576    */
577    private ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
578   
579    /**
580    * set the matrix used for calculating quality scores
581    *
582    * @param scoreMatrix
583    * - must be a valid score matrix able to evaluate all amino acid
584    * substitutions
585    */
 
586  876 toggle public void setQualitySubstMat(ScoreMatrix scoreMatrix)
587    {
588  876 if (scoreMatrix != null && scoreMatrix.isProtein())
589    {
590  876 sm = scoreMatrix;
591    }
592    }
593   
594    /**
595    * DOCUMENT ME!
596    *
597    * @param sm
598    */
 
599  876 toggle private void percentIdentity(ScoreMatrix sm)
600    {
601  876 seqNums = new Vector<>();
602  876 int i = 0, iSize = sequences.length;
603    // Do we need to calculate this again?
604  9853 for (i = 0; i < iSize; i++)
605    {
606  8977 calcSeqNum(i, sm);
607    }
608   
609  876 if ((cons2 == null) || seqNumsChanged)
610    {
611    // FIXME remove magic number 24 without changing calc
612    // sm.getSize() returns 25 so doesn't quite do it...
613  876 cons2 = new int[maxLength][24];
614  876 cons2GapCounts = new int[maxLength];
615   
616  876 int j = 0;
617   
618  9853 while (j < sequences.length)
619    {
620  8977 int[] sqnum = seqNums.elementAt(j);
621   
622  1463543 for (i = 1; i < sqnum.length; i++)
623    {
624  1454566 int index = sqnum[i];
625  1454566 if (index == GAP_INDEX)
626    {
627  292356 cons2GapCounts[i - 1]++;
628    }
629    else
630    {
631  1162210 cons2[i - 1][index]++;
632    }
633    }
634   
635    // TODO should this start from sqnum.length?
636  34634 for (i = sqnum.length - 1; i < maxLength; i++)
637    {
638  25657 cons2GapCounts[i]++;
639    }
640  8977 j++;
641    }
642    }
643    }
644   
645    /**
646    * Calculates the quality of the set of sequences over the given inclusive
647    * column range, using the specified substitution score matrix
648    *
649    * @param startCol
650    * @param endCol
651    * @param scoreMatrix
652    */
 
653  876 toggle protected void findQuality(int startCol, int endCol,
654    ScoreMatrix scoreMatrix)
655    {
656  876 quality = new Vector<>();
657   
658  876 double max = -Double.MAX_VALUE;
659  876 float[][] scores = scoreMatrix.getMatrix();
660   
661  876 percentIdentity(scoreMatrix);
662   
663  876 int size = seqNums.size();
664  876 int[] lengths = new int[size];
665   
666  9853 for (int l = 0; l < size; l++)
667    {
668  8977 lengths[l] = seqNums.elementAt(l).length - 1;
669    }
670   
671  876 final int symbolCount = scoreMatrix.getSize();
672   
673  119840 for (int j = startCol; j <= endCol; j++)
674    {
675  118965 double bigtot = 0;
676   
677    // First Xr = depends on column only
678  118966 double[] x = new double[symbolCount];
679   
680  2968980 for (int ii = 0; ii < symbolCount; ii++)
681    {
682  2850062 x[ii] = 0;
683   
684    /*
685    * todo JAL-728 currently assuming last symbol in matrix is * for gap
686    * (which we ignore as counted separately); true for BLOSUM62 but may
687    * not be once alternative matrices are supported
688    */
689  68235236 for (int i2 = 0; i2 < symbolCount - 1; i2++)
690    {
691  65408282 x[ii] += (((double) cons2[j][i2] * scores[ii][i2]) + 4D);
692    }
693  2849153 x[ii] += 4D + cons2GapCounts[j] * scoreMatrix.getMinimumScore();
694   
695  2849722 x[ii] /= size;
696    }
697   
698    // Now calculate D for each position and sum
699  1596808 for (int k = 0; k < size; k++)
700    {
701  1477890 double tot = 0;
702  1477979 double[] xx = new double[symbolCount];
703    // sequence character index, or implied gap if sequence too short
704  1477301 int seqNum = (j < lengths[k]) ? seqNums.elementAt(k)[j + 1]
705    : GAP_INDEX;
706   
707  35111581 for (int i = 0; i < symbolCount - 1; i++)
708    {
709  33741860 double sr = 4D;
710  33743734 if (seqNum == GAP_INDEX)
711    {
712  7303733 sr += scoreMatrix.getMinimumScore();
713    }
714    else
715    {
716  26491349 sr += scores[i][seqNum];
717    }
718   
719  33765554 xx[i] = x[i] - sr;
720   
721  33810158 tot += (xx[i] * xx[i]);
722    }
723   
724  1478086 bigtot += Math.sqrt(tot);
725    }
726   
727  118960 max = Math.max(max, bigtot);
728   
729  118961 quality.addElement(Double.valueOf(bigtot));
730    }
731   
732  875 double newmax = -Double.MAX_VALUE;
733   
734  119777 for (int j = startCol; j <= endCol; j++)
735    {
736  118902 double tmp = quality.elementAt(j).doubleValue();
737    // tmp = ((max - tmp) * (size - cons2[j][23])) / size;
738  118902 tmp = ((max - tmp) * (size - cons2GapCounts[j])) / size;
739   
740    // jalview.bin.Console.outPrintln(tmp+ " " + j);
741  118902 quality.setElementAt(Double.valueOf(tmp), j);
742   
743  118902 if (tmp > newmax)
744    {
745  2336 newmax = tmp;
746    }
747    }
748   
749  875 qualityMinimum = 0D;
750  875 qualityMaximum = newmax;
751    }
752   
753    /**
754    * Complete the given consensus and quuality annotation rows. Note: currently
755    * this method will reallocate the given annotation row if it is different to
756    * the calculated width, otherwise will leave its length unchanged.
757    *
758    * @param conservation
759    * conservation annotation row
760    * @param quality2
761    * (optional - may be null)
762    * @param istart
763    * first column for conservation
764    * @param alWidth
765    * extent of conservation
766    */
 
767  875 toggle public void completeAnnotations(AlignmentAnnotation conservation,
768    AlignmentAnnotation quality2, int istart, int alWidth)
769    {
770  875 SequenceI cons = getConsSequence();
771   
772    /*
773    * colour scale for Conservation and Quality;
774    */
775  875 float minR = 0.3f;
776  875 float minG = 0.0f;
777  875 float minB = 0f;
778  875 float maxR = 1.0f - minR;
779  875 float maxG = 0.9f - minG;
780  875 float maxB = 0f - minB;
781   
782  875 float min = 0f;
783  875 float max = 11f;
784  875 float qmin = 0f;
785  875 float qmax = 0f;
786  875 if (conservation!=null)
787    {
788  873 conservation.setNoOfSequencesIncluded(sequences.length);
789    }
790  875 if (quality2!=null)
791    {
792  875 quality2.setNoOfSequencesIncluded(sequences.length);
793    }
794   
795  875 if (conservation != null && conservation.annotations != null
796    && conservation.annotations.length != alWidth)
797    {
798  396 conservation.annotations = new Annotation[alWidth];
799    }
800   
801  875 if (quality2 != null)
802    {
803  875 quality2.graphMax = (float) qualityMaximum;
804  875 if (quality2.annotations != null
805    && quality2.annotations.length != alWidth)
806    {
807  397 quality2.annotations = new Annotation[alWidth];
808    }
809  875 qmin = (float) qualityMinimum;
810  875 qmax = (float) qualityMaximum;
811    }
812   
813  119385 for (int i = istart; i < alWidth; i++)
814    {
815  118513 float value = 0;
816   
817  118513 char c = cons.getCharAt(i);
818   
819  118513 if (Character.isDigit(c))
820    {
821  53692 value = c - '0';
822    }
823  64821 else if (c == '*')
824    {
825  32768 value = 11;
826    }
827  32053 else if (c == '+')
828    {
829  89 value = 10;
830    }
831   
832  118513 if (conservation != null)
833    {
834  118199 float vprop = value - min;
835  118199 vprop /= max;
836  118199 int consp = i - start;
837  118199 String conssym = (value > 0 && consp > -1
838    && consp < consSymbs.length) ? consSymbs[consp] : "";
839  118199 conservation.annotations[i] = new Annotation(String.valueOf(c),
840    conssym, ' ', value, new Color(minR + (maxR * vprop),
841    minG + (maxG * vprop), minB + (maxB * vprop)));
842    }
843   
844    // Quality calc
845  118512 if (quality2 != null)
846    {
847  118512 value = quality.elementAt(i).floatValue();
848  118512 float vprop = value - qmin;
849  118512 vprop /= qmax;
850  118512 String description = FORMAT_3DP.form(value);
851  118510 quality2.annotations[i] = new Annotation(" ", description, ' ',
852    value, new Color(minR + (maxR * vprop),
853    minG + (maxG * vprop), minB + (maxB * vprop)));
854    }
855    }
856    }
857   
858    /**
859    * construct and call the calculation methods on a new Conservation object
860    *
861    * @param name
862    * - name of conservation
863    * @param seqs
864    * @param start
865    * first column in calculation window
866    * @param end
867    * last column in calculation window
868    * @param positiveOnly
869    * calculate positive (true) or positive and negative (false)
870    * conservation
871    * @param maxPercentGaps
872    * percentage of gaps tolerated in column
873    * @param calcQuality
874    * flag indicating if alignment quality should be calculated
875    * @return Conservation object ready for use in visualization
876    */
 
877  3 toggle public static Conservation calculateConservation(String name,
878    List<SequenceI> seqs, int start, int end, boolean positiveOnly,
879    int maxPercentGaps, boolean calcQuality)
880    {
881  3 return calculateConservation(name, seqs, start, end, positiveOnly,
882    maxPercentGaps, calcQuality, null);
883    }
884   
885    /**
886    * construct and call the calculation methods on a new Conservation object
887    *
888    * @param name
889    * - name of conservation
890    * @param seqs
891    * @param start
892    * first column in calculation window
893    * @param end
894    * last column in calculation window
895    * @param positiveOnly
896    * calculate positive (true) or positive and negative (false)
897    * conservation
898    * @param maxPercentGaps
899    * percentage of gaps tolerated in column
900    * @param calcQuality
901    * flag indicating if alignment quality should be calculated
902    * @param scoreMatrix
903    * - null or peptide score matrix used for quality calculation
904    * @return Conservation object ready for use in visualization
905    */
 
906  879 toggle public static Conservation calculateConservation(String name,
907    List<SequenceI> seqs, int start, int end, boolean positiveOnly,
908    int maxPercentGaps, boolean calcQuality, ScoreMatrix scoreMatrix)
909    {
910  879 Conservation cons = new Conservation(name, seqs, start, end);
911  879 if (scoreMatrix != null)
912    {
913  876 cons.setQualitySubstMat(scoreMatrix);
914    }
915  879 cons.calculate();
916  879 cons.verdict(positiveOnly, maxPercentGaps);
917   
918  879 if (calcQuality)
919    {
920  876 cons.findQuality();
921    }
922   
923  878 return cons;
924    }
925   
926    /**
927    * Returns the computed tooltip (annotation description) for a given column.
928    * The tip is empty if the conservation score is zero, otherwise holds the
929    * conserved properties (and, optionally, properties whose absence is
930    * conserved).
931    *
932    * @param column
933    * @return
934    */
 
935  7 toggle String getTooltip(int column)
936    {
937  7 SequenceI cons = getConsSequence();
938  7 char val = column < cons.getLength() ? cons.getCharAt(column) : '-';
939  7 boolean hasConservation = val != '-' && val != '0';
940  7 int consp = column - start;
941  7 String tip = (hasConservation && consp > -1 && consp < consSymbs.length)
942    ? consSymbs[consp]
943    : "";
944  7 return tip;
945    }
946    }