1 package jalview.renderer.seqfeatures;
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertTrue;
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;
16 import java.awt.Color;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
22 import org.testng.annotations.Test;
24 public class FeatureRendererTest
27 @Test(groups = "Functional")
28 public void testFindAllFeatures()
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);
39 fr.findAllFeatures(true);
40 assertTrue(fr.getRenderOrder().isEmpty());
41 assertTrue(fr.getFeatureGroups().isEmpty());
43 List<SequenceI> seqs = av.getAlignment().getSequences();
45 // add a non-positional feature - should be ignored by FeatureRenderer
46 SequenceFeature sf1 = new SequenceFeature("Type", "Desc", 0, 0, 1f,
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"));
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,
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
89 * check min-max values
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
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);
110 * check render order (last is on top)
112 List<String> renderOrder = fr.getRenderOrder();
113 assertEquals(renderOrder, Arrays.asList("Scop", "Rfam", "Pfam"));
116 * change render order (todo: an easier way)
117 * nb here last comes first in the data array
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"));
129 * add a new feature type: should go on top of render order as visible,
130 * other feature ordering and visibility should be unchanged
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"));
141 @Test(groups = "Functional")
142 public void testFindFeaturesAtColumn()
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);
154 List<SequenceFeature> features = fr.findFeaturesAtColumn(seq, 3);
155 assertTrue(features.isEmpty());
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,
165 seq.addSequenceFeature(sf2);
166 SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
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);
174 * add contact features
176 SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 7,
178 seq.addSequenceFeature(sf5);
179 SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 7,
181 seq.addSequenceFeature(sf6);
182 SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 7,
184 seq.addSequenceFeature(sf7);
186 // feature spanning B--C
187 SequenceFeature sf8 = new SequenceFeature("Type1", "Desc", 5, 6, 1f,
189 seq.addSequenceFeature(sf8);
190 // contact feature B/C
191 SequenceFeature sf9 = new SequenceFeature("Disulphide Bond", "Desc", 5,
193 seq.addSequenceFeature(sf9);
196 * let feature renderer discover features (and make visible)
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));
209 * at a non-contact position
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));
218 * make "Type2" not displayed
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);
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));
237 * make "Group2" not displayed
239 fr.setGroupVisibility("Group2", false);
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));
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));
257 @Test(groups = "Functional")
258 public void testFilterFeaturesForDisplay()
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);
266 List<SequenceFeature> features = new ArrayList<>();
267 fr.filterFeaturesForDisplay(features, null); // empty list, does nothing
269 SequenceI seq = av.getAlignment().getSequenceAt(0);
270 SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
272 seq.addSequenceFeature(sf1);
273 SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, 2f,
275 seq.addSequenceFeature(sf2);
276 SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, 3f,
278 seq.addSequenceFeature(sf3);
279 SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, 4f,
281 seq.addSequenceFeature(sf4);
282 SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, 5f,
284 seq.addSequenceFeature(sf5);
286 fr.findAllFeatures(true);
288 features = seq.getSequenceFeatures();
289 assertEquals(features.size(), 5);
290 assertTrue(features.contains(sf1));
291 assertTrue(features.contains(sf2));
292 assertTrue(features.contains(sf3));
293 assertTrue(features.contains(sf4));
294 assertTrue(features.contains(sf5));
297 * filter out duplicate (co-located) features
298 * note: which gets removed is not guaranteed
300 fr.filterFeaturesForDisplay(features, new FeatureColour(Color.blue));
301 assertEquals(features.size(), 3);
302 assertTrue(features.contains(sf1) || features.contains(sf4));
303 assertFalse(features.contains(sf1) && features.contains(sf4));
304 assertTrue(features.contains(sf2) || features.contains(sf3));
305 assertFalse(features.contains(sf2) && features.contains(sf3));
306 assertTrue(features.contains(sf5));
309 * hide group 3 - sf3 is removed, sf2 is retained
311 fr.setGroupVisibility("group3", false);
312 features = seq.getSequenceFeatures();
313 fr.filterFeaturesForDisplay(features, new FeatureColour(Color.blue));
314 assertEquals(features.size(), 3);
315 assertTrue(features.contains(sf1) || features.contains(sf4));
316 assertFalse(features.contains(sf1) && features.contains(sf4));
317 assertTrue(features.contains(sf2));
318 assertFalse(features.contains(sf3));
319 assertTrue(features.contains(sf5));
322 * hide group 2, show group 3 - sf2 is removed, sf3 is retained
324 fr.setGroupVisibility("group2", false);
325 fr.setGroupVisibility("group3", true);
326 features = seq.getSequenceFeatures();
327 fr.filterFeaturesForDisplay(features, null);
328 assertEquals(features.size(), 3);
329 assertTrue(features.contains(sf1) || features.contains(sf4));
330 assertFalse(features.contains(sf1) && features.contains(sf4));
331 assertFalse(features.contains(sf2));
332 assertTrue(features.contains(sf3));
333 assertTrue(features.contains(sf5));
336 * no filtering of co-located features with graduated colour scheme
337 * filterFeaturesForDisplay does _not_ check colour threshold
338 * sf2 is removed as its group is hidden
340 features = seq.getSequenceFeatures();
341 fr.filterFeaturesForDisplay(features, new FeatureColour(Color.black,
342 Color.white, 0f, 1f));
343 assertEquals(features.size(), 4);
344 assertTrue(features.contains(sf1));
345 assertTrue(features.contains(sf3));
346 assertTrue(features.contains(sf4));
347 assertTrue(features.contains(sf5));
350 * co-located features with colour by label
351 * should not get filtered
353 features = seq.getSequenceFeatures();
354 FeatureColour fc = new FeatureColour(Color.black);
355 fc.setColourByLabel(true);
356 fr.filterFeaturesForDisplay(features, fc);
357 assertEquals(features.size(), 4);
358 assertTrue(features.contains(sf1));
359 assertTrue(features.contains(sf3));
360 assertTrue(features.contains(sf4));
361 assertTrue(features.contains(sf5));