From d0b6486ecb5c6572b823f9fd8676a603a4d493e9 Mon Sep 17 00:00:00 2001 From: Jim Procter Date: Sun, 25 Sep 2016 10:45:56 +0100 Subject: [PATCH] JAL-2228 interface and worker to allow multiple feature types to be counted simultaneously --- src/jalview/workers/ColumnCounterI.java | 33 +++ src/jalview/workers/ColumnCounterSetWorker.java | 265 +++++++++++++++++++++++ src/jalview/workers/FeatureSetCounterI.java | 86 ++++++++ 3 files changed, 384 insertions(+) create mode 100644 src/jalview/workers/ColumnCounterI.java create mode 100644 src/jalview/workers/ColumnCounterSetWorker.java create mode 100644 src/jalview/workers/FeatureSetCounterI.java diff --git a/src/jalview/workers/ColumnCounterI.java b/src/jalview/workers/ColumnCounterI.java new file mode 100644 index 0000000..53f1b4b --- /dev/null +++ b/src/jalview/workers/ColumnCounterI.java @@ -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 . + * 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 index 0000000..cf6a229 --- /dev/null +++ b/src/jalview/workers/ColumnCounterSetWorker.java @@ -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 . + * 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.
+ * 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(); + 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 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 index 0000000..a8cb059 --- /dev/null +++ b/src/jalview/workers/FeatureSetCounterI.java @@ -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 . + * 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 + *
    + *
  • the number of variant features at the position
  • + *
  • the number of Cath features of status 'True Positive'
  • + *
  • 1 if the residue is hydrophobic, else 0
  • + *
  • etc
  • + *
+ * + * @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 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(); +} -- 1.7.10.2