JAL-2446 merged to spike branch
[jalview.git] / test / jalview / renderer / seqfeatures / FeatureRendererTest.java
1 package jalview.renderer.seqfeatures;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertTrue;
6
7 import jalview.api.AlignViewportI;
8 import jalview.api.FeatureColourI;
9 import jalview.datamodel.SequenceFeature;
10 import jalview.datamodel.SequenceI;
11 import jalview.gui.AlignFrame;
12 import jalview.io.DataSourceType;
13 import jalview.io.FileLoader;
14 import jalview.schemes.FeatureColour;
15
16 import java.awt.Color;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.testng.annotations.Test;
23
24 public class FeatureRendererTest
25 {
26
27   @Test(groups = "Functional")
28   public void testFindAllFeatures()
29   {
30     String seqData = ">s1\nabcdef\n>s2\nabcdef\n>s3\nabcdef\n>s4\nabcdef\n";
31     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
32             DataSourceType.PASTE);
33     AlignViewportI av = af.getViewport();
34     FeatureRenderer fr = new FeatureRenderer(av);
35
36     /*
37      * with no features
38      */
39     fr.findAllFeatures(true);
40     assertTrue(fr.getRenderOrder().isEmpty());
41     assertTrue(fr.getFeatureGroups().isEmpty());
42
43     List<SequenceI> seqs = av.getAlignment().getSequences();
44
45     // add a non-positional feature - should be ignored by FeatureRenderer
46     SequenceFeature sf1 = new SequenceFeature("Type", "Desc", 0, 0, 1f,
47             "Group");
48     seqs.get(0).addSequenceFeature(sf1);
49     fr.findAllFeatures(true);
50     // ? bug - types and groups added for non-positional features
51     List<String> types = fr.getRenderOrder();
52     List<String> groups = fr.getFeatureGroups();
53     assertEquals(types.size(), 0);
54     assertFalse(types.contains("Type"));
55     assertEquals(groups.size(), 0);
56     assertFalse(groups.contains("Group"));
57
58     // add some positional features
59     seqs.get(1).addSequenceFeature(
60             new SequenceFeature("Pfam", "Desc", 5, 9, 1f, "PfamGroup"));
61     seqs.get(2).addSequenceFeature(
62             new SequenceFeature("Pfam", "Desc", 14, 22, 2f, "RfamGroup"));
63     // bug in findAllFeatures - group not checked for a known feature type
64     seqs.get(2).addSequenceFeature(
65             new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN,
66                     "RfamGroup"));
67     // existing feature type with null group
68     seqs.get(3).addSequenceFeature(
69             new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN, null));
70     // new feature type with null group
71     seqs.get(3).addSequenceFeature(
72             new SequenceFeature("Scop", "Desc", 5, 9, Float.NaN, null));
73     // null value for type produces NullPointerException
74     fr.findAllFeatures(true);
75     types = fr.getRenderOrder();
76     groups = fr.getFeatureGroups();
77     assertEquals(types.size(), 3);
78     assertFalse(types.contains("Type"));
79     assertTrue(types.contains("Pfam"));
80     assertTrue(types.contains("Rfam"));
81     assertTrue(types.contains("Scop"));
82     assertEquals(groups.size(), 2);
83     assertFalse(groups.contains("Group"));
84     assertTrue(groups.contains("PfamGroup"));
85     assertTrue(groups.contains("RfamGroup"));
86     assertFalse(groups.contains(null)); // null group is ignored
87
88     /*
89      * check min-max values
90      */
91     Map<String, float[][]> minMax = fr.getMinMax();
92     assertEquals(minMax.size(), 1); // non-positional and NaN not stored
93     assertEquals(minMax.get("Pfam")[0][0], 1f); // positional min
94     assertEquals(minMax.get("Pfam")[0][1], 2f); // positional max
95
96     // increase max for Pfam, add scores for Rfam
97     seqs.get(0).addSequenceFeature(
98             new SequenceFeature("Pfam", "Desc", 14, 22, 8f, "RfamGroup"));
99     seqs.get(1).addSequenceFeature(
100             new SequenceFeature("Rfam", "Desc", 5, 9, 6f, "RfamGroup"));
101     fr.findAllFeatures(true);
102     // note minMax is not a defensive copy, shouldn't expose this
103     assertEquals(minMax.size(), 2);
104     assertEquals(minMax.get("Pfam")[0][0], 1f);
105     assertEquals(minMax.get("Pfam")[0][1], 8f);
106     assertEquals(minMax.get("Rfam")[0][0], 6f);
107     assertEquals(minMax.get("Rfam")[0][1], 6f);
108
109     /*
110      * check render order (last is on top)
111      */
112     List<String> renderOrder = fr.getRenderOrder();
113     assertEquals(renderOrder, Arrays.asList("Scop", "Rfam", "Pfam"));
114
115     /*
116      * change render order (todo: an easier way)
117      * nb here last comes first in the data array
118      */
119     Object[][] data = new Object[3][];
120     FeatureColourI colour = new FeatureColour(Color.RED);
121     data[0] = new Object[] { "Rfam", colour, true };
122     data[1] = new Object[] { "Pfam", colour, false };
123     data[2] = new Object[] { "Scop", colour, false };
124     fr.setFeaturePriority(data);
125     assertEquals(fr.getRenderOrder(), Arrays.asList("Scop", "Pfam", "Rfam"));
126     assertEquals(fr.getDisplayedFeatureTypes(), Arrays.asList("Rfam"));
127
128     /*
129      * add a new feature type: should go on top of render order as visible,
130      * other feature ordering and visibility should be unchanged
131      */
132     seqs.get(2).addSequenceFeature(
133             new SequenceFeature("Metal", "Desc", 14, 22, 8f, "MetalGroup"));
134     fr.findAllFeatures(true);
135     assertEquals(fr.getRenderOrder(),
136             Arrays.asList("Scop", "Pfam", "Rfam", "Metal"));
137     assertEquals(fr.getDisplayedFeatureTypes(),
138             Arrays.asList("Rfam", "Metal"));
139   }
140
141   @Test(groups = "Functional")
142   public void testFindFeaturesAtColumn()
143   {
144     String seqData = ">s1/4-29\n-ab--cdefghijklmnopqrstuvwxyz\n";
145     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
146             DataSourceType.PASTE);
147     AlignViewportI av = af.getViewport();
148     FeatureRenderer fr = new FeatureRenderer(av);
149     SequenceI seq = av.getAlignment().getSequenceAt(0);
150
151     /*
152      * with no features
153      */
154     List<SequenceFeature> features = fr.findFeaturesAtColumn(seq, 3);
155     assertTrue(features.isEmpty());
156
157     /*
158      * add features
159      */
160     SequenceFeature sf1 = new SequenceFeature("Type1", "Desc", 0, 0, 1f,
161             "Group"); // non-positional
162     seq.addSequenceFeature(sf1);
163     SequenceFeature sf2 = new SequenceFeature("Type2", "Desc", 8, 18, 1f,
164             "Group1");
165     seq.addSequenceFeature(sf2);
166     SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
167             "Group2");
168     seq.addSequenceFeature(sf3);
169     SequenceFeature sf4 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
170             null); // null group is always treated as visible
171     seq.addSequenceFeature(sf4);
172
173     /*
174      * add contact features
175      */
176     SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 7,
177             15, 1f, "Group1");
178     seq.addSequenceFeature(sf5);
179     SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 7,
180             15, 1f, "Group2");
181     seq.addSequenceFeature(sf6);
182     SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 7,
183             15, 1f, null);
184     seq.addSequenceFeature(sf7);
185
186     // feature spanning B--C
187     SequenceFeature sf8 = new SequenceFeature("Type1", "Desc", 5, 6, 1f,
188             "Group");
189     seq.addSequenceFeature(sf8);
190     // contact feature B/C
191     SequenceFeature sf9 = new SequenceFeature("Disulphide Bond", "Desc", 5,
192             6, 1f, "Group");
193     seq.addSequenceFeature(sf9);
194
195     /*
196      * let feature renderer discover features (and make visible)
197      */
198     fr.findAllFeatures(true);
199     features = fr.findFeaturesAtColumn(seq, 15); // all positional
200     assertEquals(features.size(), 6);
201     assertTrue(features.contains(sf2));
202     assertTrue(features.contains(sf3));
203     assertTrue(features.contains(sf4));
204     assertTrue(features.contains(sf5));
205     assertTrue(features.contains(sf6));
206     assertTrue(features.contains(sf7));
207
208     /*
209      * at a non-contact position
210      */
211     features = fr.findFeaturesAtColumn(seq, 14);
212     assertEquals(features.size(), 3);
213     assertTrue(features.contains(sf2));
214     assertTrue(features.contains(sf3));
215     assertTrue(features.contains(sf4));
216
217     /*
218      * make "Type2" not displayed
219      */
220     Object[][] data = new Object[4][];
221     FeatureColourI colour = new FeatureColour(Color.RED);
222     data[0] = new Object[] { "Type1", colour, true };
223     data[1] = new Object[] { "Type2", colour, false };
224     data[2] = new Object[] { "Type3", colour, true };
225     data[3] = new Object[] { "Disulphide Bond", colour, true };
226     fr.setFeaturePriority(data);
227
228     features = fr.findFeaturesAtColumn(seq, 15);
229     assertEquals(features.size(), 5); // no sf2
230     assertTrue(features.contains(sf3));
231     assertTrue(features.contains(sf4));
232     assertTrue(features.contains(sf5));
233     assertTrue(features.contains(sf6));
234     assertTrue(features.contains(sf7));
235
236     /*
237      * make "Group2" not displayed
238      */
239     fr.setGroupVisibility("Group2", false);
240
241     features = fr.findFeaturesAtColumn(seq, 15);
242     assertEquals(features.size(), 3); // no sf2, sf3, sf6
243     assertTrue(features.contains(sf4));
244     assertTrue(features.contains(sf5));
245     assertTrue(features.contains(sf7));
246
247     // features 'at' a gap between b and c
248     // - returns enclosing feature BC but not contact feature B/C
249     features = fr.findFeaturesAtColumn(seq, 4);
250     assertEquals(features.size(), 1);
251     assertTrue(features.contains(sf8));
252     features = fr.findFeaturesAtColumn(seq, 5);
253     assertEquals(features.size(), 1);
254     assertTrue(features.contains(sf8));
255   }
256
257   @Test(groups = "Functional")
258   public void testFilterFeaturesForDisplay()
259   {
260     String seqData = ">s1\nabcdef\n";
261     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
262             DataSourceType.PASTE);
263     AlignViewportI av = af.getViewport();
264     FeatureRenderer fr = new FeatureRenderer(av);
265
266     List<SequenceFeature> features = new ArrayList<>();
267     fr.filterFeaturesForDisplay(features, null); // empty list, does nothing
268
269     SequenceI seq = av.getAlignment().getSequenceAt(0);
270     SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, "group1");
271     seq.addSequenceFeature(sf1);
272     SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, "group2");
273     seq.addSequenceFeature(sf2);
274     SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, "group3");
275     seq.addSequenceFeature(sf3);
276     SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, "group4");
277     seq.addSequenceFeature(sf4);
278     SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, "group4");
279     seq.addSequenceFeature(sf5);
280
281     fr.findAllFeatures(true);
282
283     features = seq.getSequenceFeatures();
284     assertEquals(features.size(), 5);
285     assertTrue(features.contains(sf1));
286     assertTrue(features.contains(sf2));
287     assertTrue(features.contains(sf3));
288     assertTrue(features.contains(sf4));
289     assertTrue(features.contains(sf5));
290
291     /*
292      * filter out duplicate (co-located) features
293      * note: which gets removed is not guaranteed
294      */
295     fr.filterFeaturesForDisplay(features, new FeatureColour(Color.blue));
296     assertEquals(features.size(), 3);
297     assertTrue(features.contains(sf1) || features.contains(sf4));
298     assertFalse(features.contains(sf1) && features.contains(sf4));
299     assertTrue(features.contains(sf2) || features.contains(sf3));
300     assertFalse(features.contains(sf2) && features.contains(sf3));
301     assertTrue(features.contains(sf5));
302
303     /*
304      * hide group 3 - sf3 is removed, sf2 is retained
305      */
306     fr.setGroupVisibility("group3", false);
307     features = seq.getSequenceFeatures();
308     fr.filterFeaturesForDisplay(features, new FeatureColour(Color.blue));
309     assertEquals(features.size(), 3);
310     assertTrue(features.contains(sf1) || features.contains(sf4));
311     assertFalse(features.contains(sf1) && features.contains(sf4));
312     assertTrue(features.contains(sf2));
313     assertFalse(features.contains(sf3));
314     assertTrue(features.contains(sf5));
315
316     /*
317      * hide group 2, show group 3 - sf2 is removed, sf3 is retained
318      */
319     fr.setGroupVisibility("group2", false);
320     fr.setGroupVisibility("group3", true);
321     features = seq.getSequenceFeatures();
322     fr.filterFeaturesForDisplay(features, null);
323     assertEquals(features.size(), 3);
324     assertTrue(features.contains(sf1) || features.contains(sf4));
325     assertFalse(features.contains(sf1) && features.contains(sf4));
326     assertFalse(features.contains(sf2));
327     assertTrue(features.contains(sf3));
328     assertTrue(features.contains(sf5));
329
330     /*
331      * no filtering of co-located features with graduated colour scheme
332      * sf2 is removed as its group is hidden
333      */
334     features = seq.getSequenceFeatures();
335     fr.filterFeaturesForDisplay(features, new FeatureColour(Color.black,
336             Color.white, 0f, 1f));
337     assertEquals(features.size(), 4);
338     assertTrue(features.contains(sf1));
339     assertTrue(features.contains(sf3));
340     assertTrue(features.contains(sf4));
341     assertTrue(features.contains(sf5));
342
343     /*
344      * filtering of co-located features with colour by label
345      */
346     features = seq.getSequenceFeatures();
347     FeatureColour fc = new FeatureColour(Color.black);
348     fc.setColourByLabel(true);
349     fr.filterFeaturesForDisplay(features, fc);
350     assertEquals(features.size(), 3);
351     assertTrue(features.contains(sf1) || features.contains(sf4));
352     assertFalse(features.contains(sf1) && features.contains(sf4));
353     assertFalse(features.contains(sf2));
354     assertTrue(features.contains(sf3));
355     assertTrue(features.contains(sf5));
356   }
357 }