Merge branch 'releases/Release_2_10_4_Branch'
[jalview.git] / test / jalview / renderer / seqfeatures / FeatureRendererTest.java
index febd306..6bb611c 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * 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.renderer.seqfeatures;
 
 import static org.testng.Assert.assertEquals;
@@ -14,6 +34,7 @@ import jalview.io.FileLoader;
 import jalview.schemes.FeatureColour;
 
 import java.awt.Color;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -63,16 +84,21 @@ public class FeatureRendererTest
     seqs.get(2).addSequenceFeature(
             new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN,
                     "RfamGroup"));
+    // existing feature type with null group
     seqs.get(3).addSequenceFeature(
             new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN, null));
+    // new feature type with null group
+    seqs.get(3).addSequenceFeature(
+            new SequenceFeature("Scop", "Desc", 5, 9, Float.NaN, null));
     // null value for type produces NullPointerException
     fr.findAllFeatures(true);
     types = fr.getRenderOrder();
     groups = fr.getFeatureGroups();
-    assertEquals(types.size(), 2);
+    assertEquals(types.size(), 3);
     assertFalse(types.contains("Type"));
     assertTrue(types.contains("Pfam"));
     assertTrue(types.contains("Rfam"));
+    assertTrue(types.contains("Scop"));
     assertEquals(groups.size(), 2);
     assertFalse(groups.contains("Group"));
     assertTrue(groups.contains("PfamGroup"));
@@ -104,18 +130,19 @@ public class FeatureRendererTest
      * check render order (last is on top)
      */
     List<String> renderOrder = fr.getRenderOrder();
-    assertEquals(renderOrder, Arrays.asList("Rfam", "Pfam"));
+    assertEquals(renderOrder, Arrays.asList("Scop", "Rfam", "Pfam"));
 
     /*
      * change render order (todo: an easier way)
      * nb here last comes first in the data array
      */
-    Object[][] data = new Object[2][];
+    Object[][] data = new Object[3][];
     FeatureColourI colour = new FeatureColour(Color.RED);
     data[0] = new Object[] { "Rfam", colour, true };
     data[1] = new Object[] { "Pfam", colour, false };
+    data[2] = new Object[] { "Scop", colour, false };
     fr.setFeaturePriority(data);
-    assertEquals(fr.getRenderOrder(), Arrays.asList("Pfam", "Rfam"));
+    assertEquals(fr.getRenderOrder(), Arrays.asList("Scop", "Pfam", "Rfam"));
     assertEquals(fr.getDisplayedFeatureTypes(), Arrays.asList("Rfam"));
 
     /*
@@ -126,8 +153,237 @@ public class FeatureRendererTest
             new SequenceFeature("Metal", "Desc", 14, 22, 8f, "MetalGroup"));
     fr.findAllFeatures(true);
     assertEquals(fr.getRenderOrder(),
-            Arrays.asList("Pfam", "Rfam", "Metal"));
+            Arrays.asList("Scop", "Pfam", "Rfam", "Metal"));
     assertEquals(fr.getDisplayedFeatureTypes(),
             Arrays.asList("Rfam", "Metal"));
   }
+
+  @Test(groups = "Functional")
+  public void testFindFeaturesAtColumn()
+  {
+    String seqData = ">s1/4-29\n-ab--cdefghijklmnopqrstuvwxyz\n";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
+            DataSourceType.PASTE);
+    AlignViewportI av = af.getViewport();
+    FeatureRenderer fr = new FeatureRenderer(av);
+    SequenceI seq = av.getAlignment().getSequenceAt(0);
+
+    /*
+     * with no features
+     */
+    List<SequenceFeature> features = fr.findFeaturesAtColumn(seq, 3);
+    assertTrue(features.isEmpty());
+
+    /*
+     * add features
+     */
+    SequenceFeature sf1 = new SequenceFeature("Type1", "Desc", 0, 0, 1f,
+            "Group"); // non-positional
+    seq.addSequenceFeature(sf1);
+    SequenceFeature sf2 = new SequenceFeature("Type2", "Desc", 8, 18, 1f,
+            "Group1");
+    seq.addSequenceFeature(sf2);
+    SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
+            "Group2");
+    seq.addSequenceFeature(sf3);
+    SequenceFeature sf4 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
+            null); // null group is always treated as visible
+    seq.addSequenceFeature(sf4);
+
+    /*
+     * add contact features
+     */
+    SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 7,
+            15, 1f, "Group1");
+    seq.addSequenceFeature(sf5);
+    SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 7,
+            15, 1f, "Group2");
+    seq.addSequenceFeature(sf6);
+    SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 7,
+            15, 1f, null);
+    seq.addSequenceFeature(sf7);
+
+    // feature spanning B--C
+    SequenceFeature sf8 = new SequenceFeature("Type1", "Desc", 5, 6, 1f,
+            "Group");
+    seq.addSequenceFeature(sf8);
+    // contact feature B/C
+    SequenceFeature sf9 = new SequenceFeature("Disulphide Bond", "Desc", 5,
+            6, 1f, "Group");
+    seq.addSequenceFeature(sf9);
+
+    /*
+     * let feature renderer discover features (and make visible)
+     */
+    fr.findAllFeatures(true);
+    features = fr.findFeaturesAtColumn(seq, 15); // all positional
+    assertEquals(features.size(), 6);
+    assertTrue(features.contains(sf2));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+    assertTrue(features.contains(sf6));
+    assertTrue(features.contains(sf7));
+
+    /*
+     * at a non-contact position
+     */
+    features = fr.findFeaturesAtColumn(seq, 14);
+    assertEquals(features.size(), 3);
+    assertTrue(features.contains(sf2));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+
+    /*
+     * make "Type2" not displayed
+     */
+    Object[][] data = new Object[4][];
+    FeatureColourI colour = new FeatureColour(Color.RED);
+    data[0] = new Object[] { "Type1", colour, true };
+    data[1] = new Object[] { "Type2", colour, false };
+    data[2] = new Object[] { "Type3", colour, true };
+    data[3] = new Object[] { "Disulphide Bond", colour, true };
+    fr.setFeaturePriority(data);
+
+    features = fr.findFeaturesAtColumn(seq, 15);
+    assertEquals(features.size(), 5); // no sf2
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+    assertTrue(features.contains(sf6));
+    assertTrue(features.contains(sf7));
+
+    /*
+     * make "Group2" not displayed
+     */
+    fr.setGroupVisibility("Group2", false);
+
+    features = fr.findFeaturesAtColumn(seq, 15);
+    assertEquals(features.size(), 3); // no sf2, sf3, sf6
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+    assertTrue(features.contains(sf7));
+
+    // features 'at' a gap between b and c
+    // - returns enclosing feature BC but not contact feature B/C
+    features = fr.findFeaturesAtColumn(seq, 4);
+    assertEquals(features.size(), 1);
+    assertTrue(features.contains(sf8));
+    features = fr.findFeaturesAtColumn(seq, 5);
+    assertEquals(features.size(), 1);
+    assertTrue(features.contains(sf8));
+  }
+
+  @Test(groups = "Functional")
+  public void testFilterFeaturesForDisplay()
+  {
+    String seqData = ">s1\nabcdef\n";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
+            DataSourceType.PASTE);
+    AlignViewportI av = af.getViewport();
+    FeatureRenderer fr = new FeatureRenderer(av);
+
+    List<SequenceFeature> features = new ArrayList<>();
+    fr.filterFeaturesForDisplay(features, null); // empty list, does nothing
+
+    SequenceI seq = av.getAlignment().getSequenceAt(0);
+    SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
+            "group1");
+    seq.addSequenceFeature(sf1);
+    SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, 2f,
+            "group2");
+    seq.addSequenceFeature(sf2);
+    SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, 3f,
+            "group3");
+    seq.addSequenceFeature(sf3);
+    SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, 4f,
+            "group4");
+    seq.addSequenceFeature(sf4);
+    SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, 5f,
+            "group4");
+    seq.addSequenceFeature(sf5);
+
+    fr.findAllFeatures(true);
+
+    features = seq.getSequenceFeatures();
+    assertEquals(features.size(), 5);
+    assertTrue(features.contains(sf1));
+    assertTrue(features.contains(sf2));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+
+    /*
+     * filter out duplicate (co-located) features
+     * note: which gets removed is not guaranteed
+     */
+    fr.filterFeaturesForDisplay(features, new FeatureColour(Color.blue));
+    assertEquals(features.size(), 3);
+    assertTrue(features.contains(sf1) || features.contains(sf4));
+    assertFalse(features.contains(sf1) && features.contains(sf4));
+    assertTrue(features.contains(sf2) || features.contains(sf3));
+    assertFalse(features.contains(sf2) && features.contains(sf3));
+    assertTrue(features.contains(sf5));
+
+    /*
+     * hide group 3 - sf3 is removed, sf2 is retained
+     */
+    fr.setGroupVisibility("group3", false);
+    features = seq.getSequenceFeatures();
+    fr.filterFeaturesForDisplay(features, new FeatureColour(Color.blue));
+    assertEquals(features.size(), 3);
+    assertTrue(features.contains(sf1) || features.contains(sf4));
+    assertFalse(features.contains(sf1) && features.contains(sf4));
+    assertTrue(features.contains(sf2));
+    assertFalse(features.contains(sf3));
+    assertTrue(features.contains(sf5));
+
+    /*
+     * hide group 2, show group 3 - sf2 is removed, sf3 is retained
+     */
+    fr.setGroupVisibility("group2", false);
+    fr.setGroupVisibility("group3", true);
+    features = seq.getSequenceFeatures();
+    fr.filterFeaturesForDisplay(features, null);
+    assertEquals(features.size(), 3);
+    assertTrue(features.contains(sf1) || features.contains(sf4));
+    assertFalse(features.contains(sf1) && features.contains(sf4));
+    assertFalse(features.contains(sf2));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf5));
+
+    /*
+     * no filtering of co-located features with graduated colour scheme
+     * filterFeaturesForDisplay does _not_ check colour threshold
+     * sf2 is removed as its group is hidden
+     */
+    features = seq.getSequenceFeatures();
+    fr.filterFeaturesForDisplay(features, new FeatureColour(Color.black,
+            Color.white, 0f, 1f));
+    assertEquals(features.size(), 4);
+    assertFalse(features.contains(sf2));
+
+    /*
+     * co-located features with colour by label
+     * should not get filtered
+     */
+    features = seq.getSequenceFeatures();
+    FeatureColour fc = new FeatureColour(Color.black);
+    fc.setColourByLabel(true);
+    fr.filterFeaturesForDisplay(features, fc);
+    assertEquals(features.size(), 4);
+    assertTrue(features.contains(sf1));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+
+    /*
+     * no filtering if transparency is applied
+     */
+    fr.setTransparency(0.5f);
+    features = seq.getSequenceFeatures();
+    fr.setGroupVisibility("group2", true);
+    fr.filterFeaturesForDisplay(features, new FeatureColour(Color.RED));
+    assertEquals(features.size(), 5);
+  }
 }