JAL-2228 interface and worker to allow multiple feature types to be counted simultane...
authorJim Procter <jprocter@issues.jalview.org>
Sun, 25 Sep 2016 09:45:56 +0000 (10:45 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Sun, 25 Sep 2016 09:45:56 +0000 (10:45 +0100)
src/jalview/workers/ColumnCounterI.java [new file with mode: 0644]
src/jalview/workers/ColumnCounterSetWorker.java [new file with mode: 0644]
src/jalview/workers/FeatureSetCounterI.java [new file with mode: 0644]

diff --git a/src/jalview/workers/ColumnCounterI.java b/src/jalview/workers/ColumnCounterI.java
new file mode 100644 (file)
index 0000000..53f1b4b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.workers;
+
+/**
+ * interfaces that provide counting routines for the newCalculator
+ * AlignmentAnnotationFactory methods
+ * 
+ * @author jprocter
+ *
+ */
+public interface ColumnCounterI
+{
+
+}
diff --git a/src/jalview/workers/ColumnCounterSetWorker.java b/src/jalview/workers/ColumnCounterSetWorker.java
new file mode 100644 (file)
index 0000000..cf6a229
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.workers;
+
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureRenderer;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 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 vector of counts for each residue position, based on the
+ * residue and and sequence features at that position.
+ * 
+ */
+class ColumnCounterSetWorker extends AlignCalcWorker
+{
+  FeatureSetCounterI counter;
+
+  /**
+   * Constructor registers the annotation for the given alignment frame
+   * 
+   * @param af
+   * @param counter
+   */
+  public ColumnCounterSetWorker(AlignViewportI viewport,
+          AlignmentViewPanel panel, FeatureSetCounterI counter)
+  {
+    super(viewport, panel);
+    ourAnnots = new ArrayList<AlignmentAnnotation>();
+    this.counter = counter;
+    calcMan.registerWorker(this);
+  }
+
+  /**
+   * method called under control of AlignCalcManager to recompute the annotation
+   * when the alignment changes
+   */
+  @Override
+  public void run()
+  {
+    try
+    {
+      calcMan.notifyStart(this);
+
+      while (!calcMan.notifyWorking(this))
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+      if (alignViewport.isClosed())
+      {
+        abortAndDestroy();
+        return;
+      }
+
+      if (alignViewport.getAlignment() != null)
+      {
+        try
+        {
+          computeAnnotations();
+        } catch (IndexOutOfBoundsException x)
+        {
+          // probable race condition. just finish and return without any fuss.
+          return;
+        }
+      }
+    } catch (OutOfMemoryError error)
+    {
+      ap.raiseOOMWarning("calculating feature counts", error);
+      calcMan.disableWorker(this);
+    } finally
+    {
+      calcMan.workerComplete(this);
+    }
+
+    if (ap != null)
+    {
+      ap.adjustAnnotationHeight();
+      ap.paintAlignment(true);
+    }
+
+  }
+
+  /**
+   * 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.
+   */
+  void computeAnnotations()
+  {
+    FeatureRenderer fr = new FeatureRenderer(alignViewport);
+    // TODO use the commented out code once JAL-2075 is fixed
+    // to get adequate performance on genomic length sequence
+    AlignmentI alignment = alignViewport.getAlignment();
+    // AlignmentView alignmentView = alignViewport.getAlignmentView(false);
+    // AlignmentI alignment = alignmentView.getVisibleAlignment(' ');
+
+    int rows = counter.getNames().length;
+
+    int width = alignment.getWidth();
+    int height = alignment.getHeight();
+    int[][] counts = new int[width][rows];
+    int max[] = new int[rows];
+    for (int crow = 0; crow < rows; crow++)
+    {
+      max[crow] = 0;
+    }
+    for (int col = 0; col < width; col++)
+    {
+      int[] count = counts[col];
+      for (int crow = 0; crow < rows; crow++)
+      {
+        count[crow] = 0;
+      }
+      for (int row = 0; row < height; row++)
+      {
+        int[] colcount = countFeaturesAt(alignment, col, row, fr);
+        if (colcount != null)
+        {
+          for (int crow = 0; crow < rows; crow++)
+          {
+            count[crow] += colcount[crow];
+          }
+        }
+      }
+      counts[col] = count;
+      for (int crow = 0; crow < rows; crow++)
+      {
+        max[crow] = Math.max(count[crow], max[crow]);
+      }
+    }
+    for (int anrow = 0; anrow < rows; anrow++)
+    {
+      Annotation[] anns = new Annotation[width];
+      /*
+       * add non-zero counts as annotations
+       */
+      for (int i = 0; i < counts.length; i++)
+      {
+        int count = counts[i][anrow];
+        if (count > 0)
+        {
+          Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan,
+                  max[anrow], Color.blue);
+          String str = String.valueOf(count);
+          anns[i] = new Annotation(str, str, '0', count, color);
+        }
+      }
+
+      /*
+       * construct or update the annotation
+       */
+      AlignmentAnnotation ann = alignViewport.getAlignment()
+              .findOrCreateAnnotation(counter.getNames()[anrow],
+                      counter.getDescriptions()[anrow], false, null,
+                      null);
+      ann.description = counter.getDescriptions()[anrow];
+      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);
+      }
+    }
+  }
+
+  /**
+   * Returns a count of any feature types present at the specified position of
+   * the alignment
+   * 
+   * @param alignment
+   * @param col
+   * @param row
+   * @param fr
+   */
+  int[] countFeaturesAt(AlignmentI alignment, int col, int row,
+          FeatureRenderer fr)
+  {
+    SequenceI seq = alignment.getSequenceAt(row);
+    if (seq == null)
+    {
+      return null;
+    }
+    if (col >= seq.getLength())
+    {
+      return null;// sequence doesn't extend this far
+    }
+    char res = seq.getCharAt(col);
+    if (Comparison.isGap(res))
+    {
+      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);
+    return count;
+  }
+
+  /**
+   * Method called when the user changes display options that may affect how the
+   * annotation is rendered, but do not change its values. Currently no such
+   * options affect user-defined annotation, so this method does nothing.
+   */
+  @Override
+  public void updateAnnotation()
+  {
+    // do nothing
+  }
+
+  /**
+   * Answers true to indicate that if this worker's annotation is deleted from
+   * the display, the worker should also be removed. This prevents it running
+   * and recreating the annotation when the alignment changes.
+   */
+  @Override
+  public boolean isDeletable()
+  {
+    return true;
+  }
+}
diff --git a/src/jalview/workers/FeatureSetCounterI.java b/src/jalview/workers/FeatureSetCounterI.java
new file mode 100644 (file)
index 0000000..a8cb059
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.workers;
+
+import jalview.datamodel.SequenceFeature;
+
+import java.util.List;
+
+/**
+ * Like FeatureCounterI but returns a vector of counts for a set of features
+ * 
+ * An interface for a type that returns counts of any value of interest at a
+ * sequence position that can be determined from the sequence character and any
+ * features present at that position
+ * 
+ */
+public interface FeatureSetCounterI extends ColumnCounterI
+{
+  /**
+   * Returns a count of some property of interest, for example
+   * <ul>
+   * <li>the number of variant features at the position</li>
+   * <li>the number of Cath features of status 'True Positive'</li>
+   * <li>1 if the residue is hydrophobic, else 0</li>
+   * <li>etc</li>
+   * </ul>
+   * 
+   * @param residue
+   *          the residue (or gap) at the position
+   * @param a
+   *          list of any sequence features which include the position
+   */
+  int[] count(String residue, List<SequenceFeature> features);
+
+  /**
+   * Returns a name for the annotation that this is counting, for use as the
+   * displayed label
+   * 
+   * @return
+   */
+  String[] getNames();
+
+  /**
+   * Returns a description for the annotation, for display as a tooltip
+   * 
+   * @return
+   */
+  String[] getDescriptions();
+
+  /**
+   * Returns the colour (as [red, green, blue] values in the range 0-255) to use
+   * for the minimum value on histogram bars. If this is different to
+   * getMaxColour(), then bars will have a graduated colour.
+   * 
+   * @return
+   */
+  int[] getMinColour();
+
+  /**
+   * Returns the colour (as [red, green, blue] values in the range 0-255) to use
+   * for the maximum value on histogram bars. If this is the same as
+   * getMinColour(), then bars will have a single colour (not graduated).
+   * 
+   * @return
+   */
+  int[] getMaxColour();
+}