JAL-4071 first working prototype
authorJim Procter <j.procter@dundee.ac.uk>
Fri, 23 Sep 2022 11:09:24 +0000 (12:09 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Fri, 23 Sep 2022 11:09:24 +0000 (12:09 +0100)
src/jalview/api/FeatureRenderer.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/FeatureRenderer.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/viewmodel/styles/ViewStyle.java
src/jalview/workers/ColumnCounterSetWorker.java
src/jalview/workers/VisibleFeaturesAnnotationTracks.java [new file with mode: 0644]

index 5430303..762350e 100644 (file)
  */
 package jalview.api;
 
-import jalview.datamodel.MappedFeatures;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.FeatureMatcherSetI;
-
 import java.awt.Color;
 import java.awt.Graphics;
+import java.beans.PropertyChangeListener;
 import java.util.List;
 import java.util.Map;
 
+import jalview.datamodel.MappedFeatures;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+
 /**
  * Abstract feature renderer interface
  * 
@@ -306,4 +307,25 @@ public interface FeatureRenderer
    */
   void notifyFeaturesChanged();
 
+  /**
+   * register as a listener for notifyFeaturesChanged events
+   * 
+   * @param ourListener
+   */
+  void addPropertyChangeListener(PropertyChangeListener ourListener);
+
+  /**
+   * remove a listener for notifyFeaturesChanged events
+   * 
+   * @param ourListener
+   */
+  void removePropertyChangeListener(PropertyChangeListener ourListener);
+
+  /**
+   * 
+   * @return associated alignment panel for this feature renderer (may return
+   *         null)
+   */
+  AlignmentViewPanel getAlignPanel();
+
 }
index e84aae0..9280fb1 100644 (file)
  */
 package jalview.gui;
 
-import jalview.analysis.AnnotationSorter;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.bin.Console;
-import jalview.bin.Jalview;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.SearchResultsI;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.gui.ImageExporter.ImageWriterI;
-import jalview.io.HTMLOutput;
-import jalview.jbgui.GAlignmentPanel;
-import jalview.math.AlignmentDimension;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.Comparison;
-import jalview.util.ImageMaker;
-import jalview.util.MessageManager;
-import jalview.viewmodel.ViewportListenerI;
-import jalview.viewmodel.ViewportRanges;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Container;
@@ -68,6 +44,31 @@ import java.util.List;
 
 import javax.swing.SwingUtilities;
 
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.ImageExporter.ImageWriterI;
+import jalview.io.HTMLOutput;
+import jalview.jbgui.GAlignmentPanel;
+import jalview.math.AlignmentDimension;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.Comparison;
+import jalview.util.ImageMaker;
+import jalview.util.MessageManager;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+import jalview.workers.VisibleFeaturesAnnotationTracks;
+
 /**
  * DOCUMENT ME!
  * 
@@ -115,6 +116,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   private CalculationChooser calculationDialog;
 
+  private VisibleFeaturesAnnotationTracks featureCounter;
+
   /**
    * Creates a new AlignmentPanel object.
    * 
@@ -130,6 +133,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     setIdPanel(new IdPanel(av, this));
 
     setScalePanel(new ScalePanel(av, this));
+    featureCounter = new VisibleFeaturesAnnotationTracks(av,
+            seqPanel.seqCanvas.fr);
 
     idPanelHolder.add(getIdPanel(), BorderLayout.CENTER);
     idwidthAdjuster = new IdwidthAdjuster(this);
@@ -179,6 +184,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     });
 
     final AlignmentPanel ap = this;
+
     propertyChangeListener = new PropertyChangeListener()
     {
       @Override
@@ -824,6 +830,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public void paintAlignment(boolean updateOverview,
           boolean updateStructures)
   {
+    featureCounter.updateFeatureAnnotationTracks();
     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
             av.isShowAutocalculatedAbove());
     sorter.sort(getAlignment().getAlignmentAnnotation(),
@@ -1450,6 +1457,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
       annotationPanel = null;
     }
 
+    if (featureCounter != null)
+    {
+      featureCounter.tidyUp();
+      featureCounter = null;
+    }
     if (av != null)
     {
       av.removePropertyChangeListener(propertyChangeListener);
index 83badd0..0cbb9bc 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import jalview.api.AlignmentViewPanel;
+
 /**
  * A class that manages drawing of sequence features for the Swing gui
  */
@@ -37,6 +39,7 @@ public class FeatureRenderer
   {
     super(alignPanel.av);
     this.ap = alignPanel;
+
     if (alignPanel.getSeqPanel() != null
             && alignPanel.getSeqPanel().seqCanvas != null
             && alignPanel.getSeqPanel().seqCanvas.fr != null)
@@ -44,4 +47,10 @@ public class FeatureRenderer
       transferSettings(alignPanel.getSeqPanel().seqCanvas.fr);
     }
   }
+
+  @Override
+  public AlignmentViewPanel getAlignPanel()
+  {
+    return ap;
+  }
 }
index e66b7d5..2d16fcd 100644 (file)
@@ -543,7 +543,7 @@ public class FeatureRenderer extends FeatureRendererModel
        */
       List<SequenceFeature> overlaps = seq.findFeatures(column, column,
               type);
-      for (int i = overlaps.size() - 1 ; i >= 0 ; i--)
+      for (int i = overlaps.size() - 1; i >= 0; i--)
       {
         SequenceFeature sequenceFeature = overlaps.get(i);
         if (!featureGroupNotShown(sequenceFeature))
index e812ed5..9155440 100644 (file)
@@ -36,6 +36,7 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
 import jalview.api.FeaturesDisplayedI;
 import jalview.datamodel.AlignedCodonFrame;
@@ -801,6 +802,7 @@ public abstract class FeatureRendererModel
    * @param listener
    * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
    */
+  @Override
   public void addPropertyChangeListener(PropertyChangeListener listener)
   {
     changeSupport.addPropertyChangeListener(listener);
@@ -810,6 +812,7 @@ public abstract class FeatureRendererModel
    * @param listener
    * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
    */
+  @Override
   public void removePropertyChangeListener(PropertyChangeListener listener)
   {
     changeSupport.removePropertyChangeListener(listener);
@@ -1330,4 +1333,10 @@ public abstract class FeatureRendererModel
     }
     return true;
   }
+
+  @Override
+  public AlignmentViewPanel getAlignPanel()
+  {
+    return null;
+  }
 }
index 0a525e2..eaa77cb 100644 (file)
@@ -219,6 +219,7 @@ public class ViewStyle implements ViewStyleI
     setShowNPFeats(vs.isShowNPFeats());
     setShowSequenceFeaturesHeight(vs.isShowSequenceFeaturesHeight());
     setShowSequenceFeatures(vs.isShowSequenceFeatures());
+    setShowSequenceFeatureCounts(vs.isShowSequenceFeatureCounts());
     setShowComplementFeatures(vs.isShowComplementFeatures());
     setShowComplementFeaturesOnTop(vs.isShowComplementFeaturesOnTop());
     setShowText(vs.getShowText());
@@ -282,6 +283,8 @@ public class ViewStyle implements ViewStyleI
             && isShowNPFeats() == vs.isShowNPFeats()
             && isShowSequenceFeaturesHeight() == vs
                     .isShowSequenceFeaturesHeight()
+            && isShowSequenceFeatureCounts() == vs
+                    .isShowSequenceFeatureCounts()
             && isShowSequenceFeatures() == vs.isShowSequenceFeatures()
             && isShowComplementFeatures() == vs.isShowComplementFeatures()
             && isShowComplementFeaturesOnTop() == vs
index 74695fe..3a4bcc4 100644 (file)
  */
 package jalview.workers;
 
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -31,10 +35,6 @@ 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>
@@ -223,6 +223,26 @@ class ColumnCounterSetWorker extends AlignCalcWorker
     return annotationAdded;
   }
 
+  void removeOldAnnotations(String[] annotDescs)
+  {
+    // TODO use the commented out code once JAL-2075 is fixed
+    // to get adequate performance on genomic length sequence
+    AlignmentI alignment = alignViewport.getAlignment();
+    ArrayList<AlignmentAnnotation> toRemove = new ArrayList<AlignmentAnnotation>();
+    for (String toRemoveDesc : annotDescs)
+    {
+      for (AlignmentAnnotation aa : ourAnnots)
+      {
+        if (toRemoveDesc.equals(aa.getCalcId()))
+          toRemove.add(aa);
+      }
+    }
+    for (AlignmentAnnotation deleted : toRemove)
+    {
+      alignment.deleteAnnotation(deleted);
+    }
+  }
+
   /**
    * Returns a count of any feature types present at the specified position of
    * the alignment
diff --git a/src/jalview/workers/VisibleFeaturesAnnotationTracks.java b/src/jalview/workers/VisibleFeaturesAnnotationTracks.java
new file mode 100644 (file)
index 0000000..baa8038
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * 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 java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import jalview.api.AlignViewportI;
+import jalview.api.FeaturesDisplayedI;
+import jalview.datamodel.SequenceFeature;
+import jalview.gui.FeatureRenderer;
+
+public class VisibleFeaturesAnnotationTracks implements FeatureSetCounterI
+{
+  AlignViewportI ourViewport = null;
+
+  jalview.api.FeatureRenderer ourFr = null;
+
+  PropertyChangeListener ourListener = new PropertyChangeListener()
+  {
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt)
+    {
+      if (ourViewport != null) // could check source is
+                               // ourFr.getChangeSupport...
+      {
+        updateFeatureAnnotationTracks();
+      }
+    }
+  };
+
+  public VisibleFeaturesAnnotationTracks(AlignViewportI viewport,
+          FeatureRenderer fr)
+  {
+    ourViewport = viewport;
+    ourFr = fr;
+    registerListener();
+  }
+
+  void registerListener()
+  {
+    ourFr.addPropertyChangeListener(ourListener);
+  }
+
+  public void tidyUp()
+  {
+    if (ourFr != null)
+    {
+      ourFr.removePropertyChangeListener(ourListener);
+    }
+    if (ourViewport != null && ourViewport.getCalcManager() != null)
+    {
+      if (ourWorker != null)
+      {
+        ourWorker.abortAndDestroy();
+        ourViewport.getCalcManager()
+                .removeRegisteredWorkersOfClass(ourWorker.getClass());
+      }
+      ourWorker = null;
+      ourViewport = null;
+    }
+  }
+
+  public void updateFeatureAnnotationTracks()
+  {
+    // if tracks are turned off, this returns null.
+    FeaturesDisplayedI featuresDisp = ourViewport
+            .isShowSequenceFeatureCounts()
+                    ? ourViewport.getFeaturesDisplayed()
+                    : null;
+    Set<String> visibleFeatures = new HashSet();
+    if (featuresDisp != null)
+    {
+      visibleFeatures.addAll(featuresDisp.getVisibleFeatures());
+    }
+    if (dispFeatures.equals(visibleFeatures))
+    {
+      // all the same features displayed
+      return;
+    }
+    // otherwise set up tracks accordingly
+
+    /*
+     * and register the counter
+     */
+    if (ourWorker != null)
+    {
+      Set<String> toRemove = new HashSet<String>(),
+              toAdd = new HashSet<String>();
+      toRemove.addAll(dispFeatures);
+      toRemove.removeAll(visibleFeatures);
+      dispFeatures = visibleFeatures;
+      ourWorker.removeOldAnnotations(toRemove.toArray(new String[0]));
+
+    }
+    else
+    {
+      dispFeatures = visibleFeatures;
+      ourWorker = new ColumnCounterSetWorker(ourViewport,
+              ourFr.getAlignPanel(), this);
+    }
+    ourViewport.getCalcManager().registerWorker(ourWorker);
+    ourViewport.getCalcManager().startWorker(ourWorker);
+  }
+
+  ColumnCounterSetWorker ourWorker = null;
+
+  Set<String> dispFeatures = Collections.EMPTY_SET;
+
+  @Override
+  public int[] count(String residue, List<SequenceFeature> features)
+  {
+    final Set<String> ourDispFeatures = dispFeatures;
+    int[] obs = new int[ourDispFeatures.size()];
+    SequenceFeature[] sfs = features.toArray(new SequenceFeature[0]);
+    for (SequenceFeature sf : sfs)
+    {
+      /*
+       * Here we inspect the type of the sequence feature.
+       * You can also test sf.description, sf.score, sf.featureGroup,
+       * sf.strand, sf.phase, sf.begin, sf.end
+       * or sf.getValue(attributeName) for GFF 'column 9' properties
+       */
+      int pos = 0;
+      for (String type : ourDispFeatures)
+      {
+        if (type.equals(sf.type))
+        {
+          obs[pos]++;
+        }
+        pos++;
+      }
+    }
+    return obs;
+  }
+
+  @Override
+  public String[] getNames()
+  {
+    return dispFeatures.toArray(new String[0]);
+  }
+
+  @Override
+  public String[] getDescriptions()
+  {
+    return dispFeatures.toArray(new String[0]);
+  }
+
+  @Override
+  public int[] getMaxColour()
+  {
+    return new int[] { 0, 0, 255 };
+  }
+
+  @Override
+  public int[] getMinColour()
+  {
+    return new int[] { 0, 255, 255 };
+  }
+}
\ No newline at end of file