Merge branch 'develop' into features/JAL-250_hideredundantseqs
[jalview.git] / src / jalview / workers / ColumnCounterSetWorker.java
@@ -36,16 +36,16 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * A class to compute an alignment annotation with column counts of any
- * properties of interest of positions in an alignment. <br>
+ * A class to compute alignment annotations with column counts for a set of
+ * properties of interest on positions in an alignment. <br>
  * This is designed to be extensible, by supplying to the constructor an object
- * that computes a count for each residue position, based on the residue value
- * and any sequence features at that position.
+ * that computes a vector of counts for each residue position, based on the
+ * residue and and sequence features at that position.
  * 
  */
-class ColumnCounterWorker extends AlignCalcWorker
+class ColumnCounterSetWorker extends AlignCalcWorker
 {
-  FeatureCounterI counter;
+  FeatureSetCounterI counter;
 
   /**
    * Constructor registers the annotation for the given alignment frame
@@ -53,8 +53,8 @@ class ColumnCounterWorker extends AlignCalcWorker
    * @param af
    * @param counter
    */
-  public ColumnCounterWorker(AlignViewportI viewport,
-          AlignmentViewPanel panel, FeatureCounterI counter)
+  public ColumnCounterSetWorker(AlignViewportI viewport,
+          AlignmentViewPanel panel, FeatureSetCounterI counter)
   {
     super(viewport, panel);
     ourAnnots = new ArrayList<AlignmentAnnotation>();
@@ -69,6 +69,7 @@ class ColumnCounterWorker extends AlignCalcWorker
   @Override
   public void run()
   {
+    boolean annotationAdded = false;
     try
     {
       calcMan.notifyStart(this);
@@ -93,7 +94,7 @@ class ColumnCounterWorker extends AlignCalcWorker
       {
         try
         {
-          computeAnnotations();
+          annotationAdded = computeAnnotations();
         } catch (IndexOutOfBoundsException x)
         {
           // probable race condition. just finish and return without any fuss.
@@ -111,7 +112,10 @@ class ColumnCounterWorker extends AlignCalcWorker
 
     if (ap != null)
     {
-      ap.adjustAnnotationHeight();
+      if (annotationAdded)
+      {
+        ap.adjustAnnotationHeight();
+      }
       ap.paintAlignment(true);
     }
 
@@ -120,8 +124,10 @@ class ColumnCounterWorker extends AlignCalcWorker
   /**
    * Scan each column of the alignment to calculate a count by feature type. Set
    * the count as the value of the alignment annotation for that feature type.
+   * 
+   * @return
    */
-  void computeAnnotations()
+  boolean computeAnnotations()
   {
     FeatureRenderer fr = new FeatureRenderer(alignViewport);
     // TODO use the commented out code once JAL-2075 is fixed
@@ -130,56 +136,91 @@ class ColumnCounterWorker extends AlignCalcWorker
     // AlignmentView alignmentView = alignViewport.getAlignmentView(false);
     // AlignmentI alignment = alignmentView.getVisibleAlignment(' ');
 
-    // int width = alignmentView.getWidth();
+    int rows = counter.getNames().length;
+
     int width = alignment.getWidth();
     int height = alignment.getHeight();
-    int[] counts = new int[width];
-    int max = 0;
+    int[][] counts = new int[width][rows];
+    int max[] = new int[rows];
+    for (int crow = 0; crow < rows; crow++)
+    {
+      max[crow] = 0;
+    }
+
+    int[] minC = counter.getMinColour();
+    int[] maxC = counter.getMaxColour();
+    Color minColour = new Color(minC[0], minC[1], minC[2]);
+    Color maxColour = new Color(maxC[0], maxC[1], maxC[2]);
 
     for (int col = 0; col < width; col++)
     {
-      int count = 0;
+      int[] count = counts[col];
+      for (int crow = 0; crow < rows; crow++)
+      {
+        count[crow] = 0;
+      }
       for (int row = 0; row < height; row++)
       {
-        count += countFeaturesAt(alignment, col, row, fr);
+        int[] colcount = countFeaturesAt(alignment, col, row, fr);
+        if (colcount != null)
+        {
+          for (int crow = 0; crow < rows; crow++)
+          {
+            count[crow] += colcount[crow];
+          }
+        }
       }
       counts[col] = count;
-      max = Math.max(count, max);
+      for (int crow = 0; crow < rows; crow++)
+      {
+        max[crow] = Math.max(count[crow], max[crow]);
+      }
     }
 
-    Annotation[] anns = new Annotation[width];
-    /*
-     * add non-zero counts as annotations
-     */
-    for (int i = 0; i < counts.length; i++)
+    boolean annotationAdded = false;
+
+    for (int anrow = 0; anrow < rows; anrow++)
     {
-      int count = counts[i];
-      if (count > 0)
+      Annotation[] anns = new Annotation[width];
+      long rmax = 0;
+      /*
+       * add counts as annotations. zeros are needed since select-by-annotation ignores empty annotation positions
+       */
+      for (int i = 0; i < counts.length; i++)
       {
-        Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan,
-                max, Color.blue);
+        int count = counts[i][anrow];
+
+        Color color = ColorUtils.getGraduatedColour(count, 0, minColour,
+                max[anrow], maxColour);
         String str = String.valueOf(count);
         anns[i] = new Annotation(str, str, '0', count, color);
+        rmax = Math.max(count, rmax);
       }
-    }
 
-    /*
-     * construct or update the annotation
-     */
-    AlignmentAnnotation ann = alignViewport.getAlignment()
-            .findOrCreateAnnotation(counter.getName(),
-                    counter.getDescription(), false, null, null);
-    ann.description = counter.getDescription();
-    ann.showAllColLabels = true;
-    ann.scaleColLabel = true;
-    ann.graph = AlignmentAnnotation.BAR_GRAPH;
-    ann.annotations = anns;
-    setGraphMinMax(ann, anns);
-    ann.validateRangeAndDisplay();
-    if (!ourAnnots.contains(ann))
-    {
-      ourAnnots.add(ann);
+      /*
+       * construct or update the annotation
+       */
+      String description = counter.getDescriptions()[anrow];
+      if (!alignment.findAnnotation(description).iterator().hasNext())
+      {
+        annotationAdded = true;
+      }
+      AlignmentAnnotation ann = alignment.findOrCreateAnnotation(
+              counter.getNames()[anrow], description, false, null, null);
+      ann.description = description;
+      ann.showAllColLabels = true;
+      ann.scaleColLabel = true;
+      ann.graph = AlignmentAnnotation.BAR_GRAPH;
+      ann.annotations = anns;
+      ann.graphMin = 0f; // minimum always zero count
+      ann.graphMax = rmax; // maximum count from loop over feature columns
+      ann.validateRangeAndDisplay();
+      if (!ourAnnots.contains(ann))
+      {
+        ourAnnots.add(ann);
+      }
     }
+    return annotationAdded;
   }
 
   /**
@@ -188,35 +229,34 @@ class ColumnCounterWorker extends AlignCalcWorker
    * 
    * @param alignment
    * @param col
+   *          (0..)
    * @param row
    * @param fr
    */
-  int countFeaturesAt(AlignmentI alignment, int col, int row,
+  int[] countFeaturesAt(AlignmentI alignment, int col, int row,
           FeatureRenderer fr)
   {
     SequenceI seq = alignment.getSequenceAt(row);
     if (seq == null)
     {
-      return 0;
+      return null;
     }
     if (col >= seq.getLength())
     {
-      return 0;// sequence doesn't extend this far
+      return null;// sequence doesn't extend this far
     }
     char res = seq.getCharAt(col);
     if (Comparison.isGap(res))
     {
-      return 0;
+      return null;
     }
-    int pos = seq.findPosition(col);
 
     /*
      * compute a count for any displayed features at residue
      */
-    // NB have to adjust pos if using AlignmentView.getVisibleAlignment
     // see JAL-2075
-    List<SequenceFeature> features = fr.findFeaturesAtRes(seq, pos);
-    int count = this.counter.count(String.valueOf(res), features);
+    List<SequenceFeature> features = fr.findFeaturesAtColumn(seq, col + 1);
+    int[] count = this.counter.count(String.valueOf(res), features);
     return count;
   }