Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.analysis

File Conservation.java

 

Coverage histogram

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

Code metrics

140
250
16
1
877
530
99
0.4
15.62
16
6.19

Classes

Class Line # Actions
Conservation 46 250 99 16
0.9605911496.1%
 

Contributing tests

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